diff --git a/flask/_compat.py b/flask/_compat.py new file mode 100644 index 00000000..3743a54c --- /dev/null +++ b/flask/_compat.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +""" + flask._compat + ~~~~~~~~~~~~~ + + Some py2/py3 compatibility support that is not yet available in + "six" 1.3.0. + There are bugs open for "six" for all this stuff, so we can remove it + again from here as soon as we require a new enough "six" release. + + :copyright: (c) 2013 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" diff --git a/flask/helpers.py b/flask/helpers.py index d68cb04b..68ebc74e 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -26,6 +26,7 @@ from functools import update_wrapper from werkzeug.datastructures import Headers from werkzeug.exceptions import NotFound +import six # this was moved in 0.7 try: @@ -128,7 +129,7 @@ def stream_with_context(generator_or_function): # pushed. This item is discarded. Then when the iteration continues the # real generator is executed. wrapped_g = generator() - wrapped_g.next() + six.advance_iterator(wrapped_g) return wrapped_g diff --git a/flask/json.py b/flask/json.py index 52e5680e..d5da4f91 100644 --- a/flask/json.py +++ b/flask/json.py @@ -17,6 +17,7 @@ from werkzeug.http import http_date # Use the same json implementation as itsdangerous on which we # depend anyways. from itsdangerous import simplejson as _json +import six # figure out if simplejson escapes slashes. This behavior was changed @@ -59,7 +60,7 @@ class JSONEncoder(_json.JSONEncoder): if isinstance(o, uuid.UUID): return str(o) if hasattr(o, '__html__'): - return unicode(o.__html__()) + return six.text_type(o.__html__()) return _json.JSONEncoder.default(self, o) diff --git a/flask/sessions.py b/flask/sessions.py index 31b5900b..04b7f8af 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -17,6 +17,7 @@ from werkzeug.datastructures import CallbackDict from . import Markup, json from itsdangerous import URLSafeTimedSerializer, BadSignature +import six def total_seconds(td): @@ -62,16 +63,16 @@ class TaggedJSONSerializer(object): elif isinstance(value, uuid.UUID): return {' u': value.hex} elif callable(getattr(value, '__html__', None)): - return {' m': unicode(value.__html__())} + return {' m': six.text_type(value.__html__())} elif isinstance(value, list): return [_tag(x) for x in value] elif isinstance(value, datetime): return {' d': http_date(value)} elif isinstance(value, dict): - return dict((k, _tag(v)) for k, v in value.iteritems()) + return dict((k, _tag(v)) for k, v in six.iteritems(value)) elif isinstance(value, str): try: - return unicode(value) + return six.text_type(value) except UnicodeError: raise UnexpectedUnicodeError(u'A byte string with ' u'non-ASCII data was passed to the session system ' @@ -84,7 +85,7 @@ class TaggedJSONSerializer(object): def object_hook(obj): if len(obj) != 1: return obj - the_key, the_value = obj.iteritems().next() + the_key, the_value = six.advance_iterator(obj.iteritems()) if the_key == ' t': return tuple(the_value) elif the_key == ' u': diff --git a/flask/templating.py b/flask/templating.py index 2cc09c4d..754c6893 100644 --- a/flask/templating.py +++ b/flask/templating.py @@ -15,6 +15,7 @@ from jinja2 import BaseLoader, Environment as BaseEnvironment, \ from .globals import _request_ctx_stack, _app_ctx_stack from .signals import template_rendered from .module import blueprint_is_module +import six def _default_template_ctx_processor(): @@ -79,7 +80,7 @@ class DispatchingJinjaLoader(BaseLoader): except (ValueError, KeyError): pass - for blueprint in self.app.blueprints.itervalues(): + for blueprint in six.itervalues(self.app.blueprints): if blueprint_is_module(blueprint): continue loader = blueprint.jinja_loader @@ -92,7 +93,7 @@ class DispatchingJinjaLoader(BaseLoader): if loader is not None: result.update(loader.list_templates()) - for name, blueprint in self.app.blueprints.iteritems(): + for name, blueprint in six.iteritems(self.app.blueprints): loader = blueprint.jinja_loader if loader is not None: for template in loader.list_templates(): diff --git a/flask/testsuite/__init__.py b/flask/testsuite/__init__.py index 084b3726..9027369c 100644 --- a/flask/testsuite/__init__.py +++ b/flask/testsuite/__init__.py @@ -10,6 +10,7 @@ :license: BSD, see LICENSE for more details. """ +from __future__ import print_function from __future__ import with_statement import os @@ -221,4 +222,4 @@ def main(): try: unittest.main(testLoader=BetterLoader(), defaultTest='suite') except Exception as e: - print 'Error: %s' % e + print('Error: %s' % e) diff --git a/flask/testsuite/basic.py b/flask/testsuite/basic.py index 2836a1b6..89159a16 100644 --- a/flask/testsuite/basic.py +++ b/flask/testsuite/basic.py @@ -22,6 +22,7 @@ from flask.testsuite import FlaskTestCase, emits_module_deprecation_warning from werkzeug.exceptions import BadRequest, NotFound from werkzeug.http import parse_date from werkzeug.routing import BuildError +import six class BasicFunctionalityTestCase(FlaskTestCase): @@ -277,7 +278,7 @@ class BasicFunctionalityTestCase(FlaskTestCase): @app.route('/test') def test(): - return unicode(flask.session.permanent) + return six.text_type(flask.session.permanent) client = app.test_client() rv = client.get('/') diff --git a/flask/testsuite/blueprints.py b/flask/testsuite/blueprints.py index 0399d48e..eba43e63 100644 --- a/flask/testsuite/blueprints.py +++ b/flask/testsuite/blueprints.py @@ -18,6 +18,7 @@ from flask.testsuite import FlaskTestCase, emits_module_deprecation_warning from werkzeug.exceptions import NotFound from werkzeug.http import parse_cache_control_header from jinja2 import TemplateNotFound +import six # import moduleapp here because it uses deprecated features and we don't @@ -304,7 +305,7 @@ class BlueprintTestCase(FlaskTestCase): @bp.route('/bar') def bar(bar): - return unicode(bar) + return six.text_type(bar) app = flask.Flask(__name__) app.register_blueprint(bp, url_prefix='/1', url_defaults={'bar': 23}) diff --git a/flask/testsuite/config.py b/flask/testsuite/config.py index 0b2c65bd..ca142cff 100644 --- a/flask/testsuite/config.py +++ b/flask/testsuite/config.py @@ -113,7 +113,7 @@ class LimitedLoaderMockWrapper(object): def __getattr__(self, name): if name in ('archive', 'get_filename'): msg = 'Mocking a loader which does not have `%s.`' % name - raise AttributeError, msg + raise AttributeError(msg) return getattr(self.loader, name) diff --git a/flask/testsuite/ext.py b/flask/testsuite/ext.py index 034ab5be..096a70f8 100644 --- a/flask/testsuite/ext.py +++ b/flask/testsuite/ext.py @@ -13,7 +13,7 @@ from __future__ import with_statement import sys import unittest from flask.testsuite import FlaskTestCase - +from six import reload_module class ExtImportHookTestCase(FlaskTestCase): @@ -29,7 +29,7 @@ class ExtImportHookTestCase(FlaskTestCase): entry == 'flaskext') and value is not None: sys.modules.pop(entry, None) from flask import ext - reload(ext) + reload_module(ext) # reloading must not add more hooks import_hooks = 0 diff --git a/flask/testsuite/helpers.py b/flask/testsuite/helpers.py index fdf2d89f..ac618538 100644 --- a/flask/testsuite/helpers.py +++ b/flask/testsuite/helpers.py @@ -18,6 +18,7 @@ from logging import StreamHandler from StringIO import StringIO from flask.testsuite import FlaskTestCase, catch_warnings, catch_stderr from werkzeug.http import parse_cache_control_header, parse_options_header +import six def has_encoding(name): @@ -35,7 +36,7 @@ class JSONTestCase(FlaskTestCase): app = flask.Flask(__name__) @app.route('/json', methods=['POST']) def return_json(): - return unicode(flask.request.json) + return six.text_type(flask.request.json) c = app.test_client() rv = c.post('/json', data='malformed', content_type='application/json') self.assert_equal(rv.status_code, 400) @@ -44,7 +45,7 @@ class JSONTestCase(FlaskTestCase): app = flask.Flask(__name__) @app.route('/json', methods=['POST']) def return_json(): - return unicode(flask.request.json) + return six.text_type(flask.request.json) c = app.test_client() rv = c.post('/json', data='malformed', content_type='application/json') self.assert_equal(rv.status_code, 400) @@ -96,7 +97,7 @@ class JSONTestCase(FlaskTestCase): app = flask.Flask(__name__) @app.route('/add', methods=['POST']) def add(): - return unicode(flask.request.json['a'] + flask.request.json['b']) + return six.text_type(flask.request.json['a'] + flask.request.json['b']) c = app.test_client() rv = c.post('/add', data=flask.json.dumps({'a': 1, 'b': 2}), content_type='application/json') @@ -507,7 +508,7 @@ class StreamingTestCase(FlaskTestCase): def close(self): called.append(42) def next(self): - return self._gen.next() + return six.advance_iterator(self._gen) @app.route('/') def index(): def generate(): diff --git a/flask/testsuite/testing.py b/flask/testsuite/testing.py index 605078a7..2872ecdc 100644 --- a/flask/testsuite/testing.py +++ b/flask/testsuite/testing.py @@ -14,6 +14,7 @@ from __future__ import with_statement import flask import unittest from flask.testsuite import FlaskTestCase +import six class TestToolsTestCase(FlaskTestCase): @@ -85,7 +86,7 @@ class TestToolsTestCase(FlaskTestCase): @app.route('/') def index(): - return unicode(flask.session['foo']) + return six.text_type(flask.session['foo']) with app.test_client() as c: with c.session_transaction() as sess: diff --git a/setup.py b/setup.py index ba5f4a67..4ad0b546 100644 --- a/setup.py +++ b/setup.py @@ -90,6 +90,7 @@ setup( zip_safe=False, platforms='any', install_requires=[ + 'six>=1.3.0', 'Werkzeug>=0.7', 'Jinja2>=2.4', 'itsdangerous>=0.17'