Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Z
Zope
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
Zope
Commits
51eab4c4
Commit
51eab4c4
authored
Jun 21, 1999
by
Jim Fulton
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Adding to distribution in support of xml-rpc integration.
parent
d8d5c911
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
542 additions
and
0 deletions
+542
-0
lib/python/xmlrpclib.py
lib/python/xmlrpclib.py
+542
-0
No files found.
lib/python/xmlrpclib.py
0 → 100755
View file @
51eab4c4
#
# XML-RPC CLIENT LIBRARY
# $Id: xmlrpclib.py,v 1.1.1.1 1999/06/11 15:04:49 emk Exp $
#
# an XML-RPC client interface for Python
#
# the marshalling and response parser code can also be used to
# implement XML-RPC servers
#
# History:
# 1999-01-14 fl Created
# 1999-01-15 fl Changed dateTime to use localtime
# 1999-01-16 fl Added Binary/base64 element, default to RPC2 service
# 1999-01-19 fl Fixed array data element (from Skip Montanaro)
# 1999-01-21 fl Fixed dateTime constructor, etc.
# 1999-02-02 fl Added fault handling, handle empty sequences, etc.
#
# written by Fredrik Lundh, January 1999.
#
# Copyright (c) 1999 by Secret Labs AB.
# Copyright (c) 1999 by Fredrik Lundh.
#
# fredrik@pythonware.com
# http://www.pythonware.com
#
# --------------------------------------------------------------------
# The XML-RPC client interface is
#
# Copyright (c) 1999 by Secret Labs AB
# Copyright (c) 1999 by Fredrik Lundh
#
# By obtaining, using, and/or copying this software and/or its
# associated documentation, you agree that you have read, understood,
# and will comply with the following terms and conditions:
#
# Permission to use, copy, modify, and distribute this software and its
# associated documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appears in all
# copies, and that both that copyright notice and this permission notice
# appear in supporting documentation, and that the name of Secret Labs
# AB or the author not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO
# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# --------------------------------------------------------------------
import
operator
,
string
,
time
import
httplib
,
urllib
import
xmllib
from
types
import
*
from
cgi
import
escape
__version__
=
"0.9.5"
USER_AGENT
=
"xmlrpclib.py/%s (by www.pythonware.com)"
%
__version__
# --------------------------------------------------------------------
# Exceptions
class
Error
:
# base class for client errors
pass
class
ProtocolError
(
Error
):
# indicates an HTTP protocol error
def
__init__
(
self
,
url
,
errcode
,
errmsg
,
headers
):
self
.
url
=
url
self
.
errcode
=
errcode
self
.
errmsg
=
errmsg
self
.
headers
=
headers
def
__repr__
(
self
):
return
(
"<ProtocolError for %s: %s %s>"
%
(
self
.
url
,
self
.
errcode
,
self
.
errmsg
)
)
class
ResponseError
(
Error
):
# indicates a broken response chunk
pass
class
Fault
(
Error
):
# indicates a XML-RPC fault package
def
__init__
(
self
,
faultCode
,
faultString
,
**
extra
):
self
.
faultCode
=
faultCode
self
.
faultString
=
faultString
def
__repr__
(
self
):
return
(
"<Fault %s: %s>"
%
(
self
.
faultCode
,
repr
(
self
.
faultString
))
)
# --------------------------------------------------------------------
# Special values
# boolean wrapper
# (you must use True or False to generate a "boolean" XML-RPC value)
class
Boolean
:
def
__init__
(
self
,
value
=
0
):
self
.
value
=
(
value
!=
0
)
def
encode
(
self
,
out
):
out
.
write
(
"<value><boolean>%d</boolean></value>
\
n
"
%
self
.
value
)
def
__repr__
(
self
):
if
self
.
value
:
return
"<Boolean True at %x>"
%
id
(
self
)
else
:
return
"<Boolean False at %x>"
%
id
(
self
)
def
__int__
(
self
):
return
self
.
value
def
__nonzero__
(
self
):
return
self
.
value
True
,
False
=
Boolean
(
1
),
Boolean
(
0
)
#
# dateTime wrapper
# (wrap your iso8601 string or localtime tuple or time value in this
# class to generate a "dateTime.iso8601" XML-RPC value)
class
DateTime
:
def
__init__
(
self
,
value
):
t
=
type
(
value
)
if
t
is
not
StringType
:
if
t
is
not
TupleType
:
value
=
time
.
localtime
(
value
)
value
=
time
.
strftime
(
"%Y%m%dT%H:%M:%S"
,
value
)
self
.
value
=
value
def
__repr__
(
self
):
return
"<DateTime %s at %x>"
%
(
self
.
value
,
id
(
self
))
def
decode
(
self
,
data
):
self
.
value
=
string
.
strip
(
data
)
def
encode
(
self
,
out
):
out
.
write
(
"<value><dateTime.iso8601>"
)
out
.
write
(
self
.
value
)
out
.
write
(
"</dateTime.iso8601></value>
\
n
"
)
#
# binary data wrapper (NOTE: this is an extension to Userland's
# XML-RPC protocol! only for use with compatible servers!)
class
Binary
:
def
__init__
(
self
,
data
=
None
):
self
.
data
=
data
def
decode
(
self
,
data
):
import
base64
self
.
data
=
base64
.
decodestring
(
data
)
def
encode
(
self
,
out
):
import
base64
,
StringIO
out
.
write
(
"<value><base64>
\
n
"
)
base64
.
encode
(
StringIO
.
StringIO
(
self
.
data
),
out
)
out
.
write
(
"</base64></value>
\
n
"
)
WRAPPERS
=
DateTime
,
Binary
,
Boolean
# --------------------------------------------------------------------
# XML-RPC response parser
class
ResponseParser
(
xmllib
.
XMLParser
):
"""Parse an XML-RPC response into a Python data structure"""
# USAGE: create a parser instance, and call "feed" to add data to
# it (in chunks or as a single string). call "close" to flush the
# internal buffers and return the resulting data structure.
# note that this reader is fairly tolerant, and gladly accepts
# bogus XML-RPC data without complaining (but not bogus XML).
# this could of course be simplified by using an XML tree builder
# (DOM, coreXML, or whatever), but this version works with any
# standard installation of 1.5 or later (except 1.5.2b1, but
# we're working on that)
# by the way, if you don't understand what's going on in here,
# that's perfectly ok.
def
__init__
(
self
):
self
.
__type
=
None
self
.
__stack
=
[]
self
.
__marks
=
[]
self
.
__data
=
[]
self
.
__methodname
=
None
xmllib
.
XMLParser
.
__init__
(
self
)
def
close
(
self
):
xmllib
.
XMLParser
.
close
(
self
)
# return response code and the actual response
if
self
.
__type
is
None
or
self
.
__marks
:
raise
ResponseError
()
if
self
.
__type
==
"fault"
:
raise
apply
(
Fault
,
(),
self
.
__stack
[
0
])
return
tuple
(
self
.
__stack
)
def
getmethodname
(
self
):
return
self
.
__methodname
#
# container types (containers can be nested, so we use a separate
# mark stack to keep track of the beginning of each container).
def
start_array
(
self
,
attrs
):
self
.
__marks
.
append
(
len
(
self
.
__stack
))
start_struct
=
start_array
def
unknown_starttag
(
self
,
tag
,
attrs
):
self
.
__data
=
[]
self
.
__value
=
(
tag
==
"value"
)
def
handle_data
(
self
,
text
):
self
.
__data
.
append
(
text
)
def
unknown_endtag
(
self
,
tag
,
join
=
string
.
join
):
# the standard dispatcher cannot handle tags with uncommon
# characters in them, so we have to do this ourselves.
if
tag
==
"dateTime.iso8601"
:
value
=
DateTime
()
value
.
decode
(
join
(
self
.
__data
,
""
))
self
.
__stack
.
append
(
value
)
#
# add values to the stack on end tags
def
end_boolean
(
self
,
join
=
string
.
join
):
value
=
join
(
self
.
__data
,
""
)
if
value
==
"0"
:
self
.
__stack
.
append
(
False
)
elif
value
==
"1"
:
self
.
__stack
.
append
(
True
)
else
:
raise
TypeError
,
"bad boolean value"
def
end_int
(
self
,
join
=
string
.
join
):
self
.
__stack
.
append
(
int
(
join
(
self
.
__data
,
""
)))
def
end_double
(
self
,
join
=
string
.
join
):
self
.
__stack
.
append
(
float
(
join
(
self
.
__data
,
""
)))
def
end_string
(
self
,
join
=
string
.
join
):
self
.
__stack
.
append
(
join
(
self
.
__data
,
""
))
# aliases
end_i4
=
end_int
end_name
=
end_string
# struct keys are always strings
def
end_array
(
self
):
mark
=
self
.
__marks
[
-
1
]
del
self
.
__marks
[
-
1
]
# map arrays to Python lists
self
.
__stack
[
mark
:]
=
[
self
.
__stack
[
mark
:]]
def
end_struct
(
self
):
mark
=
self
.
__marks
[
-
1
]
del
self
.
__marks
[
-
1
]
# map structs to Python dictionaries
dict
=
{}
items
=
self
.
__stack
[
mark
:]
for
i
in
range
(
0
,
len
(
items
),
2
):
dict
[
items
[
i
]]
=
items
[
i
+
1
]
self
.
__stack
[
mark
:]
=
[
dict
]
def
end_base64
(
self
,
join
=
string
.
join
):
value
=
Binary
()
value
.
decode
(
join
(
self
.
__data
,
""
))
self
.
__stack
.
append
(
value
)
def
end_value
(
self
):
# if we stumble upon an value element with no
# no internal elements, treat it as a string
# element
if
self
.
__value
:
self
.
end_string
()
def
end_params
(
self
):
self
.
__type
=
"params"
def
end_fault
(
self
):
self
.
__type
=
"fault"
def
end_methodName
(
self
,
join
=
string
.
join
):
self
.
__methodname
=
join
(
self
.
__data
,
""
)
# --------------------------------------------------------------------
# XML-RPC marshaller
class
Marshaller
:
"""Generate an XML-RPC params chunk from a Python data structure"""
# USAGE: create a marshaller instance for each set of parameters,
# and use "dumps" to convert your data (represented as a tuple) to
# a XML-RPC params chunk. to write a fault response, pass a Fault
# instance instead.
# again, this could of course be simplified by using an XML writer
# (coreXML or whatever), but this version works with any standard
# installation of 1.5 or later (except 1.5.2b1, but we're working
# on that)
# and again, if you don't understand what's going on in here,
# that's perfectly ok.
def
__init__
(
self
):
self
.
memo
=
{}
self
.
data
=
None
dispatch
=
{}
def
dumps
(
self
,
values
):
if
isinstance
(
values
,
Fault
):
# fault instance
self
.
__out
=
[
"<fault>
\
n
"
]
self
.
__dump
(
vars
(
values
))
self
.
write
(
"</fault>
\
n
"
)
else
:
# parameter block
self
.
__out
=
[
"<params>
\
n
"
]
for
v
in
values
:
self
.
write
(
"<param>
\
n
"
)
self
.
__dump
(
v
)
self
.
write
(
"</param>
\
n
"
)
self
.
write
(
"</params>
\
n
"
)
return
string
.
join
(
self
.
__out
,
""
)
def
write
(
self
,
string
):
self
.
__out
.
append
(
string
)
def
__dump
(
self
,
value
):
try
:
f
=
self
.
dispatch
[
type
(
value
)]
except
KeyError
:
raise
TypeError
,
"cannot marshal %s objects"
%
type
(
value
)
else
:
f
(
self
,
value
)
def
dump_int
(
self
,
value
):
self
.
write
(
"<value><int>%s</int></value>
\
n
"
%
value
)
dispatch
[
IntType
]
=
dump_int
def
dump_double
(
self
,
value
):
self
.
write
(
"<value><double>%s</double></value>
\
n
"
%
value
)
dispatch
[
FloatType
]
=
dump_double
def
dump_string
(
self
,
value
):
self
.
write
(
"<value><string>%s</string></value>
\
n
"
%
escape
(
value
))
dispatch
[
StringType
]
=
dump_string
def
container
(
self
,
value
):
if
value
:
i
=
id
(
value
)
if
self
.
memo
.
has_key
(
i
):
raise
TypeError
,
"cannot marshal recursive data structures"
self
.
memo
[
i
]
=
None
def
dump_array
(
self
,
value
):
self
.
container
(
value
)
self
.
write
(
"<value><array><data>
\
n
"
)
for
v
in
value
:
self
.
__dump
(
v
)
self
.
write
(
"</data></array></value>
\
n
"
)
dispatch
[
TupleType
]
=
dump_array
dispatch
[
ListType
]
=
dump_array
def
dump_struct
(
self
,
value
):
self
.
container
(
value
)
write
=
self
.
write
write
(
"<value><struct>
\
n
"
)
for
k
,
v
in
value
.
items
():
write
(
"<member>
\
n
"
)
if
type
(
k
)
is
not
StringType
:
raise
TypeError
,
"dictionary key must be string"
write
(
"<name>%s</name>
\
n
"
%
escape
(
k
))
self
.
__dump
(
v
)
write
(
"</member>
\
n
"
)
write
(
"</struct></value>
\
n
"
)
dispatch
[
DictType
]
=
dump_struct
def
dump_instance
(
self
,
value
):
# check for special wrappers
write
=
self
.
write
if
value
.
__class__
in
WRAPPERS
:
value
.
encode
(
self
)
else
:
# store instance attributes as a struct (?)
self
.
dump_struct
(
value
.
__dict__
)
dispatch
[
InstanceType
]
=
dump_instance
# --------------------------------------------------------------------
# convenience functions
def
dumps
(
params
,
methodname
=
None
,
methodresponse
=
None
):
assert
type
(
params
)
==
TupleType
or
isinstance
(
params
,
Fault
),
\
"argument must be tuple or Fault instance"
m
=
Marshaller
()
data
=
m
.
dumps
(
params
)
# standard XML-RPC wrappings
if
methodname
:
# a method call
data
=
(
"<?xml version='1.0'?>
\
n
"
"<methodCall>
\
n
"
"<methodName>%s</methodName>
\
n
"
"%s
\
n
"
"</methodCall>
\
n
"
%
(
methodname
,
data
)
)
elif
methodresponse
or
isinstance
(
params
,
Fault
):
# a method response
data
=
(
"<?xml version='1.0'?>
\
n
"
"<methodResponse>
\
n
"
"%s
\
n
"
"</methodResponse>
\
n
"
%
data
)
return
data
def
loads
(
data
):
# returns data plus methodname (None if not present)
p
=
ResponseParser
()
p
.
feed
(
data
)
return
p
.
close
(),
p
.
getmethodname
()
# --------------------------------------------------------------------
# request dispatcher
class
Method
:
# some magic to bind an XML-RPC method to an RPC server.
# supports "nested" methods (e.g. examples.getStateName)
def
__init__
(
self
,
send
,
name
):
self
.
__send
=
send
self
.
__name
=
name
def
__getattr__
(
self
,
name
):
return
Method
(
self
.
__send
,
self
.
__name
+
"."
+
name
)
def
__call__
(
self
,
*
args
):
return
self
.
__send
(
self
.
__name
,
args
)
class
Server
:
"""Represents a connection XML-RPC server"""
def
__init__
(
self
,
uri
):
# establish a "logical" server connection
type
,
uri
=
urllib
.
splittype
(
uri
)
if
type
!=
"http"
:
raise
IOError
,
"unsupported XML-RPC protocol"
self
.
__host
,
self
.
__handler
=
urllib
.
splithost
(
uri
)
if
not
self
.
__handler
:
self
.
__handler
=
"/RPC2"
def
__request
(
self
,
methodname
,
params
):
# call a method on the remote server
request
=
dumps
(
params
,
methodname
)
# send the request
h
=
httplib
.
HTTP
(
self
.
__host
)
h
.
putrequest
(
"POST"
,
self
.
__handler
)
h
.
putheader
(
"User-Agent"
,
USER_AGENT
)
h
.
putheader
(
"Host"
,
self
.
__host
)
h
.
putheader
(
"Content-Type"
,
"text/xml"
)
h
.
putheader
(
"Content-Length"
,
str
(
len
(
request
)))
h
.
endheaders
()
if
request
:
h
.
send
(
request
)
errcode
,
errmsg
,
headers
=
h
.
getreply
()
if
errcode
!=
200
:
raise
ProtocolError
(
self
.
__host
+
self
.
__handler
,
errcode
,
errmsg
,
headers
)
# parse the response
fp
=
h
.
getfile
()
p
=
ResponseParser
()
while
1
:
response
=
fp
.
read
(
1024
)
if
not
response
:
break
p
.
feed
(
response
)
response
=
p
.
close
()
if
len
(
response
)
==
1
:
return
response
[
0
]
return
response
def
__repr__
(
self
):
return
(
"<Server proxy for %s%s>"
%
(
self
.
__host
,
self
.
__handler
)
)
__str__
=
__repr__
def
__getattr__
(
self
,
name
):
# method dispatcher
return
Method
(
self
.
__request
,
name
)
if
__name__
==
"__main__"
:
# simple test program (from the specification)
# server = Server("http://localhost:8000") # local server
server
=
Server
(
"http://nirvana.userland.com"
)
print
server
try
:
print
server
.
examples
.
getStateName
(
41
)
except
Error
,
v
:
print
"ERROR"
,
v
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment