|
9 | 9 | 'flat_xt', 'Beforeware', 'EventStream', 'signal_shutdown', 'uri', 'decode_uri', 'flat_tuple', 'noop_body', |
10 | 10 | 'respond', 'is_full_page', 'Redirect', 'get_key', 'qp', 'def_hdrs', 'Lifespan', 'FastHTML', 'HostRoute', |
11 | 11 | 'nested_name', 'serve', 'Client', 'RouteFuncs', 'APIRouter', 'cookie', 'reg_re_param', 'StaticNoCache', |
12 | | - 'MiddlewareBase', 'FtResponse', 'unqid'] |
| 12 | + 'add_sig_param', 'into', 'MiddlewareBase', 'FtResponse', 'unqid'] |
13 | 13 |
|
14 | 14 | # %% ../nbs/api/00_core.ipynb #23503b9e |
15 | 15 | import json,uuid,inspect,types,asyncio,inspect,random,contextlib,httpx,itsdangerous,uvicorn |
@@ -163,12 +163,15 @@ async def parse_form(req: Request) -> FormData: |
163 | 163 | return await req.json() if ctype == 'application/json' else await req.form() |
164 | 164 |
|
165 | 165 |
|
166 | | -# %% ../nbs/api/00_core.ipynb #089fe388 |
| 166 | +# %% ../nbs/api/00_core.ipynb #0caedd04 |
167 | 167 | async def _from_body(conn, p, data): |
168 | 168 | "Create an instance of the annotated type from pre-parsed `data`" |
169 | 169 | anno = p.annotation |
170 | 170 | ctor = getattr(anno, '__from_request__', None) |
171 | | - if ctor: return await maybe_await(ctor(data, conn)) |
| 171 | + if ctor: |
| 172 | + ps = {k:v for k,v in _params(ctor).items() if k != 'cls'} |
| 173 | + kwargs = await _find_ps(conn, data, conn.headers, ps) |
| 174 | + return await maybe_await(ctor(**kwargs)) |
172 | 175 | d = _annotations(anno) |
173 | 176 | cargs = {k: _form_arg(k, v, d) for k, v in data.items() if not d or k in d} |
174 | 177 | return anno(**cargs) |
@@ -260,6 +263,7 @@ def flat_xt(lst): |
260 | 263 | # %% ../nbs/api/00_core.ipynb #aacff5ac |
261 | 264 | class Beforeware: |
262 | 265 | def __init__(self, f, skip=None): self.f,self.skip = f,skip or [] |
| 266 | + def __repr__(self): return f'Beforeware({self.f}, skip={self.skip})' |
263 | 267 |
|
264 | 268 | # %% ../nbs/api/00_core.ipynb #78c3c357 |
265 | 269 | async def _handle(f, *args, **kwargs): |
@@ -904,6 +908,43 @@ def file_response(self, *args, **kwargs): |
904 | 908 | resp.headers.setdefault("Cache-Control", "no-cache") |
905 | 909 | return resp |
906 | 910 |
|
| 911 | +# %% ../nbs/api/00_core.ipynb #7189daf8 |
| 912 | +from functools import wraps |
| 913 | +from inspect import signature, isawaitable |
| 914 | + |
| 915 | +# %% ../nbs/api/00_core.ipynb #7eed23b7 |
| 916 | +def add_sig_param(f, name, typ=NoneType, kind=Parameter.KEYWORD_ONLY, default=Parameter.empty): |
| 917 | + "Add a parameter to a function's signature" |
| 918 | + sig = signature(f) |
| 919 | + if name in sig.parameters: return f |
| 920 | + kw = {} if default is Parameter.empty else {'default': default} |
| 921 | + new_params = list(sig.parameters.values()) + [Parameter(name, kind, **kw)] |
| 922 | + f.__signature__ = sig.replace(parameters=new_params) |
| 923 | + f.__annotations__[name] = typ |
| 924 | + return f |
| 925 | + |
| 926 | +# %% ../nbs/api/00_core.ipynb #9e5a9e88 |
| 927 | +class into: |
| 928 | + "Decorator to pass a route's return value into `func`, with keyword params added to the route signature" |
| 929 | + def __init__(self, func): |
| 930 | + self.func = func |
| 931 | + self.params = {k:p for k,p in signature(func).parameters.items() |
| 932 | + if p.kind not in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD)} |
| 933 | + |
| 934 | + def __call__(self, f): |
| 935 | + @wraps(f) |
| 936 | + async def _inner(*args, **kw): |
| 937 | + extra = {k: kw.pop(k, None) for k in self.params} |
| 938 | + res = f(*args, **kw) |
| 939 | + if isawaitable(res): res = await res |
| 940 | + res = self.func(*tuplify(res), **extra) |
| 941 | + if isawaitable(res): res = await res |
| 942 | + return res |
| 943 | + for k,p in self.params.items(): |
| 944 | + anno = p.annotation if p.annotation is not Parameter.empty else NoneType |
| 945 | + add_sig_param(_inner, k, anno, default=p.default) |
| 946 | + return _inner |
| 947 | + |
907 | 948 | # %% ../nbs/api/00_core.ipynb #1960d7ff |
908 | 949 | class MiddlewareBase: |
909 | 950 | async def __call__(self, scope, receive, send) -> None: |
|
0 commit comments