diff --git a/src/flask/_compat.py b/src/flask/_compat.py deleted file mode 100644 index ee47d821..00000000 --- a/src/flask/_compat.py +++ /dev/null @@ -1,115 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask._compat - ~~~~~~~~~~~~~ - - Some py2/py3 compatibility support based on a stripped down - version of six so we don't have to depend on a specific version - of it. - - :copyright: 2010 Pallets - :license: BSD-3-Clause -""" -import sys - -PY2 = sys.version_info[0] == 2 -_identity = lambda x: x - -try: # Python 2 - text_type = unicode - string_types = (str, unicode) - integer_types = (int, long) -except NameError: # Python 3 - text_type = str - string_types = (str,) - integer_types = (int,) - -if not PY2: - iterkeys = lambda d: iter(d.keys()) - itervalues = lambda d: iter(d.values()) - iteritems = lambda d: iter(d.items()) - - from inspect import getfullargspec as getargspec - from io import StringIO - import collections.abc as collections_abc - - def reraise(tp, value, tb=None): - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - - implements_to_string = _identity - -else: - iterkeys = lambda d: d.iterkeys() - itervalues = lambda d: d.itervalues() - iteritems = lambda d: d.iteritems() - - from inspect import getargspec - from cStringIO import StringIO - import collections as collections_abc - - exec("def reraise(tp, value, tb=None):\n raise tp, value, tb") - - def implements_to_string(cls): - cls.__unicode__ = cls.__str__ - cls.__str__ = lambda x: x.__unicode__().encode("utf-8") - return cls - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a - # dummy metaclass for one level of class instantiation that replaces - # itself with the actual metaclass. - class metaclass(type): - def __new__(metacls, name, this_bases, d): - return meta(name, bases, d) - - return type.__new__(metaclass, "temporary_class", (), {}) - - -# Certain versions of pypy have a bug where clearing the exception stack -# breaks the __exit__ function in a very peculiar way. The second level of -# exception blocks is necessary because pypy seems to forget to check if an -# exception happened until the next bytecode instruction? -# -# Relevant PyPy bugfix commit: -# https://bitbucket.org/pypy/pypy/commits/77ecf91c635a287e88e60d8ddb0f4e9df4003301 -# According to ronan on #pypy IRC, it is released in PyPy2 2.3 and later -# versions. -# -# Ubuntu 14.04 has PyPy 2.2.1, which does exhibit this bug. -BROKEN_PYPY_CTXMGR_EXIT = False -if hasattr(sys, "pypy_version_info"): - - class _Mgr(object): - def __enter__(self): - return self - - def __exit__(self, *args): - if hasattr(sys, "exc_clear"): - # Python 3 (PyPy3) doesn't have exc_clear - sys.exc_clear() - - try: - try: - with _Mgr(): - raise AssertionError() - except: # noqa: B001 - # We intentionally use a bare except here. See the comment above - # regarding a pypy bug as to why. - raise - except TypeError: - BROKEN_PYPY_CTXMGR_EXIT = True - except AssertionError: - pass - - -try: - from os import fspath -except ImportError: - # Backwards compatibility as proposed in PEP 0519: - # https://www.python.org/dev/peps/pep-0519/#backwards-compatibility - def fspath(path): - return path.__fspath__() if hasattr(path, "__fspath__") else path diff --git a/src/flask/app.py b/src/flask/app.py index 93b12e90..c1996cf4 100644 --- a/src/flask/app.py +++ b/src/flask/app.py @@ -31,10 +31,6 @@ from werkzeug.wrappers import BaseResponse from . import cli from . import json -from ._compat import integer_types -from ._compat import reraise -from ._compat import string_types -from ._compat import text_type from .config import Config from .config import ConfigAttribute from .ctx import _AppCtxGlobals @@ -1179,10 +1175,10 @@ class Flask(_PackageBoundObject): # a tuple of only ``GET`` as default. if methods is None: methods = getattr(view_func, "methods", None) or ("GET",) - if isinstance(methods, string_types): + if isinstance(methods, str): raise TypeError( - "Allowed methods have to be iterables of strings, " - 'for example: @app.route(..., methods=["POST"])' + "Allowed methods must be a list of strings, for" + ' example: @app.route(..., methods=["POST"])' ) methods = set(item.upper() for item in methods) @@ -1278,7 +1274,7 @@ class Flask(_PackageBoundObject): :param exc_class_or_code: Any exception class, or an HTTP status code as an integer. """ - if isinstance(exc_class_or_code, integer_types): + if isinstance(exc_class_or_code, int): exc_class = default_exceptions[exc_class_or_code] else: exc_class = exc_class_or_code @@ -1727,13 +1723,6 @@ class Flask(_PackageBoundObject): .. versionadded:: 0.7 """ - exc_type, exc_value, tb = sys.exc_info() - assert exc_value is e - # ensure not to trash sys.exc_info() at that point in case someone - # wants the traceback preserved in handle_http_exception. Of course - # we cannot prevent users from trashing it themselves in a custom - # trap_http_exception method so that's their fault then. - if isinstance(e, BadRequestKeyError): if self.debug or self.config["TRAP_BAD_REQUEST_ERRORS"]: e.show_exception = True @@ -1752,7 +1741,8 @@ class Flask(_PackageBoundObject): handler = self._find_error_handler(e) if handler is None: - reraise(exc_type, exc_value, tb) + raise + return handler(e) def handle_exception(self, e): @@ -1789,20 +1779,18 @@ class Flask(_PackageBoundObject): .. versionadded:: 0.3 """ - exc_type, exc_value, tb = sys.exc_info() + exc_info = sys.exc_info() got_request_exception.send(self, exception=e) if self.propagate_exceptions: - # if we want to repropagate the exception, we can attempt to - # raise it with the whole traceback in case we can do that - # (the function was actually called from the except part) - # otherwise, we just raise the error again - if exc_value is e: - reraise(exc_type, exc_value, tb) - else: - raise e + # Re-raise if called with an active exception, otherwise + # raise the passed in exception. + if exc_info[1] is e: + raise - self.log_exception((exc_type, exc_value, tb)) + raise e + + self.log_exception(exc_info) server_error = InternalServerError() # TODO: pass as param when Werkzeug>=1.0.0 is required # TODO: also remove note about this from docstring and docs @@ -2026,7 +2014,7 @@ class Flask(_PackageBoundObject): # make sure the body is an instance of the response class if not isinstance(rv, self.response_class): - if isinstance(rv, (text_type, bytes, bytearray)): + if isinstance(rv, (str, bytes, bytearray)): # let the response class set the status and headers instead of # waiting to do it manually, so that the class can handle any # special logic @@ -2040,13 +2028,12 @@ class Flask(_PackageBoundObject): try: rv = self.response_class.force_type(rv, request.environ) except TypeError as e: - new_error = TypeError( + raise TypeError( "{e}\nThe view function did not return a valid" " response. The return type must be a string, dict, tuple," " Response instance, or WSGI callable, but it was a" " {rv.__class__.__name__}.".format(e=e, rv=rv) - ) - reraise(TypeError, new_error, sys.exc_info()[2]) + ).with_traceback(sys.exc_info()[2]) else: raise TypeError( "The view function did not return a valid" @@ -2057,7 +2044,7 @@ class Flask(_PackageBoundObject): # prefer the status if it was provided if status is not None: - if isinstance(status, (text_type, bytes, bytearray)): + if isinstance(status, (str, bytes, bytearray)): rv.status = status else: rv.status_code = status @@ -2121,23 +2108,24 @@ class Flask(_PackageBoundObject): func(endpoint, values) def handle_url_build_error(self, error, endpoint, values): - """Handle :class:`~werkzeug.routing.BuildError` on :meth:`url_for`. + """Handle :class:`~werkzeug.routing.BuildError` on + :meth:`url_for`. """ - exc_type, exc_value, tb = sys.exc_info() for handler in self.url_build_error_handlers: try: rv = handler(error, endpoint, values) + except BuildError as e: + # make error available outside except block + error = e + else: if rv is not None: return rv - except BuildError as e: - # make error available outside except block (py3) - error = e - # At this point we want to reraise the exception. If the error is - # still the same one we can reraise it with the original traceback, - # otherwise we raise it from here. - if error is exc_value: - reraise(exc_type, exc_value, tb) + # Re-raise if called with an active exception, otherwise raise + # the passed in exception. + if error is sys.exc_info()[1]: + raise + raise error def preprocess_request(self): diff --git a/src/flask/cli.py b/src/flask/cli.py index de4690b1..4b3da207 100644 --- a/src/flask/cli.py +++ b/src/flask/cli.py @@ -25,10 +25,6 @@ from threading import Thread import click from werkzeug.utils import import_string -from ._compat import getargspec -from ._compat import itervalues -from ._compat import reraise -from ._compat import text_type from .globals import current_app from .helpers import get_debug_flag from .helpers import get_env @@ -63,7 +59,7 @@ def find_best_app(script_info, module): return app # Otherwise find the only object that is a Flask instance. - matches = [v for v in itervalues(module.__dict__) if isinstance(v, Flask)] + matches = [v for v in module.__dict__.values() if isinstance(v, Flask)] if len(matches) == 1: return matches[0] @@ -105,7 +101,7 @@ def call_factory(script_info, app_factory, arguments=()): of arguments. Checks for the existence of a script_info argument and calls the app_factory depending on that and the arguments provided. """ - args_spec = getargspec(app_factory) + args_spec = inspect.getfullargspec(app_factory) arg_names = args_spec.args arg_defaults = args_spec.defaults @@ -241,7 +237,7 @@ def locate_app(script_info, module_name, app_name, raise_if_not_found=True): except ImportError: # Reraise the ImportError if it occurred within the imported module. # Determine this by checking whether the trace has a depth > 1. - if sys.exc_info()[-1].tb_next: + if sys.exc_info()[2].tb_next: raise NoAppException( 'While importing "{name}", an ImportError was raised:' "\n\n{tb}".format(name=module_name, tb=traceback.format_exc()) @@ -327,7 +323,7 @@ class DispatchingApp(object): exc_info = self._bg_loading_exc_info if exc_info is not None: self._bg_loading_exc_info = None - reraise(*exc_info) + raise exc_info def _load_unlocked(self): __traceback_hide__ = True # noqa: F841 @@ -741,11 +737,7 @@ def _validate_key(ctx, param, value): """ cert = ctx.params.get("cert") is_adhoc = cert == "adhoc" - - if sys.version_info < (2, 7, 9): - is_context = cert and not isinstance(cert, (text_type, bytes)) - else: - is_context = ssl and isinstance(cert, ssl.SSLContext) + is_context = ssl and isinstance(cert, ssl.SSLContext) if value is not None: if is_adhoc: diff --git a/src/flask/config.py b/src/flask/config.py index 7cb9f58c..3c7450cc 100644 --- a/src/flask/config.py +++ b/src/flask/config.py @@ -14,9 +14,6 @@ import types from werkzeug.utils import import_string -from ._compat import iteritems -from ._compat import string_types - class ConfigAttribute(object): """Makes an attribute forward to the config""" @@ -169,7 +166,7 @@ class Config(dict): :param obj: an import name or object """ - if isinstance(obj, string_types): + if isinstance(obj, str): obj = import_string(obj) for key in dir(obj): if key.isupper(): @@ -261,7 +258,7 @@ class Config(dict): .. versionadded:: 0.11 """ rv = {} - for k, v in iteritems(self): + for k, v in self.items(): if not k.startswith(namespace): continue if trim_namespace: diff --git a/src/flask/ctx.py b/src/flask/ctx.py index 8f96c5fd..040c4789 100644 --- a/src/flask/ctx.py +++ b/src/flask/ctx.py @@ -13,8 +13,6 @@ from functools import update_wrapper from werkzeug.exceptions import HTTPException -from ._compat import BROKEN_PYPY_CTXMGR_EXIT -from ._compat import reraise from .globals import _app_ctx_stack from .globals import _request_ctx_stack from .signals import appcontext_popped @@ -248,9 +246,6 @@ class AppContext(object): def __exit__(self, exc_type, exc_value, tb): self.pop(exc_value) - if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None: - reraise(exc_type, exc_value, tb) - class RequestContext(object): """The request context contains all request relevant information. It is @@ -463,9 +458,6 @@ class RequestContext(object): # See flask.testing for how this works. self.auto_pop(exc_value) - if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None: - reraise(exc_type, exc_value, tb) - def __repr__(self): return "<%s '%s' [%s] of %s>" % ( self.__class__.__name__, diff --git a/src/flask/debughelpers.py b/src/flask/debughelpers.py index 7117d782..623956d3 100644 --- a/src/flask/debughelpers.py +++ b/src/flask/debughelpers.py @@ -11,8 +11,6 @@ import os from warnings import warn -from ._compat import implements_to_string -from ._compat import text_type from .app import Flask from .blueprints import Blueprint from .globals import _request_ctx_stack @@ -24,7 +22,6 @@ class UnexpectedUnicodeError(AssertionError, UnicodeError): """ -@implements_to_string class DebugFilesKeyError(KeyError, AssertionError): """Raised from request.files during debugging. The idea is that it can provide a better error message than just a generic KeyError/BadRequest. @@ -110,13 +107,13 @@ def _dump_loader_info(loader): if key.startswith("_"): continue if isinstance(value, (tuple, list)): - if not all(isinstance(x, (str, text_type)) for x in value): + if not all(isinstance(x, str) for x in value): continue yield "%s:" % key for item in value: yield " - %s" % item continue - elif not isinstance(value, (str, text_type, int, float, bool)): + elif not isinstance(value, (str, int, float, bool)): continue yield "%s: %r" % (key, value) diff --git a/src/flask/helpers.py b/src/flask/helpers.py index 2811d7dc..6bfc162d 100644 --- a/src/flask/helpers.py +++ b/src/flask/helpers.py @@ -30,10 +30,6 @@ from werkzeug.routing import BuildError from werkzeug.urls import url_quote from werkzeug.wsgi import wrap_file -from ._compat import fspath -from ._compat import PY2 -from ._compat import string_types -from ._compat import text_type from .globals import _app_ctx_stack from .globals import _request_ctx_stack from .globals import current_app @@ -576,9 +572,9 @@ def send_file( fsize = None if hasattr(filename_or_fp, "__fspath__"): - filename_or_fp = fspath(filename_or_fp) + filename_or_fp = os.fspath(filename_or_fp) - if isinstance(filename_or_fp, string_types): + if isinstance(filename_or_fp, str): filename = filename_or_fp if not os.path.isabs(filename): filename = os.path.join(current_app.root_path, filename) @@ -608,7 +604,7 @@ def send_file( if attachment_filename is None: raise TypeError("filename unavailable, required for sending as attachment") - if not isinstance(attachment_filename, text_type): + if not isinstance(attachment_filename, str): attachment_filename = attachment_filename.decode("utf-8") try: @@ -618,7 +614,7 @@ def send_file( "filename": unicodedata.normalize("NFKD", attachment_filename).encode( "ascii", "ignore" ), - "filename*": "UTF-8''%s" % url_quote(attachment_filename, safe=b""), + "filename*": "UTF-8''%s" % url_quote(attachment_filename, safe=""), } else: filenames = {"filename": attachment_filename} @@ -678,7 +674,7 @@ def send_file( os.path.getsize(filename), adler32( filename.encode("utf-8") - if isinstance(filename, text_type) + if isinstance(filename, str) else filename ) & 0xFFFFFFFF, @@ -769,8 +765,8 @@ def send_from_directory(directory, filename, **options): :param options: optional keyword arguments that are directly forwarded to :func:`send_file`. """ - filename = fspath(filename) - directory = fspath(directory) + filename = os.fspath(filename) + directory = os.fspath(directory) filename = safe_join(directory, filename) if not os.path.isabs(filename): filename = os.path.join(current_app.root_path, filename) @@ -1140,22 +1136,12 @@ def total_seconds(td): def is_ip(value): """Determine if the given string is an IP address. - Python 2 on Windows doesn't provide ``inet_pton``, so this only - checks IPv4 addresses in that environment. - :param value: value to check :type value: str :return: True if string is an IP address :rtype: bool """ - if PY2 and os.name == "nt": - try: - socket.inet_aton(value) - return True - except socket.error: - return False - for family in (socket.AF_INET, socket.AF_INET6): try: socket.inet_pton(family, value) diff --git a/src/flask/json/__init__.py b/src/flask/json/__init__.py index a141068b..0ef50d37 100644 --- a/src/flask/json/__init__.py +++ b/src/flask/json/__init__.py @@ -16,14 +16,13 @@ from itsdangerous import json as _json from jinja2 import Markup from werkzeug.http import http_date -from .._compat import PY2 -from .._compat import text_type from ..globals import current_app from ..globals import request try: import dataclasses except ImportError: + # Python < 3.7 dataclasses = None # Figure out if simplejson escapes slashes. This behavior was changed @@ -96,7 +95,7 @@ class JSONEncoder(_json.JSONEncoder): if dataclasses and dataclasses.is_dataclass(o): return dataclasses.asdict(o) if hasattr(o, "__html__"): - return text_type(o.__html__()) + return str(o.__html__()) return _json.JSONEncoder.default(self, o) @@ -209,7 +208,7 @@ def dumps(obj, app=None, **kwargs): _dump_arg_defaults(kwargs, app=app) encoding = kwargs.pop("encoding", None) rv = _json.dumps(obj, **kwargs) - if encoding is not None and isinstance(rv, text_type): + if encoding is not None and isinstance(rv, str): rv = rv.encode(encoding) return rv @@ -256,8 +255,7 @@ def loads(s, app=None, **kwargs): def load(fp, app=None, **kwargs): """Like :func:`loads` but reads from a file object.""" _load_arg_defaults(kwargs, app=app) - if not PY2: - fp = _wrap_reader_for_text(fp, kwargs.pop("encoding", None) or "utf-8") + fp = _wrap_reader_for_text(fp, kwargs.pop("encoding", None) or "utf-8") return _json.load(fp, **kwargs) @@ -300,7 +298,7 @@ def htmlsafe_dumps(obj, **kwargs): def htmlsafe_dump(obj, fp, **kwargs): """Like :func:`htmlsafe_dumps` but writes into a file object.""" - fp.write(text_type(htmlsafe_dumps(obj, **kwargs))) + fp.write(str(htmlsafe_dumps(obj, **kwargs))) def jsonify(*args, **kwargs): diff --git a/src/flask/json/tag.py b/src/flask/json/tag.py index 5f338d95..42d51467 100644 --- a/src/flask/json/tag.py +++ b/src/flask/json/tag.py @@ -50,8 +50,6 @@ from jinja2 import Markup from werkzeug.http import http_date from werkzeug.http import parse_date -from .._compat import iteritems -from .._compat import text_type from ..json import dumps from ..json import loads @@ -124,7 +122,7 @@ class PassDict(JSONTag): def to_json(self, value): # JSON objects may only have string keys, so don't bother tagging the # key here. - return dict((k, self.serializer.tag(v)) for k, v in iteritems(value)) + return dict((k, self.serializer.tag(v)) for k, v in value.items()) tag = to_json @@ -181,7 +179,7 @@ class TagMarkup(JSONTag): return callable(getattr(value, "__html__", None)) def to_json(self, value): - return text_type(value.__html__()) + return str(value.__html__()) def to_python(self, value): return Markup(value) diff --git a/src/flask/sessions.py b/src/flask/sessions.py index fe2a20ee..e56163c3 100644 --- a/src/flask/sessions.py +++ b/src/flask/sessions.py @@ -10,19 +10,19 @@ """ import hashlib import warnings +from collections.abc import MutableMapping from datetime import datetime from itsdangerous import BadSignature from itsdangerous import URLSafeTimedSerializer from werkzeug.datastructures import CallbackDict -from ._compat import collections_abc from .helpers import is_ip from .helpers import total_seconds from .json.tag import TaggedJSONSerializer -class SessionMixin(collections_abc.MutableMapping): +class SessionMixin(MutableMapping): """Expands a basic dictionary with session attributes.""" @property diff --git a/src/flask/views.py b/src/flask/views.py index b3f90768..1c5828b8 100644 --- a/src/flask/views.py +++ b/src/flask/views.py @@ -8,7 +8,6 @@ :copyright: 2010 Pallets :license: BSD-3-Clause """ -from ._compat import with_metaclass from .globals import request @@ -135,7 +134,7 @@ class MethodViewType(type): cls.methods = methods -class MethodView(with_metaclass(MethodViewType, View)): +class MethodView(View, metaclass=MethodViewType): """A class-based view that dispatches request methods to the corresponding class methods. For example, if you implement a ``get`` method, it will be used to handle ``GET`` requests. :: diff --git a/tests/test_basic.py b/tests/test_basic.py index 2328a82c..d1313f2f 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -24,7 +24,6 @@ from werkzeug.http import parse_date from werkzeug.routing import BuildError import flask -from flask._compat import text_type def test_options_work(app, client): @@ -413,7 +412,7 @@ def test_session_expiration(app, client): @app.route("/test") def test(): - return text_type(flask.session.permanent) + return str(flask.session.permanent) rv = client.get("/") assert "set-cookie" in rv.headers diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index 6a5e333b..bc7fe260 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -15,7 +15,6 @@ from jinja2 import TemplateNotFound from werkzeug.http import parse_cache_control_header import flask -from flask._compat import text_type def test_blueprint_specific_error_handling(app, client): @@ -150,7 +149,7 @@ def test_blueprint_url_defaults(app, client): @bp.route("/bar") def bar(bar): - return text_type(bar) + return str(bar) app.register_blueprint(bp, url_prefix="/1", url_defaults={"bar": 23}) app.register_blueprint(bp, url_prefix="/2", url_defaults={"bar": 19}) diff --git a/tests/test_config.py b/tests/test_config.py index 450af8ce..33b270bb 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -14,7 +14,6 @@ from datetime import timedelta import pytest import flask -from flask._compat import PY2 # config keys used for the TestConfig @@ -198,6 +197,4 @@ def test_from_pyfile_weird_encoding(tmpdir, encoding): app = flask.Flask(__name__) app.config.from_pyfile(str(f)) value = app.config["TEST_VALUE"] - if PY2: - value = value.decode(encoding) assert value == u"föö" diff --git a/tests/test_helpers.py b/tests/test_helpers.py index c0138a3c..eeae481b 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -24,9 +24,6 @@ from werkzeug.http import parse_options_header import flask from flask import json -from flask._compat import PY2 -from flask._compat import StringIO -from flask._compat import text_type from flask.helpers import get_debug_flag from flask.helpers import get_env @@ -116,7 +113,7 @@ class TestJSON(object): def test_json_bad_requests(self, app, client): @app.route("/json", methods=["POST"]) def return_json(): - return flask.jsonify(foo=text_type(flask.request.get_json())) + return flask.jsonify(foo=str(flask.request.get_json())) rv = client.post("/json", data="malformed", content_type="application/json") assert rv.status_code == 400 @@ -140,7 +137,7 @@ class TestJSON(object): def test_json_dump_to_file(self, app, app_ctx): test_data = {"name": "Flask"} - out = StringIO() + out = io.StringIO() flask.json.dump(test_data, out) out.seek(0) @@ -254,7 +251,7 @@ class TestJSON(object): @app.route("/add", methods=["POST"]) def add(): json = flask.request.get_json() - return text_type(json["a"] + json["b"]) + return str(json["a"] + json["b"]) rv = client.post( "/add", @@ -267,7 +264,7 @@ class TestJSON(object): render = flask.render_template_string rv = flask.json.htmlsafe_dumps("") assert rv == u'"\\u003c/script\\u003e"' - assert type(rv) == text_type + assert type(rv) is str rv = render('{{ ""|tojson }}') assert rv == '"\\u003c/script\\u003e"' rv = render('{{ "<\0/script>"|tojson }}') @@ -447,7 +444,7 @@ class TestJSON(object): assert lines == sorted_by_str -class PyStringIO(object): +class PyBytesIO(object): def __init__(self, *args, **kwargs): self._io = io.BytesIO(*args, **kwargs) @@ -503,11 +500,7 @@ class TestSendfile(object): [ lambda app: open(os.path.join(app.static_folder, "index.html"), "rb"), lambda app: io.BytesIO(b"Test"), - pytest.param( - lambda app: StringIO("Test"), - marks=pytest.mark.skipif(not PY2, reason="Python 2 only"), - ), - lambda app: PyStringIO(b"Test"), + lambda app: PyBytesIO(b"Test"), ], ) @pytest.mark.usefixtures("req_ctx") @@ -525,10 +518,7 @@ class TestSendfile(object): "opener", [ lambda app: io.StringIO(u"Test"), - pytest.param( - lambda app: open(os.path.join(app.static_folder, "index.html")), - marks=pytest.mark.skipif(PY2, reason="Python 3 only"), - ), + lambda app: open(os.path.join(app.static_folder, "index.html")), ], ) @pytest.mark.usefixtures("req_ctx") diff --git a/tests/test_instance_config.py b/tests/test_instance_config.py index 0464565f..f892cb5d 100644 --- a/tests/test_instance_config.py +++ b/tests/test_instance_config.py @@ -12,7 +12,6 @@ import sys import pytest import flask -from flask._compat import PY2 def test_explicit_instance_paths(modules_tmpdir): @@ -128,19 +127,3 @@ def test_egg_installed_paths(install_egg, modules_tmpdir, modules_tmpdir_prefix) finally: if "site_egg" in sys.modules: del sys.modules["site_egg"] - - -@pytest.mark.skipif(not PY2, reason="This only works under Python 2.") -def test_meta_path_loader_without_is_package(request, modules_tmpdir): - app = modules_tmpdir.join("unimportable.py") - app.write("import flask\napp = flask.Flask(__name__)") - - class Loader(object): - def find_module(self, name, path=None): - return self - - sys.meta_path.append(Loader()) - request.addfinalizer(sys.meta_path.pop) - - with pytest.raises(AttributeError): - import unimportable # noqa: F401 diff --git a/tests/test_logging.py b/tests/test_logging.py index e5a96af0..b16da932 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -8,10 +8,10 @@ tests.test_logging """ import logging import sys +from io import StringIO import pytest -from flask._compat import StringIO from flask.logging import default_handler from flask.logging import has_level_handler from flask.logging import wsgi_errors_stream diff --git a/tests/test_meta.py b/tests/test_meta.py deleted file mode 100644 index 97403823..00000000 --- a/tests/test_meta.py +++ /dev/null @@ -1,6 +0,0 @@ -import io - - -def test_changelog_utf8_compatible(): - with io.open("CHANGES.rst", encoding="UTF-8") as f: - f.read() diff --git a/tests/test_subclassing.py b/tests/test_subclassing.py index 0ac278b5..01f6b666 100644 --- a/tests/test_subclassing.py +++ b/tests/test_subclassing.py @@ -9,8 +9,9 @@ :copyright: 2010 Pallets :license: BSD-3-Clause """ +from io import StringIO + import flask -from flask._compat import StringIO def test_suppressed_exception_logging(): diff --git a/tests/test_testing.py b/tests/test_testing.py index 674ec378..8571115c 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -14,7 +14,6 @@ import werkzeug import flask from flask import appcontext_popped -from flask._compat import text_type from flask.cli import ScriptInfo from flask.json import jsonify from flask.testing import EnvironBuilder @@ -184,7 +183,7 @@ def test_redirect_keep_session(app, client, app_ctx): def test_session_transactions(app, client): @app.route("/") def index(): - return text_type(flask.session["foo"]) + return str(flask.session["foo"]) with client: with client.session_transaction() as sess: