Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Registering same blueprints multiple of time with different name doesn't bring blueprint's registered error handler and before request logic with it #4124

Closed
pandabear opened this issue Jun 3, 2021 · 1 comment
Milestone

Comments

@pandabear
Copy link

pandabear commented Jun 3, 2021

Prior to version Flask 2.0.2, it was possible to register blueprints multiple of times with different url prefix during Flask App factory. Such as:

    # Flask 0.10.1
    from my_project.api.v1.user import user_api_v1
    app.register_blueprint(user_api_v1,
                           url_prefix='/api/latest/users')
    app.register_blueprint(user_api_v1,
                           url_prefix='/api/v1/users')

    from my_project.api.v1.recipe import recipe_api_v1
    app.register_blueprint(recipe_api_v1,
                           url_prefix='/api/latest/recipes')
    app.register_blueprint(recipe_api_v1,
                           url_prefix='/api/v1/recipes')

But with latest Flask version, the above was updated into this:

    from my_project.api.v1.user import user_api_v1
    app.register_blueprint(user_api_v1,
                           name='users_latest',
                           url_prefix='/api/latest/users')
    app.register_blueprint(user_api_v1,
                           name='users_v1',
                           url_prefix='/api/v1/users')

    from my_project.api.v1.recipe import recipe_api_v1
    app.register_blueprint(recipe_api_v1,
                           name='recipes_latest',
                           url_prefix='/api/latest/recipes')
    app.register_blueprint(recipe_api_v1,
                           name='recipes_v1',
                           url_prefix='/api/v1/recipes')

Basically, adding unique name. However, this change caused an issue that the error- and before_request-handlers that was registered to blueprints do not get mapped to the second blueprint.

This was the result for the new Flask App:

# App's url map
Map([<Rule '/api/latest/recipes/' (GET, HEAD, OPTIONS) -> recipes_latest.get_all>,
 <Rule '/api/latest/users/' (GET, HEAD, OPTIONS) -> users_latest.get_all>,
 <Rule '/api/v1/recipes/' (GET, HEAD, OPTIONS) -> recipes_v1.get_all>,
 <Rule '/api/v1/users/' (GET, HEAD, OPTIONS) -> users_v1.get_all>,
 <Rule '/api/latest/recipes/<id>' (POST, OPTIONS) -> recipes_latest.post>,
 <Rule '/api/v1/recipes/<id>' (POST, OPTIONS) -> recipes_v1.post>,
 <Rule '/api/latest/recipes/<id>' (GET, HEAD, OPTIONS) -> recipes_latest.get>,
 <Rule '/api/v1/recipes/<id>' (GET, HEAD, OPTIONS) -> recipes_v1.get>,
 <Rule '/static/<filename>' (GET, HEAD, OPTIONS) -> static>])

# App's before_request_funcs
defaultdict(<class 'list'>,
            {None: [<function requires_staff_permission at 0x7fe42b9be840>],
             'recipes_latest': [<function _require_chef_permission at 0x7fe429a67f28>],
             'users_latest': [<function _require_manager_permission at 0x7fe42acb90d0>]})

# App's error_handler_spec
defaultdict(<function Scaffold.__init__.<locals>.<lambda> at 0x7fe42ad0aa60>,
            {None: defaultdict(<class 'dict'>,
                               {None: {<class 'Exception'>: <function generic_error_handler.<locals>.error_handler at 0x7fe42ad0ab70>}}),
             'recipes_latest': defaultdict(<class 'dict'>,
                                           {None: {<class 'my_project.api.v1.recipe.MissingParameter'>: <function handle_missing_argument at 0x7fe429a68510>,
                                                   <class 'my_project.api.v1.recipe.MongoDocumentNotFound'>: <function handle_no_mongo_document at 0x7fe429a68400>,
                                                   <class 'my_project.api.v1.recipe.UnsupportedParameter'>: <function handle_invalid_argument at 0x7fe429a68488>}}),
             'users_latest': defaultdict(<class 'dict'>,
                                         {None: {<class 'my_project.api.v1.user.UnsupportedParameter'>: <function handle_invalid_argument at 0x7fe42acb92f0>}})})

What this means is that when request is done to GET /api/v1, it will not trigger any handlers, because handlers are registered to x_latest and /api/v1 was registered with x_v1 blueprint name.

The behaviour I'd expect after using app.register_blueprint for same blueprint object with different name is that the new registered name should also have same registered error- and before request-handler mapped like so:

# Expected App's before_request_funcs
defaultdict(<class 'list'>,
            {None: [<function requires_staff_permission at 0x7fe42b9be840>],
             'recipes_latest': [<function _require_chef_permission at 0x7fe429a67f28>],
             'recipes_v1': [<function _require_chef_permission at 0x7fe429a67f28>],
             'users_latest': [<function _require_manager_permission at 0x7fe42acb90d0>],
             'users_v1': [<function _require_manager_permission at 0x7fe42acb90d0>]})

# Expected App's error_handler_spec
defaultdict(<function Scaffold.__init__.<locals>.<lambda> at 0x7fe42ad0aa60>,
            {None: defaultdict(<class 'dict'>,
                               {None: {<class 'Exception'>: <function generic_error_handler.<locals>.error_handler at 0x7fe42ad0ab70>}}),
             'recipes_latest': defaultdict(<class 'dict'>,
                                           {None: {<class 'my_project.api.v1.recipe.MissingParameter'>: <function handle_missing_argument at 0x7fe429a68510>,
                                                   <class 'my_project.api.v1.recipe.MongoDocumentNotFound'>: <function handle_no_mongo_document at 0x7fe429a68400>,
                                                   <class 'my_project.api.v1.recipe.UnsupportedParameter'>: <function handle_invalid_argument at 0x7fe429a68488>}}),
             'recipes_v1': defaultdict(<class 'dict'>,
                                           {None: {<class 'my_project.api.v1.recipe.MissingParameter'>: <function handle_missing_argument at 0x7fe429a68510>,
                                                   <class 'my_project.api.v1.recipe.MongoDocumentNotFound'>: <function handle_no_mongo_document at 0x7fe429a68400>,
                                                   <class 'my_project.api.v1.recipe.UnsupportedParameter'>: <function handle_invalid_argument at 0x7fe429a68488>}}),
             'users_latest': defaultdict(<class 'dict'>,
                                         {None: {<class 'my_project.api.v1.iser.UnsupportedParameter'>: <function handle_invalid_argument at 0x7fe42acb92f0>}}),
             'users_v1': defaultdict(<class 'dict'>,
                                         {None: {<class 'my_project.api.v1.iser.UnsupportedParameter'>: <function handle_invalid_argument at 0x7fe42acb92f0>}})})

Environment:

  • Python version: 3.6.8
  • Flask version: 2.0.2
@pandabear pandabear changed the title Registering same blueprints multiple of time with different name doesn't bring blueprint's registered error handler and before request logic with i Registering same blueprints multiple of time with different name doesn't bring blueprint's registered error handler and before request logic with it Jun 3, 2021
@pgjones
Copy link
Member

pgjones commented Jun 5, 2021

This makes sense, I think it is fixed with #4132

@davidism davidism added this to the 2.0.2 milestone Jun 5, 2021
@pgjones pgjones closed this as completed Jun 14, 2021
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 29, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants