Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
client.py1504 linesDownload Raw Back to xmlrpc
1#2# XML-RPC CLIENT LIBRARY3# $Id$4#5# an XML-RPC client interface for Python.6#7# the marshalling and response parser code can also be used to8# implement XML-RPC servers.9#10# Notes:11# this version is designed to work with Python 2.1 or newer.12#13# History:14# 1999-01-14 fl  Created15# 1999-01-15 fl  Changed dateTime to use localtime16# 1999-01-16 fl  Added Binary/base64 element, default to RPC2 service17# 1999-01-19 fl  Fixed array data element (from Skip Montanaro)18# 1999-01-21 fl  Fixed dateTime constructor, etc.19# 1999-02-02 fl  Added fault handling, handle empty sequences, etc.20# 1999-02-10 fl  Fixed problem with empty responses (from Skip Montanaro)21# 1999-06-20 fl  Speed improvements, pluggable parsers/transports (0.9.8)22# 2000-11-28 fl  Changed boolean to check the truth value of its argument23# 2001-02-24 fl  Added encoding/Unicode/SafeTransport patches24# 2001-02-26 fl  Added compare support to wrappers (0.9.9/1.0b1)25# 2001-03-28 fl  Make sure response tuple is a singleton26# 2001-03-29 fl  Don't require empty params element (from Nicholas Riley)27# 2001-06-10 fl  Folded in _xmlrpclib accelerator support (1.0b2)28# 2001-08-20 fl  Base xmlrpclib.Error on built-in Exception (from Paul Prescod)29# 2001-09-03 fl  Allow Transport subclass to override getparser30# 2001-09-10 fl  Lazy import of urllib, cgi, xmllib (20x import speedup)31# 2001-10-01 fl  Remove containers from memo cache when done with them32# 2001-10-01 fl  Use faster escape method (80% dumps speedup)33# 2001-10-02 fl  More dumps microtuning34# 2001-10-04 fl  Make sure import expat gets a parser (from Guido van Rossum)35# 2001-10-10 sm  Allow long ints to be passed as ints if they don't overflow36# 2001-10-17 sm  Test for int and long overflow (allows use on 64-bit systems)37# 2001-11-12 fl  Use repr() to marshal doubles (from Paul Felix)38# 2002-03-17 fl  Avoid buffered read when possible (from James Rucker)39# 2002-04-07 fl  Added pythondoc comments40# 2002-04-16 fl  Added __str__ methods to datetime/binary wrappers41# 2002-05-15 fl  Added error constants (from Andrew Kuchling)42# 2002-06-27 fl  Merged with Python CVS version43# 2002-10-22 fl  Added basic authentication (based on code from Phillip Eby)44# 2003-01-22 sm  Add support for the bool type45# 2003-02-27 gvr Remove apply calls46# 2003-04-24 sm  Use cStringIO if available47# 2003-04-25 ak  Add support for nil48# 2003-06-15 gn  Add support for time.struct_time49# 2003-07-12 gp  Correct marshalling of Faults50# 2003-10-31 mvl Add multicall support51# 2004-08-20 mvl Bump minimum supported Python version to 2.152# 2014-12-02 ch/doko  Add workaround for gzip bomb vulnerability53#54# Copyright (c) 1999-2002 by Secret Labs AB.55# Copyright (c) 1999-2002 by Fredrik Lundh.56#57# info@pythonware.com58# http://www.pythonware.com59#60# --------------------------------------------------------------------61# The XML-RPC client interface is62#63# Copyright (c) 1999-2002 by Secret Labs AB64# Copyright (c) 1999-2002 by Fredrik Lundh65#66# By obtaining, using, and/or copying this software and/or its67# associated documentation, you agree that you have read, understood,68# and will comply with the following terms and conditions:69#70# Permission to use, copy, modify, and distribute this software and71# its associated documentation for any purpose and without fee is72# hereby granted, provided that the above copyright notice appears in73# all copies, and that both that copyright notice and this permission74# notice appear in supporting documentation, and that the name of75# Secret Labs AB or the author not be used in advertising or publicity76# pertaining to distribution of the software without specific, written77# prior permission.78#79# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD80# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-81# ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR82# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY83# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,84# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS85# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE86# OF THIS SOFTWARE.87# --------------------------------------------------------------------88 89"""90An XML-RPC client interface for Python.91 92The marshalling and response parser code can also be used to93implement XML-RPC servers.94 95Exported exceptions:96 97  Error          Base class for client errors98  ProtocolError  Indicates an HTTP protocol error99  ResponseError  Indicates a broken response package100  Fault          Indicates an XML-RPC fault package101 102Exported classes:103 104  ServerProxy    Represents a logical connection to an XML-RPC server105 106  MultiCall      Executor of boxcared xmlrpc requests107  DateTime       dateTime wrapper for an ISO 8601 string or time tuple or108                 localtime integer value to generate a "dateTime.iso8601"109                 XML-RPC value110  Binary         binary data wrapper111 112  Marshaller     Generate an XML-RPC params chunk from a Python data structure113  Unmarshaller   Unmarshal an XML-RPC response from incoming XML event message114  Transport      Handles an HTTP transaction to an XML-RPC server115  SafeTransport  Handles an HTTPS transaction to an XML-RPC server116 117Exported constants:118 119  (none)120 121Exported functions:122 123  getparser      Create instance of the fastest available parser & attach124                 to an unmarshalling object125  dumps          Convert an argument tuple or a Fault instance to an XML-RPC126                 request (or response, if the methodresponse option is used).127  loads          Convert an XML-RPC packet to unmarshalled data plus a method128                 name (None if not present).129"""130 131import base64132import sys133import time134from datetime import datetime135from decimal import Decimal136import http.client137import urllib.parse138from xml.parsers import expat139import errno140from io import BytesIO141try:142    import gzip143except ImportError:144    gzip = None #python can be built without zlib/gzip support145 146# --------------------------------------------------------------------147# Internal stuff148 149def escape(s):150    s = s.replace("&", "&amp;")151    s = s.replace("<", "&lt;")152    return s.replace(">", "&gt;",)153 154# used in User-Agent header sent155__version__ = '%d.%d' % sys.version_info[:2]156 157# xmlrpc integer limits158MAXINT =  2**31-1159MININT = -2**31160 161# --------------------------------------------------------------------162# Error constants (from Dan Libby's specification at163# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)164 165# Ranges of errors166PARSE_ERROR       = -32700167SERVER_ERROR      = -32600168APPLICATION_ERROR = -32500169SYSTEM_ERROR      = -32400170TRANSPORT_ERROR   = -32300171 172# Specific errors173NOT_WELLFORMED_ERROR  = -32700174UNSUPPORTED_ENCODING  = -32701175INVALID_ENCODING_CHAR = -32702176INVALID_XMLRPC        = -32600177METHOD_NOT_FOUND      = -32601178INVALID_METHOD_PARAMS = -32602179INTERNAL_ERROR        = -32603180 181# --------------------------------------------------------------------182# Exceptions183 184##185# Base class for all kinds of client-side errors.186 187class Error(Exception):188    """Base class for client errors."""189    __str__ = object.__str__190 191##192# Indicates an HTTP-level protocol error.  This is raised by the HTTP193# transport layer, if the server returns an error code other than 200194# (OK).195#196# @param url The target URL.197# @param errcode The HTTP error code.198# @param errmsg The HTTP error message.199# @param headers The HTTP header dictionary.200 201class ProtocolError(Error):202    """Indicates an HTTP protocol error."""203    def __init__(self, url, errcode, errmsg, headers):204        Error.__init__(self)205        self.url = url206        self.errcode = errcode207        self.errmsg = errmsg208        self.headers = headers209    def __repr__(self):210        return (211            "<%s for %s: %s %s>" %212            (self.__class__.__name__, self.url, self.errcode, self.errmsg)213            )214 215##216# Indicates a broken XML-RPC response package.  This exception is217# raised by the unmarshalling layer, if the XML-RPC response is218# malformed.219 220class ResponseError(Error):221    """Indicates a broken response package."""222    pass223 224##225# Indicates an XML-RPC fault response package.  This exception is226# raised by the unmarshalling layer, if the XML-RPC response contains227# a fault string.  This exception can also be used as a class, to228# generate a fault XML-RPC message.229#230# @param faultCode The XML-RPC fault code.231# @param faultString The XML-RPC fault string.232 233class Fault(Error):234    """Indicates an XML-RPC fault package."""235    def __init__(self, faultCode, faultString, **extra):236        Error.__init__(self)237        self.faultCode = faultCode238        self.faultString = faultString239    def __repr__(self):240        return "<%s %s: %r>" % (self.__class__.__name__,241                                self.faultCode, self.faultString)242 243# --------------------------------------------------------------------244# Special values245 246##247# Backwards compatibility248boolean = Boolean = bool249 250 251def _iso8601_format(value):252    if value.tzinfo is not None:253        # XML-RPC only uses the naive portion of the datetime254        value = value.replace(tzinfo=None)255    # XML-RPC doesn't use '-' separator in the date part256    return value.isoformat(timespec='seconds').replace('-', '')257 258 259def _strftime(value):260    if isinstance(value, datetime):261        return _iso8601_format(value)262 263    if not isinstance(value, (tuple, time.struct_time)):264        if value == 0:265            value = time.time()266        value = time.localtime(value)267 268    return "%04d%02d%02dT%02d:%02d:%02d" % value[:6]269 270class DateTime:271    """DateTime wrapper for an ISO 8601 string or time tuple or272    localtime integer value to generate 'dateTime.iso8601' XML-RPC273    value.274    """275 276    def __init__(self, value=0):277        if isinstance(value, str):278            self.value = value279        else:280            self.value = _strftime(value)281 282    def make_comparable(self, other):283        if isinstance(other, DateTime):284            s = self.value285            o = other.value286        elif isinstance(other, datetime):287            s = self.value288            o = _iso8601_format(other)289        elif isinstance(other, str):290            s = self.value291            o = other292        elif hasattr(other, "timetuple"):293            s = self.timetuple()294            o = other.timetuple()295        else:296            s = self297            o = NotImplemented298        return s, o299 300    def __lt__(self, other):301        s, o = self.make_comparable(other)302        if o is NotImplemented:303            return NotImplemented304        return s < o305 306    def __le__(self, other):307        s, o = self.make_comparable(other)308        if o is NotImplemented:309            return NotImplemented310        return s <= o311 312    def __gt__(self, other):313        s, o = self.make_comparable(other)314        if o is NotImplemented:315            return NotImplemented316        return s > o317 318    def __ge__(self, other):319        s, o = self.make_comparable(other)320        if o is NotImplemented:321            return NotImplemented322        return s >= o323 324    def __eq__(self, other):325        s, o = self.make_comparable(other)326        if o is NotImplemented:327            return NotImplemented328        return s == o329 330    def timetuple(self):331        return time.strptime(self.value, "%Y%m%dT%H:%M:%S")332 333    ##334    # Get date/time value.335    #336    # @return Date/time value, as an ISO 8601 string.337 338    def __str__(self):339        return self.value340 341    def __repr__(self):342        return "<%s %r at %#x>" % (self.__class__.__name__, self.value, id(self))343 344    def decode(self, data):345        self.value = str(data).strip()346 347    def encode(self, out):348        out.write("<value><dateTime.iso8601>")349        out.write(self.value)350        out.write("</dateTime.iso8601></value>\n")351 352def _datetime(data):353    # decode xml element contents into a DateTime structure.354    value = DateTime()355    value.decode(data)356    return value357 358def _datetime_type(data):359    return datetime.strptime(data, "%Y%m%dT%H:%M:%S")360 361##362# Wrapper for binary data.  This can be used to transport any kind363# of binary data over XML-RPC, using BASE64 encoding.364#365# @param data An 8-bit string containing arbitrary data.366 367class Binary:368    """Wrapper for binary data."""369 370    def __init__(self, data=None):371        if data is None:372            data = b""373        else:374            if not isinstance(data, (bytes, bytearray)):375                raise TypeError("expected bytes or bytearray, not %s" %376                                data.__class__.__name__)377            data = bytes(data)  # Make a copy of the bytes!378        self.data = data379 380    ##381    # Get buffer contents.382    #383    # @return Buffer contents, as an 8-bit string.384 385    def __str__(self):386        return str(self.data, "latin-1")  # XXX encoding?!387 388    def __eq__(self, other):389        if isinstance(other, Binary):390            other = other.data391        return self.data == other392 393    def decode(self, data):394        self.data = base64.decodebytes(data)395 396    def encode(self, out):397        out.write("<value><base64>\n")398        encoded = base64.encodebytes(self.data)399        out.write(encoded.decode('ascii'))400        out.write("</base64></value>\n")401 402def _binary(data):403    # decode xml element contents into a Binary structure404    value = Binary()405    value.decode(data)406    return value407 408WRAPPERS = (DateTime, Binary)409 410# --------------------------------------------------------------------411# XML parsers412 413class ExpatParser:414    # fast expat parser for Python 2.0 and later.415    def __init__(self, target):416        self._parser = parser = expat.ParserCreate(None, None)417        self._target = target418        parser.StartElementHandler = target.start419        parser.EndElementHandler = target.end420        parser.CharacterDataHandler = target.data421        encoding = None422        target.xml(encoding, None)423 424    def feed(self, data):425        self._parser.Parse(data, False)426 427    def close(self):428        try:429            parser = self._parser430        except AttributeError:431            pass432        else:433            del self._target, self._parser # get rid of circular references434            parser.Parse(b"", True) # end of data435 436# --------------------------------------------------------------------437# XML-RPC marshalling and unmarshalling code438 439##440# XML-RPC marshaller.441#442# @param encoding Default encoding for 8-bit strings.  The default443#     value is None (interpreted as UTF-8).444# @see dumps445 446class Marshaller:447    """Generate an XML-RPC params chunk from a Python data structure.448 449    Create a Marshaller instance for each set of parameters, and use450    the "dumps" method to convert your data (represented as a tuple)451    to an XML-RPC params chunk.  To write a fault response, pass a452    Fault instance instead.  You may prefer to use the "dumps" module453    function for this purpose.454    """455 456    # by the way, if you don't understand what's going on in here,457    # that's perfectly ok.458 459    def __init__(self, encoding=None, allow_none=False):460        self.memo = {}461        self.data = None462        self.encoding = encoding463        self.allow_none = allow_none464 465    dispatch = {}466 467    def dumps(self, values):468        out = []469        write = out.append470        dump = self.__dump471        if isinstance(values, Fault):472            # fault instance473            write("<fault>\n")474            dump({'faultCode': values.faultCode,475                  'faultString': values.faultString},476                 write)477            write("</fault>\n")478        else:479            # parameter block480            # FIXME: the xml-rpc specification allows us to leave out481            # the entire <params> block if there are no parameters.482            # however, changing this may break older code (including483            # old versions of xmlrpclib.py), so this is better left as484            # is for now.  See @XMLRPC3 for more information. /F485            write("<params>\n")486            for v in values:487                write("<param>\n")488                dump(v, write)489                write("</param>\n")490            write("</params>\n")491        result = "".join(out)492        return result493 494    def __dump(self, value, write):495        try:496            f = self.dispatch[type(value)]497        except KeyError:498            # check if this object can be marshalled as a structure499            if not hasattr(value, '__dict__'):500                raise TypeError("cannot marshal %s objects" % type(value))501            # check if this class is a sub-class of a basic type,502            # because we don't know how to marshal these types503            # (e.g. a string sub-class)504            for type_ in type(value).__mro__:505                if type_ in self.dispatch.keys():506                    raise TypeError("cannot marshal %s objects" % type(value))507            # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix508            # for the p3yk merge, this should probably be fixed more neatly.509            f = self.dispatch["_arbitrary_instance"]510        f(self, value, write)511 512    def dump_nil (self, value, write):513        if not self.allow_none:514            raise TypeError("cannot marshal None unless allow_none is enabled")515        write("<value><nil/></value>")516    dispatch[type(None)] = dump_nil517 518    def dump_bool(self, value, write):519        write("<value><boolean>")520        write(value and "1" or "0")521        write("</boolean></value>\n")522    dispatch[bool] = dump_bool523 524    def dump_long(self, value, write):525        if value > MAXINT or value < MININT:526            raise OverflowError("int exceeds XML-RPC limits")527        write("<value><int>")528        write(str(int(value)))529        write("</int></value>\n")530    dispatch[int] = dump_long531 532    # backward compatible533    dump_int = dump_long534 535    def dump_double(self, value, write):536        write("<value><double>")537        write(repr(value))538        write("</double></value>\n")539    dispatch[float] = dump_double540 541    def dump_unicode(self, value, write, escape=escape):542        write("<value><string>")543        write(escape(value))544        write("</string></value>\n")545    dispatch[str] = dump_unicode546 547    def dump_bytes(self, value, write):548        write("<value><base64>\n")549        encoded = base64.encodebytes(value)550        write(encoded.decode('ascii'))551        write("</base64></value>\n")552    dispatch[bytes] = dump_bytes553    dispatch[bytearray] = dump_bytes554 555    def dump_array(self, value, write):556        i = id(value)557        if i in self.memo:558            raise TypeError("cannot marshal recursive sequences")559        self.memo[i] = None560        dump = self.__dump561        write("<value><array><data>\n")562        for v in value:563            dump(v, write)564        write("</data></array></value>\n")565        del self.memo[i]566    dispatch[tuple] = dump_array567    dispatch[list] = dump_array568 569    def dump_struct(self, value, write, escape=escape):570        i = id(value)571        if i in self.memo:572            raise TypeError("cannot marshal recursive dictionaries")573        self.memo[i] = None574        dump = self.__dump575        write("<value><struct>\n")576        for k, v in value.items():577            write("<member>\n")578            if not isinstance(k, str):579                raise TypeError("dictionary key must be string")580            write("<name>%s</name>\n" % escape(k))581            dump(v, write)582            write("</member>\n")583        write("</struct></value>\n")584        del self.memo[i]585    dispatch[dict] = dump_struct586 587    def dump_datetime(self, value, write):588        write("<value><dateTime.iso8601>")589        write(_strftime(value))590        write("</dateTime.iso8601></value>\n")591    dispatch[datetime] = dump_datetime592 593    def dump_instance(self, value, write):594        # check for special wrappers595        if value.__class__ in WRAPPERS:596            self.write = write597            value.encode(self)598            del self.write599        else:600            # store instance attributes as a struct (really?)601            self.dump_struct(value.__dict__, write)602    dispatch[DateTime] = dump_instance603    dispatch[Binary] = dump_instance604    # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix605    # for the p3yk merge, this should probably be fixed more neatly.606    dispatch["_arbitrary_instance"] = dump_instance607 608##609# XML-RPC unmarshaller.610#611# @see loads612 613class Unmarshaller:614    """Unmarshal an XML-RPC response, based on incoming XML event615    messages (start, data, end).  Call close() to get the resulting616    data structure.617 618    Note that this reader is fairly tolerant, and gladly accepts bogus619    XML-RPC data without complaining (but not bogus XML).620    """621 622    # and again, if you don't understand what's going on in here,623    # that's perfectly ok.624 625    def __init__(self, use_datetime=False, use_builtin_types=False):626        self._type = None627        self._stack = []628        self._marks = []629        self._data = []630        self._value = False631        self._methodname = None632        self._encoding = "utf-8"633        self.append = self._stack.append634        self._use_datetime = use_builtin_types or use_datetime635        self._use_bytes = use_builtin_types636 637    def close(self):638        # return response tuple and target method639        if self._type is None or self._marks:640            raise ResponseError()641        if self._type == "fault":642            raise Fault(**self._stack[0])643        return tuple(self._stack)644 645    def getmethodname(self):646        return self._methodname647 648    #649    # event handlers650 651    def xml(self, encoding, standalone):652        self._encoding = encoding653        # FIXME: assert standalone == 1 ???654 655    def start(self, tag, attrs):656        # prepare to handle this element657        if ':' in tag:658            tag = tag.split(':')[-1]659        if tag == "array" or tag == "struct":660            self._marks.append(len(self._stack))661        self._data = []662        if self._value and tag not in self.dispatch:663            raise ResponseError("unknown tag %r" % tag)664        self._value = (tag == "value")665 666    def data(self, text):667        self._data.append(text)668 669    def end(self, tag):670        # call the appropriate end tag handler671        try:672            f = self.dispatch[tag]673        except KeyError:674            if ':' not in tag:675                return # unknown tag ?676            try:677                f = self.dispatch[tag.split(':')[-1]]678            except KeyError:679                return # unknown tag ?680        return f(self, "".join(self._data))681 682    #683    # accelerator support684 685    def end_dispatch(self, tag, data):686        # dispatch data687        try:688            f = self.dispatch[tag]689        except KeyError:690            if ':' not in tag:691                return # unknown tag ?692            try:693                f = self.dispatch[tag.split(':')[-1]]694            except KeyError:695                return # unknown tag ?696        return f(self, data)697 698    #699    # element decoders700 701    dispatch = {}702 703    def end_nil (self, data):704        self.append(None)705        self._value = 0706    dispatch["nil"] = end_nil707 708    def end_boolean(self, data):709        if data == "0":710            self.append(False)711        elif data == "1":712            self.append(True)713        else:714            raise TypeError("bad boolean value")715        self._value = 0716    dispatch["boolean"] = end_boolean717 718    def end_int(self, data):719        self.append(int(data))720        self._value = 0721    dispatch["i1"] = end_int722    dispatch["i2"] = end_int723    dispatch["i4"] = end_int724    dispatch["i8"] = end_int725    dispatch["int"] = end_int726    dispatch["biginteger"] = end_int727 728    def end_double(self, data):729        self.append(float(data))730        self._value = 0731    dispatch["double"] = end_double732    dispatch["float"] = end_double733 734    def end_bigdecimal(self, data):735        self.append(Decimal(data))736        self._value = 0737    dispatch["bigdecimal"] = end_bigdecimal738 739    def end_string(self, data):740        if self._encoding:741            data = data.decode(self._encoding)742        self.append(data)743        self._value = 0744    dispatch["string"] = end_string745    dispatch["name"] = end_string # struct keys are always strings746 747    def end_array(self, data):748        mark = self._marks.pop()749        # map arrays to Python lists750        self._stack[mark:] = [self._stack[mark:]]751        self._value = 0752    dispatch["array"] = end_array753 754    def end_struct(self, data):755        mark = self._marks.pop()756        # map structs to Python dictionaries757        dict = {}758        items = self._stack[mark:]759        for i in range(0, len(items), 2):760            dict[items[i]] = items[i+1]761        self._stack[mark:] = [dict]762        self._value = 0763    dispatch["struct"] = end_struct764 765    def end_base64(self, data):766        value = Binary()767        value.decode(data.encode("ascii"))768        if self._use_bytes:769            value = value.data770        self.append(value)771        self._value = 0772    dispatch["base64"] = end_base64773 774    def end_dateTime(self, data):775        value = DateTime()776        value.decode(data)777        if self._use_datetime:778            value = _datetime_type(data)779        self.append(value)780    dispatch["dateTime.iso8601"] = end_dateTime781 782    def end_value(self, data):783        # if we stumble upon a value element with no internal784        # elements, treat it as a string element785        if self._value:786            self.end_string(data)787    dispatch["value"] = end_value788 789    def end_params(self, data):790        self._type = "params"791    dispatch["params"] = end_params792 793    def end_fault(self, data):794        self._type = "fault"795    dispatch["fault"] = end_fault796 797    def end_methodName(self, data):798        if self._encoding:799            data = data.decode(self._encoding)800        self._methodname = data801        self._type = "methodName" # no params802    dispatch["methodName"] = end_methodName803 804## Multicall support805#806 807class _MultiCallMethod:808    # some lesser magic to store calls made to a MultiCall object809    # for batch execution810    def __init__(self, call_list, name):811        self.__call_list = call_list812        self.__name = name813    def __getattr__(self, name):814        return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name))815    def __call__(self, *args):816        self.__call_list.append((self.__name, args))817 818class MultiCallIterator:819    """Iterates over the results of a multicall. Exceptions are820    raised in response to xmlrpc faults."""821 822    def __init__(self, results):823        self.results = results824 825    def __getitem__(self, i):826        item = self.results[i]827        if isinstance(item, dict):828            raise Fault(item['faultCode'], item['faultString'])829        elif isinstance(item, list):830            return item[0]831        else:832            raise ValueError("unexpected type in multicall result")833 834class MultiCall:835    """server -> an object used to boxcar method calls836 837    server should be a ServerProxy object.838 839    Methods can be added to the MultiCall using normal840    method call syntax e.g.:841 842    multicall = MultiCall(server_proxy)843    multicall.add(2,3)844    multicall.get_address("Guido")845 846    To execute the multicall, call the MultiCall object e.g.:847 848    add_result, address = multicall()849    """850 851    def __init__(self, server):852        self.__server = server853        self.__call_list = []854 855    def __repr__(self):856        return "<%s at %#x>" % (self.__class__.__name__, id(self))857 858    def __getattr__(self, name):859        return _MultiCallMethod(self.__call_list, name)860 861    def __call__(self):862        marshalled_list = []863        for name, args in self.__call_list:864            marshalled_list.append({'methodName' : name, 'params' : args})865 866        return MultiCallIterator(self.__server.system.multicall(marshalled_list))867 868# --------------------------------------------------------------------869# convenience functions870 871FastMarshaller = FastParser = FastUnmarshaller = None872 873##874# Create a parser object, and connect it to an unmarshalling instance.875# This function picks the fastest available XML parser.876#877# return A (parser, unmarshaller) tuple.878 879def getparser(use_datetime=False, use_builtin_types=False):880    """getparser() -> parser, unmarshaller881 882    Create an instance of the fastest available parser, and attach it883    to an unmarshalling object.  Return both objects.884    """885    if FastParser and FastUnmarshaller:886        if use_builtin_types:887            mkdatetime = _datetime_type888            mkbytes = base64.decodebytes889        elif use_datetime:890            mkdatetime = _datetime_type891            mkbytes = _binary892        else:893            mkdatetime = _datetime894            mkbytes = _binary895        target = FastUnmarshaller(True, False, mkbytes, mkdatetime, Fault)896        parser = FastParser(target)897    else:898        target = Unmarshaller(use_datetime=use_datetime, use_builtin_types=use_builtin_types)899        if FastParser:900            parser = FastParser(target)901        else:902            parser = ExpatParser(target)903    return parser, target904 905##906# Convert a Python tuple or a Fault instance to an XML-RPC packet.907#908# @def dumps(params, **options)909# @param params A tuple or Fault instance.910# @keyparam methodname If given, create a methodCall request for911#     this method name.912# @keyparam methodresponse If given, create a methodResponse packet.913#     If used with a tuple, the tuple must be a singleton (that is,914#     it must contain exactly one element).915# @keyparam encoding The packet encoding.916# @return A string containing marshalled data.917 918def dumps(params, methodname=None, methodresponse=None, encoding=None,919          allow_none=False):920    """data [,options] -> marshalled data921 922    Convert an argument tuple or a Fault instance to an XML-RPC923    request (or response, if the methodresponse option is used).924 925    In addition to the data object, the following options can be given926    as keyword arguments:927 928        methodname: the method name for a methodCall packet929 930        methodresponse: true to create a methodResponse packet.931        If this option is used with a tuple, the tuple must be932        a singleton (i.e. it can contain only one element).933 934        encoding: the packet encoding (default is UTF-8)935 936    All byte strings in the data structure are assumed to use the937    packet encoding.  Unicode strings are automatically converted,938    where necessary.939    """940 941    assert isinstance(params, (tuple, Fault)), "argument must be tuple or Fault instance"942    if isinstance(params, Fault):943        methodresponse = 1944    elif methodresponse and isinstance(params, tuple):945        assert len(params) == 1, "response tuple must be a singleton"946 947    if not encoding:948        encoding = "utf-8"949 950    if FastMarshaller:951        m = FastMarshaller(encoding)952    else:953        m = Marshaller(encoding, allow_none)954 955    data = m.dumps(params)956 957    if encoding != "utf-8":958        xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)959    else:960        xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default961 962    # standard XML-RPC wrappings963    if methodname:964        # a method call965        data = (966            xmlheader,967            "<methodCall>\n"968            "<methodName>", methodname, "</methodName>\n",969            data,970            "</methodCall>\n"971            )972    elif methodresponse:973        # a method response, or a fault structure974        data = (975            xmlheader,976            "<methodResponse>\n",977            data,978            "</methodResponse>\n"979            )980    else:981        return data # return as is982    return "".join(data)983 984##985# Convert an XML-RPC packet to a Python object.  If the XML-RPC packet986# represents a fault condition, this function raises a Fault exception.987#988# @param data An XML-RPC packet, given as an 8-bit string.989# @return A tuple containing the unpacked data, and the method name990#     (None if not present).991# @see Fault992 993def loads(data, use_datetime=False, use_builtin_types=False):994    """data -> unmarshalled data, method name995 996    Convert an XML-RPC packet to unmarshalled data plus a method997    name (None if not present).998 999    If the XML-RPC packet represents a fault condition, this function1000    raises a Fault exception.1001    """1002    p, u = getparser(use_datetime=use_datetime, use_builtin_types=use_builtin_types)1003    p.feed(data)1004    p.close()1005    return u.close(), u.getmethodname()1006 1007##1008# Encode a string using the gzip content encoding such as specified by the1009# Content-Encoding: gzip1010# in the HTTP header, as described in RFC 19521011#1012# @param data the unencoded data1013# @return the encoded data1014 1015def gzip_encode(data):1016    """data -> gzip encoded data1017 1018    Encode data using the gzip content encoding as described in RFC 19521019    """1020    if not gzip:1021        raise NotImplementedError1022    f = BytesIO()1023    with gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1) as gzf:1024        gzf.write(data)1025    return f.getvalue()1026 1027##1028# Decode a string using the gzip content encoding such as specified by the1029# Content-Encoding: gzip1030# in the HTTP header, as described in RFC 19521031#1032# @param data The encoded data1033# @keyparam max_decode Maximum bytes to decode (20 MiB default), use negative1034#    values for unlimited decoding1035# @return the unencoded data1036# @raises ValueError if data is not correctly coded.1037# @raises ValueError if max gzipped payload length exceeded1038 1039def gzip_decode(data, max_decode=20971520):1040    """gzip encoded data -> unencoded data1041 1042    Decode data using the gzip content encoding as described in RFC 19521043    """1044    if not gzip:1045        raise NotImplementedError1046    with gzip.GzipFile(mode="rb", fileobj=BytesIO(data)) as gzf:1047        try:1048            if max_decode < 0: # no limit1049                decoded = gzf.read()1050            else:1051                decoded = gzf.read(max_decode + 1)1052        except OSError:1053            raise ValueError("invalid data")1054    if max_decode >= 0 and len(decoded) > max_decode:1055        raise ValueError("max gzipped payload length exceeded")1056    return decoded1057 1058##1059# Return a decoded file-like object for the gzip encoding1060# as described in RFC 1952.1061#1062# @param response A stream supporting a read() method1063# @return a file-like object that the decoded data can be read() from1064 1065class GzipDecodedResponse(gzip.GzipFile if gzip else object):1066    """a file-like object to decode a response encoded with the gzip1067    method, as described in RFC 1952.1068    """1069    def __init__(self, response):1070        #response doesn't support tell() and read(), required by1071        #GzipFile1072        if not gzip:1073            raise NotImplementedError1074        self.io = BytesIO(response.read())1075        gzip.GzipFile.__init__(self, mode="rb", fileobj=self.io)1076 1077    def close(self):1078        try:1079            gzip.GzipFile.close(self)1080        finally:1081            self.io.close()1082 1083 1084# --------------------------------------------------------------------1085# request dispatcher1086 1087class _Method:1088    # some magic to bind an XML-RPC method to an RPC server.1089    # supports "nested" methods (e.g. examples.getStateName)1090    def __init__(self, send, name):1091        self.__send = send1092        self.__name = name1093    def __getattr__(self, name):1094        return _Method(self.__send, "%s.%s" % (self.__name, name))1095    def __call__(self, *args):1096        return self.__send(self.__name, args)1097 1098##1099# Standard transport class for XML-RPC over HTTP.1100# <p>1101# You can create custom transports by subclassing this method, and1102# overriding selected methods.1103 1104class Transport:1105    """Handles an HTTP transaction to an XML-RPC server."""1106 1107    # client identifier (may be overridden)1108    user_agent = "Python-xmlrpc/%s" % __version__1109 1110    #if true, we'll request gzip encoding1111    accept_gzip_encoding = True1112 1113    # if positive, encode request using gzip if it exceeds this threshold1114    # note that many servers will get confused, so only use it if you know1115    # that they can decode such a request1116    encode_threshold = None #None = don't encode1117 1118    def __init__(self, use_datetime=False, use_builtin_types=False,1119                 *, headers=()):1120        self._use_datetime = use_datetime1121        self._use_builtin_types = use_builtin_types1122        self._connection = (None, None)1123        self._headers = list(headers)1124        self._extra_headers = []1125 1126    ##1127    # Send a complete request, and parse the response.1128    # Retry request if a cached connection has disconnected.1129    #1130    # @param host Target host.1131    # @param handler Target PRC handler.1132    # @param request_body XML-RPC request body.1133    # @param verbose Debugging flag.1134    # @return Parsed response.1135 1136    def request(self, host, handler, request_body, verbose=False):1137        #retry request once if cached connection has gone cold1138        for i in (0, 1):1139            try:1140                return self.single_request(host, handler, request_body, verbose)1141            except http.client.RemoteDisconnected:1142                if i:1143                    raise1144            except OSError as e:1145                if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED,1146                                        errno.EPIPE):1147                    raise1148 1149    def single_request(self, host, handler, request_body, verbose=False):1150        # issue XML-RPC request1151        try:1152            http_conn = self.send_request(host, handler, request_body, verbose)1153            resp = http_conn.getresponse()1154            if resp.status == 200:1155                self.verbose = verbose1156                return self.parse_response(resp)1157 1158        except Fault:1159            raise1160        except Exception:1161            #All unexpected errors leave connection in1162            # a strange state, so we clear it.1163            self.close()1164            raise1165 1166        #We got an error response.1167        #Discard any response data and raise exception1168        if resp.getheader("content-length", ""):1169            resp.read()1170        raise ProtocolError(1171            host + handler,1172            resp.status, resp.reason,1173            dict(resp.getheaders())1174            )1175 1176 1177    ##1178    # Create parser.1179    #1180    # @return A 2-tuple containing a parser and an unmarshaller.1181 1182    def getparser(self):1183        # get parser and unmarshaller1184        return getparser(use_datetime=self._use_datetime,1185                         use_builtin_types=self._use_builtin_types)1186 1187    ##1188    # Get authorization info from host parameter1189    # Host may be a string, or a (host, x509-dict) tuple; if a string,1190    # it is checked for a "user:pw@host" format, and a "Basic1191    # Authentication" header is added if appropriate.1192    #1193    # @param host Host descriptor (URL or (URL, x509 info) tuple).1194    # @return A 3-tuple containing (actual host, extra headers,1195    #     x509 info).  The header and x509 fields may be None.1196 1197    def get_host_info(self, host):1198 1199        x509 = {}1200        if isinstance(host, tuple):

Showing the first 1,200 of 1504 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai