Open vytas7 opened 1 month ago
Hi. My POC on the issue here: https://github.com/jkmnt/falcon/tree/generics_poc Diff: https://github.com/jkmnt/falcon/commit/a868ff58ac54821a70405e4b50a6d72d56332c99
I typed wsgi App class with generics. The concrete types are seems to be correctly inferred. The only problem was with the standard App: Request/Response are Unknown in this case and it's bad! I solved it by introducing the SimpleApp type alias declaring standard App[Request, Response]. (It should be done the opposite way of course: App and CustomApp)
I agree it's should be deferred for 4.1+. Too much changes for 4.0 )
BTW, there was some (missed by mypy) errors reported by pyright. I think I'll try to fix some and submit a separate PR.
BTW, there was some (missed by mypy) errors reported by pyright. I think I'll try to fix some and submit a separate PR.
that would be great. Feel free to also open an issue when you do that. also if you know tox and github action we could add a gate with it too, so that it stays tested
I have just created an issue to track the Pyright gate: #2373.
And echoing @CaselIT, re PR, your help is really appreciated, because we are no huge experts of typing (OK, @CaselIT maybe is, but I am definitely not :sweat_smile:).
Edit: and as to typing extensions, I don't want Falcon to depend on any 3rd party runtime packages for the majority of use cases. We can add it as a conditional requirement for Python versions older than 3.X
where they total less than Y
% of PyPI downloads. Y
being, say, 30% or so.
Another thing related to the typing:
I noticed the middleware type is just the object
. It's the user-facing interface so imho typing should be better.
I suggest something like
class MwWithReqHandler(Protocol):
def process_request(self, req: Request, resp: Response) -> None: ...
class MwWithRespHandler(Protocol):
def process_response(self, req: Request, resp: Response, resource: object, req_succeeded: bool) -> None: ...
class MwWithProcessHandler(Protocol):
def process_resource(self, req: Request, resp: Response, resource: object, params: dict[str, Any]) -> None: ...
Middleware = Union[MwWithReqHandler, MwWithRespHandler, MwWithProcessHandler]
This is not perfect but better than nothing. The bad thing is that it's enough to have one conforming (with others nonconformin) methods to pass the type check. The good thing: the user may inherit it's middleware class from these protocols (kind of abc but w/o runtime overhead) and get full typesafety.
class MyMiddleware(MwWithReqHandler, MwWithRespHandler):
def process_req(...)
def process_resp(...)
I thought about it while doing that part, but if we type it as Middleware = Union[MwWithReqHandler, MwWithRespHandler, MwWithProcessHandler]
won't that require that all 3 method are provided?
If not then that's for sure an option!
No afaik its enough to satisfy just one protocol of the union.
Seems like a good option then!
it probably makes sense to open a new issue with it. If you can work on a PR it would be great!
Yes, let's divide and conquer these typing improvements. Like improvement of the middleware protocols could be one issue/PR :slightly_smiling_face:
it probably makes sense to open a new issue with it. If you can work on a PR it would be great!
Ok, I'll draft the PR then.
I managed to type this all up at https://github.com/falconry/falcon/issues/2397 before looking for this, but looks like I came to the same conclusions 🙈
Thanks!
Discussed in https://github.com/falconry/falcon/discussions/2370