Ignore:
Timestamp:
Mar 19, 2014, 11:31:01 PM (11 years ago)
Author:
dmik
Message:

python: Merge vendor 2.7.6 to trunk.

Location:
python/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • python/trunk

  • python/trunk/Lib/xmlrpclib.py

    r2 r391  
    11#
    22# XML-RPC CLIENT LIBRARY
    3 # $Id: xmlrpclib.py 65467 2008-08-04 00:50:11Z brett.cannon $
     3# $Id$
    44#
    55# an XML-RPC client interface for Python.
     
    140140
    141141from types import *
     142import socket
     143import errno
     144import httplib
     145try:
     146    import gzip
     147except ImportError:
     148    gzip = None #python can be built without zlib/gzip support
    142149
    143150# --------------------------------------------------------------------
     
    527534    FastMarshaller = None
    528535
    529 #
    530 # the SGMLOP parser is about 15x faster than Python's builtin
    531 # XML parser.  SGMLOP sources can be downloaded from:
    532 #
    533 #     http://www.pythonware.com/products/xml/sgmlop.htm
    534 #
    535 
    536 try:
    537     import sgmlop
    538     if not hasattr(sgmlop, "XMLParser"):
    539         raise ImportError
    540 except ImportError:
    541     SgmlopParser = None # sgmlop accelerator not available
    542 else:
    543     class SgmlopParser:
    544         def __init__(self, target):
    545 
    546             # setup callbacks
    547             self.finish_starttag = target.start
    548             self.finish_endtag = target.end
    549             self.handle_data = target.data
    550             self.handle_xml = target.xml
    551 
    552             # activate parser
    553             self.parser = sgmlop.XMLParser()
    554             self.parser.register(self)
    555             self.feed = self.parser.feed
    556             self.entity = {
    557                 "amp": "&", "gt": ">", "lt": "<",
    558                 "apos": "'", "quot": '"'
    559                 }
    560 
    561         def close(self):
    562             try:
    563                 self.parser.close()
    564             finally:
    565                 self.parser = self.feed = None # nuke circular reference
    566 
    567         def handle_proc(self, tag, attr):
    568             m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
    569             if m:
    570                 self.handle_xml(m.group(1), 1)
    571 
    572         def handle_entityref(self, entity):
    573             # <string> entity
    574             try:
    575                 self.handle_data(self.entity[entity])
    576             except KeyError:
    577                 self.handle_data("&%s;" % entity)
    578 
    579536try:
    580537    from xml.parsers import expat
     
    585542else:
    586543    class ExpatParser:
    587         # fast expat parser for Python 2.0 and later.  this is about
    588         # 50% slower than sgmlop, on roundtrip testing
     544        # fast expat parser for Python 2.0 and later.
    589545        def __init__(self, target):
    590546            self._parser = parser = expat.ParserCreate(None, None)
     
    607563class SlowParser:
    608564    """Default XML parser (based on xmllib.XMLParser)."""
    609     # this is about 10 times slower than sgmlop, on roundtrip
    610     # testing.
     565    # this is the slowest parser.
    611566    def __init__(self, target):
    612567        import xmllib # lazy subclassing (!)
     
    991946class MultiCallIterator:
    992947    """Iterates over the results of a multicall. Exceptions are
    993     thrown in response to xmlrpc faults."""
     948    raised in response to xmlrpc faults."""
    994949
    995950    def __init__(self, results):
     
    10701025        if FastParser:
    10711026            parser = FastParser(target)
    1072         elif SgmlopParser:
    1073             parser = SgmlopParser(target)
    10741027        elif ExpatParser:
    10751028            parser = ExpatParser(target)
     
    11841137    return u.close(), u.getmethodname()
    11851138
     1139##
     1140# Encode a string using the gzip content encoding such as specified by the
     1141# Content-Encoding: gzip
     1142# in the HTTP header, as described in RFC 1952
     1143#
     1144# @param data the unencoded data
     1145# @return the encoded data
     1146
     1147def gzip_encode(data):
     1148    """data -> gzip encoded data
     1149
     1150    Encode data using the gzip content encoding as described in RFC 1952
     1151    """
     1152    if not gzip:
     1153        raise NotImplementedError
     1154    f = StringIO.StringIO()
     1155    gzf = gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1)
     1156    gzf.write(data)
     1157    gzf.close()
     1158    encoded = f.getvalue()
     1159    f.close()
     1160    return encoded
     1161
     1162##
     1163# Decode a string using the gzip content encoding such as specified by the
     1164# Content-Encoding: gzip
     1165# in the HTTP header, as described in RFC 1952
     1166#
     1167# @param data The encoded data
     1168# @return the unencoded data
     1169# @raises ValueError if data is not correctly coded.
     1170
     1171def gzip_decode(data):
     1172    """gzip encoded data -> unencoded data
     1173
     1174    Decode data using the gzip content encoding as described in RFC 1952
     1175    """
     1176    if not gzip:
     1177        raise NotImplementedError
     1178    f = StringIO.StringIO(data)
     1179    gzf = gzip.GzipFile(mode="rb", fileobj=f)
     1180    try:
     1181        decoded = gzf.read()
     1182    except IOError:
     1183        raise ValueError("invalid data")
     1184    f.close()
     1185    gzf.close()
     1186    return decoded
     1187
     1188##
     1189# Return a decoded file-like object for the gzip encoding
     1190# as described in RFC 1952.
     1191#
     1192# @param response A stream supporting a read() method
     1193# @return a file-like object that the decoded data can be read() from
     1194
     1195class GzipDecodedResponse(gzip.GzipFile if gzip else object):
     1196    """a file-like object to decode a response encoded with the gzip
     1197    method, as described in RFC 1952.
     1198    """
     1199    def __init__(self, response):
     1200        #response doesn't support tell() and read(), required by
     1201        #GzipFile
     1202        if not gzip:
     1203            raise NotImplementedError
     1204        self.stringio = StringIO.StringIO(response.read())
     1205        gzip.GzipFile.__init__(self, mode="rb", fileobj=self.stringio)
     1206
     1207    def close(self):
     1208        gzip.GzipFile.close(self)
     1209        self.stringio.close()
     1210
    11861211
    11871212# --------------------------------------------------------------------
     
    12111236    user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
    12121237
     1238    #if true, we'll request gzip encoding
     1239    accept_gzip_encoding = True
     1240
     1241    # if positive, encode request using gzip if it exceeds this threshold
     1242    # note that many server will get confused, so only use it if you know
     1243    # that they can decode such a request
     1244    encode_threshold = None #None = don't encode
     1245
    12131246    def __init__(self, use_datetime=0):
    12141247        self._use_datetime = use_datetime
    1215 
     1248        self._connection = (None, None)
     1249        self._extra_headers = []
    12161250    ##
    12171251    # Send a complete request, and parse the response.
     1252    # Retry request if a cached connection has disconnected.
    12181253    #
    12191254    # @param host Target host.
     
    12241259
    12251260    def request(self, host, handler, request_body, verbose=0):
     1261        #retry request once if cached connection has gone cold
     1262        for i in (0, 1):
     1263            try:
     1264                return self.single_request(host, handler, request_body, verbose)
     1265            except socket.error, e:
     1266                if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED, errno.EPIPE):
     1267                    raise
     1268            except httplib.BadStatusLine: #close after we sent request
     1269                if i:
     1270                    raise
     1271
     1272    ##
     1273    # Send a complete request, and parse the response.
     1274    #
     1275    # @param host Target host.
     1276    # @param handler Target PRC handler.
     1277    # @param request_body XML-RPC request body.
     1278    # @param verbose Debugging flag.
     1279    # @return Parsed response.
     1280
     1281    def single_request(self, host, handler, request_body, verbose=0):
    12261282        # issue XML-RPC request
    12271283
     
    12301286            h.set_debuglevel(1)
    12311287
    1232         self.send_request(h, handler, request_body)
    1233         self.send_host(h, host)
    1234         self.send_user_agent(h)
    1235         self.send_content(h, request_body)
    1236 
    1237         errcode, errmsg, headers = h.getreply()
    1238 
    1239         if errcode != 200:
    1240             raise ProtocolError(
    1241                 host + handler,
    1242                 errcode, errmsg,
    1243                 headers
    1244                 )
    1245 
    1246         self.verbose = verbose
    1247 
    12481288        try:
    1249             sock = h._conn.sock
    1250         except AttributeError:
    1251             sock = None
    1252 
    1253         return self._parse_response(h.getfile(), sock)
     1289            self.send_request(h, handler, request_body)
     1290            self.send_host(h, host)
     1291            self.send_user_agent(h)
     1292            self.send_content(h, request_body)
     1293
     1294            response = h.getresponse(buffering=True)
     1295            if response.status == 200:
     1296                self.verbose = verbose
     1297                return self.parse_response(response)
     1298        except Fault:
     1299            raise
     1300        except Exception:
     1301            # All unexpected errors leave connection in
     1302            # a strange state, so we clear it.
     1303            self.close()
     1304            raise
     1305
     1306        #discard any response data and raise exception
     1307        if (response.getheader("content-length", 0)):
     1308            response.read()
     1309        raise ProtocolError(
     1310            host + handler,
     1311            response.status, response.reason,
     1312            response.msg,
     1313            )
    12541314
    12551315    ##
     
    13001360
    13011361    def make_connection(self, host):
     1362        #return an existing connection if possible.  This allows
     1363        #HTTP/1.1 keep-alive.
     1364        if self._connection and host == self._connection[0]:
     1365            return self._connection[1]
     1366
    13021367        # create a HTTP connection object from a host descriptor
    1303         import httplib
    1304         host, extra_headers, x509 = self.get_host_info(host)
    1305         return httplib.HTTP(host)
     1368        chost, self._extra_headers, x509 = self.get_host_info(host)
     1369        #store the host argument along with the connection object
     1370        self._connection = host, httplib.HTTPConnection(chost)
     1371        return self._connection[1]
     1372
     1373    ##
     1374    # Clear any cached connection object.
     1375    # Used in the event of socket errors.
     1376    #
     1377    def close(self):
     1378        if self._connection[1]:
     1379            self._connection[1].close()
     1380            self._connection = (None, None)
    13061381
    13071382    ##
     
    13131388
    13141389    def send_request(self, connection, handler, request_body):
    1315         connection.putrequest("POST", handler)
     1390        if (self.accept_gzip_encoding and gzip):
     1391            connection.putrequest("POST", handler, skip_accept_encoding=True)
     1392            connection.putheader("Accept-Encoding", "gzip")
     1393        else:
     1394            connection.putrequest("POST", handler)
    13161395
    13171396    ##
     
    13201399    # @param connection Connection handle.
    13211400    # @param host Host name.
     1401    #
     1402    # Note: This function doesn't actually add the "Host"
     1403    # header anymore, it is done as part of the connection.putrequest() in
     1404    # send_request() above.
    13221405
    13231406    def send_host(self, connection, host):
    1324         host, extra_headers, x509 = self.get_host_info(host)
    1325         connection.putheader("Host", host)
     1407        extra_headers = self._extra_headers
    13261408        if extra_headers:
    13271409            if isinstance(extra_headers, DictType):
     
    13461428    def send_content(self, connection, request_body):
    13471429        connection.putheader("Content-Type", "text/xml")
     1430
     1431        #optionally encode the request
     1432        if (self.encode_threshold is not None and
     1433            self.encode_threshold < len(request_body) and
     1434            gzip):
     1435            connection.putheader("Content-Encoding", "gzip")
     1436            request_body = gzip_encode(request_body)
     1437
    13481438        connection.putheader("Content-Length", str(len(request_body)))
    1349         connection.endheaders()
    1350         if request_body:
    1351             connection.send(request_body)
     1439        connection.endheaders(request_body)
    13521440
    13531441    ##
     
    13571445    # @return Response tuple and target method.
    13581446
    1359     def parse_response(self, file):
    1360         # compatibility interface
    1361         return self._parse_response(file, None)
    1362 
    1363     ##
    1364     # Parse response (alternate interface).  This is similar to the
    1365     # parse_response method, but also provides direct access to the
    1366     # underlying socket object (where available).
    1367     #
    1368     # @param file Stream.
    1369     # @param sock Socket handle (or None, if the socket object
    1370     #    could not be accessed).
    1371     # @return Response tuple and target method.
    1372 
    1373     def _parse_response(self, file, sock):
    1374         # read response from input file/socket, and parse it
     1447    def parse_response(self, response):
     1448        # read response data from httpresponse, and parse it
     1449
     1450        # Check for new http response object, else it is a file object
     1451        if hasattr(response,'getheader'):
     1452            if response.getheader("Content-Encoding", "") == "gzip":
     1453                stream = GzipDecodedResponse(response)
     1454            else:
     1455                stream = response
     1456        else:
     1457            stream = response
    13751458
    13761459        p, u = self.getparser()
    13771460
    13781461        while 1:
    1379             if sock:
    1380                 response = sock.recv(1024)
    1381             else:
    1382                 response = file.read(1024)
    1383             if not response:
     1462            data = stream.read(1024)
     1463            if not data:
    13841464                break
    13851465            if self.verbose:
    1386                 print "body:", repr(response)
    1387             p.feed(response)
    1388 
    1389         file.close()
     1466                print "body:", repr(data)
     1467            p.feed(data)
     1468
     1469        if stream is not response:
     1470            stream.close()
    13901471        p.close()
    13911472
     
    14011482
    14021483    def make_connection(self, host):
     1484        if self._connection and host == self._connection[0]:
     1485            return self._connection[1]
    14031486        # create a HTTPS connection object from a host descriptor
    14041487        # host may be a string, or a (host, x509-dict) tuple
    1405         import httplib
    1406         host, extra_headers, x509 = self.get_host_info(host)
    14071488        try:
    1408             HTTPS = httplib.HTTPS
     1489            HTTPS = httplib.HTTPSConnection
    14091490        except AttributeError:
    14101491            raise NotImplementedError(
     
    14121493                )
    14131494        else:
    1414             return HTTPS(host, None, **(x509 or {}))
     1495            chost, self._extra_headers, x509 = self.get_host_info(host)
     1496            self._connection = host, HTTPS(chost, None, **(x509 or {}))
     1497            return self._connection[1]
    14151498
    14161499##
     
    14571540        # establish a "logical" server connection
    14581541
     1542        if isinstance(uri, unicode):
     1543            uri = uri.encode('ISO-8859-1')
     1544
    14591545        # get the url
    14601546        import urllib
     
    14771563        self.__allow_none = allow_none
    14781564
     1565    def __close(self):
     1566        self.__transport.close()
     1567
    14791568    def __request(self, methodname, params):
    14801569        # call a method on the remote server
     
    15091598    # note: to call a remote object with an non-standard name, use
    15101599    # result getattr(server, "strange-python-name")(args)
     1600
     1601    def __call__(self, attr):
     1602        """A workaround to get special attributes on the ServerProxy
     1603           without interfering with the magic __getattr__
     1604        """
     1605        if attr == "close":
     1606            return self.__close
     1607        elif attr == "transport":
     1608            return self.__transport
     1609        raise AttributeError("Attribute %r not found" % (attr,))
    15111610
    15121611# compatibility
Note: See TracChangeset for help on using the changeset viewer.