Hello community, here is the log from the commit of package python-tornado for openSUSE:Factory checked in at 2012-12-03 11:30:58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-tornado (Old) and /work/SRC/openSUSE:Factory/.python-tornado.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python-tornado", Maintainer is "CThiel@suse.com" Changes: -------- --- /work/SRC/openSUSE:Factory/python-tornado/python-tornado.changes 2012-09-11 09:18:55.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.python-tornado.new/python-tornado.changes 2012-12-03 11:31:01.000000000 +0100 @@ -1,0 +2,11 @@ +Sun Nov 25 11:41:29 UTC 2012 - alexandre@exatati.com.br + +- Update to 2.4.1: + - Fixed a memory leak in tornado.stack_context that was especially + likely with long-running @gen.engine functions. + - tornado.auth.TwitterMixin now works on Python 3. + - Fixed a bug in which IOStream.read_until_close with a streaming + callback would sometimes pass the last chunk of data to the + final callback instead of the streaming callback. + +------------------------------------------------------------------- python3-tornado.changes: same change Old: ---- tornado-2.4.tar.bz2 New: ---- tornado-2.4.1.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-tornado.spec ++++++ --- /var/tmp/diff_new_pack.ofgBvy/_old 2012-12-03 11:31:02.000000000 +0100 +++ /var/tmp/diff_new_pack.ofgBvy/_new 2012-12-03 11:31:02.000000000 +0100 @@ -17,7 +17,7 @@ %define modname tornado Name: python-%{modname} -Version: 2.4 +Version: 2.4.1 Release: 0 Url: http://www.tornadoweb.org Summary: Open source version of scalable, non-blocking web server that power FriendFeed ++++++ python3-tornado.spec ++++++ --- /var/tmp/diff_new_pack.ofgBvy/_old 2012-12-03 11:31:02.000000000 +0100 +++ /var/tmp/diff_new_pack.ofgBvy/_new 2012-12-03 11:31:02.000000000 +0100 @@ -17,7 +17,7 @@ %define modname tornado Name: python3-%{modname} -Version: 2.4 +Version: 2.4.1 Release: 0 Url: http://www.tornadoweb.org Summary: Open source version of scalable, non-blocking web server that power FriendFeed ++++++ tornado-2.4.tar.bz2 -> tornado-2.4.1.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tornado-2.4/PKG-INFO new/tornado-2.4.1/PKG-INFO --- old/tornado-2.4/PKG-INFO 2012-09-04 07:10:21.000000000 +0200 +++ new/tornado-2.4.1/PKG-INFO 2012-11-25 03:28:47.000000000 +0100 @@ -1,11 +1,11 @@ Metadata-Version: 1.1 Name: tornado -Version: 2.4 +Version: 2.4.1 Summary: Tornado is an open source version of the scalable, non-blocking web server and and tools that power FriendFeed Home-page: http://www.tornadoweb.org/ Author: Facebook Author-email: python-tornado@googlegroups.com License: http://www.apache.org/licenses/LICENSE-2.0 -Download-URL: http://github.com/downloads/facebook/tornado/tornado-2.4.tar.gz +Download-URL: http://github.com/downloads/facebook/tornado/tornado-2.4.1.tar.gz Description: UNKNOWN Platform: UNKNOWN diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tornado-2.4/setup.py new/tornado-2.4.1/setup.py --- old/tornado-2.4/setup.py 2012-09-04 07:06:58.000000000 +0200 +++ new/tornado-2.4.1/setup.py 2012-11-25 03:21:53.000000000 +0100 @@ -33,7 +33,7 @@ extensions.append(distutils.core.Extension( "tornado.epoll", ["tornado/epoll.c"])) -version = "2.4" +version = "2.4.1" if major >= 3: import setuptools # setuptools is required for use_2to3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tornado-2.4/tornado/__init__.py new/tornado-2.4.1/tornado/__init__.py --- old/tornado-2.4/tornado/__init__.py 2012-09-04 07:07:17.000000000 +0200 +++ new/tornado-2.4.1/tornado/__init__.py 2012-11-25 03:22:02.000000000 +0100 @@ -25,5 +25,5 @@ # is zero for an official release, positive for a development branch, # or negative for a release candidate (after the base version number # has been incremented) -version = "2.4" -version_info = (2, 4, 0, 0) +version = "2.4.1" +version_info = (2, 4, 1, 0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tornado-2.4/tornado/auth.py new/tornado-2.4.1/tornado/auth.py --- old/tornado-2.4/tornado/auth.py 2012-08-30 17:30:01.000000000 +0200 +++ new/tornado-2.4.1/tornado/auth.py 2012-11-25 03:04:41.000000000 +0100 @@ -95,7 +95,7 @@ args["openid.mode"] = u"check_authentication" url = self._OPENID_ENDPOINT if http_client is None: - http_client = httpclient.AsyncHTTPClient() + http_client = self.get_auth_http_client() http_client.fetch(url, self.async_callback( self._on_authentication_verified, callback), method="POST", body=urllib.urlencode(args)) @@ -208,6 +208,14 @@ user["claimed_id"] = claimed_id callback(user) + def get_auth_http_client(self): + """Returns the AsyncHTTPClient instance to be used for auth requests. + + May be overridden by subclasses to use an http client other than + the default. + """ + return httpclient.AsyncHTTPClient() + class OAuthMixin(object): """Abstract implementation of OAuth. @@ -232,7 +240,7 @@ if callback_uri and getattr(self, "_OAUTH_NO_CALLBACKS", False): raise Exception("This service does not support oauth_callback") if http_client is None: - http_client = httpclient.AsyncHTTPClient() + http_client = self.get_auth_http_client() if getattr(self, "_OAUTH_VERSION", "1.0a") == "1.0a": http_client.fetch( self._oauth_request_token_url(callback_uri=callback_uri, @@ -277,7 +285,7 @@ if oauth_verifier: token["verifier"] = oauth_verifier if http_client is None: - http_client = httpclient.AsyncHTTPClient() + http_client = self.get_auth_http_client() http_client.fetch(self._oauth_access_token_url(token), self.async_callback(self._on_access_token, callback)) @@ -394,6 +402,14 @@ base_args["oauth_signature"] = signature return base_args + def get_auth_http_client(self): + """Returns the AsyncHTTPClient instance to be used for auth requests. + + May be overridden by subclasses to use an http client other than + the default. + """ + return httpclient.AsyncHTTPClient() + class OAuth2Mixin(object): """Abstract implementation of OAuth v 2.""" @@ -471,6 +487,7 @@ _OAUTH_AUTHORIZE_URL = "http://api.twitter.com/oauth/authorize" _OAUTH_AUTHENTICATE_URL = "http://api.twitter.com/oauth/authenticate" _OAUTH_NO_CALLBACKS = False + _TWITTER_BASE_URL = "http://api.twitter.com/1" def authenticate_redirect(self, callback_uri=None): """Just like authorize_redirect(), but auto-redirects if authorized. @@ -478,7 +495,7 @@ This is generally the right interface to use if you are using Twitter for single-sign on. """ - http = httpclient.AsyncHTTPClient() + http = self.get_auth_http_client() http.fetch(self._oauth_request_token_url(callback_uri=callback_uri), self.async_callback( self._on_request_token, self._OAUTH_AUTHENTICATE_URL, None)) @@ -525,7 +542,7 @@ # usual pattern: http://search.twitter.com/search.json url = path else: - url = "http://api.twitter.com/1" + path + ".json" + url = self._TWITTER_BASE_URL + path + ".json" # Add the OAuth resource request signature if we have credentials if access_token: all_args = {} @@ -538,7 +555,7 @@ if args: url += "?" + urllib.urlencode(args) callback = self.async_callback(self._on_twitter_request, callback) - http = httpclient.AsyncHTTPClient() + http = self.get_auth_http_client() if post_args is not None: http.fetch(url, method="POST", body=urllib.urlencode(post_args), callback=callback) @@ -563,7 +580,7 @@ def _oauth_get_user(self, access_token, callback): callback = self.async_callback(self._parse_user_response, callback) self.twitter_request( - "/users/show/" + access_token["screen_name"], + "/users/show/" + escape.native_str(access_token[b("screen_name")]), access_token=access_token, callback=callback) def _parse_user_response(self, callback, user): @@ -660,7 +677,7 @@ if args: url += "?" + urllib.urlencode(args) callback = self.async_callback(self._on_friendfeed_request, callback) - http = httpclient.AsyncHTTPClient() + http = self.get_auth_http_client() if post_args is not None: http.fetch(url, method="POST", body=urllib.urlencode(post_args), callback=callback) @@ -751,7 +768,7 @@ break token = self.get_argument("openid." + oauth_ns + ".request_token", "") if token: - http = httpclient.AsyncHTTPClient() + http = self.get_auth_http_client() token = dict(key=token, secret="") http.fetch(self._oauth_access_token_url(token), self.async_callback(self._on_access_token, callback)) @@ -907,7 +924,7 @@ args["sig"] = self._signature(args) url = "http://api.facebook.com/restserver.php?" + \ urllib.urlencode(args) - http = httpclient.AsyncHTTPClient() + http = self.get_auth_http_client() http.fetch(url, callback=self.async_callback( self._parse_response, callback)) @@ -953,6 +970,14 @@ body = body.encode("utf-8") return hashlib.md5(body).hexdigest() + def get_auth_http_client(self): + """Returns the AsyncHTTPClient instance to be used for auth requests. + + May be overridden by subclasses to use an http client other than + the default. + """ + return httpclient.AsyncHTTPClient() + class FacebookGraphMixin(OAuth2Mixin): """Facebook authentication using the new Graph API and OAuth2.""" @@ -987,7 +1012,7 @@ self.finish() """ - http = httpclient.AsyncHTTPClient() + http = self.get_auth_http_client() args = { "redirect_uri": redirect_uri, "code": code, @@ -1081,7 +1106,7 @@ if all_args: url += "?" + urllib.urlencode(all_args) callback = self.async_callback(self._on_facebook_request, callback) - http = httpclient.AsyncHTTPClient() + http = self.get_auth_http_client() if post_args is not None: http.fetch(url, method="POST", body=urllib.urlencode(post_args), callback=callback) @@ -1096,6 +1121,14 @@ return callback(escape.json_decode(response.body)) + def get_auth_http_client(self): + """Returns the AsyncHTTPClient instance to be used for auth requests. + + May be overridden by subclasses to use an http client other than + the default. + """ + return httpclient.AsyncHTTPClient() + def _oauth_signature(consumer_token, method, url, parameters={}, token=None): """Calculates the HMAC-SHA1 OAuth signature for the given request. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tornado-2.4/tornado/gen.py new/tornado-2.4.1/tornado/gen.py --- old/tornado-2.4/tornado/gen.py 2012-09-04 06:59:29.000000000 +0200 +++ new/tornado-2.4.1/tornado/gen.py 2012-11-25 03:04:41.000000000 +0100 @@ -354,6 +354,7 @@ "finished without waiting for callbacks %r" % self.pending_callbacks) self.deactivate_stack_context() + self.deactivate_stack_context = None return except Exception: self.finished = True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tornado-2.4/tornado/iostream.py new/tornado-2.4.1/tornado/iostream.py --- old/tornado-2.4/tornado/iostream.py 2012-08-30 17:30:01.000000000 +0200 +++ new/tornado-2.4.1/tornado/iostream.py 2012-11-25 03:04:41.000000000 +0100 @@ -176,8 +176,14 @@ a ``streaming_callback`` is not used. """ self._set_read_callback(callback) + self._streaming_callback = stack_context.wrap(streaming_callback) if self.closed(): - self._run_callback(callback, self._consume(self._read_buffer_size)) + if self._streaming_callback is not None: + self._run_callback(self._streaming_callback, + self._consume(self._read_buffer_size)) + self._run_callback(self._read_callback, + self._consume(self._read_buffer_size)) + self._streaming_callback = None self._read_callback = None return self._read_until_close = True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tornado-2.4/tornado/stack_context.py new/tornado-2.4.1/tornado/stack_context.py --- old/tornado-2.4/tornado/stack_context.py 2012-08-30 17:30:01.000000000 +0200 +++ new/tornado-2.4.1/tornado/stack_context.py 2012-11-25 03:04:41.000000000 +0100 @@ -161,6 +161,7 @@ return self.exception_handler(type, value, traceback) finally: _state.contexts = self.old_contexts + self.old_contexts = None class NullContext(object): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tornado-2.4/tornado/test/auth_test.py new/tornado-2.4.1/tornado/test/auth_test.py --- old/tornado-2.4/tornado/test/auth_test.py 2012-08-30 17:30:01.000000000 +0200 +++ new/tornado-2.4.1/tornado/test/auth_test.py 2012-11-25 03:04:41.000000000 +0100 @@ -5,7 +5,7 @@ from __future__ import absolute_import, division, with_statement -from tornado.auth import OpenIdMixin, OAuthMixin, OAuth2Mixin +from tornado.auth import OpenIdMixin, OAuthMixin, OAuth2Mixin, TwitterMixin from tornado.escape import json_decode from tornado.testing import AsyncHTTPTestCase, LogTrapTestCase from tornado.util import b @@ -101,6 +101,37 @@ self.authorize_redirect() +class TwitterClientLoginHandler(RequestHandler, TwitterMixin): + def initialize(self, test): + self._OAUTH_REQUEST_TOKEN_URL = test.get_url('/oauth1/server/request_token') + self._OAUTH_ACCESS_TOKEN_URL = test.get_url('/twitter/server/access_token') + self._OAUTH_AUTHORIZE_URL = test.get_url('/oauth1/server/authorize') + self._TWITTER_BASE_URL = test.get_url('/twitter/api') + + @asynchronous + def get(self): + if self.get_argument("oauth_token", None): + self.get_authenticated_user(self.on_user) + return + self.authorize_redirect() + + def on_user(self, user): + if user is None: + raise Exception("user is None") + self.finish(user) + + def get_auth_http_client(self): + return self.settings['http_client'] + + +class TwitterServerAccessTokenHandler(RequestHandler): + def get(self): + self.write('oauth_token=hjkl&oauth_token_secret=vbnm&screen_name=foo') + +class TwitterServerShowUserHandler(RequestHandler): + def get(self, screen_name): + self.write(dict(screen_name=screen_name, name=screen_name.capitalize())) + class AuthTest(AsyncHTTPTestCase, LogTrapTestCase): def get_app(self): return Application( @@ -119,12 +150,19 @@ dict(version='1.0a')), ('/oauth2/client/login', OAuth2ClientLoginHandler, dict(test=self)), + ('/twitter/client/login', TwitterClientLoginHandler, dict(test=self)), + # simulated servers ('/openid/server/authenticate', OpenIdServerAuthenticateHandler), ('/oauth1/server/request_token', OAuth1ServerRequestTokenHandler), ('/oauth1/server/access_token', OAuth1ServerAccessTokenHandler), + + ('/twitter/server/access_token', TwitterServerAccessTokenHandler), + (r'/twitter/api/users/show/(.*)\.json', TwitterServerShowUserHandler), ], - http_client=self.http_client) + http_client=self.http_client, + twitter_consumer_key='test_twitter_consumer_key', + twitter_consumer_secret='test_twitter_consumer_secret') def test_openid_redirect(self): response = self.fetch('/openid/client/login', follow_redirects=False) @@ -198,3 +236,28 @@ response = self.fetch('/oauth2/client/login', follow_redirects=False) self.assertEqual(response.code, 302) self.assertTrue('/oauth2/server/authorize?' in response.headers['Location']) + + def test_twitter_redirect(self): + # Same as test_oauth10a_redirect + response = self.fetch('/twitter/client/login', follow_redirects=False) + self.assertEqual(response.code, 302) + self.assertTrue(response.headers['Location'].endswith( + '/oauth1/server/authorize?oauth_token=zxcv')) + # the cookie is base64('zxcv')|base64('1234') + self.assertTrue( + '_oauth_request_token="enhjdg==|MTIzNA=="' in response.headers['Set-Cookie'], + response.headers['Set-Cookie']) + + def test_twitter_get_user(self): + response = self.fetch( + '/twitter/client/login?oauth_token=zxcv', + headers={'Cookie': '_oauth_request_token=enhjdg==|MTIzNA=='}) + response.rethrow() + parsed = json_decode(response.body) + self.assertEqual(parsed, + {u'access_token': {u'key': u'hjkl', + u'screen_name': u'foo', + u'secret': u'vbnm'}, + u'name': u'Foo', + u'screen_name': u'foo', + u'username': u'foo'}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tornado-2.4/tornado/test/gen_test.py new/tornado-2.4.1/tornado/test/gen_test.py --- old/tornado-2.4/tornado/test/gen_test.py 2012-08-30 17:30:01.000000000 +0200 +++ new/tornado-2.4.1/tornado/test/gen_test.py 2012-11-25 03:04:41.000000000 +0100 @@ -269,6 +269,28 @@ initial_stack_depth = len(stack_context._state.contexts) self.run_gen(outer) + def test_stack_context_leak_exception(self): + # same as previous, but with a function that exits with an exception + from tornado import stack_context + + @gen.engine + def inner(callback): + yield gen.Task(self.io_loop.add_callback) + 1 / 0 + + @gen.engine + def outer(): + for i in xrange(10): + try: + yield gen.Task(inner) + except ZeroDivisionError: + pass + stack_increase = len(stack_context._state.contexts) - initial_stack_depth + self.assertTrue(stack_increase <= 2) + self.stop() + initial_stack_depth = len(stack_context._state.contexts) + self.run_gen(outer) + class GenSequenceHandler(RequestHandler): @asynchronous diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tornado-2.4/tornado/test/iostream_test.py new/tornado-2.4.1/tornado/test/iostream_test.py --- old/tornado-2.4/tornado/test/iostream_test.py 2012-09-04 06:59:29.000000000 +0200 +++ new/tornado-2.4.1/tornado/test/iostream_test.py 2012-11-25 03:04:41.000000000 +0100 @@ -296,6 +296,42 @@ server.close() client.close() + def test_read_until_close_after_close(self): + # Similar to test_delayed_close_callback, but read_until_close takes + # a separate code path so test it separately. + server, client = self.make_iostream_pair() + client.set_close_callback(self.stop) + try: + server.write(b("1234")) + server.close() + self.wait() + client.read_until_close(self.stop) + data = self.wait() + self.assertEqual(data, b("1234")) + finally: + server.close() + client.close() + + def test_streaming_read_until_close_after_close(self): + # Same as the preceding test but with a streaming_callback. + # All data should go through the streaming callback, + # and the final read callback just gets an empty string. + server, client = self.make_iostream_pair() + client.set_close_callback(self.stop) + try: + server.write(b("1234")) + server.close() + self.wait() + streaming_data = [] + client.read_until_close(self.stop, + streaming_callback=streaming_data.append) + data = self.wait() + self.assertEqual(b(''), data) + self.assertEqual(b('').join(streaming_data), b("1234")) + finally: + server.close() + client.close() + def test_large_read_until(self): # Performance test: read_until used to have a quadratic component # so a read_until of 4MB would take 8 seconds; now it takes 0.25 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tornado-2.4/tornado.egg-info/PKG-INFO new/tornado-2.4.1/tornado.egg-info/PKG-INFO --- old/tornado-2.4/tornado.egg-info/PKG-INFO 2012-09-04 07:10:19.000000000 +0200 +++ new/tornado-2.4.1/tornado.egg-info/PKG-INFO 2012-11-25 03:28:45.000000000 +0100 @@ -1,11 +1,11 @@ Metadata-Version: 1.1 Name: tornado -Version: 2.4 +Version: 2.4.1 Summary: Tornado is an open source version of the scalable, non-blocking web server and and tools that power FriendFeed Home-page: http://www.tornadoweb.org/ Author: Facebook Author-email: python-tornado@googlegroups.com License: http://www.apache.org/licenses/LICENSE-2.0 -Download-URL: http://github.com/downloads/facebook/tornado/tornado-2.4.tar.gz +Download-URL: http://github.com/downloads/facebook/tornado/tornado-2.4.1.tar.gz Description: UNKNOWN Platform: UNKNOWN -- To unsubscribe, e-mail: opensuse-commit+unsubscribe@opensuse.org For additional commands, e-mail: opensuse-commit+help@opensuse.org