From: <pj...@us...> - 2009-11-08 22:59:42
|
Revision: 6940 http://jython.svn.sourceforge.net/jython/?rev=6940&view=rev Author: pjenvey Date: 2009-11-08 22:59:13 +0000 (Sun, 08 Nov 2009) Log Message: ----------- convert gid/uids to ints Modified Paths: -------------- trunk/jython/Lib/grp.py trunk/jython/Lib/pwd.py Modified: trunk/jython/Lib/grp.py =================================================================== --- trunk/jython/Lib/grp.py 2009-11-08 22:28:36 UTC (rev 6939) +++ trunk/jython/Lib/grp.py 2009-11-08 22:59:13 UTC (rev 6940) @@ -35,7 +35,7 @@ attrs = ['gr_name', 'gr_passwd', 'gr_gid', 'gr_mem'] def __new__(cls, grp): - grp = (newString(grp.name), newString(grp.password), grp.GID, + grp = (newString(grp.name), newString(grp.password), int(grp.GID), [newString(member) for member in grp.members]) return tuple.__new__(cls, grp) Modified: trunk/jython/Lib/pwd.py =================================================================== --- trunk/jython/Lib/pwd.py 2009-11-08 22:28:36 UTC (rev 6939) +++ trunk/jython/Lib/pwd.py 2009-11-08 22:59:13 UTC (rev 6940) @@ -29,8 +29,8 @@ 'pw_dir', 'pw_shell'] def __new__(cls, pwd): - pwd = (newString(pwd.loginName), newString(pwd.password), pwd.UID, - pwd.GID, newString(pwd.GECOS), newString(pwd.home), + pwd = (newString(pwd.loginName), newString(pwd.password), int(pwd.UID), + int(pwd.GID), newString(pwd.GECOS), newString(pwd.home), newString(pwd.shell)) return tuple.__new__(cls, pwd) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <am...@us...> - 2010-04-16 17:37:51
|
Revision: 7027 http://jython.svn.sourceforge.net/jython/?rev=7027&view=rev Author: amak Date: 2010-04-16 17:37:44 +0000 (Fri, 16 Apr 2010) Log Message: ----------- Fix for bug 1510: minidom is not parsing comment information correctly http://bugs.jython.org/issue1510 Thanks to William Bernardet for the bug report and the patch. Modified Paths: -------------- trunk/jython/Lib/test/test_pulldom.py trunk/jython/Lib/xml/sax/drivers2/drv_javasax.py Modified: trunk/jython/Lib/test/test_pulldom.py =================================================================== --- trunk/jython/Lib/test/test_pulldom.py 2010-04-14 02:39:55 UTC (rev 7026) +++ trunk/jython/Lib/test/test_pulldom.py 2010-04-16 17:37:44 UTC (rev 7027) @@ -22,6 +22,7 @@ <p>Some greek: ΑΒΓΔΕ</p> <greek attrs="ΖΗΘΙΚ"/> <?greek ΛΜΝΞΟ?> + <!--ΛΜΝΞΟ--> </document> """ @@ -35,7 +36,7 @@ text.append(node.data) try: result = u"".join(text) - self.failUnlessEqual(repr(result), r"u'\n Some greek: \u0391\u0392\u0393\u0394\u0395\n \n \n'") + self.failUnlessEqual(repr(result), r"u'\n Some greek: \u0391\u0392\u0393\u0394\u0395\n \n \n \n'") except Exception, x: self.fail("Unexpected exception joining text pieces: %s" % str(x)) @@ -66,6 +67,17 @@ except Exception, x: self.fail("Unexpected exception joining pi data pieces: %s" % str(x)) + def testComment(self): + commentText = [] + for event, node in pulldom.parse(self.testFile): + if event == pulldom.COMMENT: + commentText.append(node.data) + try: + result = u"".join(commentText) + self.failUnlessEqual(repr(result), r"u'ΛΜΝΞΟ'") + except Exception, x: + self.fail("Unexpected exception joining comment data pieces: %s" % str(x)) + def test_main(): tests = [ UnicodeTests, Modified: trunk/jython/Lib/xml/sax/drivers2/drv_javasax.py =================================================================== --- trunk/jython/Lib/xml/sax/drivers2/drv_javasax.py 2010-04-14 02:39:55 UTC (rev 7026) +++ trunk/jython/Lib/xml/sax/drivers2/drv_javasax.py 2010-04-16 17:37:44 UTC (rev 7027) @@ -25,6 +25,7 @@ from org.python.core import FilelikeInputStream from org.xml.sax.helpers import XMLReaderFactory from org.xml import sax as javasax + from org.xml.sax.ext import LexicalHandler except ImportError: raise _exceptions.SAXReaderNotAvailable("SAX is not on the classpath", None) @@ -119,7 +120,7 @@ return self.sysId # --- JavaSAXParser -class JavaSAXParser(xmlreader.XMLReader, javasax.ContentHandler): +class JavaSAXParser(xmlreader.XMLReader, javasax.ContentHandler, LexicalHandler): "SAX driver for the Java SAX parsers." def __init__(self, jdriver = None): @@ -133,6 +134,10 @@ self.setEntityResolver(self.getEntityResolver()) self.setErrorHandler(self.getErrorHandler()) self.setDTDHandler(self.getDTDHandler()) + try: + self._parser.setProperty("http://xml.org/sax/properties/lexical-handler", self) + except Exception, x: + pass # XMLReader methods @@ -206,6 +211,9 @@ def processingInstruction(self, target, data): self._cont_handler.processingInstruction(target, data) + def comment(self, char, start, len): + self._cont_handler.comment(unicode(String(char, start, len))) + class AttributesImpl: def __init__(self, attrs = None): self._attrs = attrs This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <am...@us...> - 2010-04-17 18:16:51
|
Revision: 7032 http://jython.svn.sourceforge.net/jython/?rev=7032&view=rev Author: amak Date: 2010-04-17 18:16:45 +0000 (Sat, 17 Apr 2010) Log Message: ----------- Fix for bug 1375: XML SAX: attrs.get((None, 'attr')) gives NPE Modified Paths: -------------- trunk/jython/Lib/test/test_sax.py trunk/jython/Lib/xml/sax/drivers2/drv_javasax.py Modified: trunk/jython/Lib/test/test_sax.py =================================================================== --- trunk/jython/Lib/test/test_sax.py 2010-04-17 16:51:11 UTC (rev 7031) +++ trunk/jython/Lib/test/test_sax.py 2010-04-17 18:16:45 UTC (rev 7032) @@ -407,6 +407,29 @@ attrs.getValue((ns_uri, "attr")) == "val" and \ attrs[(ns_uri, "attr")] == "val" +def test_expat_nsattrs_no_namespace(): + parser = make_parser() + parser.setFeature(handler.feature_namespaces, 1) + gather = AttrGatherer() + parser.setContentHandler(gather) + + parser.parse(StringIO("<doc attr='val'/>")) + + attrs = gather._attrs + + return attrs.getLength() == 1 and \ + attrs.getNames() == [(None, "attr")] and \ + attrs.getQNames() == ["attr"] and \ + len(attrs) == 1 and \ + attrs.has_key((None, "attr")) and \ + attrs.keys() == [(None, "attr")] and \ + attrs.get((None, "attr")) == "val" and \ + attrs.get((None, "attr"), 25) == "val" and \ + attrs.items() == [((None, "attr"), "val")] and \ + attrs.values() == ["val"] and \ + attrs.getValue((None, "attr")) == "val" and \ + attrs[(None, "attr")] == "val" + # ===== InputSource support xml_test_out = open(findfile("test.xml.out")).read() Modified: trunk/jython/Lib/xml/sax/drivers2/drv_javasax.py =================================================================== --- trunk/jython/Lib/xml/sax/drivers2/drv_javasax.py 2010-04-17 16:51:11 UTC (rev 7031) +++ trunk/jython/Lib/xml/sax/drivers2/drv_javasax.py 2010-04-17 18:16:45 UTC (rev 7032) @@ -237,7 +237,22 @@ def endEntity(self, name): pass # TODO +def _fixTuple(nsTuple, frm, to): + if len(nsTuple) == 2: + nsUri, localName = nsTuple + if nsUri == frm: + nsUri = to + return (nsUri, localName) + return nsTuple + +def _makeJavaNsTuple(nsTuple): + return _fixTuple(nsTuple, None, '') + +def _makePythonNsTuple(nsTuple): + return _fixTuple(nsTuple, '', None) + class AttributesImpl: + def __init__(self, attrs = None): self._attrs = attrs @@ -245,16 +260,16 @@ return self._attrs.getLength() def getType(self, name): - return self._attrs.getType(name) + return self._attrs.getType(_makeJavaNsTuple(name)) def getValue(self, name): - value = self._attrs.getValue(name) + value = self._attrs.getValue(_makeJavaNsTuple(name)) if value == None: raise KeyError(name) return value def getNames(self): - return [self._attrs.getQName(index) for index in range(len(self))] + return [_makePythonNsTuple(self._attrs.getQName(index)) for index in range(len(self))] def getQNames(self): return [self._attrs.getQName(index) for index in range(len(self))] @@ -272,7 +287,7 @@ return qname def getQNameByName(self, name): - idx = self._attrs.getIndex(name) + idx = self._attrs.getIndex(_makeJavaNsTuple(name)) if idx == -1: raise KeyError, name return name @@ -316,10 +331,12 @@ AttributesImpl.__init__(self, attrs) def getType(self, name): + name = _makeJavaNsTuple(name) return self._attrs.getType(name[0], name[1]) def getValue(self, name): - value = self._attrs.getValue(name[0], name[1]) + jname = _makeJavaNsTuple(name) + value = self._attrs.getValue(jname[0], jname[1]) if value == None: raise KeyError(name) return value @@ -327,17 +344,17 @@ def getNames(self): names = [] for idx in range(len(self)): - names.append((self._attrs.getURI(idx), - self._attrs.getLocalName(idx))) + names.append(_makePythonNsTuple( (self._attrs.getURI(idx), self._attrs.getLocalName(idx)) )) return names def getNameByQName(self, qname): idx = self._attrs.getIndex(qname) if idx == -1: raise KeyError, qname - return (self._attrs.getURI(idx), self._attrs.getLocalName(idx)) + return _makePythonNsTuple( (self._attrs.getURI(idx), self._attrs.getLocalName(idx)) ) def getQNameByName(self, name): + name = _makeJavaNsTuple(name) idx = self._attrs.getIndex(name[0], name[1]) if idx == -1: raise KeyError, name This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <am...@us...> - 2010-10-03 17:54:56
|
Revision: 7130 http://jython.svn.sourceforge.net/jython/?rev=7130&view=rev Author: amak Date: 2010-10-03 17:54:49 +0000 (Sun, 03 Oct 2010) Log Message: ----------- Copying over SocketServer and test_socketserver.py from CPythonLib in order to make some jython-specific bug fixes. Added Paths: ----------- trunk/jython/Lib/SocketServer.py trunk/jython/Lib/test/test_socketserver.py Added: trunk/jython/Lib/SocketServer.py =================================================================== --- trunk/jython/Lib/SocketServer.py (rev 0) +++ trunk/jython/Lib/SocketServer.py 2010-10-03 17:54:49 UTC (rev 7130) @@ -0,0 +1,588 @@ +"""Generic socket server classes. + +This module tries to capture the various aspects of defining a server: + +For socket-based servers: + +- address family: + - AF_INET{,6}: IP (Internet Protocol) sockets (default) + - AF_UNIX: Unix domain sockets + - others, e.g. AF_DECNET are conceivable (see <socket.h> +- socket type: + - SOCK_STREAM (reliable stream, e.g. TCP) + - SOCK_DGRAM (datagrams, e.g. UDP) + +For request-based servers (including socket-based): + +- client address verification before further looking at the request + (This is actually a hook for any processing that needs to look + at the request before anything else, e.g. logging) +- how to handle multiple requests: + - synchronous (one request is handled at a time) + - forking (each request is handled by a new process) + - threading (each request is handled by a new thread) + +The classes in this module favor the server type that is simplest to +write: a synchronous TCP/IP server. This is bad class design, but +save some typing. (There's also the issue that a deep class hierarchy +slows down method lookups.) + +There are five classes in an inheritance diagram, four of which represent +synchronous servers of four types: + + +------------+ + | BaseServer | + +------------+ + | + v + +-----------+ +------------------+ + | TCPServer |------->| UnixStreamServer | + +-----------+ +------------------+ + | + v + +-----------+ +--------------------+ + | UDPServer |------->| UnixDatagramServer | + +-----------+ +--------------------+ + +Note that UnixDatagramServer derives from UDPServer, not from +UnixStreamServer -- the only difference between an IP and a Unix +stream server is the address family, which is simply repeated in both +unix server classes. + +Forking and threading versions of each type of server can be created +using the ForkingMixIn and ThreadingMixIn mix-in classes. For +instance, a threading UDP server class is created as follows: + + class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass + +The Mix-in class must come first, since it overrides a method defined +in UDPServer! Setting the various member variables also changes +the behavior of the underlying server mechanism. + +To implement a service, you must derive a class from +BaseRequestHandler and redefine its handle() method. You can then run +various versions of the service by combining one of the server classes +with your request handler class. + +The request handler class must be different for datagram or stream +services. This can be hidden by using the request handler +subclasses StreamRequestHandler or DatagramRequestHandler. + +Of course, you still have to use your head! + +For instance, it makes no sense to use a forking server if the service +contains state in memory that can be modified by requests (since the +modifications in the child process would never reach the initial state +kept in the parent process and passed to each child). In this case, +you can use a threading server, but you will probably have to use +locks to avoid two requests that come in nearly simultaneous to apply +conflicting changes to the server state. + +On the other hand, if you are building e.g. an HTTP server, where all +data is stored externally (e.g. in the file system), a synchronous +class will essentially render the service "deaf" while one request is +being handled -- which may be for a very long time if a client is slow +to reqd all the data it has requested. Here a threading or forking +server is appropriate. + +In some cases, it may be appropriate to process part of a request +synchronously, but to finish processing in a forked child depending on +the request data. This can be implemented by using a synchronous +server and doing an explicit fork in the request handler class +handle() method. + +Another approach to handling multiple simultaneous requests in an +environment that supports neither threads nor fork (or where these are +too expensive or inappropriate for the service) is to maintain an +explicit table of partially finished requests and to use select() to +decide which request to work on next (or whether to handle a new +incoming request). This is particularly important for stream services +where each client can potentially be connected for a long time (if +threads or subprocesses cannot be used). + +Future work: +- Standard classes for Sun RPC (which uses either UDP or TCP) +- Standard mix-in classes to implement various authentication + and encryption schemes +- Standard framework for select-based multiplexing + +XXX Open problems: +- What to do with out-of-band data? + +BaseServer: +- split generic "request" functionality out into BaseServer class. + Copyright (C) 2000 Luke Kenneth Casson Leighton <lk...@sa...> + + example: read entries from a SQL database (requires overriding + get_request() to return a table entry from the database). + entry is processed by a RequestHandlerClass. + +""" + +# Author of the BaseServer patch: Luke Kenneth Casson Leighton + +# XXX Warning! +# There is a test suite for this module, but it cannot be run by the +# standard regression test. +# To run it manually, run Lib/test/test_socketserver.py. + +__version__ = "0.4" + + +import socket +import sys +import os + +__all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer", + "ThreadingUDPServer","ThreadingTCPServer","BaseRequestHandler", + "StreamRequestHandler","DatagramRequestHandler", + "ThreadingMixIn", "ForkingMixIn"] +if hasattr(socket, "AF_UNIX"): + __all__.extend(["UnixStreamServer","UnixDatagramServer", + "ThreadingUnixStreamServer", + "ThreadingUnixDatagramServer"]) + +class BaseServer: + + """Base class for server classes. + + Methods for the caller: + + - __init__(server_address, RequestHandlerClass) + - serve_forever() + - handle_request() # if you do not use serve_forever() + - fileno() -> int # for select() + + Methods that may be overridden: + + - server_bind() + - server_activate() + - get_request() -> request, client_address + - verify_request(request, client_address) + - server_close() + - process_request(request, client_address) + - close_request(request) + - handle_error() + + Methods for derived classes: + + - finish_request(request, client_address) + + Class variables that may be overridden by derived classes or + instances: + + - address_family + - socket_type + - allow_reuse_address + + Instance variables: + + - RequestHandlerClass + - socket + + """ + + def __init__(self, server_address, RequestHandlerClass): + """Constructor. May be extended, do not override.""" + self.server_address = server_address + self.RequestHandlerClass = RequestHandlerClass + + def server_activate(self): + """Called by constructor to activate the server. + + May be overridden. + + """ + pass + + def serve_forever(self): + """Handle one request at a time until doomsday.""" + while 1: + self.handle_request() + + # The distinction between handling, getting, processing and + # finishing a request is fairly arbitrary. Remember: + # + # - handle_request() is the top-level call. It calls + # get_request(), verify_request() and process_request() + # - get_request() is different for stream or datagram sockets + # - process_request() is the place that may fork a new process + # or create a new thread to finish the request + # - finish_request() instantiates the request handler class; + # this constructor will handle the request all by itself + + def handle_request(self): + """Handle one request, possibly blocking.""" + try: + request, client_address = self.get_request() + except socket.error: + return + if self.verify_request(request, client_address): + try: + self.process_request(request, client_address) + except: + self.handle_error(request, client_address) + self.close_request(request) + + def verify_request(self, request, client_address): + """Verify the request. May be overridden. + + Return True if we should proceed with this request. + + """ + return True + + def process_request(self, request, client_address): + """Call finish_request. + + Overridden by ForkingMixIn and ThreadingMixIn. + + """ + self.finish_request(request, client_address) + self.close_request(request) + + def server_close(self): + """Called to clean-up the server. + + May be overridden. + + """ + pass + + def finish_request(self, request, client_address): + """Finish one request by instantiating RequestHandlerClass.""" + self.RequestHandlerClass(request, client_address, self) + + def close_request(self, request): + """Called to clean up an individual request.""" + pass + + def handle_error(self, request, client_address): + """Handle an error gracefully. May be overridden. + + The default is to print a traceback and continue. + + """ + print '-'*40 + print 'Exception happened during processing of request from', + print client_address + import traceback + traceback.print_exc() # XXX But this goes to stderr! + print '-'*40 + + +class TCPServer(BaseServer): + + """Base class for various socket-based server classes. + + Defaults to synchronous IP stream (i.e., TCP). + + Methods for the caller: + + - __init__(server_address, RequestHandlerClass) + - serve_forever() + - handle_request() # if you don't use serve_forever() + - fileno() -> int # for select() + + Methods that may be overridden: + + - server_bind() + - server_activate() + - get_request() -> request, client_address + - verify_request(request, client_address) + - process_request(request, client_address) + - close_request(request) + - handle_error() + + Methods for derived classes: + + - finish_request(request, client_address) + + Class variables that may be overridden by derived classes or + instances: + + - address_family + - socket_type + - request_queue_size (only for stream sockets) + - allow_reuse_address + + Instance variables: + + - server_address + - RequestHandlerClass + - socket + + """ + + address_family = socket.AF_INET + + socket_type = socket.SOCK_STREAM + + request_queue_size = 5 + + allow_reuse_address = False + + def __init__(self, server_address, RequestHandlerClass): + """Constructor. May be extended, do not override.""" + BaseServer.__init__(self, server_address, RequestHandlerClass) + self.socket = socket.socket(self.address_family, + self.socket_type) + self.server_bind() + self.server_activate() + + def server_bind(self): + """Called by constructor to bind the socket. + + May be overridden. + + """ + if self.allow_reuse_address: + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.socket.bind(self.server_address) + self.server_address = self.socket.getsockname() + + def server_activate(self): + """Called by constructor to activate the server. + + May be overridden. + + """ + self.socket.listen(self.request_queue_size) + + def server_close(self): + """Called to clean-up the server. + + May be overridden. + + """ + self.socket.close() + + def fileno(self): + """Return socket file number. + + Interface required by select(). + + """ + return self.socket.fileno() + + def get_request(self): + """Get the request and client address from the socket. + + May be overridden. + + """ + return self.socket.accept() + + def close_request(self, request): + """Called to clean up an individual request.""" + request.close() + + +class UDPServer(TCPServer): + + """UDP server class.""" + + allow_reuse_address = False + + socket_type = socket.SOCK_DGRAM + + max_packet_size = 8192 + + def get_request(self): + data, client_addr = self.socket.recvfrom(self.max_packet_size) + return (data, self.socket), client_addr + + def server_activate(self): + # No need to call listen() for UDP. + pass + + def close_request(self, request): + # No need to close anything. + pass + +class ForkingMixIn: + + """Mix-in class to handle each request in a new process.""" + + active_children = None + max_children = 40 + + def collect_children(self): + """Internal routine to wait for died children.""" + while self.active_children: + if len(self.active_children) < self.max_children: + options = os.WNOHANG + else: + # If the maximum number of children are already + # running, block while waiting for a child to exit + options = 0 + try: + pid, status = os.waitpid(0, options) + except os.error: + pid = None + if not pid: break + self.active_children.remove(pid) + + def process_request(self, request, client_address): + """Fork a new subprocess to process the request.""" + self.collect_children() + pid = os.fork() + if pid: + # Parent process + if self.active_children is None: + self.active_children = [] + self.active_children.append(pid) + self.close_request(request) + return + else: + # Child process. + # This must never return, hence os._exit()! + try: + self.finish_request(request, client_address) + os._exit(0) + except: + try: + self.handle_error(request, client_address) + finally: + os._exit(1) + + +class ThreadingMixIn: + """Mix-in class to handle each request in a new thread.""" + + # Decides how threads will act upon termination of the + # main process + daemon_threads = False + + def process_request_thread(self, request, client_address): + """Same as in BaseServer but as a thread. + + In addition, exception handling is done here. + + """ + try: + self.finish_request(request, client_address) + self.close_request(request) + except: + self.handle_error(request, client_address) + self.close_request(request) + + def process_request(self, request, client_address): + """Start a new thread to process the request.""" + import threading + t = threading.Thread(target = self.process_request_thread, + args = (request, client_address)) + if self.daemon_threads: + t.setDaemon (1) + t.start() + + +class ForkingUDPServer(ForkingMixIn, UDPServer): pass +class ForkingTCPServer(ForkingMixIn, TCPServer): pass + +class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass +class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass + +if hasattr(socket, 'AF_UNIX'): + + class UnixStreamServer(TCPServer): + address_family = socket.AF_UNIX + + class UnixDatagramServer(UDPServer): + address_family = socket.AF_UNIX + + class ThreadingUnixStreamServer(ThreadingMixIn, UnixStreamServer): pass + + class ThreadingUnixDatagramServer(ThreadingMixIn, UnixDatagramServer): pass + +class BaseRequestHandler: + + """Base class for request handler classes. + + This class is instantiated for each request to be handled. The + constructor sets the instance variables request, client_address + and server, and then calls the handle() method. To implement a + specific service, all you need to do is to derive a class which + defines a handle() method. + + The handle() method can find the request as self.request, the + client address as self.client_address, and the server (in case it + needs access to per-server information) as self.server. Since a + separate instance is created for each request, the handle() method + can define arbitrary other instance variariables. + + """ + + def __init__(self, request, client_address, server): + self.request = request + self.client_address = client_address + self.server = server + try: + self.setup() + self.handle() + self.finish() + finally: + sys.exc_traceback = None # Help garbage collection + + def setup(self): + pass + + def handle(self): + pass + + def finish(self): + pass + + +# The following two classes make it possible to use the same service +# class for stream or datagram servers. +# Each class sets up these instance variables: +# - rfile: a file object from which receives the request is read +# - wfile: a file object to which the reply is written +# When the handle() method returns, wfile is flushed properly + + +class StreamRequestHandler(BaseRequestHandler): + + """Define self.rfile and self.wfile for stream sockets.""" + + # Default buffer sizes for rfile, wfile. + # We default rfile to buffered because otherwise it could be + # really slow for large data (a getc() call per byte); we make + # wfile unbuffered because (a) often after a write() we want to + # read and we need to flush the line; (b) big writes to unbuffered + # files are typically optimized by stdio even when big reads + # aren't. + rbufsize = -1 + wbufsize = 0 + + def setup(self): + self.connection = self.request + self.rfile = self.connection.makefile('rb', self.rbufsize) + self.wfile = self.connection.makefile('wb', self.wbufsize) + + def finish(self): + if not self.wfile.closed: + self.wfile.flush() + self.wfile.close() + self.rfile.close() + + +class DatagramRequestHandler(BaseRequestHandler): + + # XXX Regrettably, I cannot get this working on Linux; + # s.recvfrom() doesn't return a meaningful client address. + + """Define self.rfile and self.wfile for datagram sockets.""" + + def setup(self): + try: + from cStringIO import StringIO + except ImportError: + from StringIO import StringIO + self.packet, self.socket = self.request + self.rfile = StringIO(self.packet) + self.wfile = StringIO() + + def finish(self): + self.socket.sendto(self.wfile.getvalue(), self.client_address) Added: trunk/jython/Lib/test/test_socketserver.py =================================================================== --- trunk/jython/Lib/test/test_socketserver.py (rev 0) +++ trunk/jython/Lib/test/test_socketserver.py 2010-10-03 17:54:49 UTC (rev 7130) @@ -0,0 +1,218 @@ +# Test suite for SocketServer.py + +from test import test_support +from test.test_support import (verbose, verify, TESTFN, TestSkipped, + reap_children) +test_support.requires('network') + +from SocketServer import * +import socket +import errno +import select +import time +import threading +import os + +NREQ = 3 +DELAY = 0.5 + +class MyMixinHandler: + def handle(self): + time.sleep(DELAY) + line = self.rfile.readline() + time.sleep(DELAY) + self.wfile.write(line) + +class MyStreamHandler(MyMixinHandler, StreamRequestHandler): + pass + +class MyDatagramHandler(MyMixinHandler, DatagramRequestHandler): + pass + +class MyMixinServer: + def serve_a_few(self): + for i in range(NREQ): + self.handle_request() + def handle_error(self, request, client_address): + self.close_request(request) + self.server_close() + raise + +teststring = "hello world\n" + +def receive(sock, n, timeout=20): + r, w, x = select.select([sock], [], [], timeout) + if sock in r: + return sock.recv(n) + else: + raise RuntimeError, "timed out on %r" % (sock,) + +def testdgram(proto, addr): + s = socket.socket(proto, socket.SOCK_DGRAM) + s.sendto(teststring, addr) + buf = data = receive(s, 100) + while data and '\n' not in buf: + data = receive(s, 100) + buf += data + verify(buf == teststring) + s.close() + +def teststream(proto, addr): + s = socket.socket(proto, socket.SOCK_STREAM) + s.connect(addr) + s.sendall(teststring) + buf = data = receive(s, 100) + while data and '\n' not in buf: + data = receive(s, 100) + buf += data + verify(buf == teststring) + s.close() + +class ServerThread(threading.Thread): + def __init__(self, addr, svrcls, hdlrcls): + threading.Thread.__init__(self) + self.__addr = addr + self.__svrcls = svrcls + self.__hdlrcls = hdlrcls + def run(self): + class svrcls(MyMixinServer, self.__svrcls): + pass + if verbose: print "thread: creating server" + svr = svrcls(self.__addr, self.__hdlrcls) + # pull the address out of the server in case it changed + # this can happen if another process is using the port + addr = svr.server_address + if addr: + self.__addr = addr + if self.__addr != svr.socket.getsockname(): + raise RuntimeError('server_address was %s, expected %s' % + (self.__addr, svr.socket.getsockname())) + if verbose: print "thread: serving three times" + svr.serve_a_few() + if verbose: print "thread: done" + +seed = 0 +def pickport(): + global seed + seed += 1 + return 10000 + (os.getpid() % 1000)*10 + seed + +host = "localhost" +testfiles = [] +def pickaddr(proto): + if proto == socket.AF_INET: + return (host, pickport()) + else: + fn = TESTFN + str(pickport()) + if os.name == 'os2': + # AF_UNIX socket names on OS/2 require a specific prefix + # which can't include a drive letter and must also use + # backslashes as directory separators + if fn[1] == ':': + fn = fn[2:] + if fn[0] in (os.sep, os.altsep): + fn = fn[1:] + fn = os.path.join('\socket', fn) + if os.sep == '/': + fn = fn.replace(os.sep, os.altsep) + else: + fn = fn.replace(os.altsep, os.sep) + testfiles.append(fn) + return fn + +def cleanup(): + for fn in testfiles: + try: + os.remove(fn) + except os.error: + pass + testfiles[:] = [] + +def testloop(proto, servers, hdlrcls, testfunc): + for svrcls in servers: + addr = pickaddr(proto) + if verbose: + print "ADDR =", addr + print "CLASS =", svrcls + t = ServerThread(addr, svrcls, hdlrcls) + if verbose: print "server created" + t.start() + if verbose: print "server running" + for i in range(NREQ): + time.sleep(DELAY) + if verbose: print "test client", i + testfunc(proto, addr) + if verbose: print "waiting for server" + t.join() + if verbose: print "done" + +class ForgivingTCPServer(TCPServer): + # prevent errors if another process is using the port we want + def server_bind(self): + host, default_port = self.server_address + # this code shamelessly stolen from test.test_support + # the ports were changed to protect the innocent + import sys + for port in [default_port, 3434, 8798, 23833]: + try: + self.server_address = host, port + TCPServer.server_bind(self) + break + except socket.error, (err, msg): + if err != errno.EADDRINUSE: + raise + print >>sys.__stderr__, \ + ' WARNING: failed to listen on port %d, trying another' % port + +tcpservers = [ForgivingTCPServer, ThreadingTCPServer] +if hasattr(os, 'fork') and os.name not in ('os2',): + tcpservers.append(ForkingTCPServer) +udpservers = [UDPServer, ThreadingUDPServer] +if hasattr(os, 'fork') and os.name not in ('os2',): + udpservers.append(ForkingUDPServer) + +if not hasattr(socket, 'AF_UNIX'): + streamservers = [] + dgramservers = [] +else: + class ForkingUnixStreamServer(ForkingMixIn, UnixStreamServer): pass + streamservers = [UnixStreamServer, ThreadingUnixStreamServer] + if hasattr(os, 'fork') and os.name not in ('os2',): + streamservers.append(ForkingUnixStreamServer) + class ForkingUnixDatagramServer(ForkingMixIn, UnixDatagramServer): pass + dgramservers = [UnixDatagramServer, ThreadingUnixDatagramServer] + if hasattr(os, 'fork') and os.name not in ('os2',): + dgramservers.append(ForkingUnixDatagramServer) + +def sloppy_cleanup(): + # See http://python.org/sf/1540386 + # We need to reap children here otherwise a child from one server + # can be left running for the next server and cause a test failure. + time.sleep(DELAY) + reap_children() + +def testall(): + testloop(socket.AF_INET, tcpservers, MyStreamHandler, teststream) + sloppy_cleanup() + testloop(socket.AF_INET, udpservers, MyDatagramHandler, testdgram) + if hasattr(socket, 'AF_UNIX'): + sloppy_cleanup() + testloop(socket.AF_UNIX, streamservers, MyStreamHandler, teststream) + # Alas, on Linux (at least) recvfrom() doesn't return a meaningful + # client address so this cannot work: + ##testloop(socket.AF_UNIX, dgramservers, MyDatagramHandler, testdgram) + +def test_main(): + import imp + if imp.lock_held(): + # If the import lock is held, the threads will hang. + raise TestSkipped("can't run when import lock is held") + + try: + testall() + finally: + cleanup() + reap_children() + +if __name__ == "__main__": + test_main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <am...@us...> - 2010-10-03 19:47:19
|
Revision: 7131 http://jython.svn.sourceforge.net/jython/?rev=7131&view=rev Author: amak Date: 2010-10-03 19:47:13 +0000 (Sun, 03 Oct 2010) Log Message: ----------- Fix and tests for a jython-specific bug relating to deferred creation of sockets. Modified Paths: -------------- trunk/jython/Lib/SocketServer.py Added Paths: ----------- trunk/jython/Lib/test/test_socketserver_jython.py Modified: trunk/jython/Lib/SocketServer.py =================================================================== --- trunk/jython/Lib/SocketServer.py 2010-10-03 17:54:49 UTC (rev 7130) +++ trunk/jython/Lib/SocketServer.py 2010-10-03 19:47:13 UTC (rev 7131) @@ -348,6 +348,9 @@ """ self.socket.listen(self.request_queue_size) + # Adding a second call to getsockname() because of this issue + # http://wiki.python.org/jython/NewSocketModule#Deferredsocketcreationonjython + self.server_address = self.socket.getsockname() def server_close(self): """Called to clean-up the server. @@ -517,12 +520,11 @@ self.request = request self.client_address = client_address self.server = server + self.setup() try: - self.setup() self.handle() + finally: self.finish() - finally: - sys.exc_traceback = None # Help garbage collection def setup(self): pass Added: trunk/jython/Lib/test/test_socketserver_jython.py =================================================================== --- trunk/jython/Lib/test/test_socketserver_jython.py (rev 0) +++ trunk/jython/Lib/test/test_socketserver_jython.py 2010-10-03 19:47:13 UTC (rev 7131) @@ -0,0 +1,17 @@ +# -*- coding: windows-1252 -*- + +import unittest + +import SocketServer + +class TestSocketServer(unittest.TestCase): + + def testEphemeralPort(self): + """ Test that an ephemeral port is set correctly """ + host, port = "localhost", 0 # If we specify 0, system should pick an emphemeral port + server = SocketServer.TCPServer( (host, port), None) # Request handler never instantiated + server_host, server_port = server.server_address + self.failIfEqual(server_port, 0, "System assigned ephemeral port should not be zero") + +if __name__ == "__main__": + unittest.main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <otm...@us...> - 2011-01-23 15:51:40
|
Revision: 7189 http://jython.svn.sourceforge.net/jython/?rev=7189&view=rev Author: otmarhumbel Date: 2011-01-23 15:51:34 +0000 (Sun, 23 Jan 2011) Log Message: ----------- fix bug #1697 by excluding IPv6 addresses from socket.getaddrinfo(); this is a temporary fix, since we should support IPv6 in the future Modified Paths: -------------- trunk/jython/Lib/socket.py Added Paths: ----------- trunk/jython/Lib/test/test_socket_jy.py Modified: trunk/jython/Lib/socket.py =================================================================== --- trunk/jython/Lib/socket.py 2011-01-22 21:24:58 UTC (rev 7188) +++ trunk/jython/Lib/socket.py 2011-01-23 15:51:34 UTC (rev 7189) @@ -617,16 +617,18 @@ for a in java.net.InetAddress.getAllByName(host): if len([f for f in filter_fns if f(a)]): family = {java.net.Inet4Address: AF_INET, java.net.Inet6Address: AF_INET6}[a.getClass()] - if passive_mode and not canonname_mode: - canonname = "" - else: - canonname = asPyString(a.getCanonicalHostName()) - if host is None and passive_mode and not canonname_mode: - sockname = INADDR_ANY - else: - sockname = asPyString(a.getHostAddress()) - # TODO: Include flowinfo and scopeid in a 4-tuple for IPv6 addresses - results.append((family, socktype, proto, canonname, (sockname, port))) + # bug 1697: exclude IPv6 addresses from being returned + if family != AF_INET6: + if passive_mode and not canonname_mode: + canonname = "" + else: + canonname = asPyString(a.getCanonicalHostName()) + if host is None and passive_mode and not canonname_mode: + sockname = INADDR_ANY + else: + sockname = asPyString(a.getHostAddress()) + # TODO: Include flowinfo and scopeid in a 4-tuple for IPv6 addresses + results.append((family, socktype, proto, canonname, (sockname, port))) return results except java.lang.Exception, jlx: raise _map_exception(jlx) Added: trunk/jython/Lib/test/test_socket_jy.py =================================================================== --- trunk/jython/Lib/test/test_socket_jy.py (rev 0) +++ trunk/jython/Lib/test/test_socket_jy.py 2011-01-23 15:51:34 UTC (rev 7189) @@ -0,0 +1,25 @@ +import httplib +import socket +import sys +from test import test_support +import unittest + + +class SocketIPv6Test(unittest.TestCase): + + def test_connect_localhost(self): + '''Ensures a correct socket.error message''' + conn = httplib.HTTPConnection('localhost', 18080) + body = "" + headers = {} + try: + conn.request("GET", "/RELEASE-NOTES.txt", body, headers) + except socket.error: + pass # used to get an AssertionError (see bug 1697) + + +def test_main(): + test_support.run_unittest(SocketIPv6Test) + +if __name__ == "__main__": + test_main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <am...@us...> - 2011-01-25 23:46:33
|
Revision: 7191 http://jython.svn.sourceforge.net/jython/?rev=7191&view=rev Author: amak Date: 2011-01-25 23:46:27 +0000 (Tue, 25 Jan 2011) Log Message: ----------- 1. Re-enabling the use of IPV6/AF_INET6 addresses 2. Adding workarounds for systems that have problems with IPV6 on java 3. Test for #2 Modified Paths: -------------- trunk/jython/Lib/socket.py trunk/jython/Lib/test/test_socket.py Modified: trunk/jython/Lib/socket.py =================================================================== --- trunk/jython/Lib/socket.py 2011-01-23 15:58:55 UTC (rev 7190) +++ trunk/jython/Lib/socket.py 2011-01-25 23:46:27 UTC (rev 7191) @@ -94,29 +94,40 @@ class timeout(error): pass class sslerror(error): pass +def _unmapped_exception(exc): + return error(-1, 'Unmapped exception: %s' % exc) + +def java_net_socketexception_handler(exc): + if exc.message.startswith("Address family not supported by protocol family"): + return error(errno.EAFNOSUPPORT, 'Address family not supported by protocol family: See http://wiki.python.org/jython/NewSocketModule#IPV6addresssupport') + return _unmapped_exception(exc) + +def would_block_error(exc=None): + return error(errno.EWOULDBLOCK, 'The socket operation could not complete without blocking') + ALL = None _exception_map = { -# (<javaexception>, <circumstance>) : lambda: <code that raises the python equivalent>, or None to stub out as unmapped +# (<javaexception>, <circumstance>) : callable that raises the python equivalent exception, or None to stub out as unmapped -(java.io.IOException, ALL) : lambda: error(errno.ECONNRESET, 'Software caused connection abort'), -(java.io.InterruptedIOException, ALL) : lambda: timeout('timed out'), +(java.io.IOException, ALL) : lambda x: error(errno.ECONNRESET, 'Software caused connection abort'), +(java.io.InterruptedIOException, ALL) : lambda x: timeout('timed out'), -(java.net.BindException, ALL) : lambda: error(errno.EADDRINUSE, 'Address already in use'), -(java.net.ConnectException, ALL) : lambda: error(errno.ECONNREFUSED, 'Connection refused'), +(java.net.BindException, ALL) : lambda x: error(errno.EADDRINUSE, 'Address already in use'), +(java.net.ConnectException, ALL) : lambda x: error(errno.ECONNREFUSED, 'Connection refused'), (java.net.NoRouteToHostException, ALL) : None, (java.net.PortUnreachableException, ALL) : None, (java.net.ProtocolException, ALL) : None, -(java.net.SocketException, ALL) : None, -(java.net.SocketTimeoutException, ALL) : lambda: timeout('timed out'), -(java.net.UnknownHostException, ALL) : lambda: gaierror(errno.EGETADDRINFOFAILED, 'getaddrinfo failed'), +(java.net.SocketException, ALL) : java_net_socketexception_handler, +(java.net.SocketTimeoutException, ALL) : lambda x: timeout('timed out'), +(java.net.UnknownHostException, ALL) : lambda x: gaierror(errno.EGETADDRINFOFAILED, 'getaddrinfo failed'), -(java.nio.channels.AlreadyConnectedException, ALL) : lambda: error(errno.EISCONN, 'Socket is already connected'), +(java.nio.channels.AlreadyConnectedException, ALL) : lambda x: error(errno.EISCONN, 'Socket is already connected'), (java.nio.channels.AsynchronousCloseException, ALL) : None, (java.nio.channels.CancelledKeyException, ALL) : None, (java.nio.channels.ClosedByInterruptException, ALL) : None, -(java.nio.channels.ClosedChannelException, ALL) : lambda: error(errno.EPIPE, 'Socket closed'), +(java.nio.channels.ClosedChannelException, ALL) : lambda x: error(errno.EPIPE, 'Socket closed'), (java.nio.channels.ClosedSelectorException, ALL) : None, (java.nio.channels.ConnectionPendingException, ALL) : None, (java.nio.channels.IllegalBlockingModeException, ALL) : None, @@ -126,28 +137,25 @@ (java.nio.channels.NonWritableChannelException, ALL) : None, (java.nio.channels.NotYetBoundException, ALL) : None, (java.nio.channels.NotYetConnectedException, ALL) : None, -(java.nio.channels.UnresolvedAddressException, ALL) : lambda: gaierror(errno.EGETADDRINFOFAILED, 'getaddrinfo failed'), +(java.nio.channels.UnresolvedAddressException, ALL) : lambda x: gaierror(errno.EGETADDRINFOFAILED, 'getaddrinfo failed'), (java.nio.channels.UnsupportedAddressTypeException, ALL) : None, # These error codes are currently wrong: getting them correct is going to require # some investigation. Cpython 2.6 introduced extensive SSL support. -(javax.net.ssl.SSLException, ALL) : lambda: sslerror(-1, 'SSL exception'), -(javax.net.ssl.SSLHandshakeException, ALL) : lambda: sslerror(-1, 'SSL handshake exception'), -(javax.net.ssl.SSLKeyException, ALL) : lambda: sslerror(-1, 'SSL key exception'), -(javax.net.ssl.SSLPeerUnverifiedException, ALL) : lambda: sslerror(-1, 'SSL peer unverified exception'), -(javax.net.ssl.SSLProtocolException, ALL) : lambda: sslerror(-1, 'SSL protocol exception'), +(javax.net.ssl.SSLException, ALL) : lambda x: sslerror(-1, 'SSL exception'), +(javax.net.ssl.SSLHandshakeException, ALL) : lambda x: sslerror(-1, 'SSL handshake exception'), +(javax.net.ssl.SSLKeyException, ALL) : lambda x: sslerror(-1, 'SSL key exception'), +(javax.net.ssl.SSLPeerUnverifiedException, ALL) : lambda x: sslerror(-1, 'SSL peer unverified exception'), +(javax.net.ssl.SSLProtocolException, ALL) : lambda x: sslerror(-1, 'SSL protocol exception'), } -def would_block_error(exc=None): - return error(errno.EWOULDBLOCK, 'The socket operation could not complete without blocking') - def _map_exception(exc, circumstance=ALL): # print "Mapping exception: %s" % exc mapped_exception = _exception_map.get((exc.__class__, circumstance)) if mapped_exception: - exception = mapped_exception() + exception = mapped_exception(exc) else: exception = error(-1, 'Unmapped exception: %s' % exc) exception.java_exception = exc @@ -588,7 +596,7 @@ return Protocol.getProtocolByName(protocol_name).getProto() def _realsocket(family = AF_INET, type = SOCK_STREAM, protocol=0): - assert family == AF_INET, "Only AF_INET sockets are currently supported on jython" + assert family in (AF_INET, AF_INET6), "Only AF_INET and AF_INET6 sockets are currently supported on jython" assert type in (SOCK_DGRAM, SOCK_STREAM), "Only SOCK_STREAM and SOCK_DGRAM sockets are currently supported on jython" if type == SOCK_STREAM: if protocol != 0: @@ -599,16 +607,25 @@ assert protocol == IPPROTO_UDP, "Only IPPROTO_UDP supported on SOCK_DGRAM sockets" return _udpsocket() +_ipv4_addresses_only = False + +def _use_ipv4_addresses_only(value): + global _ipv4_addresses_only + _ipv4_addresses_only = value + def getaddrinfo(host, port, family=AF_INET, socktype=None, proto=0, flags=None): try: if not family in [AF_INET, AF_INET6, AF_UNSPEC]: raise gaierror(errno.EIO, 'ai_family not supported') filter_fns = [] - filter_fns.append({ - AF_INET: lambda x: isinstance(x, java.net.Inet4Address), - AF_INET6: lambda x: isinstance(x, java.net.Inet6Address), - AF_UNSPEC: lambda x: isinstance(x, java.net.InetAddress), - }[family]) + if _ipv4_addresses_only: + filter_fns.append( lambda x: isinstance(x, java.net.Inet4Address) ) + else: + filter_fns.append({ + AF_INET: lambda x: isinstance(x, java.net.Inet4Address), + AF_INET6: lambda x: isinstance(x, java.net.Inet6Address), + AF_UNSPEC: lambda x: isinstance(x, java.net.InetAddress), + }[family]) if host == "": host = java.net.InetAddress.getLocalHost().getHostName() passive_mode = flags is not None and flags & AI_PASSIVE @@ -617,18 +634,16 @@ for a in java.net.InetAddress.getAllByName(host): if len([f for f in filter_fns if f(a)]): family = {java.net.Inet4Address: AF_INET, java.net.Inet6Address: AF_INET6}[a.getClass()] - # bug 1697: exclude IPv6 addresses from being returned - if family != AF_INET6: - if passive_mode and not canonname_mode: - canonname = "" - else: - canonname = asPyString(a.getCanonicalHostName()) - if host is None and passive_mode and not canonname_mode: - sockname = INADDR_ANY - else: - sockname = asPyString(a.getHostAddress()) - # TODO: Include flowinfo and scopeid in a 4-tuple for IPv6 addresses - results.append((family, socktype, proto, canonname, (sockname, port))) + if passive_mode and not canonname_mode: + canonname = "" + else: + canonname = asPyString(a.getCanonicalHostName()) + if host is None and passive_mode and not canonname_mode: + sockname = INADDR_ANY + else: + sockname = asPyString(a.getHostAddress()) + # TODO: Include flowinfo and scopeid in a 4-tuple for IPv6 addresses + results.append((family, socktype, proto, canonname, (sockname, port))) return results except java.lang.Exception, jlx: raise _map_exception(jlx) Modified: trunk/jython/Lib/test/test_socket.py =================================================================== --- trunk/jython/Lib/test/test_socket.py 2011-01-23 15:58:55 UTC (rev 7190) +++ trunk/jython/Lib/test/test_socket.py 2011-01-25 23:46:27 UTC (rev 7191) @@ -1493,6 +1493,15 @@ self.failUnlessEqual(expected_canonname, canonname, "For hostname '%s' and flags %d, canonname '%s' != '%s'" % (host_param, flags, expected_canonname, canonname) ) self.failUnlessEqual(expected_sockaddr, sockaddr[0], "For hostname '%s' and flags %d, sockaddr '%s' != '%s'" % (host_param, flags, expected_sockaddr, sockaddr[0]) ) + def testIPV4AddressesOnly(self): + socket._use_ipv4_addresses_only(True) + def doAddressTest(addrinfos): + for family, socktype, proto, canonname, sockaddr in addrinfos: + self.failIf(":" in sockaddr[0], "Incorrectly received IPv6 address '%s'" % (sockaddr[0]) ) + doAddressTest(socket.getaddrinfo("localhost", 0, socket.AF_INET6, socket.SOCK_STREAM, 0, 0)) + doAddressTest(socket.getaddrinfo("localhost", 0, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, 0)) + socket._use_ipv4_addresses_only(False) + class TestExceptions(unittest.TestCase): def testExceptionTree(self): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <am...@us...> - 2011-02-11 14:11:32
|
Revision: 7196 http://jython.svn.sourceforge.net/jython/?rev=7196&view=rev Author: amak Date: 2011-02-11 14:11:26 +0000 (Fri, 11 Feb 2011) Log Message: ----------- Adding repr methods to the internal address tuple types, so that they are more user friendly. Finalizes http://bugs.jython.org/issue1697 Modified Paths: -------------- trunk/jython/Lib/socket.py trunk/jython/Lib/test/test_socket.py Modified: trunk/jython/Lib/socket.py =================================================================== --- trunk/jython/Lib/socket.py 2011-02-08 01:16:53 UTC (rev 7195) +++ trunk/jython/Lib/socket.py 2011-02-11 14:11:26 UTC (rev 7196) @@ -591,11 +591,8 @@ assert protocol == IPPROTO_UDP, "Only IPPROTO_UDP supported on SOCK_DGRAM sockets" return _udpsocket() -class _ip_address_t: +class _ip_address_t: pass - def __str__(self): - return "('%s', %d)" % (self.sockaddr, self.port) - class _ipv4_address_t(_ip_address_t): def __init__(self, sockaddr, port, jaddress): @@ -614,6 +611,11 @@ def __len__(self): return 2 + def __str__(self): + return "('%s', %d)" % (self.sockaddr, self.port) + + __repr__ = __str__ + class _ipv6_address_t(_ip_address_t): def __init__(self, sockaddr, port, jaddress): @@ -636,6 +638,11 @@ def __len__(self): return 4 + def __str__(self): + return "('%s', %d, 0, %d)" % (self.sockaddr, self.port, self.jaddress.scopeId) + + __repr__ = __str__ + def _get_jsockaddr(address_object, for_udp=False): if address_object is None: return java.net.InetSocketAddress(0) # Let the system pick an ephemeral port Modified: trunk/jython/Lib/test/test_socket.py =================================================================== --- trunk/jython/Lib/test/test_socket.py 2011-02-08 01:16:53 UTC (rev 7195) +++ trunk/jython/Lib/test/test_socket.py 2011-02-11 14:11:26 UTC (rev 7196) @@ -1508,6 +1508,7 @@ self.failUnlessEqual(ipv4_address_tuple[1], 80) self.failUnlessRaises(IndexError, lambda: ipv4_address_tuple[2]) self.failUnlessEqual(str(ipv4_address_tuple), "('127.0.0.1', 80)") + self.failUnlessEqual(repr(ipv4_address_tuple), "('127.0.0.1', 80)") ipv6_address_tuple = socket.getaddrinfo("localhost", 80, socket.AF_INET6, socket.SOCK_STREAM, 0, 0)[0][4] self.failUnless (ipv6_address_tuple[0] in ["::1", "0:0:0:0:0:0:0:1"]) @@ -1519,7 +1520,10 @@ except IndexError: self.fail("Failed to retrieve third element of ipv6 4-tuple") self.failUnlessRaises(IndexError, lambda: ipv6_address_tuple[4]) - self.failUnless(str(ipv6_address_tuple) in ["('::1', 80)", "('0:0:0:0:0:0:0:1', 80)"]) + # These str/repr tests may fail on some systems: the scope element of the tuple may be non-zero + # In this case, we'll have to change the test to use .startswith() or .split() to exclude the scope element + self.failUnless(str(ipv6_address_tuple) in ["('::1', 80, 0, 0)", "('0:0:0:0:0:0:0:1', 80, 0, 0)"]) + self.failUnless(repr(ipv6_address_tuple) in ["('::1', 80, 0, 0)", "('0:0:0:0:0:0:0:1', 80, 0, 0)"]) class TestJython_get_jsockaddr(unittest.TestCase): "These tests are specific to jython: they test a key internal routine" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <am...@us...> - 2011-02-13 20:50:04
|
Revision: 7198 http://jython.svn.sourceforge.net/jython/?rev=7198&view=rev Author: amak Date: 2011-02-13 20:49:57 +0000 (Sun, 13 Feb 2011) Log Message: ----------- Checking in support for Internationalized Domain Names (RFC 3490), plus unit-tests for same. This new support only works on Java 6. The new feature and workarounds for Java 5 are documented on the jython wiki. http://wiki.python.org/jython/NewSocketModule Partially fixes http://bugs.jython.org/issue1153 Modified Paths: -------------- trunk/jython/Lib/socket.py trunk/jython/Lib/test/test_socket.py Modified: trunk/jython/Lib/socket.py =================================================================== --- trunk/jython/Lib/socket.py 2011-02-12 01:50:38 UTC (rev 7197) +++ trunk/jython/Lib/socket.py 2011-02-13 20:49:57 UTC (rev 7198) @@ -161,6 +161,17 @@ exception.java_exception = exc return exception +_feature_support_map = { + 'ipv6': True, + 'idna': False, + 'tipc': False, +} + +def supports(feature, *args): + if len(args) == 1: + _feature_support_map[feature] = args[0] + return _feature_support_map.get(feature, False) + MODE_BLOCKING = 'block' MODE_NONBLOCKING = 'nonblock' MODE_TIMEOUT = 'timeout' @@ -591,6 +602,36 @@ assert protocol == IPPROTO_UDP, "Only IPPROTO_UDP supported on SOCK_DGRAM sockets" return _udpsocket() +# +# Attempt to provide IDNA (RFC 3490) support. +# +# Try java.net.IDN, built into java 6 +# + +idna_libraries = [ + ('java.net.IDN', 'toASCII', java.lang.IllegalArgumentException) +] + +for idna_lib, idna_fn_name, exc in idna_libraries: + try: + m = __import__(idna_lib, globals(), locals(), [idna_fn_name]) + idna_fn = getattr(m, idna_fn_name) + def _encode_idna(name): + try: + return idna_fn(name) + except exc: + raise UnicodeEncodeError(name) + supports('idna', True) + break + except (AttributeError, ImportError), e: + pass +else: + _encode_idna = lambda x: x.encode("ascii") + +# +# Define data structures to support IPV4 and IPV6. +# + class _ip_address_t: pass class _ipv4_address_t(_ip_address_t): @@ -668,10 +709,7 @@ if hostname is None: return java.net.InetSocketAddress(port) if isinstance(hostname, unicode): - # XXX: Should be encode('idna') (See CPython - # socketmodule::getsockaddrarg), but Jython's idna support is - # currently broken - hostname = hostname.encode() + hostname = _encode_idna(hostname) if len(address_object) == 4: # There is no way to get a Inet6Address: Inet6Address.getByName() simply calls # InetAddress.getByName,() which also returns Inet4Address objects @@ -701,6 +739,8 @@ }[family]) if host == "": host = java.net.InetAddress.getLocalHost().getHostName() + if isinstance(host, unicode): + host = _encode_idna(host) passive_mode = flags is not None and flags & AI_PASSIVE canonname_mode = flags is not None and flags & AI_CANONNAME results = [] Modified: trunk/jython/Lib/test/test_socket.py =================================================================== --- trunk/jython/Lib/test/test_socket.py 2011-02-12 01:50:38 UTC (rev 7197) +++ trunk/jython/Lib/test/test_socket.py 2011-02-13 20:49:57 UTC (rev 7198) @@ -1742,6 +1742,47 @@ def _testUnicodeHostname(self): self.cli.connect((unicode(self.HOST), self.PORT)) +class IDNATest(unittest.TestCase): + + def testGetAddrInfoIDNAHostname(self): + idna_domain = u"al\u00e1n.com" + if socket.supports('idna'): + try: + addresses = socket.getaddrinfo(idna_domain, 80) + self.failUnless(len(addresses) > 0, "No addresses returned for test IDNA domain '%s'" % repr(idna_domain)) + except Exception, x: + self.fail("Unexpected exception raised for socket.getaddrinfo(%s)" % repr(idna_domain)) + else: + try: + socket.getaddrinfo(idna_domain, 80) + except UnicodeEncodeError: + pass + except Exception, x: + self.fail("Non ascii domain '%s' should have raised UnicodeEncodeError, not %s" % (repr(idna_domain), str(x))) + else: + self.fail("Non ascii domain '%s' should have raised UnicodeEncodeError: no exception raised" % repr(idna_domain)) + + def testAddrTupleIDNAHostname(self): + idna_domain = u"al\u00e1n.com" + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + if socket.supports('idna'): + try: + s.bind( (idna_domain, 80) ) + except socket.error: + # We're not worried about socket errors, i.e. bind problems, etc. + pass + except Exception, x: + self.fail("Unexpected exception raised for socket.bind(%s)" % repr(idna_domain)) + else: + try: + s.bind( (idna_domain, 80) ) + except UnicodeEncodeError: + pass + except Exception, x: + self.fail("Non ascii domain '%s' should have raised UnicodeEncodeError, not %s" % (repr(idna_domain), str(x))) + else: + self.fail("Non ascii domain '%s' should have raised UnicodeEncodeError: no exception raised" % repr(idna_domain)) + class TestInvalidUsage(unittest.TestCase): def setUp(self): @@ -1793,6 +1834,7 @@ LineBufferedFileObjectClassTestCase, SmallBufferedFileObjectClassTestCase, UnicodeTest, + IDNATest, ] if hasattr(socket, "socketpair"): tests.append(BasicSocketPairTest) @@ -1804,7 +1846,6 @@ # Need some way to discover the network setup on the test machine if False: tests.append(UDPBroadcastTest) -# tests = [TestJython_get_jsockaddr] suites = [unittest.makeSuite(klass, 'test') for klass in tests] test_support.run_suite(unittest.TestSuite(suites)) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <am...@us...> - 2011-02-15 21:39:51
|
Revision: 7202 http://jython.svn.sourceforge.net/jython/?rev=7202&view=rev Author: amak Date: 2011-02-15 21:39:44 +0000 (Tue, 15 Feb 2011) Log Message: ----------- Some simple docstring and line-endings changes Modified Paths: -------------- trunk/jython/Lib/select.py trunk/jython/Lib/socket.py Modified: trunk/jython/Lib/select.py =================================================================== --- trunk/jython/Lib/select.py 2011-02-14 23:40:19 UTC (rev 7201) +++ trunk/jython/Lib/select.py 2011-02-15 21:39:44 UTC (rev 7202) @@ -1,260 +1,262 @@ -""" -AMAK: 20070515: New select implementation that uses java.nio -""" - -import java.nio.channels.SelectableChannel -import java.nio.channels.SelectionKey -import java.nio.channels.Selector -from java.nio.channels.SelectionKey import OP_ACCEPT, OP_CONNECT, OP_WRITE, OP_READ - -import errno -import os -import Queue -import socket - -class error(Exception): pass - -ALL = None - -_exception_map = { - -# (<javaexception>, <circumstance>) : lambda: <code that raises the python equivalent> - -(java.nio.channels.IllegalBlockingModeException, ALL) : error(errno.ESOCKISBLOCKING, 'socket must be in non-blocking mode'), -} - -def _map_exception(exc, circumstance=ALL): - try: - mapped_exception = _exception_map[(exc.__class__, circumstance)] - mapped_exception.java_exception = exc - return mapped_exception - except KeyError: - return error(-1, 'Unmapped java exception: <%s:%s>' % (exc.toString(), circumstance)) - -POLLIN = 1 -POLLOUT = 2 - -# The following event types are completely ignored on jython -# Java does not support them, AFAICT -# They are declared only to support code compatibility with cpython - -POLLPRI = 4 -POLLERR = 8 -POLLHUP = 16 -POLLNVAL = 32 - -def _getselectable(selectable_object): - try: - channel = selectable_object.getchannel() - except: - try: - channel = selectable_object.fileno().getChannel() - except: - raise TypeError("Object '%s' is not watchable" % selectable_object, - errno.ENOTSOCK) - - if channel and not isinstance(channel, java.nio.channels.SelectableChannel): - raise TypeError("Object '%s' is not watchable" % selectable_object, - errno.ENOTSOCK) - return channel - -class poll: - - def __init__(self): - self.selector = java.nio.channels.Selector.open() - self.chanmap = {} - self.unconnected_sockets = [] - - def _register_channel(self, socket_object, channel, mask): - jmask = 0 - if mask & POLLIN: - # Note that OP_READ is NOT a valid event on server socket channels. - if channel.validOps() & OP_ACCEPT: - jmask = OP_ACCEPT - else: - jmask = OP_READ - if mask & POLLOUT: - if channel.validOps() & OP_WRITE: - jmask |= OP_WRITE - if channel.validOps() & OP_CONNECT: - jmask |= OP_CONNECT - selectionkey = channel.register(self.selector, jmask) - self.chanmap[channel] = (socket_object, selectionkey) - - def _check_unconnected_sockets(self): - temp_list = [] - for socket_object, mask in self.unconnected_sockets: - channel = _getselectable(socket_object) - if channel is not None: - self._register_channel(socket_object, channel, mask) - else: - temp_list.append( (socket_object, mask) ) - self.unconnected_sockets = temp_list - - def register(self, socket_object, mask = POLLIN|POLLOUT|POLLPRI): - try: - channel = _getselectable(socket_object) - if channel is None: - # The socket is not yet connected, and thus has no channel - # Add it to a pending list, and return - self.unconnected_sockets.append( (socket_object, mask) ) - return - self._register_channel(socket_object, channel, mask) - except java.lang.Exception, jlx: - raise _map_exception(jlx) - - def unregister(self, socket_object): - try: - channel = _getselectable(socket_object) - self.chanmap[channel][1].cancel() - del self.chanmap[channel] - except java.lang.Exception, jlx: - raise _map_exception(jlx) - - def _dopoll(self, timeout): - if timeout is None or timeout < 0: - self.selector.select() - else: - try: - timeout = int(timeout) - if not timeout: - self.selector.selectNow() - else: - # No multiplication required: both cpython and java use millisecond timeouts - self.selector.select(timeout) - except ValueError, vx: - raise error("poll timeout must be a number of milliseconds or None", errno.EINVAL) - # The returned selectedKeys cannot be used from multiple threads! - return self.selector.selectedKeys() - - def poll(self, timeout=None): - try: - self._check_unconnected_sockets() - selectedkeys = self._dopoll(timeout) - results = [] - for k in selectedkeys.iterator(): - jmask = k.readyOps() - pymask = 0 - if jmask & OP_READ: pymask |= POLLIN - if jmask & OP_WRITE: pymask |= POLLOUT - if jmask & OP_ACCEPT: pymask |= POLLIN - if jmask & OP_CONNECT: pymask |= POLLOUT - # Now return the original userobject, and the return event mask - results.append( (self.chanmap[k.channel()][0], pymask) ) - return results - except java.lang.Exception, jlx: - raise _map_exception(jlx) - - def _deregister_all(self): - try: - for k in self.selector.keys(): - k.cancel() - # Keys are not actually removed from the selector until the next select operation. - self.selector.selectNow() - except java.lang.Exception, jlx: - raise _map_exception(jlx) - - def close(self): - try: - self._deregister_all() - self.selector.close() - except java.lang.Exception, jlx: - raise _map_exception(jlx) - -def _calcselecttimeoutvalue(value): - if value is None: - return None - try: - floatvalue = float(value) - except Exception, x: - raise TypeError("Select timeout value must be a number or None") - if value < 0: - raise error("Select timeout value cannot be negative", errno.EINVAL) - if floatvalue < 0.000001: - return 0 - return int(floatvalue * 1000) # Convert to milliseconds - -# This cache for poll objects is required because of a bug in java on MS Windows -# http://bugs.jython.org/issue1291 - -class poll_object_cache: - - def __init__(self): - self.is_windows = os._name == 'nt' - if self.is_windows: - self.poll_object_queue = Queue.Queue() - import atexit - atexit.register(self.finalize) - - def get_poll_object(self): - if not self.is_windows: - return poll() - try: - return self.poll_object_queue.get(False) - except Queue.Empty: - return poll() - - def release_poll_object(self, pobj): - if self.is_windows: - pobj._deregister_all() - self.poll_object_queue.put(pobj) - else: - pobj.close() - - def finalize(self): - if self.is_windows: - while True: - try: - p = self.poll_object_queue.get(False) - p.close() - except Queue.Empty: - return - -_poll_object_cache = poll_object_cache() - -def native_select(read_fd_list, write_fd_list, outofband_fd_list, timeout=None): - timeout = _calcselecttimeoutvalue(timeout) - # First create a poll object to do the actual watching. - pobj = _poll_object_cache.get_poll_object() - try: - registered_for_read = {} - # Check the read list - for fd in read_fd_list: - pobj.register(fd, POLLIN) - registered_for_read[fd] = 1 - # And now the write list - for fd in write_fd_list: - if fd in registered_for_read: - # registering a second time overwrites the first - pobj.register(fd, POLLIN|POLLOUT) - else: - pobj.register(fd, POLLOUT) - results = pobj.poll(timeout) - # Now start preparing the results - read_ready_list, write_ready_list, oob_ready_list = [], [], [] - for fd, mask in results: - if mask & POLLIN: - read_ready_list.append(fd) - if mask & POLLOUT: - write_ready_list.append(fd) - return read_ready_list, write_ready_list, oob_ready_list - finally: - _poll_object_cache.release_poll_object(pobj) - -select = native_select - -def cpython_compatible_select(read_fd_list, write_fd_list, outofband_fd_list, timeout=None): - # First turn all sockets to non-blocking - # keeping track of which ones have changed - modified_channels = [] - try: - for socket_list in [read_fd_list, write_fd_list, outofband_fd_list]: - for s in socket_list: - channel = _getselectable(s) - if channel.isBlocking(): - modified_channels.append(channel) - channel.configureBlocking(0) - return native_select(read_fd_list, write_fd_list, outofband_fd_list, timeout) - finally: - for channel in modified_channels: - channel.configureBlocking(1) +""" +This is an select module for use on JVMs > 1.5. +It is documented, along with known issues and workarounds, on the jython wiki. +http://wiki.python.org/jython/SelectModule +""" + +import java.nio.channels.SelectableChannel +import java.nio.channels.SelectionKey +import java.nio.channels.Selector +from java.nio.channels.SelectionKey import OP_ACCEPT, OP_CONNECT, OP_WRITE, OP_READ + +import errno +import os +import Queue +import socket + +class error(Exception): pass + +ALL = None + +_exception_map = { + +# (<javaexception>, <circumstance>) : lambda: <code that raises the python equivalent> + +(java.nio.channels.IllegalBlockingModeException, ALL) : error(errno.ESOCKISBLOCKING, 'socket must be in non-blocking mode'), +} + +def _map_exception(exc, circumstance=ALL): + try: + mapped_exception = _exception_map[(exc.__class__, circumstance)] + mapped_exception.java_exception = exc + return mapped_exception + except KeyError: + return error(-1, 'Unmapped java exception: <%s:%s>' % (exc.toString(), circumstance)) + +POLLIN = 1 +POLLOUT = 2 + +# The following event types are completely ignored on jython +# Java does not support them, AFAICT +# They are declared only to support code compatibility with cpython + +POLLPRI = 4 +POLLERR = 8 +POLLHUP = 16 +POLLNVAL = 32 + +def _getselectable(selectable_object): + try: + channel = selectable_object.getchannel() + except: + try: + channel = selectable_object.fileno().getChannel() + except: + raise TypeError("Object '%s' is not watchable" % selectable_object, + errno.ENOTSOCK) + + if channel and not isinstance(channel, java.nio.channels.SelectableChannel): + raise TypeError("Object '%s' is not watchable" % selectable_object, + errno.ENOTSOCK) + return channel + +class poll: + + def __init__(self): + self.selector = java.nio.channels.Selector.open() + self.chanmap = {} + self.unconnected_sockets = [] + + def _register_channel(self, socket_object, channel, mask): + jmask = 0 + if mask & POLLIN: + # Note that OP_READ is NOT a valid event on server socket channels. + if channel.validOps() & OP_ACCEPT: + jmask = OP_ACCEPT + else: + jmask = OP_READ + if mask & POLLOUT: + if channel.validOps() & OP_WRITE: + jmask |= OP_WRITE + if channel.validOps() & OP_CONNECT: + jmask |= OP_CONNECT + selectionkey = channel.register(self.selector, jmask) + self.chanmap[channel] = (socket_object, selectionkey) + + def _check_unconnected_sockets(self): + temp_list = [] + for socket_object, mask in self.unconnected_sockets: + channel = _getselectable(socket_object) + if channel is not None: + self._register_channel(socket_object, channel, mask) + else: + temp_list.append( (socket_object, mask) ) + self.unconnected_sockets = temp_list + + def register(self, socket_object, mask = POLLIN|POLLOUT|POLLPRI): + try: + channel = _getselectable(socket_object) + if channel is None: + # The socket is not yet connected, and thus has no channel + # Add it to a pending list, and return + self.unconnected_sockets.append( (socket_object, mask) ) + return + self._register_channel(socket_object, channel, mask) + except java.lang.Exception, jlx: + raise _map_exception(jlx) + + def unregister(self, socket_object): + try: + channel = _getselectable(socket_object) + self.chanmap[channel][1].cancel() + del self.chanmap[channel] + except java.lang.Exception, jlx: + raise _map_exception(jlx) + + def _dopoll(self, timeout): + if timeout is None or timeout < 0: + self.selector.select() + else: + try: + timeout = int(timeout) + if not timeout: + self.selector.selectNow() + else: + # No multiplication required: both cpython and java use millisecond timeouts + self.selector.select(timeout) + except ValueError, vx: + raise error("poll timeout must be a number of milliseconds or None", errno.EINVAL) + # The returned selectedKeys cannot be used from multiple threads! + return self.selector.selectedKeys() + + def poll(self, timeout=None): + try: + self._check_unconnected_sockets() + selectedkeys = self._dopoll(timeout) + results = [] + for k in selectedkeys.iterator(): + jmask = k.readyOps() + pymask = 0 + if jmask & OP_READ: pymask |= POLLIN + if jmask & OP_WRITE: pymask |= POLLOUT + if jmask & OP_ACCEPT: pymask |= POLLIN + if jmask & OP_CONNECT: pymask |= POLLOUT + # Now return the original userobject, and the return event mask + results.append( (self.chanmap[k.channel()][0], pymask) ) + return results + except java.lang.Exception, jlx: + raise _map_exception(jlx) + + def _deregister_all(self): + try: + for k in self.selector.keys(): + k.cancel() + # Keys are not actually removed from the selector until the next select operation. + self.selector.selectNow() + except java.lang.Exception, jlx: + raise _map_exception(jlx) + + def close(self): + try: + self._deregister_all() + self.selector.close() + except java.lang.Exception, jlx: + raise _map_exception(jlx) + +def _calcselecttimeoutvalue(value): + if value is None: + return None + try: + floatvalue = float(value) + except Exception, x: + raise TypeError("Select timeout value must be a number or None") + if value < 0: + raise error("Select timeout value cannot be negative", errno.EINVAL) + if floatvalue < 0.000001: + return 0 + return int(floatvalue * 1000) # Convert to milliseconds + +# This cache for poll objects is required because of a bug in java on MS Windows +# http://bugs.jython.org/issue1291 + +class poll_object_cache: + + def __init__(self): + self.is_windows = os._name == 'nt' + if self.is_windows: + self.poll_object_queue = Queue.Queue() + import atexit + atexit.register(self.finalize) + + def get_poll_object(self): + if not self.is_windows: + return poll() + try: + return self.poll_object_queue.get(False) + except Queue.Empty: + return poll() + + def release_poll_object(self, pobj): + if self.is_windows: + pobj._deregister_all() + self.poll_object_queue.put(pobj) + else: + pobj.close() + + def finalize(self): + if self.is_windows: + while True: + try: + p = self.poll_object_queue.get(False) + p.close() + except Queue.Empty: + return + +_poll_object_cache = poll_object_cache() + +def native_select(read_fd_list, write_fd_list, outofband_fd_list, timeout=None): + timeout = _calcselecttimeoutvalue(timeout) + # First create a poll object to do the actual watching. + pobj = _poll_object_cache.get_poll_object() + try: + registered_for_read = {} + # Check the read list + for fd in read_fd_list: + pobj.register(fd, POLLIN) + registered_for_read[fd] = 1 + # And now the write list + for fd in write_fd_list: + if fd in registered_for_read: + # registering a second time overwrites the first + pobj.register(fd, POLLIN|POLLOUT) + else: + pobj.register(fd, POLLOUT) + results = pobj.poll(timeout) + # Now start preparing the results + read_ready_list, write_ready_list, oob_ready_list = [], [], [] + for fd, mask in results: + if mask & POLLIN: + read_ready_list.append(fd) + if mask & POLLOUT: + write_ready_list.append(fd) + return read_ready_list, write_ready_list, oob_ready_list + finally: + _poll_object_cache.release_poll_object(pobj) + +select = native_select + +def cpython_compatible_select(read_fd_list, write_fd_list, outofband_fd_list, timeout=None): + # First turn all sockets to non-blocking + # keeping track of which ones have changed + modified_channels = [] + try: + for socket_list in [read_fd_list, write_fd_list, outofband_fd_list]: + for s in socket_list: + channel = _getselectable(s) + if channel.isBlocking(): + modified_channels.append(channel) + channel.configureBlocking(0) + return native_select(read_fd_list, write_fd_list, outofband_fd_list, timeout) + finally: + for channel in modified_channels: + channel.configureBlocking(1) Modified: trunk/jython/Lib/socket.py =================================================================== --- trunk/jython/Lib/socket.py 2011-02-14 23:40:19 UTC (rev 7201) +++ trunk/jython/Lib/socket.py 2011-02-15 21:39:44 UTC (rev 7202) @@ -1,16 +1,7 @@ """ -This is an updated socket module for use on JVMs > 1.4; it is derived from the -old jython socket module. -The primary extra it provides is non-blocking support. - -XXX Restrictions: - -- Only INET sockets -- Can't do a very good gethostbyaddr() right... -AMAK: 20050527: added socket timeouts -AMAK: 20070515: Added non-blocking (asynchronous) support -AMAK: 20070515: Added client-side SSL support -AMAK: 20080513: Added support for options +This is an updated socket module for use on JVMs > 1.5; it is derived from the old jython socket module. +It is documented, along with known issues and workarounds, on the jython wiki. +http://wiki.python.org/jython/NewSocketModule """ _defaulttimeout = None This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <fwi...@us...> - 2011-03-12 16:58:24
|
Revision: 7213 http://jython.svn.sourceforge.net/jython/?rev=7213&view=rev Author: fwierzbicki Date: 2011-03-12 16:58:16 +0000 (Sat, 12 Mar 2011) Log Message: ----------- from: http://svn.python.org/projects/python/branches/release26-maint/Lib/robotparser.py@88766 http://svn.python.org/projects/python/branches/release26-maint/Lib/test/test_robotparser.py@88766 Added Paths: ----------- trunk/jython/Lib/robotparser.py trunk/jython/Lib/test/test_robotparser.py Added: trunk/jython/Lib/robotparser.py =================================================================== --- trunk/jython/Lib/robotparser.py (rev 0) +++ trunk/jython/Lib/robotparser.py 2011-03-12 16:58:16 UTC (rev 7213) @@ -0,0 +1,217 @@ +""" robotparser.py + + Copyright (C) 2000 Bastian Kleineidam + + You can choose between two licenses when using this package: + 1) GNU GPLv2 + 2) PSF license for Python 2.2 + + The robots.txt Exclusion Protocol is implemented as specified in + http://info.webcrawler.com/mak/projects/robots/norobots-rfc.html +""" +import urlparse +import urllib + +__all__ = ["RobotFileParser"] + + +class RobotFileParser: + """ This class provides a set of methods to read, parse and answer + questions about a single robots.txt file. + + """ + + def __init__(self, url=''): + self.entries = [] + self.default_entry = None + self.disallow_all = False + self.allow_all = False + self.set_url(url) + self.last_checked = 0 + + def mtime(self): + """Returns the time the robots.txt file was last fetched. + + This is useful for long-running web spiders that need to + check for new robots.txt files periodically. + + """ + return self.last_checked + + def modified(self): + """Sets the time the robots.txt file was last fetched to the + current time. + + """ + import time + self.last_checked = time.time() + + def set_url(self, url): + """Sets the URL referring to a robots.txt file.""" + self.url = url + self.host, self.path = urlparse.urlparse(url)[1:3] + + def read(self): + """Reads the robots.txt URL and feeds it to the parser.""" + opener = URLopener() + f = opener.open(self.url) + lines = [line.strip() for line in f] + f.close() + self.errcode = opener.errcode + if self.errcode in (401, 403): + self.disallow_all = True + elif self.errcode >= 400: + self.allow_all = True + elif self.errcode == 200 and lines: + self.parse(lines) + + def _add_entry(self, entry): + if "*" in entry.useragents: + # the default entry is considered last + if self.default_entry is None: + # the first default entry wins + self.default_entry = entry + else: + self.entries.append(entry) + + def parse(self, lines): + """parse the input lines from a robots.txt file. + We allow that a user-agent: line is not preceded by + one or more blank lines.""" + # states: + # 0: start state + # 1: saw user-agent line + # 2: saw an allow or disallow line + state = 0 + linenumber = 0 + entry = Entry() + + for line in lines: + linenumber += 1 + if not line: + if state == 1: + entry = Entry() + state = 0 + elif state == 2: + self._add_entry(entry) + entry = Entry() + state = 0 + # remove optional comment and strip line + i = line.find('#') + if i >= 0: + line = line[:i] + line = line.strip() + if not line: + continue + line = line.split(':', 1) + if len(line) == 2: + line[0] = line[0].strip().lower() + line[1] = urllib.unquote(line[1].strip()) + if line[0] == "user-agent": + if state == 2: + self._add_entry(entry) + entry = Entry() + entry.useragents.append(line[1]) + state = 1 + elif line[0] == "disallow": + if state != 0: + entry.rulelines.append(RuleLine(line[1], False)) + state = 2 + elif line[0] == "allow": + if state != 0: + entry.rulelines.append(RuleLine(line[1], True)) + state = 2 + if state == 2: + self._add_entry(entry) + + + def can_fetch(self, useragent, url): + """using the parsed robots.txt decide if useragent can fetch url""" + if self.disallow_all: + return False + if self.allow_all: + return True + # search for given user agent matches + # the first match counts + url = urllib.quote(urlparse.urlparse(urllib.unquote(url))[2]) or "/" + for entry in self.entries: + if entry.applies_to(useragent): + return entry.allowance(url) + # try the default entry last + if self.default_entry: + return self.default_entry.allowance(url) + # agent not found ==> access granted + return True + + + def __str__(self): + return ''.join([str(entry) + "\n" for entry in self.entries]) + + +class RuleLine: + """A rule line is a single "Allow:" (allowance==True) or "Disallow:" + (allowance==False) followed by a path.""" + def __init__(self, path, allowance): + if path == '' and not allowance: + # an empty value means allow all + allowance = True + self.path = urllib.quote(path) + self.allowance = allowance + + def applies_to(self, filename): + return self.path == "*" or filename.startswith(self.path) + + def __str__(self): + return (self.allowance and "Allow" or "Disallow") + ": " + self.path + + +class Entry: + """An entry has one or more user-agents and zero or more rulelines""" + def __init__(self): + self.useragents = [] + self.rulelines = [] + + def __str__(self): + ret = [] + for agent in self.useragents: + ret.extend(["User-agent: ", agent, "\n"]) + for line in self.rulelines: + ret.extend([str(line), "\n"]) + return ''.join(ret) + + def applies_to(self, useragent): + """check if this entry applies to the specified agent""" + # split the name token and make it lower case + useragent = useragent.split("/")[0].lower() + for agent in self.useragents: + if agent == '*': + # we have the catch-all agent + return True + agent = agent.lower() + if agent in useragent: + return True + return False + + def allowance(self, filename): + """Preconditions: + - our agent applies to this entry + - filename is URL decoded""" + for line in self.rulelines: + if line.applies_to(filename): + return line.allowance + return True + +class URLopener(urllib.FancyURLopener): + def __init__(self, *args): + urllib.FancyURLopener.__init__(self, *args) + self.errcode = 200 + + def prompt_user_passwd(self, host, realm): + ## If robots.txt file is accessible only with a password, + ## we act as if the file wasn't there. + return None, None + + def http_error_default(self, url, fp, errcode, errmsg, headers): + self.errcode = errcode + return urllib.FancyURLopener.http_error_default(self, url, fp, errcode, + errmsg, headers) Added: trunk/jython/Lib/test/test_robotparser.py =================================================================== --- trunk/jython/Lib/test/test_robotparser.py (rev 0) +++ trunk/jython/Lib/test/test_robotparser.py 2011-03-12 16:58:16 UTC (rev 7213) @@ -0,0 +1,236 @@ +import unittest, StringIO, robotparser +from test import test_support + +class RobotTestCase(unittest.TestCase): + def __init__(self, index, parser, url, good, agent): + unittest.TestCase.__init__(self) + if good: + self.str = "RobotTest(%d, good, %s)" % (index, url) + else: + self.str = "RobotTest(%d, bad, %s)" % (index, url) + self.parser = parser + self.url = url + self.good = good + self.agent = agent + + def runTest(self): + if isinstance(self.url, tuple): + agent, url = self.url + else: + url = self.url + agent = self.agent + if self.good: + self.failUnless(self.parser.can_fetch(agent, url)) + else: + self.failIf(self.parser.can_fetch(agent, url)) + + def __str__(self): + return self.str + +tests = unittest.TestSuite() + +def RobotTest(index, robots_txt, good_urls, bad_urls, + agent="test_robotparser"): + + lines = StringIO.StringIO(robots_txt).readlines() + parser = robotparser.RobotFileParser() + parser.parse(lines) + for url in good_urls: + tests.addTest(RobotTestCase(index, parser, url, 1, agent)) + for url in bad_urls: + tests.addTest(RobotTestCase(index, parser, url, 0, agent)) + +# Examples from http://www.robotstxt.org/wc/norobots.html (fetched 2002) + +# 1. +doc = """ +User-agent: * +Disallow: /cyberworld/map/ # This is an infinite virtual URL space +Disallow: /tmp/ # these will soon disappear +Disallow: /foo.html +""" + +good = ['/','/test.html'] +bad = ['/cyberworld/map/index.html','/tmp/xxx','/foo.html'] + +RobotTest(1, doc, good, bad) + +# 2. +doc = """ +# robots.txt for http://www.example.com/ + +User-agent: * +Disallow: /cyberworld/map/ # This is an infinite virtual URL space + +# Cybermapper knows where to go. +User-agent: cybermapper +Disallow: + +""" + +good = ['/','/test.html',('cybermapper','/cyberworld/map/index.html')] +bad = ['/cyberworld/map/index.html'] + +RobotTest(2, doc, good, bad) + +# 3. +doc = """ +# go away +User-agent: * +Disallow: / +""" + +good = [] +bad = ['/cyberworld/map/index.html','/','/tmp/'] + +RobotTest(3, doc, good, bad) + +# Examples from http://www.robotstxt.org/wc/norobots-rfc.html (fetched 2002) + +# 4. +doc = """ +User-agent: figtree +Disallow: /tmp +Disallow: /a%3cd.html +Disallow: /a%2fb.html +Disallow: /%7ejoe/index.html +""" + +good = [] # XFAIL '/a/b.html' +bad = ['/tmp','/tmp.html','/tmp/a.html', + '/a%3cd.html','/a%3Cd.html','/a%2fb.html', + '/~joe/index.html' + ] + +RobotTest(4, doc, good, bad, 'figtree') +RobotTest(5, doc, good, bad, 'FigTree Robot libwww-perl/5.04') + +# 6. +doc = """ +User-agent: * +Disallow: /tmp/ +Disallow: /a%3Cd.html +Disallow: /a/b.html +Disallow: /%7ejoe/index.html +""" + +good = ['/tmp',] # XFAIL: '/a%2fb.html' +bad = ['/tmp/','/tmp/a.html', + '/a%3cd.html','/a%3Cd.html',"/a/b.html", + '/%7Ejoe/index.html'] + +RobotTest(6, doc, good, bad) + +# From bug report #523041 + +# 7. +doc = """ +User-Agent: * +Disallow: /. +""" + +good = ['/foo.html'] +bad = [] # Bug report says "/" should be denied, but that is not in the RFC + +RobotTest(7, doc, good, bad) + +# From Google: http://www.google.com/support/webmasters/bin/answer.py?hl=en&answer=40364 + +# 8. +doc = """ +User-agent: Googlebot +Allow: /folder1/myfile.html +Disallow: /folder1/ +""" + +good = ['/folder1/myfile.html'] +bad = ['/folder1/anotherfile.html'] + +RobotTest(8, doc, good, bad, agent="Googlebot") + +# 9. This file is incorrect because "Googlebot" is a substring of +# "Googlebot-Mobile", so test 10 works just like test 9. +doc = """ +User-agent: Googlebot +Disallow: / + +User-agent: Googlebot-Mobile +Allow: / +""" + +good = [] +bad = ['/something.jpg'] + +RobotTest(9, doc, good, bad, agent="Googlebot") + +good = [] +bad = ['/something.jpg'] + +RobotTest(10, doc, good, bad, agent="Googlebot-Mobile") + +# 11. Get the order correct. +doc = """ +User-agent: Googlebot-Mobile +Allow: / + +User-agent: Googlebot +Disallow: / +""" + +good = [] +bad = ['/something.jpg'] + +RobotTest(11, doc, good, bad, agent="Googlebot") + +good = ['/something.jpg'] +bad = [] + +RobotTest(12, doc, good, bad, agent="Googlebot-Mobile") + + +# 13. Google also got the order wrong in #8. You need to specify the +# URLs from more specific to more general. +doc = """ +User-agent: Googlebot +Allow: /folder1/myfile.html +Disallow: /folder1/ +""" + +good = ['/folder1/myfile.html'] +bad = ['/folder1/anotherfile.html'] + +RobotTest(13, doc, good, bad, agent="googlebot") + + +# 14. For issue #4108 (obey first * entry) +doc = """ +User-agent: * +Disallow: /some/path + +User-agent: * +Disallow: /another/path +""" + +good = ['/another/path'] +bad = ['/some/path'] + +RobotTest(14, doc, good, bad) + + +class TestCase(unittest.TestCase): + def runTest(self): + test_support.requires('network') + # whole site is password-protected. + url = 'http://mueblesmoraleda.com' + parser = robotparser.RobotFileParser() + parser.set_url(url) + parser.read() + self.assertEqual(parser.can_fetch("*", url+"/robots.txt"), False) + +def test_main(): + test_support.run_unittest(tests) + TestCase().run() + +if __name__=='__main__': + test_support.verbose = 1 + test_main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <fwi...@us...> - 2011-03-13 04:17:36
|
Revision: 7224 http://jython.svn.sourceforge.net/jython/?rev=7224&view=rev Author: fwierzbicki Date: 2011-03-13 04:17:29 +0000 (Sun, 13 Mar 2011) Log Message: ----------- from: https://bitbucket.org/pypy/pypy/lib-python/2.7.0/_abcoll.py@42546:f93911d7479c https://bitbucket.org/pypy/pypy/lib-python/2.7.0/_weakrefset.py@42546:f93911d7479c https://bitbucket.org/pypy/pypy/lib-python/2.7.0/abc.py@42546:f93911d7479c Added Paths: ----------- trunk/jython/Lib/_abcoll.py trunk/jython/Lib/_weakrefset.py trunk/jython/Lib/abc.py Added: trunk/jython/Lib/_abcoll.py =================================================================== --- trunk/jython/Lib/_abcoll.py (rev 0) +++ trunk/jython/Lib/_abcoll.py 2011-03-13 04:17:29 UTC (rev 7224) @@ -0,0 +1,601 @@ +# Copyright 2007 Google, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +"""Abstract Base Classes (ABCs) for collections, according to PEP 3119. + +DON'T USE THIS MODULE DIRECTLY! The classes here should be imported +via collections; they are defined here only to alleviate certain +bootstrapping issues. Unit tests are in test_collections. +""" + +from abc import ABCMeta, abstractmethod +import sys + +__all__ = ["Hashable", "Iterable", "Iterator", + "Sized", "Container", "Callable", + "Set", "MutableSet", + "Mapping", "MutableMapping", + "MappingView", "KeysView", "ItemsView", "ValuesView", + "Sequence", "MutableSequence", + ] + +### ONE-TRICK PONIES ### + +def _hasattr(C, attr): + try: + return any(attr in B.__dict__ for B in C.__mro__) + except AttributeError: + # Old-style class + return hasattr(C, attr) + + +class Hashable: + __metaclass__ = ABCMeta + + @abstractmethod + def __hash__(self): + return 0 + + @classmethod + def __subclasshook__(cls, C): + if cls is Hashable: + try: + for B in C.__mro__: + if "__hash__" in B.__dict__: + if B.__dict__["__hash__"]: + return True + break + except AttributeError: + # Old-style class + if getattr(C, "__hash__", None): + return True + return NotImplemented + + +class Iterable: + __metaclass__ = ABCMeta + + @abstractmethod + def __iter__(self): + while False: + yield None + + @classmethod + def __subclasshook__(cls, C): + if cls is Iterable: + if _hasattr(C, "__iter__"): + return True + return NotImplemented + +Iterable.register(str) + + +class Iterator(Iterable): + + @abstractmethod + def next(self): + raise StopIteration + + def __iter__(self): + return self + + @classmethod + def __subclasshook__(cls, C): + if cls is Iterator: + if _hasattr(C, "next"): + return True + return NotImplemented + + +class Sized: + __metaclass__ = ABCMeta + + @abstractmethod + def __len__(self): + return 0 + + @classmethod + def __subclasshook__(cls, C): + if cls is Sized: + if _hasattr(C, "__len__"): + return True + return NotImplemented + + +class Container: + __metaclass__ = ABCMeta + + @abstractmethod + def __contains__(self, x): + return False + + @classmethod + def __subclasshook__(cls, C): + if cls is Container: + if _hasattr(C, "__contains__"): + return True + return NotImplemented + + +class Callable: + __metaclass__ = ABCMeta + + @abstractmethod + def __call__(self, *args, **kwds): + return False + + @classmethod + def __subclasshook__(cls, C): + if cls is Callable: + if _hasattr(C, "__call__"): + return True + return NotImplemented + + +### SETS ### + + +class Set(Sized, Iterable, Container): + """A set is a finite, iterable container. + + This class provides concrete generic implementations of all + methods except for __contains__, __iter__ and __len__. + + To override the comparisons (presumably for speed, as the + semantics are fixed), all you have to do is redefine __le__ and + then the other operations will automatically follow suit. + """ + + def __le__(self, other): + if not isinstance(other, Set): + return NotImplemented + if len(self) > len(other): + return False + for elem in self: + if elem not in other: + return False + return True + + def __lt__(self, other): + if not isinstance(other, Set): + return NotImplemented + return len(self) < len(other) and self.__le__(other) + + def __gt__(self, other): + if not isinstance(other, Set): + return NotImplemented + return other < self + + def __ge__(self, other): + if not isinstance(other, Set): + return NotImplemented + return other <= self + + def __eq__(self, other): + if not isinstance(other, Set): + return NotImplemented + return len(self) == len(other) and self.__le__(other) + + def __ne__(self, other): + return not (self == other) + + @classmethod + def _from_iterable(cls, it): + '''Construct an instance of the class from any iterable input. + + Must override this method if the class constructor signature + does not accept an iterable for an input. + ''' + return cls(it) + + def __and__(self, other): + if not isinstance(other, Iterable): + return NotImplemented + return self._from_iterable(value for value in other if value in self) + + def isdisjoint(self, other): + for value in other: + if value in self: + return False + return True + + def __or__(self, other): + if not isinstance(other, Iterable): + return NotImplemented + chain = (e for s in (self, other) for e in s) + return self._from_iterable(chain) + + def __sub__(self, other): + if not isinstance(other, Set): + if not isinstance(other, Iterable): + return NotImplemented + other = self._from_iterable(other) + return self._from_iterable(value for value in self + if value not in other) + + def __xor__(self, other): + if not isinstance(other, Set): + if not isinstance(other, Iterable): + return NotImplemented + other = self._from_iterable(other) + return (self - other) | (other - self) + + # Sets are not hashable by default, but subclasses can change this + __hash__ = None + + def _hash(self): + """Compute the hash value of a set. + + Note that we don't define __hash__: not all sets are hashable. + But if you define a hashable set type, its __hash__ should + call this function. + + This must be compatible __eq__. + + All sets ought to compare equal if they contain the same + elements, regardless of how they are implemented, and + regardless of the order of the elements; so there's not much + freedom for __eq__ or __hash__. We match the algorithm used + by the built-in frozenset type. + """ + MAX = sys.maxint + MASK = 2 * MAX + 1 + n = len(self) + h = 1927868237 * (n + 1) + h &= MASK + for x in self: + hx = hash(x) + h ^= (hx ^ (hx << 16) ^ 89869747) * 3644798167 + h &= MASK + h = h * 69069 + 907133923 + h &= MASK + if h > MAX: + h -= MASK + 1 + if h == -1: + h = 590923713 + return h + +Set.register(frozenset) + + +class MutableSet(Set): + + @abstractmethod + def add(self, value): + """Add an element.""" + raise NotImplementedError + + @abstractmethod + def discard(self, value): + """Remove an element. Do not raise an exception if absent.""" + raise NotImplementedError + + def remove(self, value): + """Remove an element. If not a member, raise a KeyError.""" + if value not in self: + raise KeyError(value) + self.discard(value) + + def pop(self): + """Return the popped value. Raise KeyError if empty.""" + it = iter(self) + try: + value = next(it) + except StopIteration: + raise KeyError + self.discard(value) + return value + + def clear(self): + """This is slow (creates N new iterators!) but effective.""" + try: + while True: + self.pop() + except KeyError: + pass + + def __ior__(self, it): + for value in it: + self.add(value) + return self + + def __iand__(self, it): + for value in (self - it): + self.discard(value) + return self + + def __ixor__(self, it): + if it is self: + self.clear() + else: + if not isinstance(it, Set): + it = self._from_iterable(it) + for value in it: + if value in self: + self.discard(value) + else: + self.add(value) + return self + + def __isub__(self, it): + if it is self: + self.clear() + else: + for value in it: + self.discard(value) + return self + +MutableSet.register(set) + + +### MAPPINGS ### + + +class Mapping(Sized, Iterable, Container): + + @abstractmethod + def __getitem__(self, key): + raise KeyError + + def get(self, key, default=None): + try: + return self[key] + except KeyError: + return default + + def __contains__(self, key): + try: + self[key] + except KeyError: + return False + else: + return True + + def iterkeys(self): + return iter(self) + + def itervalues(self): + for key in self: + yield self[key] + + def iteritems(self): + for key in self: + yield (key, self[key]) + + def keys(self): + return list(self) + + def items(self): + return [(key, self[key]) for key in self] + + def values(self): + return [self[key] for key in self] + + # Mappings are not hashable by default, but subclasses can change this + __hash__ = None + + def __eq__(self, other): + if not isinstance(other, Mapping): + return NotImplemented + return dict(self.items()) == dict(other.items()) + + def __ne__(self, other): + return not (self == other) + +class MappingView(Sized): + + def __init__(self, mapping): + self._mapping = mapping + + def __len__(self): + return len(self._mapping) + + def __repr__(self): + return '{0.__class__.__name__}({0._mapping!r})'.format(self) + + +class KeysView(MappingView, Set): + + @classmethod + def _from_iterable(self, it): + return set(it) + + def __contains__(self, key): + return key in self._mapping + + def __iter__(self): + for key in self._mapping: + yield key + + +class ItemsView(MappingView, Set): + + @classmethod + def _from_iterable(self, it): + return set(it) + + def __contains__(self, item): + key, value = item + try: + v = self._mapping[key] + except KeyError: + return False + else: + return v == value + + def __iter__(self): + for key in self._mapping: + yield (key, self._mapping[key]) + + +class ValuesView(MappingView): + + def __contains__(self, value): + for key in self._mapping: + if value == self._mapping[key]: + return True + return False + + def __iter__(self): + for key in self._mapping: + yield self._mapping[key] + + +class MutableMapping(Mapping): + + @abstractmethod + def __setitem__(self, key, value): + raise KeyError + + @abstractmethod + def __delitem__(self, key): + raise KeyError + + __marker = object() + + def pop(self, key, default=__marker): + try: + value = self[key] + except KeyError: + if default is self.__marker: + raise + return default + else: + del self[key] + return value + + def popitem(self): + try: + key = next(iter(self)) + except StopIteration: + raise KeyError + value = self[key] + del self[key] + return key, value + + def clear(self): + try: + while True: + self.popitem() + except KeyError: + pass + + def update(*args, **kwds): + if len(args) > 2: + raise TypeError("update() takes at most 2 positional " + "arguments ({} given)".format(len(args))) + elif not args: + raise TypeError("update() takes at least 1 argument (0 given)") + self = args[0] + other = args[1] if len(args) >= 2 else () + + if isinstance(other, Mapping): + for key in other: + self[key] = other[key] + elif hasattr(other, "keys"): + for key in other.keys(): + self[key] = other[key] + else: + for key, value in other: + self[key] = value + for key, value in kwds.items(): + self[key] = value + + def setdefault(self, key, default=None): + try: + return self[key] + except KeyError: + self[key] = default + return default + +MutableMapping.register(dict) + + +### SEQUENCES ### + + +class Sequence(Sized, Iterable, Container): + """All the operations on a read-only sequence. + + Concrete subclasses must override __new__ or __init__, + __getitem__, and __len__. + """ + + @abstractmethod + def __getitem__(self, index): + raise IndexError + + def __iter__(self): + i = 0 + try: + while True: + v = self[i] + yield v + i += 1 + except IndexError: + return + + def __contains__(self, value): + for v in self: + if v == value: + return True + return False + + def __reversed__(self): + for i in reversed(range(len(self))): + yield self[i] + + def index(self, value): + for i, v in enumerate(self): + if v == value: + return i + raise ValueError + + def count(self, value): + return sum(1 for v in self if v == value) + +Sequence.register(tuple) +Sequence.register(basestring) +Sequence.register(buffer) +Sequence.register(xrange) + + +class MutableSequence(Sequence): + + @abstractmethod + def __setitem__(self, index, value): + raise IndexError + + @abstractmethod + def __delitem__(self, index): + raise IndexError + + @abstractmethod + def insert(self, index, value): + raise IndexError + + def append(self, value): + self.insert(len(self), value) + + def reverse(self): + n = len(self) + for i in range(n//2): + self[i], self[n-i-1] = self[n-i-1], self[i] + + def extend(self, values): + for v in values: + self.append(v) + + def pop(self, index=-1): + v = self[index] + del self[index] + return v + + def remove(self, value): + del self[self.index(value)] + + def __iadd__(self, values): + self.extend(values) + return self + +MutableSequence.register(list) Added: trunk/jython/Lib/_weakrefset.py =================================================================== --- trunk/jython/Lib/_weakrefset.py (rev 0) +++ trunk/jython/Lib/_weakrefset.py 2011-03-13 04:17:29 UTC (rev 7224) @@ -0,0 +1,212 @@ +# Access WeakSet through the weakref module. +# This code is separated-out because it is needed +# by abc.py to load everything else at startup. + +from _weakref import ref + +__all__ = ['WeakSet'] + + +class _IterationGuard(object): + # This context manager registers itself in the current iterators of the + # weak container, such as to delay all removals until the context manager + # exits. + # This technique should be relatively thread-safe (since sets are). + + def __init__(self, weakcontainer): + # Don't create cycles + self.weakcontainer = ref(weakcontainer) + + def __enter__(self): + w = self.weakcontainer() + if w is not None: + w._iterating.add(self) + return self + + def __exit__(self, e, t, b): + w = self.weakcontainer() + if w is not None: + s = w._iterating + s.remove(self) + if not s: + w._commit_removals() + + +class WeakSet(object): + def __init__(self, data=None): + self.data = set() + def _remove(item, selfref=ref(self)): + self = selfref() + if self is not None: + if self._iterating: + self._pending_removals.append(item) + else: + self.data.discard(item) + self._remove = _remove + # A list of keys to be removed + self._pending_removals = [] + self._iterating = set() + if data is not None: + self.update(data) + + def _commit_removals(self): + l = self._pending_removals + discard = self.data.discard + while l: + discard(l.pop()) + + def __iter__(self): + with _IterationGuard(self): + for itemref in self.data: + item = itemref() + if item is not None: + yield item + + def __len__(self): + return sum(x() is not None for x in self.data) + + def __contains__(self, item): + return ref(item) in self.data + + def __reduce__(self): + return (self.__class__, (list(self),), + getattr(self, '__dict__', None)) + + __hash__ = None + + def add(self, item): + if self._pending_removals: + self._commit_removals() + self.data.add(ref(item, self._remove)) + + def clear(self): + if self._pending_removals: + self._commit_removals() + self.data.clear() + + def copy(self): + return self.__class__(self) + + def pop(self): + if self._pending_removals: + self._commit_removals() + while True: + try: + itemref = self.data.pop() + except KeyError: + raise KeyError('pop from empty WeakSet') + item = itemref() + if item is not None: + return item + + def remove(self, item): + if self._pending_removals: + self._commit_removals() + self.data.remove(ref(item)) + + def discard(self, item): + if self._pending_removals: + self._commit_removals() + self.data.discard(ref(item)) + + def update(self, other): + if self._pending_removals: + self._commit_removals() + if isinstance(other, self.__class__): + self.data.update(other.data) + else: + for element in other: + self.add(element) + + def __ior__(self, other): + self.update(other) + return self + + # Helper functions for simple delegating methods. + def _apply(self, other, method): + if not isinstance(other, self.__class__): + other = self.__class__(other) + newdata = method(other.data) + newset = self.__class__() + newset.data = newdata + return newset + + def difference(self, other): + return self._apply(other, self.data.difference) + __sub__ = difference + + def difference_update(self, other): + if self._pending_removals: + self._commit_removals() + if self is other: + self.data.clear() + else: + self.data.difference_update(ref(item) for item in other) + def __isub__(self, other): + if self._pending_removals: + self._commit_removals() + if self is other: + self.data.clear() + else: + self.data.difference_update(ref(item) for item in other) + return self + + def intersection(self, other): + return self._apply(other, self.data.intersection) + __and__ = intersection + + def intersection_update(self, other): + if self._pending_removals: + self._commit_removals() + self.data.intersection_update(ref(item) for item in other) + def __iand__(self, other): + if self._pending_removals: + self._commit_removals() + self.data.intersection_update(ref(item) for item in other) + return self + + def issubset(self, other): + return self.data.issubset(ref(item) for item in other) + __lt__ = issubset + + def __le__(self, other): + return self.data <= set(ref(item) for item in other) + + def issuperset(self, other): + return self.data.issuperset(ref(item) for item in other) + __gt__ = issuperset + + def __ge__(self, other): + return self.data >= set(ref(item) for item in other) + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return self.data == set(ref(item) for item in other) + + def symmetric_difference(self, other): + return self._apply(other, self.data.symmetric_difference) + __xor__ = symmetric_difference + + def symmetric_difference_update(self, other): + if self._pending_removals: + self._commit_removals() + if self is other: + self.data.clear() + else: + self.data.symmetric_difference_update(ref(item) for item in other) + def __ixor__(self, other): + if self._pending_removals: + self._commit_removals() + if self is other: + self.data.clear() + else: + self.data.symmetric_difference_update(ref(item) for item in other) + return self + + def union(self, other): + return self._apply(other, self.data.union) + __or__ = union + + def isdisjoint(self, other): + return len(self.intersection(other)) == 0 Added: trunk/jython/Lib/abc.py =================================================================== --- trunk/jython/Lib/abc.py (rev 0) +++ trunk/jython/Lib/abc.py 2011-03-13 04:17:29 UTC (rev 7224) @@ -0,0 +1,185 @@ +# Copyright 2007 Google, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +"""Abstract Base Classes (ABCs) according to PEP 3119.""" + +import types + +from _weakrefset import WeakSet + +# Instance of old-style class +class _C: pass +_InstanceType = type(_C()) + + +def abstractmethod(funcobj): + """A decorator indicating abstract methods. + + Requires that the metaclass is ABCMeta or derived from it. A + class that has a metaclass derived from ABCMeta cannot be + instantiated unless all of its abstract methods are overridden. + The abstract methods can be called using any of the normal + 'super' call mechanisms. + + Usage: + + class C: + __metaclass__ = ABCMeta + @abstractmethod + def my_abstract_method(self, ...): + ... + """ + funcobj.__isabstractmethod__ = True + return funcobj + + +class abstractproperty(property): + """A decorator indicating abstract properties. + + Requires that the metaclass is ABCMeta or derived from it. A + class that has a metaclass derived from ABCMeta cannot be + instantiated unless all of its abstract properties are overridden. + The abstract properties can be called using any of the normal + 'super' call mechanisms. + + Usage: + + class C: + __metaclass__ = ABCMeta + @abstractproperty + def my_abstract_property(self): + ... + + This defines a read-only property; you can also define a read-write + abstract property using the 'long' form of property declaration: + + class C: + __metaclass__ = ABCMeta + def getx(self): ... + def setx(self, value): ... + x = abstractproperty(getx, setx) + """ + __isabstractmethod__ = True + + +class ABCMeta(type): + + """Metaclass for defining Abstract Base Classes (ABCs). + + Use this metaclass to create an ABC. An ABC can be subclassed + directly, and then acts as a mix-in class. You can also register + unrelated concrete classes (even built-in classes) and unrelated + ABCs as 'virtual subclasses' -- these and their descendants will + be considered subclasses of the registering ABC by the built-in + issubclass() function, but the registering ABC won't show up in + their MRO (Method Resolution Order) nor will method + implementations defined by the registering ABC be callable (not + even via super()). + + """ + + # A global counter that is incremented each time a class is + # registered as a virtual subclass of anything. It forces the + # negative cache to be cleared before its next use. + _abc_invalidation_counter = 0 + + def __new__(mcls, name, bases, namespace): + cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace) + # Compute set of abstract method names + abstracts = set(name + for name, value in namespace.items() + if getattr(value, "__isabstractmethod__", False)) + for base in bases: + for name in getattr(base, "__abstractmethods__", set()): + value = getattr(cls, name, None) + if getattr(value, "__isabstractmethod__", False): + abstracts.add(name) + cls.__abstractmethods__ = frozenset(abstracts) + # Set up inheritance registry + cls._abc_registry = WeakSet() + cls._abc_cache = WeakSet() + cls._abc_negative_cache = WeakSet() + cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter + return cls + + def register(cls, subclass): + """Register a virtual subclass of an ABC.""" + if not isinstance(subclass, (type, types.ClassType)): + raise TypeError("Can only register classes") + if issubclass(subclass, cls): + return # Already a subclass + # Subtle: test for cycles *after* testing for "already a subclass"; + # this means we allow X.register(X) and interpret it as a no-op. + if issubclass(cls, subclass): + # This would create a cycle, which is bad for the algorithm below + raise RuntimeError("Refusing to create an inheritance cycle") + cls._abc_registry.add(subclass) + ABCMeta._abc_invalidation_counter += 1 # Invalidate negative cache + + def _dump_registry(cls, file=None): + """Debug helper to print the ABC registry.""" + print >> file, "Class: %s.%s" % (cls.__module__, cls.__name__) + print >> file, "Inv.counter: %s" % ABCMeta._abc_invalidation_counter + for name in sorted(cls.__dict__.keys()): + if name.startswith("_abc_"): + value = getattr(cls, name) + print >> file, "%s: %r" % (name, value) + + def __instancecheck__(cls, instance): + """Override for isinstance(instance, cls).""" + # Inline the cache checking when it's simple. + subclass = getattr(instance, '__class__', None) + if subclass is not None and subclass in cls._abc_cache: + return True + subtype = type(instance) + # Old-style instances + if subtype is _InstanceType: + subtype = subclass + if subtype is subclass or subclass is None: + if (cls._abc_negative_cache_version == + ABCMeta._abc_invalidation_counter and + subtype in cls._abc_negative_cache): + return False + # Fall back to the subclass check. + return cls.__subclasscheck__(subtype) + return (cls.__subclasscheck__(subclass) or + cls.__subclasscheck__(subtype)) + + def __subclasscheck__(cls, subclass): + """Override for issubclass(subclass, cls).""" + # Check cache + if subclass in cls._abc_cache: + return True + # Check negative cache; may have to invalidate + if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter: + # Invalidate the negative cache + cls._abc_negative_cache = WeakSet() + cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter + elif subclass in cls._abc_negative_cache: + return False + # Check the subclass hook + ok = cls.__subclasshook__(subclass) + if ok is not NotImplemented: + assert isinstance(ok, bool) + if ok: + cls._abc_cache.add(subclass) + else: + cls._abc_negative_cache.add(subclass) + return ok + # Check if it's a direct subclass + if cls in getattr(subclass, '__mro__', ()): + cls._abc_cache.add(subclass) + return True + # Check if it's a subclass of a registered class (recursive) + for rcls in cls._abc_registry: + if issubclass(subclass, rcls): + cls._abc_cache.add(subclass) + return True + # Check if it's a subclass of a subclass (recursive) + for scls in cls.__subclasses__(): + if issubclass(subclass, scls): + cls._abc_cache.add(subclass) + return True + # No dice; update negative cache + cls._abc_negative_cache.add(subclass) + return False This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2011-03-17 22:18:44
|
Revision: 7242 http://jython.svn.sourceforge.net/jython/?rev=7242&view=rev Author: pjenvey Date: 2011-03-17 22:18:37 +0000 (Thu, 17 Mar 2011) Log Message: ----------- update inspect/test_inspect to 2.6 we now pass test_inspect's test_getargspec_method and all of its test_excluding_predicates Modified Paths: -------------- trunk/jython/Lib/inspect.py trunk/jython/Lib/test/test_inspect.py Modified: trunk/jython/Lib/inspect.py =================================================================== --- trunk/jython/Lib/inspect.py 2011-03-16 05:44:13 UTC (rev 7241) +++ trunk/jython/Lib/inspect.py 2011-03-17 22:18:37 UTC (rev 7242) @@ -7,8 +7,9 @@ Here are some of the useful functions provided by this module: - ismodule(), isclass(), ismethod(), isfunction(), istraceback(), - isframe(), iscode(), isbuiltin(), isroutine() - check object types + ismodule(), isclass(), ismethod(), isfunction(), isgeneratorfunction(), + isgenerator(), istraceback(), isframe(), iscode(), isbuiltin(), + isroutine() - check object types getmembers() - get members of an object that satisfy a given condition getfile(), getsourcefile(), getsource() - find an object's source code @@ -28,12 +29,27 @@ __author__ = 'Ka-Ping Yee <pi...@lf...>' __date__ = '1 Jan 2001' -import sys, os, types, string, re, dis, imp, tokenize, linecache +import sys +import os +import types +import string +import re +import dis +import imp +import tokenize +import linecache from operator import attrgetter +from collections import namedtuple _jython = sys.platform.startswith('java') if _jython: _ReflectedFunctionType = type(os.listdir) +# These constants are from Include/code.h. +CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS = 0x1, 0x2, 0x4, 0x8 +CO_NESTED, CO_GENERATOR, CO_NOFREE = 0x10, 0x20, 0x40 +# See Include/object.h +TPFLAGS_IS_ABSTRACT = 1 << 20 + # ----------------------------------------------------------- type-checking def ismodule(object): """Return true if the object is a module. @@ -139,6 +155,32 @@ func_name (same as __name__)""" return isinstance(object, types.FunctionType) +def isgeneratorfunction(object): + """Return true if the object is a user-defined generator function. + + Generator function objects provides same attributes as functions. + + See isfunction.__doc__ for attributes listing.""" + return bool((isfunction(object) or ismethod(object)) and + object.func_code.co_flags & CO_GENERATOR) + +def isgenerator(object): + """Return true if the object is a generator. + + Generator objects provide these attributes: + __iter__ defined to support interation over container + close raises a new GeneratorExit exception inside the + generator to terminate the iteration + gi_code code object + gi_frame frame object or possibly None once the generator has + been exhausted + gi_running set to 1 when generator is executing, 0 otherwise + next return the next item from the container + send resumes the generator and "sends" a value that becomes + the result of the current yield-expression + throw used to raise an exception inside the generator""" + return isinstance(object, types.GeneratorType) + def istraceback(object): """Return true if the object is a traceback. @@ -202,6 +244,10 @@ or ismethoddescriptor(object) or (_jython and isinstance(object, _ReflectedFunctionType))) +def isabstract(object): + """Return true if the object is an abstract base class (ABC).""" + return isinstance(object, type) and object.__flags__ & TPFLAGS_IS_ABSTRACT + def getmembers(object, predicate=None): """Return all members of an object as (name, value) pairs sorted by name. Optionally, only return members that satisfy a given predicate.""" @@ -213,6 +259,8 @@ results.sort() return results +Attribute = namedtuple('Attribute', 'name kind defining_class object') + def classify_class_attrs(cls): """Return list of attribute-descriptor tuples. @@ -279,7 +327,7 @@ else: kind = "data" - result.append((name, kind, homecls, obj)) + result.append(Attribute(name, kind, homecls, obj)) return result @@ -374,15 +422,18 @@ raise TypeError('arg is not a module, class, method, ' 'function, traceback, frame, or code object') +ModuleInfo = namedtuple('ModuleInfo', 'name suffix mode module_type') + def getmoduleinfo(path): """Get the module name, suffix, mode, and module type for a given file.""" filename = os.path.basename(path) - suffixes = map(lambda (suffix, mode, mtype): - (-len(suffix), suffix, mode, mtype), imp.get_suffixes()) + suffixes = map(lambda info: + (-len(info[0]), info[0], info[1], info[2]), + imp.get_suffixes()) suffixes.sort() # try longest suffixes first, in case they overlap for neglen, suffix, mode, mtype in suffixes: if filename[neglen:] == suffix: - return filename[:neglen], suffix, mode, mtype + return ModuleInfo(filename[:neglen], suffix, mode, mtype) def getmodulename(path): """Return the module name for a given file, or None.""" @@ -581,7 +632,9 @@ self.passline = False self.last = 1 - def tokeneater(self, type, token, (srow, scol), (erow, ecol), line): + def tokeneater(self, type, token, srow_scol, erow_ecol, line): + srow, scol = srow_scol + erow, ecol = erow_ecol if not self.started: # look for the first "def", "class" or "lambda" if token in ("def", "class", "lambda"): @@ -679,8 +732,7 @@ return walktree(roots, children, None) # ------------------------------------------------ argument list extraction -# These constants are from Python's compile.h. -CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS = 1, 2, 4, 8 +Arguments = namedtuple('Arguments', 'args varargs keywords') def getargs(co): """Get information about the arguments accepted by a code object. @@ -742,8 +794,10 @@ varkw = None if co.co_flags & CO_VARKEYWORDS: varkw = co.co_varnames[nargs] - return args, varargs, varkw + return Arguments(args, varargs, varkw) +ArgSpec = namedtuple('ArgSpec', 'args varargs keywords defaults') + def getargspec(func): """Get the names and default values of a function's arguments. @@ -758,8 +812,10 @@ if not isfunction(func): raise TypeError('arg is not a Python function') args, varargs, varkw = getargs(func.func_code) - return args, varargs, varkw, func.func_defaults + return ArgSpec(args, varargs, varkw, func.func_defaults) +ArgInfo = namedtuple('ArgInfo', 'args varargs keywords locals') + def getargvalues(frame): """Get information about arguments passed into a particular frame. @@ -768,7 +824,7 @@ 'varargs' and 'varkw' are the names of the * and ** arguments or None. 'locals' is the locals dictionary of the given frame.""" args, varargs, varkw = getargs(frame.f_code) - return args, varargs, varkw, frame.f_locals + return ArgInfo(args, varargs, varkw, frame.f_locals) def joinseq(seq): if len(seq) == 1: @@ -834,6 +890,9 @@ return '(' + string.join(specs, ', ') + ')' # -------------------------------------------------- stack frame extraction + +Traceback = namedtuple('Traceback', 'filename lineno function code_context index') + def getframeinfo(frame, context=1): """Get information about a frame or traceback object. @@ -865,7 +924,7 @@ else: lines = index = None - return (filename, lineno, frame.f_code.co_name, lines, index) + return Traceback(filename, lineno, frame.f_code.co_name, lines, index) def getlineno(frame): """Get the line number from a frame object, allowing for optimization.""" @@ -894,7 +953,10 @@ tb = tb.tb_next return framelist -currentframe = sys._getframe +if hasattr(sys, '_getframe'): + currentframe = sys._getframe +else: + currentframe = lambda _=None: None def stack(context=1): """Return a list of records for the stack above the caller's frame.""" Modified: trunk/jython/Lib/test/test_inspect.py =================================================================== --- trunk/jython/Lib/test/test_inspect.py 2011-03-16 05:44:13 UTC (rev 7241) +++ trunk/jython/Lib/test/test_inspect.py 2011-03-17 22:18:37 UTC (rev 7242) @@ -4,19 +4,24 @@ import inspect import datetime -from test.test_support import TESTFN, run_unittest, is_jython +from test.test_support import is_jython, run_unittest, _check_py3k_warnings -from test import inspect_fodder as mod -from test import inspect_fodder2 as mod2 -from test import test_support +with _check_py3k_warnings( + ("tuple parameter unpacking has been removed", SyntaxWarning), + quiet=True): + from test import inspect_fodder as mod + from test import inspect_fodder2 as mod2 # Functions tested in this suite: # ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode, -# isbuiltin, isroutine, getmembers, getdoc, getfile, getmodule, -# getsourcefile, getcomments, getsource, getclasstree, getargspec, -# getargvalues, formatargspec, formatargvalues, currentframe, stack, trace -# isdatadescriptor +# isbuiltin, isroutine, isgenerator, isgeneratorfunction, getmembers, +# getdoc, getfile, getmodule, getsourcefile, getcomments, getsource, +# getclasstree, getargspec, getargvalues, formatargspec, formatargvalues, +# currentframe, stack, trace, isdatadescriptor +# NOTE: There are some additional tests relating to interaction with +# zipimport in the test_zipimport_support test module. + modfile = mod.__file__ if modfile.endswith(('c', 'o')): modfile = modfile[:-1] @@ -26,7 +31,7 @@ import __builtin__ try: - 1/0 + 1 // 0 except: tb = sys.exc_traceback @@ -44,27 +49,37 @@ class IsTestBase(unittest.TestCase): predicates = set([inspect.isbuiltin, inspect.isclass, inspect.iscode, inspect.isframe, inspect.isfunction, inspect.ismethod, - inspect.ismodule, inspect.istraceback]) + inspect.ismodule, inspect.istraceback, + inspect.isgenerator, inspect.isgeneratorfunction]) def istest(self, predicate, exp): obj = eval(exp) self.failUnless(predicate(obj), '%s(%s)' % (predicate.__name__, exp)) for other in self.predicates - set([predicate]): + if predicate == inspect.isgeneratorfunction and\ + other == inspect.isfunction: + continue self.failIf(other(obj), 'not %s(%s)' % (other.__name__, exp)) +def generator_function_example(self): + for i in xrange(2): + yield i + class TestPredicates(IsTestBase): - def test_thirteen(self): + def test_sixteen(self): count = len(filter(lambda x:x.startswith('is'), dir(inspect))) - # Doc/lib/libinspect.tex claims there are 13 such functions - expected = 13 + # This test is here for remember you to update Doc/library/inspect.rst + # which claims there are 16 such functions + expected = 16 err_msg = "There are %d (not %d) is* functions" % (count, expected) self.assertEqual(count, expected, err_msg) + def test_excluding_predicates(self): - #XXX: Jython's PySystemState needs more work before this - #will be doable. - if not test_support.is_jython: + # XXX: Jython's PySystemState needs more work before this will + # be doable. + if not is_jython: self.istest(inspect.isbuiltin, 'sys.exit') self.istest(inspect.isbuiltin, '[].append') self.istest(inspect.isclass, 'mod.StupidGit') @@ -77,11 +92,12 @@ self.istest(inspect.istraceback, 'tb') self.istest(inspect.isdatadescriptor, '__builtin__.file.closed') self.istest(inspect.isdatadescriptor, '__builtin__.file.softspace') + self.istest(inspect.isgenerator, '(x for x in xrange(2))') + self.istest(inspect.isgeneratorfunction, 'generator_function_example') if hasattr(types, 'GetSetDescriptorType'): self.istest(inspect.isgetsetdescriptor, 'type(tb.tb_frame).f_locals') - #XXX: This detail of PyFrames is not yet supported in Jython - elif not test_support.is_jython: + else: self.failIf(inspect.isgetsetdescriptor(type(tb.tb_frame).f_locals)) if hasattr(types, 'MemberDescriptorType'): self.istest(inspect.ismemberdescriptor, 'datetime.timedelta.days') @@ -120,7 +136,7 @@ self.assertEqual(git.tr[1][1:], (modfile, 9, 'spam', [' eggs(b + d, c + f)\n'], 0)) self.assertEqual(git.tr[2][1:], (modfile, 18, 'eggs', - [' q = y / 0\n'], 0)) + [' q = y // 0\n'], 0)) def test_frame(self): args, varargs, varkw, locals = inspect.getargvalues(mod.fr) @@ -194,6 +210,10 @@ self.assertEqual(inspect.getdoc(git.abuse), 'Another\n\ndocstring\n\ncontaining\n\ntabs') + def test_cleandoc(self): + self.assertEqual(inspect.cleandoc('An\n indented\n docstring.'), + 'An\nindented\ndocstring.') + def test_getcomments(self): self.assertEqual(inspect.getcomments(mod), '# line 1\n') self.assertEqual(inspect.getcomments(mod.StupidGit), '# line 20\n') @@ -224,9 +244,9 @@ self.assertEqual(inspect.getfile(mod.StupidGit), mod.__file__) def test_getmodule_recursion(self): - from new import module + from types import ModuleType name = '__inspect_dummy' - m = sys.modules[name] = module(name) + m = sys.modules[name] = ModuleType(name) m.__file__ = "<string>" # hopefully not a real filename... m.__loader__ = "dummy" # pretend the filename is understood by a loader exec "def x(): pass" in m.__dict__ @@ -357,7 +377,6 @@ 'g', 'h', (3, (4, (5,))), '(a, b, c, d=3, (e, (f,))=(4, (5,)), *g, **h)') - @na_for_jython def test_getargspec_method(self): class A(object): def m(self): @@ -366,11 +385,14 @@ @na_for_jython def test_getargspec_sublistofone(self): - def sublistOfOne((foo,)): return 1 - self.assertArgSpecEquals(sublistOfOne, [['foo']]) + with _check_py3k_warnings( + ("tuple parameter unpacking has been removed", SyntaxWarning), + ("parenthesized argument names are invalid", SyntaxWarning)): + exec 'def sublistOfOne((foo,)): return 1' + self.assertArgSpecEquals(sublistOfOne, [['foo']]) - def fakeSublistOfOne((foo)): return 1 - self.assertArgSpecEquals(fakeSublistOfOne, ['foo']) + exec 'def fakeSublistOfOne((foo)): return 1' + self.assertArgSpecEquals(fakeSublistOfOne, ['foo']) def test_classify_oldstyle(self): class A: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |