mirror of
https://github.com/davidhalter/typeshed.git
synced 2026-05-24 17:28:40 +08:00
WebOb: Complete the stubs and activate stricter pyright config (#11460)
This commit is contained in:
@@ -137,6 +137,17 @@ webob.request.AdhocAttrMixin.__setattr__
|
||||
# make sense to annotate them and pretend they're part of the API.
|
||||
webob.request.BaseRequest.__init__
|
||||
|
||||
# We needed to add a dummy *_: _P.args in order to support ParamSpec
|
||||
webob.dec.wsgify.middleware
|
||||
webob.dec._MiddlewareFactory.__call__
|
||||
|
||||
# We renamed some of the arguments in positional only overloads for greater
|
||||
# clarity about what the arguments mean, stubtest should probably be a bit
|
||||
# more lenient here, since this is only unsafe if that overload accepts
|
||||
# arbitrary named arguments, that could overlap with the argument in that
|
||||
# specific position
|
||||
webob.dec.wsgify.__call__
|
||||
|
||||
# Error: is not present at runtime
|
||||
# =============================
|
||||
# This attribute is there to help mypy type narrow NoVars based on its static
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from _typeshed.wsgi import StartResponse, WSGIApplication, WSGIEnvironment
|
||||
from collections.abc import Iterable # noqa: F401
|
||||
from typing_extensions import assert_type
|
||||
|
||||
from webob.dec import _AnyResponse, wsgify
|
||||
from webob.request import Request
|
||||
|
||||
|
||||
class App:
|
||||
@wsgify
|
||||
def __call__(self, request: Request) -> str:
|
||||
return "hello"
|
||||
|
||||
|
||||
env: WSGIEnvironment = {}
|
||||
start_response: StartResponse = lambda x, y, z=None: lambda b: None
|
||||
application: WSGIApplication = lambda e, s: [b""]
|
||||
request: Request = Request(env)
|
||||
|
||||
x = App()
|
||||
# since we wsgified our __call__ we should now be a valid WSGIApplication
|
||||
application = x
|
||||
assert_type(x(env, start_response), "Iterable[bytes]")
|
||||
# currently we lose the exact response type, but that should be fine in
|
||||
# most use-cases, since middlewares operate on an application level, not
|
||||
# on these raw intermediary functions
|
||||
assert_type(x(request), _AnyResponse)
|
||||
|
||||
# accessing the method from the class should work as you expect it to
|
||||
assert_type(App.__call__(x, env, start_response), "Iterable[bytes]")
|
||||
assert_type(App.__call__(x, request), _AnyResponse)
|
||||
|
||||
|
||||
# but we can also wrap it with a middleware that expects to deal with requests
|
||||
class Middleware:
|
||||
@wsgify.middleware
|
||||
def restrict_ip(self, req: Request, app: WSGIApplication, ips: list[str]) -> WSGIApplication:
|
||||
return app
|
||||
|
||||
__call__ = restrict_ip(x, ips=["127.0.0.1"])
|
||||
|
||||
|
||||
# and we still end up with a valid WSGIApplication
|
||||
m = Middleware()
|
||||
application = m
|
||||
assert_type(m(env, start_response), "Iterable[bytes]")
|
||||
assert_type(m(request), _AnyResponse)
|
||||
|
||||
|
||||
# the same should work with plain functions
|
||||
@wsgify
|
||||
def app(request: Request) -> str:
|
||||
return "hello"
|
||||
|
||||
|
||||
application = app
|
||||
assert_type(app, "wsgify[Request, []]")
|
||||
assert_type(app(env, start_response), "Iterable[bytes]")
|
||||
assert_type(app(request), _AnyResponse)
|
||||
# FIXME: For some reason pyright complains here with
|
||||
# mismatch: expected "wsgify[Request, ()]" but received "wsgify[Request, ()]"
|
||||
# can you spot the difference?
|
||||
# assert_type(app(application), "wsgify[Request, []]")
|
||||
application = app(application)
|
||||
|
||||
|
||||
@wsgify.middleware
|
||||
def restrict_ip(req: Request, app: WSGIApplication, ips: list[str]) -> WSGIApplication:
|
||||
return app
|
||||
|
||||
|
||||
@restrict_ip(ips=["127.0.0.1"])
|
||||
@wsgify
|
||||
def m_app(request: Request) -> str:
|
||||
return "hello"
|
||||
|
||||
|
||||
application = m_app
|
||||
# FIXME: same weird pyright error where it complains about the types
|
||||
# being the same
|
||||
# assert_type(m_app, "wsgify[Request, [WSGIApplication]]")
|
||||
assert_type(m_app(env, start_response), "Iterable[bytes]")
|
||||
assert_type(m_app(request), _AnyResponse)
|
||||
# FIXME: and also here
|
||||
# assert_type(m_app(application), "wsgify[Request, [WSGIApplication]]")
|
||||
application = m_app(application)
|
||||
|
||||
|
||||
# custom request
|
||||
class MyRequest(Request):
|
||||
pass
|
||||
|
||||
|
||||
@wsgify(RequestClass=MyRequest)
|
||||
def my_request_app(request: MyRequest) -> None:
|
||||
pass
|
||||
|
||||
|
||||
application = my_request_app
|
||||
assert_type(my_request_app, "wsgify[MyRequest, []]")
|
||||
|
||||
|
||||
# we are allowed to accept a less specific request class
|
||||
@wsgify(RequestClass=MyRequest)
|
||||
def valid_request_app(request: Request) -> None:
|
||||
pass
|
||||
|
||||
|
||||
# but the opposite is not allowed
|
||||
@wsgify # type:ignore
|
||||
def invalid_request_app(request: MyRequest) -> None:
|
||||
pass
|
||||
|
||||
|
||||
# we can't really make passing extra arguments directly work
|
||||
# otherwise we have to give up most of our type safety for
|
||||
# something that should only be used through wsgify.middleware
|
||||
wsgify(args=(1,)) # type:ignore
|
||||
wsgify(kwargs={"ips": ["127.0.0.1"]}) # type:ignore
|
||||
Reference in New Issue
Block a user