diff options
author | Benjamin Peterson <benjamin@python.org> | 2014-12-05 20:30:54 -0500 |
---|---|---|
committer | Benjamin Peterson <benjamin@python.org> | 2014-12-05 20:30:54 -0500 |
commit | bf4cc2e260c888b285152657fc0b70d6d4d9d4c8 (patch) | |
tree | b472ceb9d60550fd6ff490d3c7d29c5e0b239606 /Lib/xmlrpc | |
parent | 4299141437ebae1f4e4f1ee783549ca874455e7c (diff) | |
parent | 816c88a3be5ac5b02ff39bf0ede5162ba92738a0 (diff) | |
download | cpython-bf4cc2e260c888b285152657fc0b70d6d4d9d4c8.tar.gz |
merge 3.2 (#16043)
Diffstat (limited to 'Lib/xmlrpc')
-rw-r--r-- | Lib/xmlrpc/client.py | 144 | ||||
-rw-r--r-- | Lib/xmlrpc/server.py | 65 |
2 files changed, 126 insertions, 83 deletions
diff --git a/Lib/xmlrpc/client.py b/Lib/xmlrpc/client.py index c03a5f0937..ca2ac9f094 100644 --- a/Lib/xmlrpc/client.py +++ b/Lib/xmlrpc/client.py @@ -129,8 +129,11 @@ Exported functions: """ import base64 +import sys import time +from datetime import datetime import http.client +import urllib.parse from xml.parsers import expat import socket import errno @@ -143,17 +146,13 @@ except ImportError: # -------------------------------------------------------------------- # Internal stuff -try: - import datetime -except ImportError: - datetime = None - def escape(s): s = s.replace("&", "&") s = s.replace("<", "<") return s.replace(">", ">",) -__version__ = "1.0.1" +# used in User-Agent header sent +__version__ = sys.version[:3] # xmlrpc integer limits MAXINT = 2**31-1 @@ -253,21 +252,33 @@ boolean = Boolean = bool # 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 +# The value can be given as a datetime object, 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. +# @param value The time, given as a datetime object, an ISO 8601 string, +# a time tuple, or an integer time value. + + +# Issue #13305: different format codes across platforms +_day0 = datetime(1, 1, 1) +if _day0.strftime('%Y') == '0001': # Mac OS X + def _iso8601_format(value): + return value.strftime("%Y%m%dT%H:%M:%S") +elif _day0.strftime('%4Y') == '0001': # Linux + def _iso8601_format(value): + return value.strftime("%4Y%m%dT%H:%M:%S") +else: + def _iso8601_format(value): + return value.strftime("%Y%m%dT%H:%M:%S").zfill(17) +del _day0 + def _strftime(value): - if datetime: - if isinstance(value, datetime.datetime): - return "%04d%02d%02dT%02d:%02d:%02d" % ( - value.year, value.month, value.day, - value.hour, value.minute, value.second) + if isinstance(value, datetime): + return _iso8601_format(value) if not isinstance(value, (tuple, time.struct_time)): if value == 0: @@ -292,9 +303,9 @@ class DateTime: if isinstance(other, DateTime): s = self.value o = other.value - elif datetime and isinstance(other, datetime.datetime): + elif isinstance(other, datetime): s = self.value - o = other.strftime("%Y%m%dT%H:%M:%S") + o = _iso8601_format(other) elif isinstance(other, str): s = self.value o = other @@ -362,8 +373,7 @@ def _datetime(data): return value def _datetime_type(data): - t = time.strptime(data, "%Y%m%dT%H:%M:%S") - return datetime.datetime(*tuple(t)[:6]) + return datetime.strptime(data, "%Y%m%dT%H:%M:%S") ## # Wrapper for binary data. This can be used to transport any kind @@ -378,8 +388,8 @@ class Binary: if data is None: data = b"" else: - if not isinstance(data, bytes): - raise TypeError("expected bytes, not %s" % + if not isinstance(data, (bytes, bytearray)): + raise TypeError("expected bytes or bytearray, not %s" % data.__class__.__name__) data = bytes(data) # Make a copy of the bytes! self.data = data @@ -409,7 +419,6 @@ class Binary: out.write("<value><base64>\n") encoded = base64.encodebytes(self.data) out.write(encoded.decode('ascii')) - out.write('\n') out.write("</base64></value>\n") def _binary(data): @@ -523,15 +532,6 @@ class Marshaller: write("<value><nil/></value>") dispatch[type(None)] = dump_nil - def dump_int(self, value, write): - # in case ints are > 32 bits - if value > MAXINT or value < MININT: - raise OverflowError("int exceeds XML-RPC limits") - write("<value><int>") - write(str(value)) - write("</int></value>\n") - #dispatch[int] = dump_int - def dump_bool(self, value, write): write("<value><boolean>") write(value and "1" or "0") @@ -540,12 +540,15 @@ class Marshaller: def dump_long(self, value, write): if value > MAXINT or value < MININT: - raise OverflowError("long int exceeds XML-RPC limits") + raise OverflowError("int exceeds XML-RPC limits") write("<value><int>") write(str(int(value))) write("</int></value>\n") dispatch[int] = dump_long + # backward compatible + dump_int = dump_long + def dump_double(self, value, write): write("<value><double>") write(repr(value)) @@ -558,6 +561,14 @@ class Marshaller: write("</string></value>\n") dispatch[str] = dump_unicode + def dump_bytes(self, value, write): + write("<value><base64>\n") + encoded = base64.encodebytes(value) + write(encoded.decode('ascii')) + write("</base64></value>\n") + dispatch[bytes] = dump_bytes + dispatch[bytearray] = dump_bytes + def dump_array(self, value, write): i = id(value) if i in self.memo: @@ -590,12 +601,11 @@ class Marshaller: del self.memo[i] dispatch[dict] = dump_struct - if datetime: - def dump_datetime(self, value, write): - write("<value><dateTime.iso8601>") - write(_strftime(value)) - write("</dateTime.iso8601></value>\n") - dispatch[datetime.datetime] = dump_datetime + def dump_datetime(self, value, write): + write("<value><dateTime.iso8601>") + write(_strftime(value)) + write("</dateTime.iso8601></value>\n") + dispatch[datetime] = dump_datetime def dump_instance(self, value, write): # check for special wrappers @@ -629,7 +639,7 @@ class Unmarshaller: # and again, if you don't understand what's going on in here, # that's perfectly ok. - def __init__(self, use_datetime=False): + def __init__(self, use_datetime=False, use_builtin_types=False): self._type = None self._stack = [] self._marks = [] @@ -637,9 +647,8 @@ class Unmarshaller: self._methodname = None self._encoding = "utf-8" self.append = self._stack.append - self._use_datetime = use_datetime - if use_datetime and not datetime: - raise ValueError("the datetime module is not available") + self._use_datetime = use_builtin_types or use_datetime + self._use_bytes = use_builtin_types def close(self): # return response tuple and target method @@ -751,6 +760,8 @@ class Unmarshaller: def end_base64(self, data): value = Binary() value.decode(data.encode("ascii")) + if self._use_bytes: + value = value.data self.append(value) self._value = 0 dispatch["base64"] = end_base64 @@ -862,23 +873,26 @@ FastMarshaller = FastParser = FastUnmarshaller = None # # return A (parser, unmarshaller) tuple. -def getparser(use_datetime=False): +def getparser(use_datetime=False, use_builtin_types=False): """getparser() -> parser, unmarshaller Create an instance of the fastest available parser, and attach it to an unmarshalling object. Return both objects. """ - if use_datetime and not datetime: - raise ValueError("the datetime module is not available") if FastParser and FastUnmarshaller: - if use_datetime: + if use_builtin_types: + mkdatetime = _datetime_type + mkbytes = base64.decodebytes + elif use_datetime: mkdatetime = _datetime_type + mkbytes = _binary else: mkdatetime = _datetime - target = FastUnmarshaller(True, False, _binary, mkdatetime, Fault) + mkbytes = _binary + target = FastUnmarshaller(True, False, mkbytes, mkdatetime, Fault) parser = FastParser(target) else: - target = Unmarshaller(use_datetime=use_datetime) + target = Unmarshaller(use_datetime=use_datetime, use_builtin_types=use_builtin_types) if FastParser: parser = FastParser(target) else: @@ -916,7 +930,7 @@ def dumps(params, methodname=None, methodresponse=None, encoding=None, encoding: the packet encoding (default is UTF-8) - All 8-bit strings in the data structure are assumed to use the + All byte strings in the data structure are assumed to use the packet encoding. Unicode strings are automatically converted, where necessary. """ @@ -975,7 +989,7 @@ def dumps(params, methodname=None, methodresponse=None, encoding=None, # (None if not present). # @see Fault -def loads(data, use_datetime=False): +def loads(data, use_datetime=False, use_builtin_types=False): """data -> unmarshalled data, method name Convert an XML-RPC packet to unmarshalled data plus a method @@ -984,7 +998,7 @@ def loads(data, use_datetime=False): If the XML-RPC packet represents a fault condition, this function raises a Fault exception. """ - p, u = getparser(use_datetime=use_datetime) + p, u = getparser(use_datetime=use_datetime, use_builtin_types=use_builtin_types) p.feed(data) p.close() return u.close(), u.getmethodname() @@ -1094,7 +1108,7 @@ class Transport: """Handles an HTTP transaction to an XML-RPC server.""" # client identifier (may be overridden) - user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__ + user_agent = "Python-xmlrpc/%s" % __version__ #if true, we'll request gzip encoding accept_gzip_encoding = True @@ -1104,8 +1118,9 @@ class Transport: # that they can decode such a request encode_threshold = None #None = don't encode - def __init__(self, use_datetime=False): + def __init__(self, use_datetime=False, use_builtin_types=False): self._use_datetime = use_datetime + self._use_builtin_types = use_builtin_types self._connection = (None, None) self._extra_headers = [] @@ -1166,7 +1181,8 @@ class Transport: def getparser(self): # get parser and unmarshaller - return getparser(use_datetime=self._use_datetime) + return getparser(use_datetime=self._use_datetime, + use_builtin_types=self._use_builtin_types) ## # Get authorization info from host parameter @@ -1184,7 +1200,6 @@ class Transport: if isinstance(host, tuple): host, x509 = host - import urllib.parse auth, host = urllib.parse.splituser(host) if auth: @@ -1373,11 +1388,10 @@ class ServerProxy: """ def __init__(self, uri, transport=None, encoding=None, verbose=False, - allow_none=False, use_datetime=False): + allow_none=False, use_datetime=False, use_builtin_types=False): # establish a "logical" server connection # get the url - import urllib.parse type, uri = urllib.parse.splittype(uri) if type not in ("http", "https"): raise IOError("unsupported XML-RPC protocol") @@ -1387,9 +1401,11 @@ class ServerProxy: if transport is None: if type == "https": - transport = SafeTransport(use_datetime=use_datetime) + handler = SafeTransport else: - transport = Transport(use_datetime=use_datetime) + handler = Transport + transport = handler(use_datetime=use_datetime, + use_builtin_types=use_builtin_types) self.__transport = transport self.__encoding = encoding or 'utf-8' @@ -1453,18 +1469,18 @@ if __name__ == "__main__": # simple test program (from the XML-RPC specification) - # server = ServerProxy("http://localhost:8000") # local server - server = ServerProxy("http://time.xmlrpc.com/RPC2") + # local server, available from Lib/xmlrpc/server.py + server = ServerProxy("http://localhost:8000") try: print(server.currentTime.getCurrentTime()) except Error as v: print("ERROR", v) - # The server at xmlrpc.com doesn't seem to support multicall anymore. multi = MultiCall(server) - multi.currentTime.getCurrentTime() - multi.currentTime.getCurrentTime() + multi.getData() + multi.pow(2,9) + multi.add(1,2) try: for response in multi(): print(response) diff --git a/Lib/xmlrpc/server.py b/Lib/xmlrpc/server.py index fd17026583..d27bf5a177 100644 --- a/Lib/xmlrpc/server.py +++ b/Lib/xmlrpc/server.py @@ -160,11 +160,13 @@ class SimpleXMLRPCDispatcher: can be instanced when used by the MultiPathXMLRPCServer """ - def __init__(self, allow_none=False, encoding=None): + def __init__(self, allow_none=False, encoding=None, + use_builtin_types=False): self.funcs = {} self.instance = None self.allow_none = allow_none self.encoding = encoding or 'utf-8' + self.use_builtin_types = use_builtin_types def register_instance(self, instance, allow_dotted_names=False): """Registers an instance to respond to XML-RPC requests. @@ -245,7 +247,7 @@ class SimpleXMLRPCDispatcher: """ try: - params, method = loads(data) + params, method = loads(data, use_builtin_types=self.use_builtin_types) # generate response if dispatch_method is not None: @@ -575,10 +577,11 @@ class SimpleXMLRPCServer(socketserver.TCPServer, _send_traceback_header = False def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, - logRequests=True, allow_none=False, encoding=None, bind_and_activate=True): + logRequests=True, allow_none=False, encoding=None, + bind_and_activate=True, use_builtin_types=False): self.logRequests = logRequests - SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding) + SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types) socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate) # [Bug #1222790] If possible, set close-on-exec flag; if a @@ -598,10 +601,11 @@ class MultiPathXMLRPCServer(SimpleXMLRPCServer): Make sure that the requestHandler accepts the paths in question. """ def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, - logRequests=True, allow_none=False, encoding=None, bind_and_activate=True): + logRequests=True, allow_none=False, encoding=None, + bind_and_activate=True, use_builtin_types=False): SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none, - encoding, bind_and_activate) + encoding, bind_and_activate, use_builtin_types) self.dispatchers = {} self.allow_none = allow_none self.encoding = encoding or 'utf-8' @@ -631,8 +635,8 @@ class MultiPathXMLRPCServer(SimpleXMLRPCServer): class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher): """Simple handler for XML-RPC data passed through CGI.""" - def __init__(self, allow_none=False, encoding=None): - SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding) + def __init__(self, allow_none=False, encoding=None, use_builtin_types=False): + SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types) def handle_xmlrpc(self, request_text): """Handle a single XML-RPC request""" @@ -752,20 +756,23 @@ class ServerHTMLDoc(pydoc.HTMLDoc): self.escape(anchor), self.escape(name)) if inspect.ismethod(object): - args, varargs, varkw, defaults = inspect.getargspec(object) + args = inspect.getfullargspec(object) # exclude the argument bound to the instance, it will be # confusing to the non-Python user argspec = inspect.formatargspec ( - args[1:], - varargs, - varkw, - defaults, + args.args[1:], + args.varargs, + args.varkw, + args.defaults, + annotations=args.annotations, formatvalue=self.formatvalue ) elif inspect.isfunction(object): - args, varargs, varkw, defaults = inspect.getargspec(object) + args = inspect.getfullargspec(object) argspec = inspect.formatargspec( - args, varargs, varkw, defaults, formatvalue=self.formatvalue) + args.args, args.varargs, args.varkw, args.defaults, + annotations=args.annotations, + formatvalue=self.formatvalue) else: argspec = '(...)' @@ -927,9 +934,10 @@ class DocXMLRPCServer( SimpleXMLRPCServer, def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler, logRequests=True, allow_none=False, encoding=None, - bind_and_activate=True): + bind_and_activate=True, use_builtin_types=False): SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, - allow_none, encoding, bind_and_activate) + allow_none, encoding, bind_and_activate, + use_builtin_types) XMLRPCDocGenerator.__init__(self) class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler, @@ -959,8 +967,27 @@ class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler, if __name__ == '__main__': - print('Running XML-RPC server on port 8000') + import datetime + + class ExampleService: + def getData(self): + return '42' + + class currentTime: + @staticmethod + def getCurrentTime(): + return datetime.datetime.now() + server = SimpleXMLRPCServer(("localhost", 8000)) server.register_function(pow) server.register_function(lambda x,y: x+y, 'add') - server.serve_forever() + server.register_instance(ExampleService(), allow_dotted_names=True) + server.register_multicall_functions() + print('Serving XML-RPC on localhost port 8000') + print('It is advisable to run this example server within a secure, closed network.') + try: + server.serve_forever() + except KeyboardInterrupt: + print("\nKeyboard interrupt received, exiting.") + server.server_close() + sys.exit(0) |