python-injector / flask_injector

Adds Injector support to Flask.
BSD 3-Clause "New" or "Revised" License
277 stars 42 forks source link

Strange exception when open swggaer doc -> TypeError('Injecting Any is not supported') #78

Open M1LKYWVYs opened 1 year ago

M1LKYWVYs commented 1 year ago

Hi! I am facing very strange behaviour when trying to open swgger doc provided by flask smorest. When trying to open swagger page (http://127.0.0.1:5001/application/swagger-ui) application falls down with error TypeError('Injecting Any is not supported').

I have removed all bindings to make example more understandable and clear, so sources:

from flask import Flask
from flask.views import MethodView
from flask_injector import FlaskInjector
from flask_smorest import Api, Blueprint

healthcheck_blueprint = Blueprint("healthcheck", __name__, description="App status checking")

def setup_app_injector(flask_app: Flask) -> None:
    FlaskInjector(app=flask_app)

@healthcheck_blueprint.route("/healthcheck")
class HealthcheckView(MethodView):
    @healthcheck_blueprint.response(status_code=200)
    def get(self):  # type: ignore
        """Simple healthcheck"""

        return {"project": "test_injector"}

def create_flask_app() -> Flask:
    flask_app = Flask(__name__)

    class Config:
        OPENAPI_VERSION = "3.0.2"
        OPENAPI_URL_PREFIX = "/application"
        OPENAPI_SWAGGER_UI_PATH = "/swagger-ui"
        OPENAPI_SWAGGER_UI_VERSION = "3.24.2"
        OPENAPI_SWAGGER_UI_URL = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.24.2/"
        OPENAPI_REDOC_PATH = "redoc"

    flask_app.config["API_TITLE"] = "test_injector"
    flask_app.config["API_VERSION"] = "v1"
    flask_app.config.from_object(Config)

    api = Api(app=flask_app)
    api.register_blueprint(healthcheck_blueprint, url_prefix="/application")

    setup_app_injector(flask_app=flask_app)

    return flask_app

app = create_flask_app()

def main() -> None:
    local_runtime_configuration: dict[str, int | bool] = {"port": 5001, "debug": True}
    app.run(**local_runtime_configuration)  # type: ignore[arg-type]

if __name__ == "__main__":
    main()

My pyproject:

[tool.poetry]
name = "test-injector"
version = "0.1.0"
description = ""
authors = []
readme = "README.md"
packages = [{include = "test_injector"}]

[tool.poetry.dependencies]
python = "^3.11"
flask = "2.2.3"
flask-smorest = "0.40.0"
injector = "0.20.1"
flask-injector = "0.14.0"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

Everything works fine if remove injector (comment line 40 setup_app_injector(flask_app=flask_app)).

Full trace:

Traceback (most recent call last):
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/injector/__init__.py", line 637, in get_binding
    return self._get_binding(interface, only_this_binder=is_scope or is_assisted_builder)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/injector/__init__.py", line 631, in _get_binding
    raise KeyError
KeyError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/flask/app.py", line 2551, in __call__
    return self.wsgi_app(environ, start_response)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/flask/app.py", line 2531, in wsgi_app
    response = self.handle_exception(e)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/flask/app.py", line 2528, in wsgi_app
    response = self.full_dispatch_request()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/flask/app.py", line 1825, in full_dispatch_request
    rv = self.handle_user_exception(e)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/flask/app.py", line 1823, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/flask_injector/__init__.py", line 45, in wrapper
    return im(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/flask_smorest/spec/__init__.py", line 139, in _openapi_swagger_ui
    return flask.render_template(
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/flask/templating.py", line 147, in render_template
    return _render(app, template, context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/flask/templating.py", line 130, in _render
    rv = template.render(context)
         ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/jinja2/environment.py", line 1301, in render
    self.environment.handle_exception()
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/jinja2/environment.py", line 936, in handle_exception
    raise rewrite_traceback_stack(source=source)
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/flask_smorest/spec/templates/swagger_ui.html", line 18, in top-level template code
    url: "{{ url_for('api-docs.openapi_json') }}",
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/flask_injector/__init__.py", line 89, in wrapper
    return injector.call_with_injection(callable=fun, args=args, kwargs=kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/injector/__init__.py", line 999, in call_with_injection
    dependencies = self.args_to_inject(
                   ^^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/injector/__init__.py", line 91, in wrapper
    return function(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/injector/__init__.py", line 1047, in args_to_inject
    instance: Any = self.get(interface)
                    ^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/injector/__init__.py", line 932, in get
    binding, binder = self.binder.get_binding(interface)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/injector/__init__.py", line 646, in get_binding
    binding = self.create_binding(interface)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/injector/__init__.py", line 560, in create_binding
    provider = self.provider_for(interface, to)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dreamer/Projects/Development/test_injector/venv/lib/python3.11/site-packages/injector/__init__.py", line 571, in provider_for
    raise TypeError('Injecting Any is not supported')

For someone who facing similiar problem -> Add flasgger to provide openapi documentation to your application.

jstasiak commented 1 year ago

Hey @M1LKYWVYs, thank you for a really precise and well written bug report – it's appreciated. I'm sorry you're experiencing issues.

This library does some shady things behind the scenes to make things work and that will interact weirdly with some other libraries.

I don't have resources to investigate this for the time being, unfortunately.

Bartolomeo624 commented 1 year ago

Hi, I am facing the same issue. According to my debugger it is related to the _urlfor method which 7th parameter **values has a type hint "t.Any". Were you able to find a workaround by chance?


Traceback (most recent call last): File "/usr/local/lib/python3.8/dist-packages/injector/init.py", line 637, in get_binding return self._get_binding(interface, only_this_binder=is_scope or is_assisted_builder) File "/usr/local/lib/python3.8/dist-packages/injector/init.py", line 631, in _get_binding raise KeyError KeyError

During handling of the above exception, another exception occurred:

Traceback (most recent call last): File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 2551, in call return self.wsgi_app(environ, start_response) File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 2531, in wsgi_app response = self.handle_exception(e) File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 2528, in wsgi_app response = self.full_dispatch_request() File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 1825, in full_dispatch_request rv = self.handle_user_exception(e) File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 1823, in full_dispatch_request rv = self.dispatch_request() File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 1799, in dispatch_request return self.ensure_sync(self.view_functions[rule.endpoint])(view_args) File "/home/bartolomeo/aeh/zjo-pwa/APZ-CeneoScrapper/flask_app/app/services/decorators.py", line 15, in decorated_function return func(*args, *kwargs) File "/home/bartolomeo/aeh/zjo-pwa/APZ-CeneoScrapper/flask_app/app/controllers/accounts/routes.py", line 53, in login return render_template("accounts/login.html", form=form) File "/usr/local/lib/python3.8/dist-packages/flask/templating.py", line 147, in render_template return _render(app, template, context) File "/usr/local/lib/python3.8/dist-packages/flask/templating.py", line 130, in _render rv = template.render(context) File "/usr/local/lib/python3.8/dist-packages/jinja2/environment.py", line 1301, in render self.environment.handle_exception() File "/usr/local/lib/python3.8/dist-packages/jinja2/environment.py", line 936, in handle_exception raise rewrite_traceback_stack(source=source) File "/home/bartolomeo/aeh/zjo-pwa/APZ-CeneoScrapper/flask_app/app/templates/accounts/login.html", line 1, in top-level template code {% extends "base.html" %} File "/home/bartolomeo/aeh/zjo-pwa/APZ-CeneoScrapper/flask_app/app/templates/base.html", line 13, in top-level template code {% include "navigation.html" %} File "/home/bartolomeo/aeh/zjo-pwa/APZ-CeneoScrapper/flask_app/app/templates/navigation.html", line 21, in top-level template code Login File "/usr/local/lib/python3.8/dist-packages/flask_injector/init.py", line 89, in wrapper return injector.call_with_injection(callable=fun, args=args, kwargs=kwargs) File "/usr/local/lib/python3.8/dist-packages/injector/init.py", line 999, in call_with_injection dependencies = self.args_to_inject( File "/usr/local/lib/python3.8/dist-packages/injector/init.py", line 91, in wrapper return function(args, kwargs) File "/usr/local/lib/python3.8/dist-packages/injector/init.py", line 1047, in args_to_inject instance: Any = self.get(interface) File "/usr/local/lib/python3.8/dist-packages/injector/init.py", line 932, in get binding, binder = self.binder.get_binding(interface) File "/usr/local/lib/python3.8/dist-packages/injector/init.py", line 646, in get_binding binding = self.create_binding(interface) File "/usr/local/lib/python3.8/dist-packages/injector/init.py", line 560, in create_binding provider = self.provider_for(interface, to) File "/usr/local/lib/python3.8/dist-packages/injector/init.py", line 571, in provider_for raise TypeError('Injecting Any is not supported') TypeError: Injecting Any is not supported

wsz commented 1 year ago

@M1LKYWVYs @Bartolomeo624 Workaround: clear the type hints, which cause the problem, add Flask.url_for.__annotations__ = {} before app = Flask(...) I tried Flask.url_for = noninjectable(Flask.url_for, ...) but it seems not working as expected. I get TypeError: too many positional arguments

robob4him commented 11 months ago

@wsz Worked perfectly for this error using apiflask. Thank you!

aderbas commented 6 months ago

No update on this issue? =/

Kraziyi commented 1 month ago

Any updates on this issue? Meet the same issue: TypeError: Injecting Any is not supported when trying to render_template even though I did not add @inject annotation on that blueprint.