mirror of
https://github.com/python/cpython.git
synced 2025-10-17 12:18:23 +00:00
merged with SLAB codebase (version 1.0.1)
This commit is contained in:
parent
4fb7027ec0
commit
3d9addd55a
1 changed files with 344 additions and 78 deletions
422
Lib/xmlrpclib.py
422
Lib/xmlrpclib.py
|
@ -33,12 +33,19 @@
|
||||||
# 2001-09-10 fl Lazy import of urllib, cgi, xmllib (20x import speedup)
|
# 2001-09-10 fl Lazy import of urllib, cgi, xmllib (20x import speedup)
|
||||||
# 2001-10-01 fl Remove containers from memo cache when done with them
|
# 2001-10-01 fl Remove containers from memo cache when done with them
|
||||||
# 2001-10-01 fl Use faster escape method (80% dumps speedup)
|
# 2001-10-01 fl Use faster escape method (80% dumps speedup)
|
||||||
|
# 2001-10-02 fl More dumps microtuning
|
||||||
|
# 2001-10-04 fl Make sure import expat gets a parser (from Guido van Rossum)
|
||||||
# 2001-10-10 sm Allow long ints to be passed as ints if they don't overflow
|
# 2001-10-10 sm Allow long ints to be passed as ints if they don't overflow
|
||||||
# 2001-10-17 sm test for int and long overflow (allows use on 64-bit systems)
|
# 2001-10-17 sm Test for int and long overflow (allows use on 64-bit systems)
|
||||||
# 2001-11-12 fl Use repr() to marshal doubles (from Paul Felix)
|
# 2001-11-12 fl Use repr() to marshal doubles (from Paul Felix)
|
||||||
|
# 2002-03-17 fl Avoid buffered read when possible (from James Rucker)
|
||||||
|
# 2002-04-07 fl Added pythondoc comments
|
||||||
|
# 2002-04-16 fl Added __str__ methods to datetime/binary wrappers
|
||||||
|
# 2002-05-15 fl Added error constants (from Andrew Kuchling)
|
||||||
|
# 2002-06-27 fl Merged with Python CVS version
|
||||||
#
|
#
|
||||||
# Copyright (c) 1999-2001 by Secret Labs AB.
|
# Copyright (c) 1999-2002 by Secret Labs AB.
|
||||||
# Copyright (c) 1999-2001 by Fredrik Lundh.
|
# Copyright (c) 1999-2002 by Fredrik Lundh.
|
||||||
#
|
#
|
||||||
# info@pythonware.com
|
# info@pythonware.com
|
||||||
# http://www.pythonware.com
|
# http://www.pythonware.com
|
||||||
|
@ -46,8 +53,8 @@
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# The XML-RPC client interface is
|
# The XML-RPC client interface is
|
||||||
#
|
#
|
||||||
# Copyright (c) 1999-2001 by Secret Labs AB
|
# Copyright (c) 1999-2002 by Secret Labs AB
|
||||||
# Copyright (c) 1999-2001 by Fredrik Lundh
|
# Copyright (c) 1999-2002 by Fredrik Lundh
|
||||||
#
|
#
|
||||||
# By obtaining, using, and/or copying this software and/or its
|
# By obtaining, using, and/or copying this software and/or its
|
||||||
# associated documentation, you agree that you have read, understood,
|
# associated documentation, you agree that you have read, understood,
|
||||||
|
@ -73,13 +80,9 @@
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
#
|
#
|
||||||
# things to look into:
|
# things to look into some day:
|
||||||
|
|
||||||
# TODO: support basic authentication (see robin's patch)
|
# TODO: sort out True/False/boolean issues for Python 2.3
|
||||||
# TODO: fix host tuple handling in the server constructor
|
|
||||||
# TODO: let transport verify schemes
|
|
||||||
# TODO: update documentation
|
|
||||||
# TODO: authentication plugins
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
An XML-RPC client interface for Python.
|
An XML-RPC client interface for Python.
|
||||||
|
@ -130,6 +133,9 @@ import re, string, time, operator
|
||||||
|
|
||||||
from types import *
|
from types import *
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------
|
||||||
|
# Internal stuff
|
||||||
|
|
||||||
try:
|
try:
|
||||||
unicode
|
unicode
|
||||||
except NameError:
|
except NameError:
|
||||||
|
@ -146,9 +152,6 @@ def escape(s, replace=string.replace):
|
||||||
s = replace(s, "<", "<")
|
s = replace(s, "<", "<")
|
||||||
return replace(s, ">", ">",)
|
return replace(s, ">", ">",)
|
||||||
|
|
||||||
MAXINT = 2L**31-1
|
|
||||||
MININT = -2L**31
|
|
||||||
|
|
||||||
if unicode:
|
if unicode:
|
||||||
def _stringify(string):
|
def _stringify(string):
|
||||||
# convert to 7-bit ascii if possible
|
# convert to 7-bit ascii if possible
|
||||||
|
@ -160,16 +163,53 @@ else:
|
||||||
def _stringify(string):
|
def _stringify(string):
|
||||||
return string
|
return string
|
||||||
|
|
||||||
__version__ = "1.0.0"
|
__version__ = "1.0.1"
|
||||||
|
|
||||||
|
# xmlrpc integer limits
|
||||||
|
MAXINT = 2L**31-1
|
||||||
|
MININT = -2L**31
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------
|
||||||
|
# Error constants (from Dan Libby's specification at
|
||||||
|
# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
|
||||||
|
|
||||||
|
# Ranges of errors
|
||||||
|
PARSE_ERROR = -32700
|
||||||
|
SERVER_ERROR = -32600
|
||||||
|
APPLICATION_ERROR = -32500
|
||||||
|
SYSTEM_ERROR = -32400
|
||||||
|
TRANSPORT_ERROR = -32300
|
||||||
|
|
||||||
|
# Specific errors
|
||||||
|
NOT_WELLFORMED_ERROR = -32700
|
||||||
|
UNSUPPORTED_ENCODING = -32701
|
||||||
|
INVALID_ENCODING_CHAR = -32702
|
||||||
|
INVALID_XMLRPC = -32600
|
||||||
|
METHOD_NOT_FOUND = -32601
|
||||||
|
INVALID_METHOD_PARAMS = -32602
|
||||||
|
INTERNAL_ERROR = -32603
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Exceptions
|
# Exceptions
|
||||||
|
|
||||||
|
##
|
||||||
|
# Base class for all kinds of client-side errors.
|
||||||
|
|
||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
"""Base class for client errors."""
|
"""Base class for client errors."""
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return repr(self)
|
return repr(self)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Indicates an HTTP-level protocol error. This is raised by the HTTP
|
||||||
|
# transport layer, if the server returns an error code other than 200
|
||||||
|
# (OK).
|
||||||
|
#
|
||||||
|
# @param url The target URL.
|
||||||
|
# @param errcode The HTTP error code.
|
||||||
|
# @param errmsg The HTTP error message.
|
||||||
|
# @param headers The HTTP header dictionary.
|
||||||
|
|
||||||
class ProtocolError(Error):
|
class ProtocolError(Error):
|
||||||
"""Indicates an HTTP protocol error."""
|
"""Indicates an HTTP protocol error."""
|
||||||
def __init__(self, url, errcode, errmsg, headers):
|
def __init__(self, url, errcode, errmsg, headers):
|
||||||
|
@ -184,10 +224,24 @@ class ProtocolError(Error):
|
||||||
(self.url, self.errcode, self.errmsg)
|
(self.url, self.errcode, self.errmsg)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Indicates a broken XML-RPC response package. This exception is
|
||||||
|
# raised by the unmarshalling layer, if the XML-RPC response is
|
||||||
|
# malformed.
|
||||||
|
|
||||||
class ResponseError(Error):
|
class ResponseError(Error):
|
||||||
"""Indicates a broken response package."""
|
"""Indicates a broken response package."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
##
|
||||||
|
# Indicates an XML-RPC fault response package. This exception is
|
||||||
|
# raised by the unmarshalling layer, if the XML-RPC response contains
|
||||||
|
# a fault string. This exception can also used as a class, to
|
||||||
|
# generate a fault XML-RPC message.
|
||||||
|
#
|
||||||
|
# @param faultCode The XML-RPC fault code.
|
||||||
|
# @param faultString The XML-RPC fault string.
|
||||||
|
|
||||||
class Fault(Error):
|
class Fault(Error):
|
||||||
"""Indicates an XML-RPC fault package."""
|
"""Indicates an XML-RPC fault package."""
|
||||||
def __init__(self, faultCode, faultString, **extra):
|
def __init__(self, faultCode, faultString, **extra):
|
||||||
|
@ -203,6 +257,14 @@ class Fault(Error):
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Special values
|
# Special values
|
||||||
|
|
||||||
|
##
|
||||||
|
# Wrapper for XML-RPC boolean values. Use the xmlrpclib.True and
|
||||||
|
# xmlrpclib.False constants, or the xmlrpclib.boolean() function, to
|
||||||
|
# generate boolean XML-RPC values.
|
||||||
|
#
|
||||||
|
# @param value A boolean value. Any true value is interpreted as True,
|
||||||
|
# all other values are interpreted as False.
|
||||||
|
|
||||||
class Boolean:
|
class Boolean:
|
||||||
"""Boolean-value wrapper.
|
"""Boolean-value wrapper.
|
||||||
|
|
||||||
|
@ -234,9 +296,33 @@ class Boolean:
|
||||||
|
|
||||||
True, False = Boolean(1), Boolean(0)
|
True, False = Boolean(1), Boolean(0)
|
||||||
|
|
||||||
def boolean(value, truefalse=(False, True)):
|
##
|
||||||
|
# Map true or false value to XML-RPC boolean values.
|
||||||
|
#
|
||||||
|
# @def boolean(value)
|
||||||
|
# @param value A boolean value. Any true value is mapped to True,
|
||||||
|
# all other values are mapped to False.
|
||||||
|
# @return xmlrpclib.True or xmlrpclib.False.
|
||||||
|
# @see Boolean
|
||||||
|
# @see True
|
||||||
|
# @see False
|
||||||
|
|
||||||
|
def boolean(value, _truefalse=(False, True)):
|
||||||
"""Convert any Python value to XML-RPC 'boolean'."""
|
"""Convert any Python value to XML-RPC 'boolean'."""
|
||||||
return truefalse[operator.truth(value)]
|
return _truefalse[operator.truth(value)]
|
||||||
|
|
||||||
|
##
|
||||||
|
# Wrapper for XML-RPC DateTime values. This converts a time value to
|
||||||
|
# the format used by XML-RPC.
|
||||||
|
# <p>
|
||||||
|
# The value can be given as a string in the format
|
||||||
|
# "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
|
||||||
|
# time.localtime()), or an integer value (as returned by time.time()).
|
||||||
|
# The wrapper uses time.localtime() to convert an integer to a time
|
||||||
|
# tuple.
|
||||||
|
#
|
||||||
|
# @param value The time, given as an ISO 8601 string, a time
|
||||||
|
# tuple, or a integer time value.
|
||||||
|
|
||||||
class DateTime:
|
class DateTime:
|
||||||
"""DateTime wrapper for an ISO 8601 string or time tuple or
|
"""DateTime wrapper for an ISO 8601 string or time tuple or
|
||||||
|
@ -258,8 +344,16 @@ class DateTime:
|
||||||
other = other.value
|
other = other.value
|
||||||
return cmp(self.value, other)
|
return cmp(self.value, other)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Get date/time value.
|
||||||
|
#
|
||||||
|
# @return Date/time value, as an ISO 8601 string.
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.value
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<DateTime %s at %x>" % (self.value, id(self))
|
return "<DateTime %s at %x>" % (repr(self.value), id(self))
|
||||||
|
|
||||||
def decode(self, data):
|
def decode(self, data):
|
||||||
self.value = string.strip(data)
|
self.value = string.strip(data)
|
||||||
|
@ -269,17 +363,32 @@ class DateTime:
|
||||||
out.write(self.value)
|
out.write(self.value)
|
||||||
out.write("</dateTime.iso8601></value>\n")
|
out.write("</dateTime.iso8601></value>\n")
|
||||||
|
|
||||||
def datetime(data):
|
def _datetime(data):
|
||||||
|
# decode xml element contents into a DateTime structure.
|
||||||
value = DateTime()
|
value = DateTime()
|
||||||
value.decode(data)
|
value.decode(data)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
##
|
||||||
|
# Wrapper for binary data. This can be used to transport any kind
|
||||||
|
# of binary data over XML-RPC, using BASE64 encoding.
|
||||||
|
#
|
||||||
|
# @param data An 8-bit string containing arbitrary data.
|
||||||
|
|
||||||
class Binary:
|
class Binary:
|
||||||
"""Wrapper for binary data."""
|
"""Wrapper for binary data."""
|
||||||
|
|
||||||
def __init__(self, data=None):
|
def __init__(self, data=None):
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
|
##
|
||||||
|
# Get buffer contents.
|
||||||
|
#
|
||||||
|
# @return Buffer contents, as an 8-bit string.
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.data or ""
|
||||||
|
|
||||||
def __cmp__(self, other):
|
def __cmp__(self, other):
|
||||||
if isinstance(other, Binary):
|
if isinstance(other, Binary):
|
||||||
other = other.data
|
other = other.data
|
||||||
|
@ -295,7 +404,8 @@ class Binary:
|
||||||
base64.encode(StringIO.StringIO(self.data), out)
|
base64.encode(StringIO.StringIO(self.data), out)
|
||||||
out.write("</base64></value>\n")
|
out.write("</base64></value>\n")
|
||||||
|
|
||||||
def binary(data):
|
def _binary(data):
|
||||||
|
# decode xml element contents into a Binary structure
|
||||||
value = Binary()
|
value = Binary()
|
||||||
value.decode(data)
|
value.decode(data)
|
||||||
return value
|
return value
|
||||||
|
@ -314,6 +424,12 @@ try:
|
||||||
except (AttributeError, ImportError):
|
except (AttributeError, ImportError):
|
||||||
FastParser = FastUnmarshaller = None
|
FastParser = FastUnmarshaller = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
import _xmlrpclib
|
||||||
|
FastMarshaller = _xmlrpclib.Marshaller
|
||||||
|
except (AttributeError, ImportError):
|
||||||
|
FastMarshaller = None
|
||||||
|
|
||||||
#
|
#
|
||||||
# the SGMLOP parser is about 15x faster than Python's builtin
|
# the SGMLOP parser is about 15x faster than Python's builtin
|
||||||
# XML parser. SGMLOP sources can be downloaded from:
|
# XML parser. SGMLOP sources can be downloaded from:
|
||||||
|
@ -367,13 +483,13 @@ else:
|
||||||
try:
|
try:
|
||||||
from xml.parsers import expat
|
from xml.parsers import expat
|
||||||
if not hasattr(expat, "ParserCreate"):
|
if not hasattr(expat, "ParserCreate"):
|
||||||
raise ImportError, "ParserCreate"
|
raise ImportError
|
||||||
except ImportError:
|
except ImportError:
|
||||||
ExpatParser = None
|
ExpatParser = None # expat not available
|
||||||
else:
|
else:
|
||||||
class ExpatParser:
|
class ExpatParser:
|
||||||
# fast expat parser for Python 2.0. this is about 50%
|
# fast expat parser for Python 2.0 and later. this is about
|
||||||
# slower than sgmlop, on roundtrip testing
|
# 50% slower than sgmlop, on roundtrip testing
|
||||||
def __init__(self, target):
|
def __init__(self, target):
|
||||||
self._parser = parser = expat.ParserCreate(None, None)
|
self._parser = parser = expat.ParserCreate(None, None)
|
||||||
self._target = target
|
self._target = target
|
||||||
|
@ -403,6 +519,7 @@ class SlowParser:
|
||||||
self.handle_xml = target.xml
|
self.handle_xml = target.xml
|
||||||
self.unknown_starttag = target.start
|
self.unknown_starttag = target.start
|
||||||
self.handle_data = target.data
|
self.handle_data = target.data
|
||||||
|
self.handle_cdata = target.data
|
||||||
self.unknown_endtag = target.end
|
self.unknown_endtag = target.end
|
||||||
try:
|
try:
|
||||||
xmllib.XMLParser.__init__(self, accept_utf8=1)
|
xmllib.XMLParser.__init__(self, accept_utf8=1)
|
||||||
|
@ -412,6 +529,13 @@ class SlowParser:
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# XML-RPC marshalling and unmarshalling code
|
# XML-RPC marshalling and unmarshalling code
|
||||||
|
|
||||||
|
##
|
||||||
|
# XML-RPC marshaller.
|
||||||
|
#
|
||||||
|
# @param encoding Default encoding for 8-bit strings. The default
|
||||||
|
# value is None (interpreted as UTF-8).
|
||||||
|
# @see dumps
|
||||||
|
|
||||||
class Marshaller:
|
class Marshaller:
|
||||||
"""Generate an XML-RPC params chunk from a Python data structure.
|
"""Generate an XML-RPC params chunk from a Python data structure.
|
||||||
|
|
||||||
|
@ -433,12 +557,13 @@ class Marshaller:
|
||||||
dispatch = {}
|
dispatch = {}
|
||||||
|
|
||||||
def dumps(self, values):
|
def dumps(self, values):
|
||||||
self.__out = []
|
out = []
|
||||||
self.write = write = self.__out.append
|
write = out.append
|
||||||
|
dump = self.__dump
|
||||||
if isinstance(values, Fault):
|
if isinstance(values, Fault):
|
||||||
# fault instance
|
# fault instance
|
||||||
write("<fault>\n")
|
write("<fault>\n")
|
||||||
self.__dump(vars(values))
|
dump(vars(values), write)
|
||||||
write("</fault>\n")
|
write("</fault>\n")
|
||||||
else:
|
else:
|
||||||
# parameter block
|
# parameter block
|
||||||
|
@ -450,97 +575,105 @@ class Marshaller:
|
||||||
write("<params>\n")
|
write("<params>\n")
|
||||||
for v in values:
|
for v in values:
|
||||||
write("<param>\n")
|
write("<param>\n")
|
||||||
self.__dump(v)
|
dump(v, write)
|
||||||
write("</param>\n")
|
write("</param>\n")
|
||||||
write("</params>\n")
|
write("</params>\n")
|
||||||
result = string.join(self.__out, "")
|
result = string.join(out, "")
|
||||||
del self.__out, self.write # don't need this any more
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def __dump(self, value):
|
def __dump(self, value, write):
|
||||||
try:
|
try:
|
||||||
f = self.dispatch[type(value)]
|
f = self.dispatch[type(value)]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise TypeError, "cannot marshal %s objects" % type(value)
|
raise TypeError, "cannot marshal %s objects" % type(value)
|
||||||
else:
|
else:
|
||||||
f(self, value)
|
f(self, value, write)
|
||||||
|
|
||||||
def dump_int(self, value):
|
def dump_int(self, value, write):
|
||||||
# in case ints are > 32 bits
|
# in case ints are > 32 bits
|
||||||
if value > MAXINT or value < MININT:
|
if value > MAXINT or value < MININT:
|
||||||
raise OverflowError, "int exceeds XML-RPC limits"
|
raise OverflowError, "int exceeds XML-RPC limits"
|
||||||
self.write("<value><int>%s</int></value>\n" % value)
|
write("<value><int>")
|
||||||
|
write(str(value))
|
||||||
|
write("</int></value>\n")
|
||||||
dispatch[IntType] = dump_int
|
dispatch[IntType] = dump_int
|
||||||
|
|
||||||
def dump_long(self, value):
|
def dump_long(self, value, write):
|
||||||
# in case ints are > 32 bits
|
|
||||||
if value > MAXINT or value < MININT:
|
if value > MAXINT or value < MININT:
|
||||||
raise OverflowError, "long int exceeds XML-RPC limits"
|
raise OverflowError, "long int exceeds XML-RPC limits"
|
||||||
self.write("<value><int>%s</int></value>\n" % int(value))
|
write("<value><int>")
|
||||||
|
write(str(int(value)))
|
||||||
|
write("</int></value>\n")
|
||||||
dispatch[LongType] = dump_long
|
dispatch[LongType] = dump_long
|
||||||
|
|
||||||
def dump_double(self, value):
|
def dump_double(self, value, write):
|
||||||
self.write("<value><double>%s</double></value>\n" % repr(value))
|
write("<value><double>")
|
||||||
|
write(repr(value))
|
||||||
|
write("</double></value>\n")
|
||||||
dispatch[FloatType] = dump_double
|
dispatch[FloatType] = dump_double
|
||||||
|
|
||||||
def dump_string(self, value, escape=escape):
|
def dump_string(self, value, write, escape=escape):
|
||||||
self.write("<value><string>%s</string></value>\n" % escape(value))
|
write("<value><string>")
|
||||||
|
write(escape(value))
|
||||||
|
write("</string></value>\n")
|
||||||
dispatch[StringType] = dump_string
|
dispatch[StringType] = dump_string
|
||||||
|
|
||||||
if unicode:
|
if unicode:
|
||||||
def dump_unicode(self, value, escape=escape):
|
def dump_unicode(self, value, write, escape=escape):
|
||||||
value = value.encode(self.encoding)
|
value = value.encode(self.encoding)
|
||||||
self.write("<value><string>%s</string></value>\n" % escape(value))
|
write("<value><string>")
|
||||||
|
write(escape(value))
|
||||||
|
write("</string></value>\n")
|
||||||
dispatch[UnicodeType] = dump_unicode
|
dispatch[UnicodeType] = dump_unicode
|
||||||
|
|
||||||
def opencontainer(self, value):
|
def dump_array(self, value, write):
|
||||||
if value:
|
i = id(value)
|
||||||
i = id(value)
|
if self.memo.has_key(i):
|
||||||
if i in self.memo:
|
raise TypeError, "cannot marshal recursive sequences"
|
||||||
raise TypeError, "cannot marshal recursive data structures"
|
self.memo[i] = None
|
||||||
self.memo[i] = None
|
|
||||||
|
|
||||||
def closecontainer(self, value):
|
|
||||||
if value:
|
|
||||||
del self.memo[id(value)]
|
|
||||||
|
|
||||||
def dump_array(self, value):
|
|
||||||
self.opencontainer(value)
|
|
||||||
write = self.write
|
|
||||||
dump = self.__dump
|
dump = self.__dump
|
||||||
write("<value><array><data>\n")
|
write("<value><array><data>\n")
|
||||||
for v in value:
|
for v in value:
|
||||||
dump(v)
|
dump(v, write)
|
||||||
write("</data></array></value>\n")
|
write("</data></array></value>\n")
|
||||||
self.closecontainer(value)
|
del self.memo[i]
|
||||||
dispatch[TupleType] = dump_array
|
dispatch[TupleType] = dump_array
|
||||||
dispatch[ListType] = dump_array
|
dispatch[ListType] = dump_array
|
||||||
|
|
||||||
def dump_struct(self, value, escape=escape):
|
def dump_struct(self, value, write, escape=escape):
|
||||||
self.opencontainer(value)
|
i = id(value)
|
||||||
write = self.write
|
if self.memo.has_key(i):
|
||||||
|
raise TypeError, "cannot marshal recursive dictionaries"
|
||||||
|
self.memo[i] = None
|
||||||
dump = self.__dump
|
dump = self.__dump
|
||||||
write("<value><struct>\n")
|
write("<value><struct>\n")
|
||||||
for k, v in value.items():
|
for k in value.keys():
|
||||||
write("<member>\n")
|
write("<member>\n")
|
||||||
if type(k) is not StringType:
|
if type(k) is not StringType:
|
||||||
raise TypeError, "dictionary key must be string"
|
raise TypeError, "dictionary key must be string"
|
||||||
write("<name>%s</name>\n" % escape(k))
|
write("<name>%s</name>\n" % escape(k))
|
||||||
dump(v)
|
dump(value[k], write)
|
||||||
write("</member>\n")
|
write("</member>\n")
|
||||||
write("</struct></value>\n")
|
write("</struct></value>\n")
|
||||||
self.closecontainer(value)
|
del self.memo[i]
|
||||||
dispatch[DictType] = dump_struct
|
dispatch[DictType] = dump_struct
|
||||||
|
|
||||||
def dump_instance(self, value):
|
def dump_instance(self, value, write):
|
||||||
# check for special wrappers
|
# check for special wrappers
|
||||||
if value.__class__ in WRAPPERS:
|
if value.__class__ in WRAPPERS:
|
||||||
|
self.write = write
|
||||||
value.encode(self)
|
value.encode(self)
|
||||||
|
del self.write
|
||||||
else:
|
else:
|
||||||
# store instance attributes as a struct (really?)
|
# store instance attributes as a struct (really?)
|
||||||
self.dump_struct(value.__dict__)
|
self.dump_struct(value.__dict__, write)
|
||||||
dispatch[InstanceType] = dump_instance
|
dispatch[InstanceType] = dump_instance
|
||||||
|
|
||||||
|
##
|
||||||
|
# XML-RPC unmarshaller.
|
||||||
|
#
|
||||||
|
# @see loads
|
||||||
|
|
||||||
class Unmarshaller:
|
class Unmarshaller:
|
||||||
"""Unmarshal an XML-RPC response, based on incoming XML event
|
"""Unmarshal an XML-RPC response, based on incoming XML event
|
||||||
messages (start, data, end). Call close() to get the resulting
|
messages (start, data, end). Call close() to get the resulting
|
||||||
|
@ -679,7 +812,7 @@ class Unmarshaller:
|
||||||
dispatch["dateTime.iso8601"] = end_dateTime
|
dispatch["dateTime.iso8601"] = end_dateTime
|
||||||
|
|
||||||
def end_value(self, data):
|
def end_value(self, data):
|
||||||
# if we stumble upon an value element with no internal
|
# if we stumble upon a value element with no internal
|
||||||
# elements, treat it as a string element
|
# elements, treat it as a string element
|
||||||
if self._value:
|
if self._value:
|
||||||
self.end_string(data)
|
self.end_string(data)
|
||||||
|
@ -704,6 +837,12 @@ class Unmarshaller:
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# convenience functions
|
# convenience functions
|
||||||
|
|
||||||
|
##
|
||||||
|
# Create a parser object, and connect it to an unmarshalling instance.
|
||||||
|
# This function picks the fastest available XML parser.
|
||||||
|
#
|
||||||
|
# return A (parser, unmarshaller) tuple.
|
||||||
|
|
||||||
def getparser():
|
def getparser():
|
||||||
"""getparser() -> parser, unmarshaller
|
"""getparser() -> parser, unmarshaller
|
||||||
|
|
||||||
|
@ -711,7 +850,7 @@ def getparser():
|
||||||
to an unmarshalling object. Return both objects.
|
to an unmarshalling object. Return both objects.
|
||||||
"""
|
"""
|
||||||
if FastParser and FastUnmarshaller:
|
if FastParser and FastUnmarshaller:
|
||||||
target = FastUnmarshaller(True, False, binary, datetime)
|
target = FastUnmarshaller(True, False, _binary, _datetime, Fault)
|
||||||
parser = FastParser(target)
|
parser = FastParser(target)
|
||||||
else:
|
else:
|
||||||
target = Unmarshaller()
|
target = Unmarshaller()
|
||||||
|
@ -725,6 +864,19 @@ def getparser():
|
||||||
parser = SlowParser(target)
|
parser = SlowParser(target)
|
||||||
return parser, target
|
return parser, target
|
||||||
|
|
||||||
|
##
|
||||||
|
# Convert a Python tuple or a Fault instance to an XML-RPC packet.
|
||||||
|
#
|
||||||
|
# @def dumps(params, **options)
|
||||||
|
# @param params A tuple or Fault instance.
|
||||||
|
# @keyparam methodname If given, create a methodCall request for
|
||||||
|
# this method name.
|
||||||
|
# @keyparam methodresponse If given, create a methodResponse packet.
|
||||||
|
# If used with a tuple, the tuple must be a singleton (that is,
|
||||||
|
# it must contain exactly one element).
|
||||||
|
# @keyparam encoding The packet encoding.
|
||||||
|
# @return A string containing marshalled data.
|
||||||
|
|
||||||
def dumps(params, methodname=None, methodresponse=None, encoding=None):
|
def dumps(params, methodname=None, methodresponse=None, encoding=None):
|
||||||
"""data [,options] -> marshalled data
|
"""data [,options] -> marshalled data
|
||||||
|
|
||||||
|
@ -755,19 +907,23 @@ def dumps(params, methodname=None, methodresponse=None, encoding=None):
|
||||||
elif methodresponse and isinstance(params, TupleType):
|
elif methodresponse and isinstance(params, TupleType):
|
||||||
assert len(params) == 1, "response tuple must be a singleton"
|
assert len(params) == 1, "response tuple must be a singleton"
|
||||||
|
|
||||||
if encoding is None:
|
if not encoding:
|
||||||
encoding = "utf-8"
|
encoding = "utf-8"
|
||||||
|
|
||||||
m = Marshaller(encoding)
|
if FastMarshaller:
|
||||||
|
m = FastMarshaller(encoding)
|
||||||
|
else:
|
||||||
|
m = Marshaller(encoding)
|
||||||
|
|
||||||
data = m.dumps(params)
|
data = m.dumps(params)
|
||||||
|
|
||||||
if encoding != "utf-8":
|
if encoding != "utf-8":
|
||||||
xmlheader = "<?xml version='1.0' encoding=%s?>\n" % repr(encoding)
|
xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
|
||||||
else:
|
else:
|
||||||
xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
|
xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
|
||||||
|
|
||||||
# standard XML-RPC wrappings
|
# standard XML-RPC wrappings
|
||||||
if methodname is not None:
|
if methodname:
|
||||||
# a method call
|
# a method call
|
||||||
if not isinstance(methodname, StringType):
|
if not isinstance(methodname, StringType):
|
||||||
methodname = methodname.encode(encoding)
|
methodname = methodname.encode(encoding)
|
||||||
|
@ -790,6 +946,15 @@ def dumps(params, methodname=None, methodresponse=None, encoding=None):
|
||||||
return data # return as is
|
return data # return as is
|
||||||
return string.join(data, "")
|
return string.join(data, "")
|
||||||
|
|
||||||
|
##
|
||||||
|
# Convert an XML-RPC packet to a Python object. If the XML-RPC packet
|
||||||
|
# represents a fault condition, this function raises a Fault exception.
|
||||||
|
#
|
||||||
|
# @param data An XML-RPC packet, given as an 8-bit string.
|
||||||
|
# @return A tuple containing the the unpacked data, and the method name
|
||||||
|
# (None if not present).
|
||||||
|
# @see Fault
|
||||||
|
|
||||||
def loads(data):
|
def loads(data):
|
||||||
"""data -> unmarshalled data, method name
|
"""data -> unmarshalled data, method name
|
||||||
|
|
||||||
|
@ -799,6 +964,7 @@ def loads(data):
|
||||||
If the XML-RPC packet represents a fault condition, this function
|
If the XML-RPC packet represents a fault condition, this function
|
||||||
raises a Fault exception.
|
raises a Fault exception.
|
||||||
"""
|
"""
|
||||||
|
import sys
|
||||||
p, u = getparser()
|
p, u = getparser()
|
||||||
p.feed(data)
|
p.feed(data)
|
||||||
p.close()
|
p.close()
|
||||||
|
@ -819,6 +985,11 @@ class _Method:
|
||||||
def __call__(self, *args):
|
def __call__(self, *args):
|
||||||
return self.__send(self.__name, args)
|
return self.__send(self.__name, args)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Standard transport class for XML-RPC over HTTP.
|
||||||
|
# <p>
|
||||||
|
# You can create custom transports by subclassing this method, and
|
||||||
|
# overriding selected methods.
|
||||||
|
|
||||||
class Transport:
|
class Transport:
|
||||||
"""Handles an HTTP transaction to an XML-RPC server."""
|
"""Handles an HTTP transaction to an XML-RPC server."""
|
||||||
|
@ -826,6 +997,15 @@ class Transport:
|
||||||
# client identifier (may be overridden)
|
# client identifier (may be overridden)
|
||||||
user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
|
user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
|
||||||
|
|
||||||
|
##
|
||||||
|
# Send a complete request, and parse the response.
|
||||||
|
#
|
||||||
|
# @param host Target host.
|
||||||
|
# @param handler Target PRC handler.
|
||||||
|
# @param request_body XML-RPC request body.
|
||||||
|
# @param verbose Debugging flag.
|
||||||
|
# @return Parsed response.
|
||||||
|
|
||||||
def request(self, host, handler, request_body, verbose=0):
|
def request(self, host, handler, request_body, verbose=0):
|
||||||
# issue XML-RPC request
|
# issue XML-RPC request
|
||||||
|
|
||||||
|
@ -849,26 +1029,66 @@ class Transport:
|
||||||
|
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
|
|
||||||
return self.parse_response(h.getfile())
|
try:
|
||||||
|
sock = h._conn.sock
|
||||||
|
except AttributeError:
|
||||||
|
sock = None
|
||||||
|
|
||||||
|
return self._parse_response(h.getfile(), sock)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Create parser.
|
||||||
|
#
|
||||||
|
# @return A 2-tuple containing a parser and a unmarshaller.
|
||||||
|
|
||||||
def getparser(self):
|
def getparser(self):
|
||||||
# get parser and unmarshaller
|
# get parser and unmarshaller
|
||||||
return getparser()
|
return getparser()
|
||||||
|
|
||||||
|
##
|
||||||
|
# Connect to server.
|
||||||
|
#
|
||||||
|
# @param host Target host.
|
||||||
|
# @return A connection handle.
|
||||||
|
|
||||||
def make_connection(self, host):
|
def make_connection(self, host):
|
||||||
# create a HTTP connection object from a host descriptor
|
# create a HTTP connection object from a host descriptor
|
||||||
import httplib
|
import httplib
|
||||||
return httplib.HTTP(host)
|
return httplib.HTTP(host)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Send request header.
|
||||||
|
#
|
||||||
|
# @param connection Connection handle.
|
||||||
|
# @param handler Target RPC handler.
|
||||||
|
# @param request_body XML-RPC body.
|
||||||
|
|
||||||
def send_request(self, connection, handler, request_body):
|
def send_request(self, connection, handler, request_body):
|
||||||
connection.putrequest("POST", handler)
|
connection.putrequest("POST", handler)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Send host name.
|
||||||
|
#
|
||||||
|
# @param connection Connection handle.
|
||||||
|
# @param host Host name.
|
||||||
|
|
||||||
def send_host(self, connection, host):
|
def send_host(self, connection, host):
|
||||||
connection.putheader("Host", host)
|
connection.putheader("Host", host)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Send user-agent identifier.
|
||||||
|
#
|
||||||
|
# @param connection Connection handle.
|
||||||
|
|
||||||
def send_user_agent(self, connection):
|
def send_user_agent(self, connection):
|
||||||
connection.putheader("User-Agent", self.user_agent)
|
connection.putheader("User-Agent", self.user_agent)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Send request body.
|
||||||
|
#
|
||||||
|
# @param connection Connection handle.
|
||||||
|
# @param request_body XML-RPC request body.
|
||||||
|
|
||||||
def send_content(self, connection, request_body):
|
def send_content(self, connection, request_body):
|
||||||
connection.putheader("Content-Type", "text/xml")
|
connection.putheader("Content-Type", "text/xml")
|
||||||
connection.putheader("Content-Length", str(len(request_body)))
|
connection.putheader("Content-Length", str(len(request_body)))
|
||||||
|
@ -876,27 +1096,55 @@ class Transport:
|
||||||
if request_body:
|
if request_body:
|
||||||
connection.send(request_body)
|
connection.send(request_body)
|
||||||
|
|
||||||
def parse_response(self, f):
|
##
|
||||||
# read response from input file, and parse it
|
# Parse response.
|
||||||
|
#
|
||||||
|
# @param file Stream.
|
||||||
|
# @return Response tuple and target method.
|
||||||
|
|
||||||
|
def parse_response(self, file):
|
||||||
|
# compatibility interface
|
||||||
|
return self._parse_response(file, None)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Parse response (alternate interface). This is similar to the
|
||||||
|
# parse_response method, but also provides direct access to the
|
||||||
|
# underlying socket object (where available).
|
||||||
|
#
|
||||||
|
# @param file Stream.
|
||||||
|
# @param sock Socket handle (or None, if the socket object
|
||||||
|
# could not be accessed).
|
||||||
|
# @return Response tuple and target method.
|
||||||
|
|
||||||
|
def _parse_response(self, file, sock):
|
||||||
|
# read response from input file/socket, and parse it
|
||||||
|
|
||||||
p, u = self.getparser()
|
p, u = self.getparser()
|
||||||
|
|
||||||
while 1:
|
while 1:
|
||||||
response = f.read(1024)
|
if sock:
|
||||||
|
response = sock.recv(1024)
|
||||||
|
else:
|
||||||
|
response = file.read(1024)
|
||||||
if not response:
|
if not response:
|
||||||
break
|
break
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
print "body:", repr(response)
|
print "body:", repr(response)
|
||||||
p.feed(response)
|
p.feed(response)
|
||||||
|
|
||||||
f.close()
|
file.close()
|
||||||
p.close()
|
p.close()
|
||||||
|
|
||||||
return u.close()
|
return u.close()
|
||||||
|
|
||||||
|
##
|
||||||
|
# Standard transport class for XML-RPC over HTTPS.
|
||||||
|
|
||||||
class SafeTransport(Transport):
|
class SafeTransport(Transport):
|
||||||
"""Handles an HTTPS transaction to an XML-RPC server."""
|
"""Handles an HTTPS transaction to an XML-RPC server."""
|
||||||
|
|
||||||
|
# FIXME: mostly untested
|
||||||
|
|
||||||
def make_connection(self, host):
|
def make_connection(self, host):
|
||||||
# create a HTTPS connection object from a host descriptor
|
# create a HTTPS connection object from a host descriptor
|
||||||
# host may be a string, or a (host, x509-dict) tuple
|
# host may be a string, or a (host, x509-dict) tuple
|
||||||
|
@ -918,6 +1166,23 @@ class SafeTransport(Transport):
|
||||||
host, x509 = host
|
host, x509 = host
|
||||||
connection.putheader("Host", host)
|
connection.putheader("Host", host)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Standard server proxy. This class establishes a virtual connection
|
||||||
|
# to an XML-RPC server.
|
||||||
|
# <p>
|
||||||
|
# This class is available as ServerProxy and Server. New code should
|
||||||
|
# use ServerProxy, to avoid confusion.
|
||||||
|
#
|
||||||
|
# @def ServerProxy(uri, **options)
|
||||||
|
# @param uri The connection point on the server.
|
||||||
|
# @keyparam transport A transport factory, compatible with the
|
||||||
|
# standard transport class.
|
||||||
|
# @keyparam encoding The default encoding used for 8-bit strings
|
||||||
|
# (default is UTF-8).
|
||||||
|
# @keyparam verbose Use a true value to enable debugging output.
|
||||||
|
# (printed to standard output).
|
||||||
|
# @see Transport
|
||||||
|
|
||||||
class ServerProxy:
|
class ServerProxy:
|
||||||
"""uri [,options] -> a logical connection to an XML-RPC server
|
"""uri [,options] -> a logical connection to an XML-RPC server
|
||||||
|
|
||||||
|
@ -995,6 +1260,7 @@ class ServerProxy:
|
||||||
# result getattr(server, "strange-python-name")(args)
|
# result getattr(server, "strange-python-name")(args)
|
||||||
|
|
||||||
# compatibility
|
# compatibility
|
||||||
|
|
||||||
Server = ServerProxy
|
Server = ServerProxy
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue