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

mypy: Blueprint type mismatch with flask.sansio.blueprints.Blueprint #404

Open
hrimov opened this issue Feb 8, 2025 · 0 comments · May be fixed by #405
Open

mypy: Blueprint type mismatch with flask.sansio.blueprints.Blueprint #404

hrimov opened this issue Feb 8, 2025 · 0 comments · May be fixed by #405

Comments

@hrimov
Copy link

hrimov commented Feb 8, 2025

When using Quart's Blueprint, mypy doesn't recognize its inheritance from flask.sansio.blueprints.Blueprint. This causes type errors when accessing blueprints through app.blueprints, which returns the Flask Blueprint type.

Environment

  • Python version: 3.12.7
  • Quart version: 0.20.0

Minimal Reproducible Example

from quart import Quart
from quart.blueprints import Blueprint


app = Quart(__name__)
bp = Blueprint("test", __name__)
app.register_blueprint(bp)

# This works at runtime but fails type check
def process_blueprint(blueprint: Blueprint) -> None:
    print(f"Processing blueprint: {blueprint.name}")

# Type error here - blueprints.values() returns flask.sansio.blueprints.Blueprint
for blueprint in app.blueprints.values():
    process_blueprint(blueprint)  # Error: Expected quart.blueprints.Blueprint, got flask.sansio.blueprints.Blueprint

# Show inferred types
reveal_type(bp)  # Shows quart.blueprints.Blueprint
reveal_type(app.blueprints)  # Shows Dict[str, flask.sansio.blueprints.Blueprint]

Mypy output:

mypy quart_blueprint_mre.py --show-error-codes --strict

quart_blueprint_mre.py:19: error: Argument 1 to "process_blueprint" has incompatible type "flask.sansio.blueprints.Blueprint"; expected "quart.blueprints.Blueprint"  [arg-type]
quart_blueprint_mre.py:22: note: Revealed type is "quart.blueprints.Blueprint"
quart_blueprint_mre.py:23: note: Revealed type is "builtins.dict[builtins.str, flask.sansio.blueprints.Blueprint]"
Found 1 error in 1 file (checked 1 source file)

In my real codebase, I have something like this:

from typing import TypeAlias

from quart import Quart
from quart.blueprints import Blueprint

Scaffold: TypeAlias = Blueprint | Quart


def _inject_routes(app: Scaffold) -> None:
    for endpoint, func in app.view_functions.items():
        if not injected(func):
            wrapped = _make_wrapper(func)
            app.view_functions[endpoint] = wrapped

It can be fixed, of course, with suppression or with adding flask.sansio.blueprints.Blueprint to the type alias, but I guess it's not the best solution.

As I found out, that's because of the more complex hierarchy of the App/Blueprint classes for Quart. So I'm wondering what would be the best way to handle this inheritance in the type system:

  1. Make Flask's sansio layer more generic to support framework extension:
TBlueprint = TypeVar('TBlueprint', bound='Blueprint', covariant=True)
blueprints: Dict[str, TBlueprint]
  1. Or add type casting on Quart's side:
@property
def blueprints(self) -> Dict[str, Blueprint]:
    return cast(Dict[str, Blueprint], super().blueprints)

I'd be happy to submit a PR with either approach once you suggest me which is more appropriate for the Quart/Flask ecosystem.

@hrimov hrimov linked a pull request Feb 8, 2025 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant