2
votes

Comment gérer l'erreur FlaskInjector au démarrage de l'application Python Flask Api?

J'ai mis en place la structure la plus basique d'une API en python avec la pile

flask - connexion - flaskInjector

selon le code ci-dessous. Lorsque l'API est exécutée et que FlaskInjector est initialisé, il échoue avec le message d'erreur:

injector.CallError: appel à RequestScope. init () a échoué: init () manque 1 argument de position requis: 'injector' (pile d'injection: [])

Mon système est Ubuntu 18.04, les packages dans l'environnement conda comme suit:

Extrait pertinent de la liste conda:

127.0.0.1 - - [21/May/2019 08:16:31] "GET / HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/home/christian/.conda/envs/miccroservice/lib/python3.6/site-packages/flask/app.py", line 2309, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/christian/.conda/envs/miccroservice/lib/python3.6/site-packages/flask/app.py", line 2295, in wsgi_app
    response = self.handle_exception(e)
  File "/home/christian/.conda/envs/miccroservice/lib/python3.6/site-packages/flask/app.py", line 1741, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/christian/.conda/envs/miccroservice/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/home/christian/.conda/envs/miccroservice/lib/python3.6/site-packages/flask/app.py", line 2292, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/christian/.conda/envs/miccroservice/lib/python3.6/site-packages/flask/app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/christian/.conda/envs/miccroservice/lib/python3.6/site-packages/flask/app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/christian/.conda/envs/miccroservice/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/home/christian/.conda/envs/miccroservice/lib/python3.6/site-packages/flask/app.py", line 1811, in full_dispatch_request
    rv = self.preprocess_request()
  File "/home/christian/.conda/envs/miccroservice/lib/python3.6/site-packages/flask/app.py", line 2087, in preprocess_request
    rv = func()
  File "/home/christian/.conda/envs/miccroservice/lib/python3.6/site-packages/flask_injector.py", line 327, in reset_request_scope_before
    injector_not_null.get(request_scope_class).prepare()
  File "/home/christian/.conda/envs/miccroservice/lib/python3.6/site-packages/injector.py", line 738, in get
    result = scope_instance.get(key, binding.provider).get(self)
  File "/home/christian/.conda/envs/miccroservice/lib/python3.6/site-packages/injector.py", line 144, in get
    return injector.create_object(self._cls)
  File "/home/christian/.conda/envs/miccroservice/lib/python3.6/site-packages/injector.py", line 789, in create_object
    (), additional_kwargs, e, self._stack,)
  File "/home/christian/.conda/envs/miccroservice/lib/python3.6/site-packages/injector.py", line 73, in reraise
    raise exception.with_traceback(tb)
  File "/home/christian/.conda/envs/miccroservice/lib/python3.6/site-packages/injector.py", line 782, in create_object
    init(instance, **additional_kwargs)
injector.CallError: Call to RequestScope.__init__() failed: __init__() missing 1 required positional argument: 'injector' (injection stack: [])
127.0.0.1 - - [21/May/2019 08:16:31] "GET /?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 200 -
127.0.0.1 - - [21/May/2019 08:16:31] "GET /?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 200 -
127.0.0.1 - - [21/May/2019 08:16:31] "GET /?__debugger__=yes&cmd=resource&f=jquery.js HTTP/1.1" 200 -
127.0.0.1 - - [21/May/2019 08:16:31] "GET /?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -

Le Le projet est organisé comme suit:

./app.py:

 * Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://0.0.0.0:9090/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 498-670-595

./api/items.py:

swagger: "2.0"

info:
  title: "My first API"
  version: "1.0"

basePath: /api

paths:
  /items/:
    get:
      responses:
        '200':
          description: 'Fetch a list of items'
          schema:
            type: array
            items:
              $ref: '#/definitions/Item'

definitions:
  Item:
    type: object
    properties:
      id:
        type: integer
        format: int64
      name: { type: string }

./providers/provider.py:

class ItemsProvider(object):
    def __init__(self, items: list = []):
        self._items = items

    def get(self, number_of_items: int = 5) -> list:
        if not self._items:
            return []

        if number_of_items > len(self._items):
            number_of_items = len(self._items)

        return self._items[0:number_of_items]

./swagger/app.yaml:

from flask_injector import inject
from providers.provider import ItemsProvider


@inject(data_provider=ItemsProvider)
def search(data_provider) -> list:
    return data_provider.get()

Lors de l'exécution de l'application avec python app.py, le serveur api démarre comme prévu:

from injector import Binder
from flask_injector import FlaskInjector
from connexion.resolver import RestyResolver
import connexion
from providers.provider import ItemsProvider


def configure(binder: Binder) -> Binder:
    binder.bind(
        ItemsProvider,
        ItemsProvider([{'Name': 'Test 1'}])
    )
    return binder


if __name__ == '__main__':
    app = connexion.App(__name__, specification_dir='./swagger/')
    app.add_api('app.yaml', resolver=RestyResolver('api'))
    FlaskInjector(app=app.app, modules=[configure])
    app.run(port=9090, debug=True)

Cependant, en adressant le serveur ou le point de terminaison des éléments dans le navigateur, les éléments suivants la pile d'échecs apparaît:

# Name                    Version                   Build  Channel
...
connexion                 2.2.0                    pypi_0    pypi
...
elasticsearch             7.0.1                    pypi_0    pypi
fastavro                  0.21.23                  pypi_0    pypi
flask                     1.0.2                    pypi_0    pypi
flask-cors                3.0.7                    pypi_0    pypi
flask-injector            0.12.0                   pypi_0    pypi
flask-opentracing         1.0.0                    pypi_0    pypi
flask-restplus            0.9.2                    pypi_0    pypi
flask-script              2.0.6                    pypi_0    pypi
flask-sqlalchemy          2.3.2                    pypi_0    pypi
...
injector                  0.12.0                   pypi_0    pypi
...
swagger-ui-bundle         0.0.3                    pypi_0    pypi
...


2 commentaires

Il semble que le démarrage de Connexion est erroné par rapport à la documentation de l'injecteur de flacon github.com/alecthomas/ …


Avez-vous essayé de renommer vos importations? Ex: 'from flask_injector import inject as i' .. C'est un problème courant si vous avez des collisions dans votre espace de noms entre les importations.


3 Réponses :


1
votes

merci pour vos réponses. Je suis venu en attendant une solution de travail. Le décorateur @inject peut, au moins dans mes versions actuelles, ne pas être utilisé avec des paramètres. Cela fonctionne si je change la fonction en:

@inject
def search(dataprovider=ItemsProvider_()) -> list:
    data = dataprovider.get(0)
    return data

Merci!


0 commentaires

1
votes

La mise à jour de la version de l'injecteur de injector-0.12.0 à injector-0.17.0 a résolu mon problème.


0 commentaires

0
votes

J'ai eu cette erreur dans la situation suivante:

J'ai défini la classe to-be-inject dans le module où j'instancie mon application flask et configure l'injecteur:

app.py p>

from app import MyClass

@inject
def foo(my_object: MyClass):
    return 'Something'

J'injecte la classe dans un module différent:

controller.py

class MyClass:
    def __init__(bar)
        self.bar = bar
# ... instantiate flask app and injector

Pour moi le la solution était de créer un module séparé où je définis MyClass qui est différent de celui où j'instancie l'injecteur.


0 commentaires