git: 0335abf42682 - main - www/py-flask-restx: unbreak after 3232af189573

From: Jan Beich <jbeich_at_FreeBSD.org>
Date: Mon, 27 Nov 2023 16:54:58 UTC
The branch main has been updated by jbeich:

URL: https://cgit.FreeBSD.org/ports/commit/?id=0335abf4268235877a1c3007afb870e7ca51195c

commit 0335abf4268235877a1c3007afb870e7ca51195c
Author:     Jan Beich <jbeich@FreeBSD.org>
AuthorDate: 2023-11-27 16:37:39 +0000
Commit:     Jan Beich <jbeich@FreeBSD.org>
CommitDate: 2023-11-27 16:54:14 +0000

    www/py-flask-restx: unbreak after 3232af189573
    
    >>> import flask_restx
    Traceback (most recent call last):
      File "/usr/local/lib/python3.9/site-packages/flask_restx/api.py", line 18, in <module>
        from flask.helpers import _endpoint_from_view_func
    ImportError: cannot import name '_endpoint_from_view_func' from 'flask.helpers' (/usr/local/lib/python3.9/site-packages/flask/helpers.py)
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/local/lib/python3.9/site-packages/flask_restx/__init__.py", line 2, in <module>
        from .api import Api  # noqa
      File "/usr/local/lib/python3.9/site-packages/flask_restx/api.py", line 20, in <module>
        from flask.scaffold import _endpoint_from_view_func
    ModuleNotFoundError: No module named 'flask.scaffold'
---
 www/py-flask-restx/Makefile           |   1 +
 www/py-flask-restx/files/patch-flask3 | 127 ++++++++++++++++++++++++++++++++++
 2 files changed, 128 insertions(+)

diff --git a/www/py-flask-restx/Makefile b/www/py-flask-restx/Makefile
index 0b333b98d825..2e7730d5c155 100644
--- a/www/py-flask-restx/Makefile
+++ b/www/py-flask-restx/Makefile
@@ -1,5 +1,6 @@
 PORTNAME=	flask-restx
 PORTVERSION=	1.2.0
+PORTREVISION=	1
 CATEGORIES=	www python
 MASTER_SITES=	PYPI
 PKGNAMEPREFIX=	${PYTHON_PKGNAMEPREFIX}
diff --git a/www/py-flask-restx/files/patch-flask3 b/www/py-flask-restx/files/patch-flask3
new file mode 100644
index 000000000000..22887f79e25f
--- /dev/null
+++ b/www/py-flask-restx/files/patch-flask3
@@ -0,0 +1,127 @@
+https://github.com/python-restx/flask-restx/pull/572
+
+--- flask_restx/api.py.orig	2023-10-22 12:20:40 UTC
++++ flask_restx/api.py
+@@ -14,10 +14,6 @@ from flask import make_response as original_flask_make
+ 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
+@@ -45,10 +41,13 @@ from .swagger import Swagger
+ from .postman import PostmanCollectionV1
+ from .resource import Resource
+ from .swagger import Swagger
+-from .utils import default_id, camel_to_dash, unpack
++from .utils import default_id, camel_to_dash, unpack, import_check_view_func
+ 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
+@@ -850,7 +849,7 @@ class Api(object):
+             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"))
+--- flask_restx/utils.py.orig	2023-10-22 12:20:40 UTC
++++ flask_restx/utils.py
+@@ -1,4 +1,6 @@ import re
+ import re
++import warnings
++import typing
+ 
+ from collections import OrderedDict
+ from copy import deepcopy
+@@ -17,9 +19,14 @@ __all__ = (
+     "not_none",
+     "not_none_sorted",
+     "unpack",
++    "import_check_view_func",
+ )
+ 
+ 
++class FlaskCompatibilityWarning(DeprecationWarning):
++    pass
++
++
+ def merge(first, second):
+     """
+     Recursively merges two dictionaries.
+@@ -118,3 +125,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
+--- tests/test_utils.py.orig	2023-10-22 12:20:40 UTC
++++ tests/test_utils.py
+@@ -98,3 +98,17 @@ class UnpackTest(object):
+     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 utils.to_view_name(self.test_none) == self.test_none.__name__
++
++
++class ImportCheckViewFuncTest(object):
++    def test_callable(self):
++        assert callable(utils.import_check_view_func())