pallets / pallets/quart

mypy: Blueprint type mismatch with `flask.sansio.blueprints.Blueprint`

Open
#404 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
3.7k
Forks
206
PR merge metrics
No merged PRs in 30d

Description

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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the Quart Blueprint and Quart entry points that expose app.blueprints, then compare their annotations with Flask's sansio Blueprint definitions. Use the provided minimal example with mypy --strict to evaluate whether the Flask typing or a Quart-side cast best resolves the mismatch, and consider the impact on the Quart/Flask ecosystem before defining done.

Written by the indexing model from the issue text.

Assessment

Tech stack
flask, python
Domain
backend, developer-experience
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.