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

Make _MiddlewareFactory compatible with Callable #2768

Merged
merged 1 commit into from
Nov 26, 2024

Conversation

eltoder
Copy link
Contributor

@eltoder eltoder commented Nov 26, 2024

Summary

Make app a positional-only argument so that a Callable[[ASGIApp, ...], ASGIApp] can be passed in place of _MiddlewareFactory.

We can also consider making _MiddlewareFactory and moving it into starlette.types. This will make it easier to type arguments for add_middleware().

Checklist

  • I understand that this PR may be closed in case there was no previous discussion. (This doesn't apply to typos!)
  • I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.

Comment on lines 16 to +17
class _MiddlewareFactory(Protocol[P]):
def __call__(self, app: ASGIApp, *args: P.args, **kwargs: P.kwargs) -> ASGIApp: ... # pragma: no cover
def __call__(self, app: ASGIApp, /, *args: P.args, **kwargs: P.kwargs) -> ASGIApp: ... # pragma: no cover
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find specifying positional only parameters in Protocols confusion, however I checked this case and I think it's correct:

import typing

class Plain(typing.Protocol):
    def __call__(self, x: int, y: str) -> None: ...

class Positional(typing.Protocol):
    def __call__(self, a: int, /, y: str) -> None: ...


def foo(x: int, y: str) -> None:
    ...


def bar(x: int, /, y: str) -> None:
    ...


a: Plain = foo  # ok
b: Plain = bar # err
c: Positional = foo  # ok
d: Positional = bar # ok

Passes on https://mypy-play.net/?mypy=1.4.1&python=3.13&gist=8972af53af6b9d67e6c255a5de32db18

@adriangb
Copy link
Member

We can also consider making _MiddlewareFactory and moving it into starlette.types. This will make it easier to type arguments for add_middleware()

This part I did not understand

@adriangb adriangb merged commit 13d0c1f into encode:master Nov 26, 2024
6 checks passed
@eltoder eltoder deleted the feature/middleware-factory-callable branch November 26, 2024 14:38
@eltoder
Copy link
Contributor Author

eltoder commented Nov 26, 2024

@adriangb thank you for merging!

This part I did not understand

Sorry, missed a word:

We can also consider making _MiddlewareFactory public.

The rationale is that add_middleware() is a public function and occasionally I need to refer to the type of its argument. For example, I have a function that returns which middleware to use based on environment. I need to write its return type. With this PR I can write it as Callalble[], which is nice. But it may be even more clear and explicit to write it as MiddlewareFactory. However, currently the latter is not public. Here's a sketch of how this can be used: https://mypy-play.net/?mypy=1.13.0&python=3.13&gist=e0652be937c8cfc1d56a5d4dafa0bd89

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 this pull request may close these issues.

2 participants