-
-
Notifications
You must be signed in to change notification settings - Fork 935
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
Add ContentType
generic to Response
#2547
base: master
Are you sure you want to change the base?
Conversation
@@ -27,7 +27,7 @@ classifiers = [ | |||
] | |||
dependencies = [ | |||
"anyio>=3.4.0,<5", | |||
"typing_extensions>=3.10.0; python_version < '3.10'", | |||
"typing_extensions>=4.4.0; python_version < '3.13'", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
default=
was introduced in this version: https://typing-extensions.readthedocs.io/en/latest/#typing_extensions.TypeVar
afcbdb0
to
ba8a89b
Compare
@@ -4,7 +4,7 @@ | |||
# Testing | |||
coverage==7.4.3 | |||
importlib-metadata==7.0.1 | |||
mypy==1.8.0 | |||
mypy==1.9.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Last release supports default=
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think JsonResponse and others don’t actually accept Any, they accept a limited set of types. Is that codified in this PR? If it is I’m missing it.
Content = typing.Union[str, bytes] | ||
SyncContentStream = typing.Iterable[Content] | ||
AsyncContentStream = typing.AsyncIterable[Content] | ||
_Content = typing.Union[str, bytes] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to confirm, this is a breaking change right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah, it is...
You are right, but I'm not changing that in this PR. And I think there was an attempt to change the type, but it failed, and we had to revert. |
Content = TypeVar("Content", default=typing.Any) | ||
|
||
|
||
class Response(typing.Generic[Content]): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to me like the reality is that the default should be str | bytes
given what Response
accepts. Then subclasses like StreamingResponse
should override this generic with whatever they accept. If we make the default typing.Any
then Response
is not correctly typed. Since we're setting the default in this PR we might as well choose an appropriate value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not changing this in this PR. There was a previous attempt to change the type to str | bytes | None
, and it was reverted - I don't remember the reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. It would be good to find that reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I see. So yes the right thing to do would be to make a BaseResponse
and make Response
just one of the many concrete classes:
_ResponseDataT = TypeVar('_ResponseDataT')
class BaseResponse(Generic[_ResponseDataT]):
def render(self, data: _ResponseDataT | None) -> bytes:
raise NotImplementedError
class Response(BaseResponse[str | bytes]):
...
_JsonData = str | bytes | float | int | None | ...
class JsonResponse(BaseResponse[_JsonData]):
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But the Response
class does have the .render()
method... So I don't see how I'd that? I can't make the classes stop inheriting Response
, because that would be a breaking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is also what I think is unreasonable about Starlette. For example, StreamResponse and FileReponse do not use render, but they inherit this method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you suggesting adding a breaking change then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. 😊
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we do some naming to make it a non-breaking change or minimize the breakage? We could leave the render() method on the base class but mark it as deprecated vi a warning. Adding a BaseResponse class is not a breaking change. I think removing the render() method from eg StreamingResponse (because it inherits from BaseResponse and not Response) would also be okay.
At the time that PR was implemented the
default=
parameter was not available onTypeVar
, and mypy didn't support it.Now it's possible to achieve what that PR intended.