Hello community, here is the log from the commit of package python-eventlet for openSUSE:Factory checked in at 2013-09-17 16:18:32 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-eventlet (Old) and /work/SRC/openSUSE:Factory/.python-eventlet.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-eventlet" Changes: -------- --- /work/SRC/openSUSE:Factory/python-eventlet/python-eventlet.changes 2013-07-04 10:14:21.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.python-eventlet.new/python-eventlet.changes 2013-09-17 16:25:46.000000000 +0200 @@ -1,0 +2,13 @@ +Tue Sep 17 08:56:56 UTC 2013 - [email protected] + +- update to 0.14.0: + * wsgi: handle connection socket timeouts; Thanks to Paul Oppenheim + * wsgi: close timed out client connections + * greenio: socket pypy compatibility; Thanks to Alex Gaynor + * wsgi: env['wsgi.input'] was returning 1 byte strings; Thanks to Eric Urban + * green.ssl: fix NameError; Github #17; Thanks to Jakub Stasiak + * websocket: allow "websocket" in lowercase in Upgrade header; Compatibility with current Google Chrome; Thanks to Dmitry Orlov + * wsgi: allow minimum_chunk_size to be overriden on a per request basis; Thanks to David Goetz + * wsgi: configurable socket_timeout + +------------------------------------------------------------------- Old: ---- eventlet-0.13.0.tar.gz New: ---- eventlet-0.14.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-eventlet.spec ++++++ --- /var/tmp/diff_new_pack.b5bfbY/_old 2013-09-17 16:25:47.000000000 +0200 +++ /var/tmp/diff_new_pack.b5bfbY/_new 2013-09-17 16:25:47.000000000 +0200 @@ -17,7 +17,7 @@ Name: python-eventlet -Version: 0.13.0 +Version: 0.14.0 Release: 0 Url: http://eventlet.net Summary: Highly concurrent networking library @@ -68,7 +68,7 @@ %files %defattr(-,root,root,-) -%doc AUTHORS LICENSE NEWS README README.twisted +%doc AUTHORS LICENSE NEWS README.rst README.twisted %{python_sitelib}/* %files doc ++++++ eventlet-0.13.0.tar.gz -> eventlet-0.14.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/AUTHORS new/eventlet-0.14.0/AUTHORS --- old/eventlet-0.13.0/AUTHORS 2013-07-02 18:25:04.000000000 +0200 +++ new/eventlet-0.14.0/AUTHORS 2013-09-13 18:36:48.000000000 +0200 @@ -33,6 +33,8 @@ * Geoff Salmon * Edward George * Floris Bruynooghe +* Paul Oppenheim +* Jakub Stasiak Linden Lab Contributors ----------------------- @@ -84,6 +86,7 @@ * Peter Portante, save syscalls in socket.dup(), environ[REMOTE_PORT] in wsgi * Peter Skirko, fixing socket.settimeout(0) bug * Derk Tegeler, Pre-cache proxied GreenSocket methods (Bitbucket #136) -* Jakub Stasiak, Travis integration, wsgi fix -* Paul Oppenheim, bug reports * David Malcolm, optional "timeout" argument to the subprocess module (Bitbucket #89) +* Eric Urban, fix wsgi.input 1-byte (Bitbucket #150) +* David Goetz, wsgi: Allow minimum_chunk_size to be overriden on a per request basis +* Dmitry Orlov, websocket: accept Upgrade: websocket (lowercase) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/MANIFEST.in new/eventlet-0.14.0/MANIFEST.in --- old/eventlet-0.13.0/MANIFEST.in 2013-07-02 18:25:04.000000000 +0200 +++ new/eventlet-0.14.0/MANIFEST.in 2013-09-13 15:50:20.000000000 +0200 @@ -1,4 +1,4 @@ recursive-include tests *.py *.crt *.key recursive-include doc *.rst *.txt *.py Makefile *.png recursive-include examples *.py *.html -include MANIFEST.in README.twisted NEWS AUTHORS LICENSE README +include MANIFEST.in README.twisted NEWS AUTHORS LICENSE README.rst diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/NEWS new/eventlet-0.14.0/NEWS --- old/eventlet-0.13.0/NEWS 2013-07-02 18:25:04.000000000 +0200 +++ new/eventlet-0.14.0/NEWS 2013-09-13 18:34:13.000000000 +0200 @@ -1,3 +1,14 @@ +0.14 +==== +* wsgi: handle connection socket timeouts; Thanks to Paul Oppenheim +* wsgi: close timed out client connections +* greenio: socket pypy compatibility; Thanks to Alex Gaynor +* wsgi: env['wsgi.input'] was returning 1 byte strings; Thanks to Eric Urban +* green.ssl: fix NameError; Github #17; Thanks to Jakub Stasiak +* websocket: allow "websocket" in lowercase in Upgrade header; Compatibility with current Google Chrome; Thanks to Dmitry Orlov +* wsgi: allow minimum_chunk_size to be overriden on a per request basis; Thanks to David Goetz +* wsgi: configurable socket_timeout + 0.13 ==== * hubs: kqueue support! Thanks to YAMAMOTO Takashi, Edward George diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/PKG-INFO new/eventlet-0.14.0/PKG-INFO --- old/eventlet-0.13.0/PKG-INFO 2013-07-02 18:38:04.000000000 +0200 +++ new/eventlet-0.14.0/PKG-INFO 2013-09-13 18:43:21.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: eventlet -Version: 0.13.0 +Version: 0.14.0 Summary: Highly concurrent networking library Home-page: http://eventlet.net Author: Linden Lab diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/README new/eventlet-0.14.0/README --- old/eventlet-0.13.0/README 2013-07-02 18:25:04.000000000 +0200 +++ new/eventlet-0.14.0/README 1970-01-01 01:00:00.000000000 +0100 @@ -1,50 +0,0 @@ -Eventlet is a concurrent networking library for Python that allows you to change how you run your code, not how you write it. - -It uses epoll or libevent for highly scalable non-blocking I/O. Coroutines ensure that the developer uses a blocking style of programming that is similar to threading, but provide the benefits of non-blocking I/O. The event dispatch is implicit, which means you can easily use Eventlet from the Python interpreter, or as a small part of a larger application. - -It's easy to get started using Eventlet, and easy to convert existing -applications to use it. Start off by looking at the `examples`_, -`common design patterns`_, and the list of `basic API primitives`_. - -.. _examples: http://eventlet.net/doc/examples.html -.. _common design patterns: http://eventlet.net/doc/design_patterns.html -.. _basic API primitives: http://eventlet.net/doc/basic_usage.html - -Quick Example -=============== - -Here's something you can try right on the command line:: - - % python - >>> import eventlet - >>> from eventlet.green import urllib2 - >>> gt = eventlet.spawn(urllib2.urlopen, 'http://eventlet.net') - >>> gt2 = eventlet.spawn(urllib2.urlopen, 'http://secondlife.com') - >>> gt2.wait() - >>> gt.wait() - - -Getting Eventlet -================== - -The easiest way to get Eventlet is to use easy_install or pip:: - - easy_install eventlet - pip install eventlet - -The development `tip`_ is available via easy_install as well:: - - easy_install 'eventlet==dev' - pip install 'eventlet==dev' - -.. _tip: http://bitbucket.org/eventlet/eventlet/get/tip.zip#egg=eventlet-dev - -Building the Docs Locally -========================= - -To build a complete set of HTML documentation, you must have Sphinx, which can be found at http://sphinx.pocoo.org/ (or installed with `easy_install sphinx`) - - cd doc - make html - -The built html files can be found in doc/_build/html afterward. \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/README.rst new/eventlet-0.14.0/README.rst --- old/eventlet-0.13.0/README.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/eventlet-0.14.0/README.rst 2013-09-13 15:50:20.000000000 +0200 @@ -0,0 +1,50 @@ +Eventlet is a concurrent networking library for Python that allows you to change how you run your code, not how you write it. + +It uses epoll or libevent for highly scalable non-blocking I/O. Coroutines ensure that the developer uses a blocking style of programming that is similar to threading, but provide the benefits of non-blocking I/O. The event dispatch is implicit, which means you can easily use Eventlet from the Python interpreter, or as a small part of a larger application. + +It's easy to get started using Eventlet, and easy to convert existing +applications to use it. Start off by looking at the `examples`_, +`common design patterns`_, and the list of `basic API primitives`_. + +.. _examples: http://eventlet.net/doc/examples.html +.. _common design patterns: http://eventlet.net/doc/design_patterns.html +.. _basic API primitives: http://eventlet.net/doc/basic_usage.html + +Quick Example +=============== + +Here's something you can try right on the command line:: + + % python + >>> import eventlet + >>> from eventlet.green import urllib2 + >>> gt = eventlet.spawn(urllib2.urlopen, 'http://eventlet.net') + >>> gt2 = eventlet.spawn(urllib2.urlopen, 'http://secondlife.com') + >>> gt2.wait() + >>> gt.wait() + + +Getting Eventlet +================== + +The easiest way to get Eventlet is to use easy_install or pip:: + + easy_install eventlet + pip install eventlet + +The development `tip`_ is available via easy_install as well:: + + easy_install 'eventlet==dev' + pip install 'eventlet==dev' + +.. _tip: http://bitbucket.org/eventlet/eventlet/get/tip.zip#egg=eventlet-dev + +Building the Docs Locally +========================= + +To build a complete set of HTML documentation, you must have Sphinx, which can be found at http://sphinx.pocoo.org/ (or installed with `easy_install sphinx`) + + cd doc + make html + +The built html files can be found in doc/_build/html afterward. \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/eventlet/__init__.py new/eventlet-0.14.0/eventlet/__init__.py --- old/eventlet-0.13.0/eventlet/__init__.py 2013-07-02 18:29:16.000000000 +0200 +++ new/eventlet-0.14.0/eventlet/__init__.py 2013-09-13 18:34:47.000000000 +0200 @@ -1,4 +1,4 @@ -version_info = (0, 13, 0) +version_info = (0, 14, 0) __version__ = ".".join(map(str, version_info)) try: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/eventlet/green/ssl.py new/eventlet-0.14.0/eventlet/green/ssl.py --- old/eventlet-0.13.0/eventlet/green/ssl.py 2013-07-02 18:25:04.000000000 +0200 +++ new/eventlet-0.14.0/eventlet/green/ssl.py 2013-08-20 14:50:45.000000000 +0200 @@ -136,7 +136,7 @@ else: while True: try: - return socket.sendall(self, buflen, flags) + return socket.sendall(self, data, flags) except orig_socket.error, e: if self.act_non_blocking: raise diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/eventlet/greenio.py new/eventlet-0.14.0/eventlet/greenio.py --- old/eventlet-0.13.0/eventlet/greenio.py 2013-07-02 18:25:04.000000000 +0200 +++ new/eventlet-0.14.0/eventlet/greenio.py 2013-08-20 14:50:45.000000000 +0200 @@ -127,8 +127,6 @@ fd = _original_socket(family_or_realsock, *args, **kwargs) else: fd = family_or_realsock - assert not args, args - assert not kwargs, kwargs # import timeout from other socket, if it was there try: @@ -235,7 +233,11 @@ return newsock def makefile(self, *args, **kw): - return _fileobject(self.dup(), *args, **kw) + dupped = self.dup() + res = _fileobject(dupped, *args, **kw) + if hasattr(dupped, "_drop"): + dupped._drop() + return res def makeGreenFile(self, *args, **kw): warnings.warn("makeGreenFile has been deprecated, please use " @@ -342,6 +344,13 @@ def gettimeout(self): return self._timeout + if "__pypy__" in sys.builtin_module_names: + def _reuse(self): + self.fd._sock._reuse() + + def _drop(self): + self.fd._sock._drop() + class _SocketDuckForFd(object): """ Class implementing all socket method used by _fileobject in cooperative manner using low level os I/O calls.""" @@ -393,6 +402,13 @@ def __repr__(self): return "%s:%d" % (self.__class__.__name__, self._fileno) + if "__pypy__" in sys.builtin_module_names: + def _reuse(self): + pass + + def _drop(self): + pass + def _operationOnClosedFile(*args, **kwargs): raise ValueError("I/O operation on closed file") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/eventlet/support/greendns.py new/eventlet-0.14.0/eventlet/support/greendns.py --- old/eventlet-0.13.0/eventlet/support/greendns.py 2013-07-02 18:25:04.000000000 +0200 +++ new/eventlet-0.14.0/eventlet/support/greendns.py 2013-08-20 14:50:45.000000000 +0200 @@ -40,13 +40,13 @@ from eventlet.green import time from eventlet.green import select -dns = patcher.import_patched('dns', +dns = patcher.import_patched('dns', socket=_socket_nodns, time=time, select=select) -for pkg in ('dns.query', 'dns.exception', 'dns.inet', 'dns.message', +for pkg in ('dns.query', 'dns.exception', 'dns.inet', 'dns.message', 'dns.rdatatype','dns.resolver', 'dns.reversename'): - setattr(dns, pkg.split('.')[1], patcher.import_patched(pkg, + setattr(dns, pkg.split('.')[1], patcher.import_patched(pkg, socket=_socket_nodns, time=time, select=select)) @@ -252,12 +252,13 @@ raise socket.gaierror( (socket.EAI_NODATA, 'No address associated with hostname')) - if not (flags & socket.NI_NUMERICSERV): - proto = (flags & socket.NI_DGRAM) and 'udp' or 'tcp' - port = socket.getservbyport(port, proto) + if not (flags & socket.NI_NUMERICSERV): + proto = (flags & socket.NI_DGRAM) and 'udp' or 'tcp' + port = socket.getservbyport(port, proto) return (host, port) + def is_ipv4_addr(host): """is_ipv4_addr returns true if host is a valid IPv4 address in dotted quad notation. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/eventlet/websocket.py new/eventlet-0.14.0/eventlet/websocket.py --- old/eventlet-0.13.0/eventlet/websocket.py 2013-07-02 18:25:04.000000000 +0200 +++ new/eventlet-0.14.0/eventlet/websocket.py 2013-09-13 15:53:28.000000000 +0200 @@ -40,7 +40,7 @@ def __call__(self, environ, start_response): if not (environ.get('HTTP_CONNECTION') == 'Upgrade' and - environ.get('HTTP_UPGRADE') == 'WebSocket'): + environ.get('HTTP_UPGRADE').lower() == 'websocket'): # need to check a few more things here for true compliance start_response('400 Bad Request', [('Connection','close')]) return [] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/eventlet/wsgi.py new/eventlet-0.14.0/eventlet/wsgi.py --- old/eventlet-0.13.0/eventlet/wsgi.py 2013-07-02 18:25:04.000000000 +0200 +++ new/eventlet-0.14.0/eventlet/wsgi.py 2013-09-13 15:50:20.000000000 +0200 @@ -31,6 +31,7 @@ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + def format_date_time(timestamp): """Formats a unix timestamp into an HTTP standard string.""" year, month, day, hh, mm, ss, wd, _y, _z = time.gmtime(timestamp) @@ -38,11 +39,13 @@ _weekdayname[wd], day, _monthname[month], year, hh, mm, ss ) + # Collections of error codes to compare against. Not all attributes are set # on errno module on all platforms, so some are literals :( BAD_SOCK = set((errno.EBADF, 10053)) BROKEN_SOCK = set((errno.EPIPE, errno.ECONNRESET)) + # special flag return value for apps class _AlreadyHandled(object): @@ -54,7 +57,9 @@ ALREADY_HANDLED = _AlreadyHandled() + class Input(object): + def __init__(self, rfile, content_length, @@ -161,7 +166,7 @@ return self._do_read(self.rfile.readlines, hint) def __iter__(self): - return iter(self.read()) + return iter(self.read, '') def get_socket(self): return self.rfile._sock @@ -392,10 +397,12 @@ towrite = [] towrite_size = 0 just_written_size = 0 + minimum_write_chunk_size = int(self.environ.get( + 'eventlet.minimum_write_chunk_size', self.minimum_chunk_size)) for data in result: towrite.append(data) towrite_size += len(data) - if towrite_size >= self.minimum_chunk_size: + if towrite_size >= minimum_write_chunk_size: write(''.join(towrite)) towrite = [] just_written_size = towrite_size @@ -519,8 +526,8 @@ self.connection.close() - class Server(BaseHTTPServer.HTTPServer): + def __init__(self, socket, address, @@ -535,7 +542,8 @@ log_output=True, log_format=DEFAULT_LOG_FORMAT, url_length_limit=MAX_REQUEST_LINE, - debug=True): + debug=True, + socket_timeout=None): self.outstanding_requests = 0 self.socket = socket @@ -556,6 +564,7 @@ self.log_format = log_format self.url_length_limit = url_length_limit self.debug = debug + self.socket_timeout = socket_timeout def get_environ(self): d = { @@ -574,18 +583,26 @@ d.update(self.environ) return d - def process_request(self, (socket, address)): + def process_request(self, (sock, address)): # The actual request handling takes place in __init__, so we need to # set minimum_chunk_size before __init__ executes and we don't want to modify # class variable proto = types.InstanceType(self.protocol) if self.minimum_chunk_size is not None: proto.minimum_chunk_size = self.minimum_chunk_size - proto.__init__(socket, address, self) + try: + proto.__init__(sock, address, self) + except socket.timeout: + # Expected exceptions are not exceptional + sock.close() + if self.debug: + # similar to logging "accepted" in server() + self.log_message('(%s) timed out %r' % (self.pid, address)) def log_message(self, message): self.log.write(message + '\n') + try: import ssl ACCEPT_EXCEPTIONS = (socket.error, ssl.SSLError) @@ -595,6 +612,7 @@ ACCEPT_EXCEPTIONS = (socket.error,) ACCEPT_ERRNO = set((errno.EPIPE, errno.EBADF, errno.ECONNRESET)) + def server(sock, site, log=None, environ=None, @@ -609,8 +627,9 @@ log_output=True, log_format=DEFAULT_LOG_FORMAT, url_length_limit=MAX_REQUEST_LINE, - debug=True): - """ Start up a wsgi server handling requests from the supplied server + debug=True, + socket_timeout=None): + """Start up a WSGI server handling requests from the supplied server socket. This function loops forever. The *sock* object will be closed after server exits, but the underlying file descriptor will remain open, so if you have a dup() of *sock*, it will remain usable. @@ -623,7 +642,7 @@ :param max_http_version: Set to "HTTP/1.0" to make the server pretend it only supports HTTP 1.0. This can help with applications or clients that don't behave properly using HTTP 1.1. :param protocol: Protocol class. Deprecated. :param server_event: Used to collect the Server object. Deprecated. - :param minimum_chunk_size: Minimum size in bytes for http chunks. This can be used to improve performance of applications which yield many small strings, though using it technically violates the WSGI spec. + :param minimum_chunk_size: Minimum size in bytes for http chunks. This can be used to improve performance of applications which yield many small strings, though using it technically violates the WSGI spec. This can be overridden on a per request basis by setting environ['eventlet.minimum_write_chunk_size']. :param log_x_forwarded_for: If True (the default), logs the contents of the x-forwarded-for header in addition to the actual client ip address in the 'client_ip' field of the log line. :param custom_pool: A custom GreenPool instance which is used to spawn client green threads. If this is supplied, max_size is ignored. :param keepalive: If set to False, disables keepalives on the server; all connections will be closed after serving one request. @@ -631,6 +650,7 @@ :param log_format: A python format string that is used as the template to generate log lines. The following values can be formatted into it: client_ip, date_time, request_line, status_code, body_length, wall_seconds. The default is a good example of how to use it. :param url_length_limit: A maximum allowed length of the request url. If exceeded, 414 error is returned. :param debug: True if the server should send exception tracebacks to the clients on 500 errors. If False, the server will respond with empty bodies. + :param socket_timeout: Timeout for client connections' socket operations. Default None means wait forever. """ serv = Server(sock, sock.getsockname(), site, log, @@ -643,7 +663,9 @@ log_output=log_output, log_format=log_format, url_length_limit=url_length_limit, - debug=debug) + debug=debug, + socket_timeout=socket_timeout, + ) if server_event is not None: server_event.send(serv) if max_size is None: @@ -669,6 +691,7 @@ while True: try: client_socket = sock.accept() + client_socket[0].settimeout(serv.socket_timeout) if debug: serv.log.write("(%s) accepted %r\n" % ( serv.pid, client_socket[1])) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/eventlet.egg-info/PKG-INFO new/eventlet-0.14.0/eventlet.egg-info/PKG-INFO --- old/eventlet-0.13.0/eventlet.egg-info/PKG-INFO 2013-07-02 18:37:51.000000000 +0200 +++ new/eventlet-0.14.0/eventlet.egg-info/PKG-INFO 2013-09-13 18:43:17.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: eventlet -Version: 0.13.0 +Version: 0.14.0 Summary: Highly concurrent networking library Home-page: http://eventlet.net Author: Linden Lab diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/eventlet.egg-info/SOURCES.txt new/eventlet-0.14.0/eventlet.egg-info/SOURCES.txt --- old/eventlet-0.13.0/eventlet.egg-info/SOURCES.txt 2013-07-02 18:37:51.000000000 +0200 +++ new/eventlet-0.14.0/eventlet.egg-info/SOURCES.txt 2013-09-13 18:43:18.000000000 +0200 @@ -2,7 +2,7 @@ LICENSE MANIFEST.in NEWS -README +README.rst README.twisted setup.py doc/Makefile @@ -153,6 +153,7 @@ tests/env_test.py tests/event_test.py tests/fork_test.py +tests/greendns_test.py tests/greenio_test.py tests/greenpipe_test_with_statement.py tests/greenpool_test.py @@ -189,6 +190,7 @@ tests/tpool_test.py tests/websocket_test.py tests/wsgi_test.py +tests/wsgi_test_conntimeout.py tests/zmq_test.py tests/stdlib/all.py tests/stdlib/all_modules.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/setup.py new/eventlet-0.14.0/setup.py --- old/eventlet-0.13.0/setup.py 2013-07-02 18:25:04.000000000 +0200 +++ new/eventlet-0.14.0/setup.py 2013-09-13 15:50:20.000000000 +0200 @@ -19,7 +19,7 @@ long_description=open( path.join( path.dirname(__file__), - 'README' + 'README.rst' ) ).read(), test_suite='nose.collector', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/tests/__init__.py new/eventlet-0.14.0/tests/__init__.py --- old/eventlet-0.13.0/tests/__init__.py 2013-07-02 18:25:04.000000000 +0200 +++ new/eventlet-0.14.0/tests/__init__.py 2013-09-13 15:52:46.000000000 +0200 @@ -1,13 +1,18 @@ # package is named tests, not test, so it won't be confused with test in stdlib import errno import os -import resource +try: + import resource +except ImportError: + resource = None import signal +import subprocess +import sys import unittest import warnings import eventlet -from eventlet import debug, hubs +from eventlet import debug, hubs, tpool # convenience for importers @@ -173,15 +178,8 @@ signal.signal(signal.SIGALRM, self.previous_alarm[0]) signal.alarm(self.previous_alarm[1]) - try: - hub = hubs.get_hub() - num_readers = len(hub.get_readers()) - num_writers = len(hub.get_writers()) - assert num_readers == num_writers == 0 - except AssertionError: - print "ERROR: Hub not empty" - print debug.format_hub_timers() - print debug.format_hub_listeners() + tpool.killall() + verify_hub_empty() def assert_less_than(self, a,b,msg=None): if msg: @@ -201,6 +199,11 @@ def check_idle_cpu_usage(duration, allowed_part): + if resource is None: + # TODO: use https://code.google.com/p/psutil/ + from nose.plugins.skip import SkipTest + raise SkipTest('CPU usage testing not supported (`import resource` failed)') + r1 = resource.getrusage(resource.RUSAGE_SELF) eventlet.sleep(duration) r2 = resource.getrusage(resource.RUSAGE_SELF) @@ -282,5 +285,24 @@ pass return retval + +def run_python(path): + if not path.endswith('.py'): + path += '.py' + path = os.path.abspath(path) + dir_ = os.path.dirname(path) + new_env = os.environ.copy() + new_env['PYTHONPATH'] = os.pathsep.join(sys.path + [dir_]) + p = subprocess.Popen( + [sys.executable, path], + env=new_env, + stderr=subprocess.STDOUT, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + ) + output, _ = p.communicate() + return output + + certificate_file = os.path.join(os.path.dirname(__file__), 'test_server.crt') private_key_file = os.path.join(os.path.dirname(__file__), 'test_server.key') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/tests/greendns_test.py new/eventlet-0.14.0/tests/greendns_test.py --- old/eventlet-0.13.0/tests/greendns_test.py 1970-01-01 01:00:00.000000000 +0100 +++ new/eventlet-0.14.0/tests/greendns_test.py 2013-08-20 14:50:45.000000000 +0200 @@ -0,0 +1,13 @@ +from nose.plugins.skip import SkipTest + + +def test_greendns_getnameinfo_resolve_port(): + try: + from eventlet.support import greendns + except ImportError: + raise SkipTest('greendns requires package dnspython') + + # https://bitbucket.org/eventlet/eventlet/issue/152 + _, port1 = greendns.getnameinfo(('127.0.0.1', 80), 0) + _, port2 = greendns.getnameinfo(('localhost', 80), 0) + assert port1 == port2 == 'http' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/tests/mysqldb_test.py new/eventlet-0.14.0/tests/mysqldb_test.py --- old/eventlet-0.13.0/tests/mysqldb_test.py 2013-07-02 18:25:04.000000000 +0200 +++ new/eventlet-0.14.0/tests/mysqldb_test.py 2013-08-20 14:50:45.000000000 +0200 @@ -34,6 +34,8 @@ class MySQLdbTester(LimitedTestCase): def setUp(self): + super(MySQLdbTester, self).setUp() + self._auth = get_database_auth()['MySQLdb'] self.create_db() self.connection = None @@ -51,6 +53,8 @@ self.connection.close() self.drop_db() + super(MySQLdbTester, self).tearDown() + @skip_unless(mysql_requirement) def create_db(self): auth = self._auth.copy() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/tests/patcher_test.py new/eventlet-0.14.0/tests/patcher_test.py --- old/eventlet-0.13.0/tests/patcher_test.py 2013-07-02 18:25:04.000000000 +0200 +++ new/eventlet-0.14.0/tests/patcher_test.py 2013-08-20 14:50:45.000000000 +0200 @@ -4,7 +4,8 @@ import sys import tempfile -from tests import LimitedTestCase, main, skip_with_pyevent +from tests import LimitedTestCase, main, run_python, skip_with_pyevent + base_module_contents = """ import socket @@ -27,32 +28,31 @@ print "importing", patching, socket, patching.socket, patching.urllib """ + class ProcessBase(LimitedTestCase): - TEST_TIMEOUT=3 # starting processes is time-consuming + TEST_TIMEOUT = 3 # starting processes is time-consuming + def setUp(self): + super(ProcessBase, self).setUp() self._saved_syspath = sys.path self.tempdir = tempfile.mkdtemp('_patcher_test') - + def tearDown(self): + super(ProcessBase, self).tearDown() sys.path = self._saved_syspath shutil.rmtree(self.tempdir) - + def write_to_tempfile(self, name, contents): - filename = os.path.join(self.tempdir, name + '.py') - fd = open(filename, "w") + filename = os.path.join(self.tempdir, name) + if not filename.endswith('.py'): + filename = filename + '.py' + fd = open(filename, "wb") fd.write(contents) fd.close() - + def launch_subprocess(self, filename): - python_path = os.pathsep.join(sys.path + [self.tempdir]) - new_env = os.environ.copy() - new_env['PYTHONPATH'] = python_path - if not filename.endswith('.py'): - filename = filename + '.py' - p = subprocess.Popen([sys.executable, - os.path.join(self.tempdir, filename)], - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=new_env) - output, _ = p.communicate() + path = os.path.join(self.tempdir, filename) + output = run_python(path) lines = output.split("\n") return output, lines @@ -77,7 +77,7 @@ self.assert_('eventlet.green.socket' in lines[2], repr(output)) self.assert_('eventlet.green.urllib' in lines[2], repr(output)) self.assert_('eventlet.green.httplib' not in lines[2], repr(output)) - + def test_import_patched_defaults(self): self.write_to_tempfile("base", base_module_contents) new_mod = """ @@ -93,7 +93,7 @@ self.assert_('GreenSocket' in lines[1], repr(output)) -class MonkeyPatch(ProcessBase): +class MonkeyPatch(ProcessBase): def test_patched_modules(self): new_mod = """ from eventlet import patcher @@ -106,7 +106,7 @@ output, lines = self.launch_subprocess('newmod.py') self.assert_(lines[0].startswith('newmod'), repr(output)) self.assertEqual(lines[0].count('GreenSocket'), 2, repr(output)) - + def test_early_patching(self): new_mod = """ from eventlet import patcher @@ -133,7 +133,7 @@ output, lines = self.launch_subprocess('newmod.py') self.assertEqual(len(lines), 2, repr(output)) self.assert_(lines[0].startswith('newmod'), repr(output)) - + def test_typeerror(self): new_mod = """ @@ -144,7 +144,7 @@ output, lines = self.launch_subprocess('newmod.py') self.assert_(lines[-2].startswith('TypeError'), repr(output)) self.assert_('finagle' in lines[-2], repr(output)) - + def assert_boolean_logic(self, call, expected, not_expected=''): expected_list = ", ".join(['"%s"' % x for x in expected.split(',') if len(x)]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/tests/ssl_test.py new/eventlet-0.14.0/tests/ssl_test.py --- old/eventlet-0.13.0/tests/ssl_test.py 2013-07-02 18:25:04.000000000 +0200 +++ new/eventlet-0.14.0/tests/ssl_test.py 2013-08-20 14:50:45.000000000 +0200 @@ -1,9 +1,17 @@ -from tests import LimitedTestCase, certificate_file, private_key_file, check_idle_cpu_usage -from tests import skip_if_no_ssl +import socket +import warnings from unittest import main + import eventlet from eventlet import util, greenio -import socket +try: + from eventlet.green.socket import ssl +except ImportError: + pass +from tests import ( + LimitedTestCase, certificate_file, private_key_file, check_idle_cpu_usage, + skip_if_no_ssl +) def listen_ssl_socket(address=('127.0.0.1', 0)): @@ -16,6 +24,15 @@ class SSLTest(LimitedTestCase): + def setUp(self): + # disabling socket.ssl warnings because we're testing it here + warnings.filterwarnings( + action='ignore', + message='.*socket.ssl.*', + category=DeprecationWarning) + + super(SSLTest, self).setUp() + @skip_if_no_ssl def test_duplex_response(self): def serve(listener): @@ -133,16 +150,8 @@ check_idle_cpu_usage(0.2, 0.1) server_coro.kill() - -class SocketSSLTest(LimitedTestCase): @skip_if_no_ssl def test_greensslobject(self): - import warnings - # disabling socket.ssl warnings because we're testing it here - warnings.filterwarnings(action = 'ignore', - message='.*socket.ssl.*', - category=DeprecationWarning) - def serve(listener): sock, addr = listener.accept() sock.write('content') @@ -150,11 +159,24 @@ sock.close() listener = listen_ssl_socket(('', 0)) killer = eventlet.spawn(serve, listener) - from eventlet.green.socket import ssl client = ssl(eventlet.connect(('localhost', listener.getsockname()[1]))) self.assertEquals(client.read(1024), 'content') self.assertEquals(client.read(1024), '') + @skip_if_no_ssl + def test_regression_gh_17(self): + def serve(listener): + sock, addr = listener.accept() + + # to simulate condition mentioned in GH-17 + sock._sslobj = None + sock.sendall('some data') + greenio.shutdown_safe(sock) + sock.close() + + listener = listen_ssl_socket(('', 0)) + killer = eventlet.spawn(serve, listener) + client = ssl(eventlet.connect(('localhost', listener.getsockname()[1]))) if __name__ == '__main__': main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/tests/wsgi_test.py new/eventlet-0.14.0/tests/wsgi_test.py --- old/eventlet-0.13.0/tests/wsgi_test.py 2013-07-02 18:25:04.000000000 +0200 +++ new/eventlet-0.14.0/tests/wsgi_test.py 2013-09-13 15:50:20.000000000 +0200 @@ -14,7 +14,7 @@ from eventlet import wsgi from eventlet.support import get_errno -from tests import find_command +from tests import find_command, run_python httplib = eventlet.import_patched('httplib') @@ -195,15 +195,10 @@ super(_TestBase, self).tearDown() def spawn_server(self, **kwargs): - """Spawns a new wsgi server with the given arguments. - Sets self.port to the port of the server, and self.killer is the greenlet - running it. - - Kills any previously-running server.""" - eventlet.sleep(0) # give previous server a chance to start - if self.killer: - greenthread.kill(self.killer) + """Spawns a new wsgi server with the given arguments using + :meth:`spawn_thread`. + Sets self.port to the port of the server""" new_kwargs = dict(max_size=128, log=self.logfile, site=self.site) @@ -213,9 +208,19 @@ new_kwargs['sock'] = eventlet.listen(('localhost', 0)) self.port = new_kwargs['sock'].getsockname()[1] - self.killer = eventlet.spawn_n( - wsgi.server, - **new_kwargs) + self.spawn_thread(wsgi.server, **new_kwargs) + + def spawn_thread(self, target, **kwargs): + """Spawns a new greenthread using specified target and arguments. + + Kills any previously-running server and sets self.killer to the + greenthread running the target. + """ + eventlet.sleep(0) # give previous server a chance to start + if self.killer: + greenthread.kill(self.killer) + + self.killer = eventlet.spawn_n(target, **kwargs) def set_site(self): raise NotImplementedError @@ -956,6 +961,34 @@ 'HTTP/1.0 400 Headers Too Large\r\n') fd.close() + def test_032_wsgi_input_as_iterable(self): + # https://bitbucket.org/eventlet/eventlet/issue/150 + # env['wsgi.input'] returns a single byte at a time + # when used as an iterator + g = [0] + + def echo_by_iterating(env, start_response): + start_response('200 OK', [('Content-type', 'text/plain')]) + for chunk in env['wsgi.input']: + g[0] += 1 + yield chunk + + self.site.application = echo_by_iterating + upload_data = '123456789abcdef' * 100 + request = ( + 'POST / HTTP/1.0\r\n' + 'Host: localhost\r\n' + 'Content-Length: %i\r\n\r\n%s' + ) % (len(upload_data), upload_data) + sock = eventlet.connect(('localhost', self.port)) + fd = sock.makefile('rw') + fd.write(request) + fd.flush() + response_line, headers, body = read_http(sock) + self.assertEquals(body, upload_data) + fd.close() + self.assertEquals(g[0], 1) + def test_zero_length_chunked_response(self): def zero_chunked_app(env, start_response): start_response('200 OK', [('Content-type', 'text/plain')]) @@ -1076,12 +1109,15 @@ return log = StringIO() # first thing the server does is try to log the IP it's bound to + def run_server(): try: - server = wsgi.server(sock=sock, log=log, site=Site()) + wsgi.server(sock=sock, log=log, site=Site()) except ValueError: log.write('broked') - eventlet.spawn_n(run_server) + + self.spawn_thread(run_server) + logval = log.getvalue() while not logval: eventlet.sleep(0.0) @@ -1144,6 +1180,38 @@ request_thread.wait() server_sock.close() + def test_server_connection_timeout_exception(self): + # Handle connection socket timeouts + # https://bitbucket.org/eventlet/eventlet/issue/143/ + # Runs tests.wsgi_test_conntimeout in a separate process. + testcode_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'wsgi_test_conntimeout.py') + output = run_python(testcode_path) + sections = output.split("SEPERATOR_SENTINEL") + # first section is empty + self.assertEqual(3, len(sections), output) + # if the "BOOM" check fails, it's because our timeout didn't happen + # (if eventlet stops using file.readline() to read HTTP headers, + # for instance) + for runlog in sections[1:]: + debug = False if "debug set to: False" in runlog else True + if debug: + self.assertTrue("timed out" in runlog) + self.assertTrue("BOOM" in runlog) + self.assertFalse("Traceback" in runlog) + + def test_server_socket_timeout(self): + self.spawn_server(socket_timeout=0.1) + sock = eventlet.connect(('localhost', self.port)) + sock.send('GET / HTTP/1.1\r\n') + eventlet.sleep(0.1) + try: + read_http(sock) + assert False, 'Expected ConnectionClosed exception' + except ConnectionClosed: + pass + def read_headers(sock): fd = sock.makefile() @@ -1173,6 +1241,7 @@ headers[key.lower()] = value return response_line, headers + class IterableAlreadyHandledTest(_TestBase): def set_site(self): self.site = IterableSite() @@ -1199,6 +1268,7 @@ self.assertEqual(headers.get('transfer-encoding'), 'chunked') self.assertEqual(body, '0\r\n\r\n') # Still coming back chunked + class ProxiedIterableAlreadyHandledTest(IterableAlreadyHandledTest): # same thing as the previous test but ensuring that it works with tpooled # results as well as regular ones @@ -1212,6 +1282,7 @@ tpool.killall() super(ProxiedIterableAlreadyHandledTest, self).tearDown() + class TestChunkedInput(_TestBase): dirt = "" validator = None @@ -1230,6 +1301,24 @@ elif pi=="/ping": input.read() response.append("pong") + elif pi.startswith("/yield_spaces"): + if pi.endswith('override_min'): + env['eventlet.minimum_write_chunk_size'] = 1 + self.yield_next_space = False + + def response_iter(): + yield ' ' + num_sleeps = 0 + while not self.yield_next_space and num_sleeps < 200: + eventlet.sleep(.01) + num_sleeps += 1 + + yield ' ' + + start_response('200 OK', + [('Content-Type', 'text/plain'), + ('Content-Length', '2')]) + return response_iter() else: raise RuntimeError("bad path") @@ -1306,6 +1395,50 @@ fd.sendall(req) self.assertEquals(read_http(fd)[-1], 'this is chunked\nline 2\nline3') + def test_chunked_readline_wsgi_override_minimum_chunk_size(self): + + fd = self.connect() + fd.sendall("POST /yield_spaces/override_min HTTP/1.1\r\nContent-Length: 0\r\n\r\n") + + resp_so_far = '' + with eventlet.Timeout(.1): + while True: + one_byte = fd.recv(1) + resp_so_far += one_byte + if resp_so_far.endswith('\r\n\r\n'): + break + self.assertEquals(fd.recv(1), ' ') + try: + with eventlet.Timeout(.1): + fd.recv(1) + except eventlet.Timeout: + pass + else: + self.assert_(False) + self.yield_next_space = True + + with eventlet.Timeout(.1): + self.assertEquals(fd.recv(1), ' ') + + def test_chunked_readline_wsgi_not_override_minimum_chunk_size(self): + + fd = self.connect() + fd.sendall("POST /yield_spaces HTTP/1.1\r\nContent-Length: 0\r\n\r\n") + + resp_so_far = '' + try: + with eventlet.Timeout(.1): + while True: + one_byte = fd.recv(1) + resp_so_far += one_byte + if resp_so_far.endswith('\r\n\r\n'): + break + self.assertEquals(fd.recv(1), ' ') + except eventlet.Timeout: + pass + else: + self.assert_(False) + def test_close_before_finished(self): import signal diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/eventlet-0.13.0/tests/wsgi_test_conntimeout.py new/eventlet-0.14.0/tests/wsgi_test_conntimeout.py --- old/eventlet-0.13.0/tests/wsgi_test_conntimeout.py 1970-01-01 01:00:00.000000000 +0100 +++ new/eventlet-0.14.0/tests/wsgi_test_conntimeout.py 2013-08-20 14:50:45.000000000 +0200 @@ -0,0 +1,163 @@ +"""Issue #143 - Socket timeouts in wsgi server not caught. +https://bitbucket.org/eventlet/eventlet/issue/143/ + +This file intentionally ignored by nose. +Caller process (tests.wsgi_test.TestWsgiConnTimeout) handles success / failure + + +Simulate server connection socket timeout without actually waiting. +Logs 'timed out' if server debug=True (similar to 'accepted' logging) + +FAIL: if log (ie, _spawn_n_impl 'except:' catches timeout, logs TB) +NOTE: timeouts are NOT on server_sock, but on the conn sockets produced +by the socket.accept() call + +server's socket.listen() sock - NaughtySocketAcceptWrap + / | \ + | | | (1 - many) + V V V +server / client accept() conn - ExplodingConnectionWrap + / | \ + | | | (1 - many) + V V V +connection makefile() file objects - ExplodingSocketFile <-- these raise +""" + +import eventlet + +import socket +import sys + +import tests.wsgi_test + + +# no standard tests in this file, ignore +__test__ = False + + +# This test might make you wince +class NaughtySocketAcceptWrap(object): + # server's socket.accept(); patches resulting connection sockets + + def __init__(self, sock): + self.sock = sock + self.sock._really_accept = self.sock.accept + self.sock.accept = self + self.conn_reg = [] + + def unwrap(self): + self.sock.accept = self.sock._really_accept + del self.sock._really_accept + for conn_wrap in self.conn_reg: + conn_wrap.unwrap() + + def arm(self): + print "ca-click" + for i in self.conn_reg: + i.arm() + + def __call__(self): + print self.__class__.__name__ + ".__call__" + conn, addr = self.sock._really_accept() + self.conn_reg.append(ExplodingConnectionWrap(conn)) + return conn, addr + + +class ExplodingConnectionWrap(object): + # new connection's socket.makefile + # eventlet *tends* to use socket.makefile, not raw socket methods. + # need to patch file operations + + def __init__(self, conn): + self.conn = conn + self.conn._really_makefile = self.conn.makefile + self.conn.makefile = self + self.armed = False + self.file_reg = [] + + def unwrap(self): + self.conn.makefile = self.conn._really_makefile + del self.conn._really_makefile + + def arm(self): + print "tick" + for i in self.file_reg: + i.arm() + + def __call__(self, mode='r', bufsize=-1): + print self.__class__.__name__ + ".__call__" + # file_obj = self.conn._really_makefile(*args, **kwargs) + file_obj = ExplodingSocketFile(self.conn._sock, mode, bufsize) + self.file_reg.append(file_obj) + return file_obj + + +class ExplodingSocketFile(socket._fileobject): + + def __init__(self, sock, mode='rb', bufsize=-1, close=False): + super(self.__class__, self).__init__(sock, mode, bufsize, close) + self.armed = False + + def arm(self): + print "beep" + self.armed = True + + def _fuse(self): + if self.armed: + print "=== ~* BOOM *~ ===" + raise socket.timeout("timed out") + + def readline(self, *args, **kwargs): + print self.__class__.__name__ + ".readline" + self._fuse() + return super(self.__class__, self).readline(*args, **kwargs) + + +if __name__ == '__main__': + for debug in (False, True): + print "SEPERATOR_SENTINEL" + print "debug set to: %s" % debug + + server_sock = eventlet.listen(('localhost', 0)) + server_addr = server_sock.getsockname() + sock_wrap = NaughtySocketAcceptWrap(server_sock) + + eventlet.spawn_n( + eventlet.wsgi.server, + debug=debug, + log=sys.stdout, + max_size=128, + site=tests.wsgi_test.Site(), + sock=server_sock, + ) + + try: + # req #1 - normal + sock1 = eventlet.connect(server_addr) + sock1.settimeout(0.1) + fd1 = sock1.makefile('rw') + fd1.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n') + fd1.flush() + tests.wsgi_test.read_http(sock1) + + # let the server socket ops catch up, set bomb + eventlet.sleep(0) + print "arming..." + sock_wrap.arm() + + # req #2 - old conn, post-arm - timeout + fd1.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n') + fd1.flush() + try: + tests.wsgi_test.read_http(sock1) + assert False, 'Expected ConnectionClosed exception' + except tests.wsgi_test.ConnectionClosed: + pass + + fd1.close() + sock1.close() + finally: + # reset streams, then output trapped tracebacks + sock_wrap.unwrap() + # check output asserts in tests.wsgi_test.TestHttpd + # test_143_server_connection_timeout_exception -- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
