Skip to content

Commit

Permalink
Merge branch 'master' into werkzeug-importlib-573
Browse files Browse the repository at this point in the history
  • Loading branch information
Tomáš Trval committed Oct 25, 2023
2 parents 84b893d + e912d3d commit 5730306
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 13 deletions.
13 changes: 12 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ Releases prior to 0.3.0 were “best effort” filled out, but are missing
some info. If you see your contribution missing info, please open a PR
on the Changelog!

.. _section-1.2.1:
1.2.1
-----
.. _bug_fixes-1.2.1
Bug Fixes
~~~~~~~~~

::
* Fixing werkzeug 3 deprecated version import. Import is replaced by new style version check with importlib (#573) [Ryu-CZ]


.. _section-1.2.0:
1.2.0
-----
Expand All @@ -35,7 +46,7 @@ Bug Fixes
::

* Fixing test as HTTP Header MIMEAccept expects quality-factor number in form of `X.X` (#547) [chipndell]
* Fixing werkzeug 3 deprecated version import. Import is replaced by new style version check with importlib (#573) [Ryu-CZ]
* Introduce temporary restrictions on some package versions. (`flask<3.0.0`, `werkzeug<3.0.0`, `jsonschema<=4.17.3`) [peter-doggart]


.. _enhancements-1.2.0:
Expand Down
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ Flask and Werkzeug moved to versions 2.0 in March 2020. This caused a breaking c
- < 2.0.0
- pinned in Flask-RESTX.
* - >= 0.5.0
- All (For Now)
- < 3.0.0
- unpinned, import statements wrapped for compatibility
* - trunk branch in Github
- All (and updated more often)
- < 3.0.0 (Flask >=3.0.0 support is in progress, see https://github.com/python-restx/flask-restx/issues/566)
- unpinned, will address issues faster than releases.

Installation
Expand Down
2 changes: 1 addition & 1 deletion flask_restx/__about__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
__version__ = "1.1.1.dev"
__version__ = "1.2.1.dev"
__description__ = (
"Fully featured framework for fast, easy and documented API development with Flask"
)
13 changes: 7 additions & 6 deletions flask_restx/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@
from flask import url_for, request, current_app
from flask import make_response as original_flask_make_response

try:
from flask.helpers import _endpoint_from_view_func
except ImportError:
from flask.scaffold import _endpoint_from_view_func
from flask.signals import got_request_exception

from jsonschema import RefResolver
Expand All @@ -38,10 +34,15 @@
from .postman import PostmanCollectionV1
from .resource import Resource
from .swagger import Swagger
from .utils import default_id, camel_to_dash, unpack, BaseResponse

from .utils import default_id, camel_to_dash, unpack, import_check_view_func, BaseResponse

from .representations import output_json
from ._http import HTTPStatus

endpoint_from_view_func = import_check_view_func()


RE_RULES = re.compile("(<.*>)")

# List headers that should never be handled by Flask-RESTX
Expand Down Expand Up @@ -843,7 +844,7 @@ def _blueprint_setup_add_url_rule_patch(
rule = blueprint_setup.url_prefix + rule
options.setdefault("subdomain", blueprint_setup.subdomain)
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
endpoint = endpoint_from_view_func(view_func)
defaults = blueprint_setup.url_defaults
if "defaults" in options:
defaults = dict(defaults, **options.pop("defaults"))
Expand Down
46 changes: 46 additions & 0 deletions flask_restx/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import re
import warnings
import typing

from collections import OrderedDict
from copy import deepcopy
Expand Down Expand Up @@ -40,6 +42,10 @@ def import_werkzeug_response():
BaseResponse = import_werkzeug_response()


class FlaskCompatibilityWarning(DeprecationWarning):
pass


def merge(first, second):
"""
Recursively merges two dictionaries.
Expand Down Expand Up @@ -138,3 +144,43 @@ def unpack(response, default_code=HTTPStatus.OK):
return data, code or default_code, headers
else:
raise ValueError("Too many response values")


def to_view_name(view_func: typing.Callable) -> str:
"""Helper that returns the default endpoint for a given
function. This always is the function name.
Note: copy of simple flask internal helper
"""
assert view_func is not None, "expected view func if endpoint is not provided."
return view_func.__name__


def import_check_view_func():
"""
Resolve import flask _endpoint_from_view_func.
Show warning if function cannot be found and provide copy of last known implementation.
Note: This helper method exists because reoccurring problem with flask function, but
actual method body remaining the same in each flask version.
"""
import importlib.metadata

flask_version = importlib.metadata.version("flask").split(".")
try:
if flask_version[0] == "1":
from flask.helpers import _endpoint_from_view_func
elif flask_version[0] == "2":
from flask.scaffold import _endpoint_from_view_func
elif flask_version[0] == "3":
from flask.sansio.scaffold import _endpoint_from_view_func
else:
warnings.simplefilter("once", FlaskCompatibilityWarning)
_endpoint_from_view_func = None
except ImportError:
warnings.simplefilter("once", FlaskCompatibilityWarning)
_endpoint_from_view_func = None
if _endpoint_from_view_func is None:
_endpoint_from_view_func = to_view_name
return _endpoint_from_view_func
6 changes: 3 additions & 3 deletions requirements/install.pip
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
aniso8601>=0.82
jsonschema
Flask>=0.8, !=2.0.0
werkzeug !=2.0.0
jsonschema<=4.17.3
Flask>=0.8, !=2.0.0, <3.0.0
werkzeug!=2.0.0, <3.0.0
pytz
importlib_resources
14 changes: 14 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,17 @@ def test_value_headers_default_code(self):
def test_too_many_values(self):
with pytest.raises(ValueError):
utils.unpack((None, None, None, None))


class ToViewNameTest(object):
def test_none(self):
with pytest.raises(AssertionError):
_ = utils.to_view_name(None)

def test_name(self):
assert self.test_none == self.test_none.__name__


class ImportCheckViewFuncTest(object):
def test_callable(self):
assert callable(utils.import_check_view_func())

0 comments on commit 5730306

Please sign in to comment.