sync with pythonware codebase: much faster import (doesn't import

xmllib unless needed), merged docstring patches, added overridable
Transport.getparser to simplify plugging in different parsers.
This commit is contained in:
Fredrik Lundh 2001-09-10 19:45:02 +00:00
parent 54328388f7
commit c4c062f507
1 changed files with 70 additions and 65 deletions

View File

@ -2,6 +2,17 @@
# XML-RPC CLIENT LIBRARY # XML-RPC CLIENT LIBRARY
# $Id$ # $Id$
# #
# an XML-RPC client interface for Python.
#
# the marshalling and response parser code can also be used to
# implement XML-RPC servers.
#
# Notes:
# this version is designed to work with Python 1.5.2 or newer.
# unicode encoding support requires at least Python 1.6.
# experimental HTTPS requires Python 2.0 built with SSL sockets.
# expat parser support requires Python 2.0 with pyexpat support.
#
# History: # History:
# 1999-01-14 fl Created # 1999-01-14 fl Created
# 1999-01-15 fl Changed dateTime to use localtime # 1999-01-15 fl Changed dateTime to use localtime
@ -18,6 +29,8 @@
# 2001-03-29 fl Don't require empty params element (from Nicholas Riley) # 2001-03-29 fl Don't require empty params element (from Nicholas Riley)
# 2001-06-10 fl Folded in _xmlrpclib accelerator support (1.0b2) # 2001-06-10 fl Folded in _xmlrpclib accelerator support (1.0b2)
# 2001-08-20 fl Base xmlrpclib.Error on built-in Exception (from Paul Prescod) # 2001-08-20 fl Base xmlrpclib.Error on built-in Exception (from Paul Prescod)
# 2001-09-03 fl Allow Transport subclass to override getparser
# 2001-09-10 fl Lazy import of urllib, cgi, xmllib (20x import speedup)
# #
# Copyright (c) 1999-2001 by Secret Labs AB. # Copyright (c) 1999-2001 by Secret Labs AB.
# Copyright (c) 1999-2001 by Fredrik Lundh. # Copyright (c) 1999-2001 by Fredrik Lundh.
@ -82,35 +95,28 @@ An XML-RPC client interface for Python.
The marshalling and response parser code can also be used to The marshalling and response parser code can also be used to
implement XML-RPC servers. implement XML-RPC servers.
Notes:
This version is designed to work with Python 1.5.2 or newer.
Unicode encoding support requires at least Python 1.6.
Experimental HTTPS requires Python 2.0 built with SSL sockets.
Expat parser support requires Python 2.0 with pyexpat support.
Exported exceptions: Exported exceptions:
Error Base class for client errors Error Base class for client errors
ProtocolError Indicates an HTTP protocol error ProtocolError Indicates an HTTP protocol error
ResponseError Indicates a broken response package ResponseError Indicates a broken response package
Fault Indicates a XML-RPC fault package Fault Indicates an XML-RPC fault package
Exported classes: Exported classes:
ServerProxy Represents a logical connection to an XML-RPC server
Boolean boolean wrapper to generate a "boolean" XML-RPC value Boolean boolean wrapper to generate a "boolean" XML-RPC value
DateTime dateTime wrapper for an ISO 8601 string or time tuple or DateTime dateTime wrapper for an ISO 8601 string or time tuple or
localtime integer value to generate a "dateTime.iso8601" localtime integer value to generate a "dateTime.iso8601"
XML-RPC value XML-RPC value
Binary binary data wrapper Binary binary data wrapper
SlowParser Slow but safe standard parser SlowParser Slow but safe standard parser (based on xmllib)
Marshaller Generate an XML-RPC params chunk from a Python data structure Marshaller Generate an XML-RPC params chunk from a Python data structure
Unmarshaller Unmarshal an XML-RPC response from incoming XML event message Unmarshaller Unmarshal an XML-RPC response from incoming XML event message
Transport Handles an HTTP transaction to an XML-RPC server Transport Handles an HTTP transaction to an XML-RPC server
SafeTransport Handles an HTTPS transaction to an XML-RPC server SafeTransport Handles an HTTPS transaction to an XML-RPC server
ServerProxy Connect to a server through a proxy
Server Same as ServerProxy
Exported constants: Exported constants:
@ -120,21 +126,16 @@ Exported constants:
Exported functions: Exported functions:
boolean Convert any Python value to an XML-RPC boolean boolean Convert any Python value to an XML-RPC boolean
datetime Convert value to an XML-RPC datetime
binary Convert value to an XML-RPC binary value
getparser Create instance of the fastest available parser & attach getparser Create instance of the fastest available parser & attach
to an unmarshalling object to an unmarshalling object
dumps Convert an argument tuple or a Fault instance to an XML-RPC dumps Convert an argument tuple or a Fault instance to an XML-RPC
request (or response, if the methodresponse option is used). request (or response, if the methodresponse option is used).
loads Convert an XML-RPC packet to unmarshalled data plus a method loads Convert an XML-RPC packet to unmarshalled data plus a method
name (None if not present). name (None if not present).
""" """
import re, string, time, operator import re, string, time, operator
import urllib, xmllib
from types import * from types import *
from cgi import escape
try: try:
unicode unicode
@ -181,24 +182,23 @@ class ProtocolError(Error):
) )
class ResponseError(Error): class ResponseError(Error):
"""Indicates a broken response package""" """Indicates a broken response package."""
pass pass
class Fault(Error): class Fault(Error):
"""indicates a XML-RPC fault package""" """Indicates an XML-RPC fault package."""
def __init__(self, faultCode, faultString, **extra): def __init__(self, faultCode, faultString, **extra):
self.faultCode = faultCode self.faultCode = faultCode
self.faultString = faultString self.faultString = faultString
def __repr__(self): def __repr__(self):
return ( return (
"<Fault %s: %s>" % "<Fault %s: %s>" %
(self.faultCode, repr(self.faultString)) (repr(self.faultCode), repr(self.faultString))
) )
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Special values # Special values
class Boolean: class Boolean:
"""Boolean-value wrapper. """Boolean-value wrapper.
@ -231,18 +231,14 @@ class Boolean:
True, False = Boolean(1), Boolean(0) True, False = Boolean(1), Boolean(0)
def boolean(value, truefalse=(False, True)): 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)]
#
# dateTime wrapper
# wrap your iso8601 string or time tuple or localtime integer value
# in this class to generate a "dateTime.iso8601" XML-RPC value
class DateTime: class DateTime:
"""DataTime wrapper for an ISO 8601 string or time tuple or """DateTime wrapper for an ISO 8601 string or time tuple or
localtime integer value to generate a 'dateTime.iso8601' XML-RPC localtime integer value to generate 'dateTime.iso8601' XML-RPC
value.""" value.
"""
def __init__(self, value=0): def __init__(self, value=0):
if not isinstance(value, StringType): if not isinstance(value, StringType):
@ -274,7 +270,6 @@ def datetime(data):
value.decode(data) value.decode(data)
return value return value
class Binary: class Binary:
"""Wrapper for binary data.""" """Wrapper for binary data."""
@ -354,6 +349,7 @@ else:
self.parser = self.feed = None # nuke circular reference self.parser = self.feed = None # nuke circular reference
def handle_proc(self, tag, attr): def handle_proc(self, tag, attr):
import re
m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr) m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
if m: if m:
self.handle_xml(m.group(1), 1) self.handle_xml(m.group(1), 1)
@ -391,13 +387,14 @@ else:
self._parser.Parse("", 1) # end of data self._parser.Parse("", 1) # end of data
del self._target, self._parser # get rid of circular references del self._target, self._parser # get rid of circular references
class SlowParser(xmllib.XMLParser): class SlowParser:
"""XML parser using xmllib.XMLParser. """Default XML parser (based on xmllib.XMLParser)."""
# this is about 10 times slower than sgmlop, on roundtrip
This is about 10 times slower than sgmlop on roundtrip testing. # testing.
"""
def __init__(self, target): def __init__(self, target):
import xmllib # lazy subclassing (!)
if xmllib.XMLParser not in SlowParser.__bases__:
SlowParser.__bases__ = (xmllib.XMLParser,)
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
@ -411,11 +408,11 @@ class SlowParser(xmllib.XMLParser):
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.
Create a marshaller instance for each set of parameters, and use Create a Marshaller instance for each set of parameters, and use
"dumps" method to convert your data (represented as a tuple) to a the "dumps" method to convert your data (represented as a tuple)
XML-RPC params chunk. to write a fault response, pass a Fault to an XML-RPC params chunk. To write a fault response, pass a
instance instead. You may prefer to use the "dumps" convenience Fault instance instead. You may prefer to use the "dumps" module
function for this purpose (see below). function for this purpose.
""" """
# by the way, if you don't understand what's going on in here, # by the way, if you don't understand what's going on in here,
@ -470,12 +467,14 @@ class Marshaller:
dispatch[FloatType] = dump_double dispatch[FloatType] = dump_double
def dump_string(self, value): def dump_string(self, value):
from cgi import escape
self.write("<value><string>%s</string></value>\n" % escape(value)) self.write("<value><string>%s</string></value>\n" % escape(value))
dispatch[StringType] = dump_string dispatch[StringType] = dump_string
if unicode: if unicode:
def dump_unicode(self, value): def dump_unicode(self, value):
value = value.encode(self.encoding) value = value.encode(self.encoding)
from cgi import escape
self.write("<value><string>%s</string></value>\n" % escape(value)) self.write("<value><string>%s</string></value>\n" % escape(value))
dispatch[UnicodeType] = dump_unicode dispatch[UnicodeType] = dump_unicode
@ -504,6 +503,7 @@ class Marshaller:
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"
from cgi import escape
write("<name>%s</name>\n" % escape(k)) write("<name>%s</name>\n" % escape(k))
self.__dump(v) self.__dump(v)
write("</member>\n") write("</member>\n")
@ -521,7 +521,7 @@ class Marshaller:
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
data structure. data structure.
Note that this reader is fairly tolerant, and gladly accepts Note that this reader is fairly tolerant, and gladly accepts
@ -802,7 +802,7 @@ class _Method:
class Transport: class Transport:
"""Handles an HTTP transaction to an XML-RPC server""" """Handles an HTTP transaction to an XML-RPC server."""
# 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__
@ -832,6 +832,10 @@ class Transport:
return self.parse_response(h.getfile()) return self.parse_response(h.getfile())
def getparser(self):
# get parser and unmarshaller
return getparser()
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
@ -856,7 +860,7 @@ class Transport:
def parse_response(self, f): def parse_response(self, f):
# read response from input file, and parse it # read response from input file, and parse it
p, u = getparser() p, u = self.getparser()
while 1: while 1:
response = f.read(1024) response = f.read(1024)
@ -872,7 +876,7 @@ class Transport:
return u.close() return u.close()
class SafeTransport(Transport): class SafeTransport(Transport):
"""Handles an HTTPS transaction to an XML-RPC server""" """Handles an HTTPS transaction to an XML-RPC server."""
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
@ -921,6 +925,7 @@ class ServerProxy:
# establish a "logical" server connection # establish a "logical" server connection
# get the url # get the url
import urllib
type, uri = urllib.splittype(uri) type, uri = urllib.splittype(uri)
if type not in ("http", "https"): if type not in ("http", "https"):
raise IOError, "unsupported XML-RPC protocol" raise IOError, "unsupported XML-RPC protocol"