diff --git a/fasthtml/core.py b/fasthtml/core.py index 796df0f7..995908de 100644 --- a/fasthtml/core.py +++ b/fasthtml/core.py @@ -73,21 +73,22 @@ def _get_htmx(h): return HtmxHeaders(**res) # %% ../nbs/api/00_core.ipynb -def _mk_list(t, v): return [t(o) for o in v] +def _mk_list(t, v): return [t(o) for o in listify(v)] # %% ../nbs/api/00_core.ipynb fh_cfg = AttrDict(indent=True) # %% ../nbs/api/00_core.ipynb -def _fix_anno(t): +def _fix_anno(t, o): "Create appropriate callable type for casting a `str` to type `t` (or first type in `t` if union)" origin = get_origin(t) if origin is Union or origin is UnionType or origin in (list,List): t = first(o for o in get_args(t) if o!=type(None)) d = {bool: str2bool, int: str2int, date: str2date, UploadFile: noop} res = d.get(t, t) - if origin in (list,List): return partial(_mk_list, res) - return lambda o: res(o[-1]) if isinstance(o,(list,tuple)) else res(o) + if origin in (list,List): return _mk_list(res, o) + if not isinstance(o, (str,list,tuple)): return o + return res(o[-1]) if isinstance(o,(list,tuple)) else res(o) # %% ../nbs/api/00_core.ipynb def _form_arg(k, v, d): @@ -97,7 +98,7 @@ def _form_arg(k, v, d): # This is the type we want to cast `v` to anno = d.get(k, None) if not anno: return v - return _fix_anno(anno)(v) + return _fix_anno(anno, v) # %% ../nbs/api/00_core.ipynb @dataclass @@ -195,9 +196,8 @@ async def _find_p(req, arg:str, p:Parameter): # If we have a default, return that if we have no value if res in (empty,None): res = p.default # We can cast str and list[str] to types; otherwise just return what we have - if not isinstance(res, (list,str)) or anno is empty: return res - anno = _fix_anno(anno) - try: return anno(res) + if anno is empty: return res + try: return _fix_anno(anno, res) except ValueError: raise HTTPException(404, req.url.path) from None async def _wrap_req(req, params): @@ -241,8 +241,7 @@ def _find_wsp(ws, data, hdrs, arg:str, p:Parameter): if res is empty or res is None: res = p.default # We can cast str and list[str] to types; otherwise just return what we have if not isinstance(res, (list,str)) or anno is empty: return res - anno = _fix_anno(anno) - return [anno(o) for o in res] if isinstance(res,list) else anno(res) + return [_fix_anno(anno, o) for o in res] if isinstance(res,list) else _fix_anno(anno, res) def _wrap_ws(ws, data, params): hdrs = {k.lower().replace('-','_'):v for k,v in data.pop('HEADERS', {}).items()} diff --git a/nbs/api/00_core.ipynb b/nbs/api/00_core.ipynb index 936d1f3f..9081d720 100644 --- a/nbs/api/00_core.ipynb +++ b/nbs/api/00_core.ipynb @@ -131,7 +131,7 @@ { "data": { "text/plain": [ - "datetime.datetime(2024, 10, 16, 14, 0)" + "datetime.datetime(2024, 10, 19, 14, 0)" ] }, "execution_count": null, @@ -280,7 +280,7 @@ "outputs": [], "source": [ "#| export\n", - "def _mk_list(t, v): return [t(o) for o in v]" + "def _mk_list(t, v): return [t(o) for o in listify(v)]" ] }, { @@ -310,15 +310,16 @@ "outputs": [], "source": [ "#| export\n", - "def _fix_anno(t):\n", + "def _fix_anno(t, o):\n", " \"Create appropriate callable type for casting a `str` to type `t` (or first type in `t` if union)\"\n", " origin = get_origin(t)\n", " if origin is Union or origin is UnionType or origin in (list,List):\n", " t = first(o for o in get_args(t) if o!=type(None))\n", " d = {bool: str2bool, int: str2int, date: str2date, UploadFile: noop}\n", " res = d.get(t, t)\n", - " if origin in (list,List): return partial(_mk_list, res)\n", - " return lambda o: res(o[-1]) if isinstance(o,(list,tuple)) else res(o)" + " if origin in (list,List): return _mk_list(res, o)\n", + " if not isinstance(o, (str,list,tuple)): return o\n", + " return res(o[-1]) if isinstance(o,(list,tuple)) else res(o)" ] }, { @@ -328,12 +329,12 @@ "metadata": {}, "outputs": [], "source": [ - "test_eq(_fix_anno(Union[str,None])('a'), 'a')\n", - "test_eq(_fix_anno(float)(0.9), 0.9)\n", - "test_eq(_fix_anno(int)('1'), 1)\n", - "test_eq(_fix_anno(int)(['1','2']), 2)\n", - "test_eq(_fix_anno(list[int])(['1','2']), [1,2])\n", - "test_eq(_fix_anno(list[int])('1'), [1])" + "test_eq(_fix_anno(Union[str,None], 'a'), 'a')\n", + "test_eq(_fix_anno(float, 0.9), 0.9)\n", + "test_eq(_fix_anno(int, '1'), 1)\n", + "test_eq(_fix_anno(int, ['1','2']), 2)\n", + "test_eq(_fix_anno(list[int], ['1','2']), [1,2])\n", + "test_eq(_fix_anno(list[int], '1'), [1])" ] }, { @@ -351,7 +352,7 @@ " # This is the type we want to cast `v` to\n", " anno = d.get(k, None)\n", " if not anno: return v\n", - " return _fix_anno(anno)(v)" + " return _fix_anno(anno, v)" ] }, { @@ -651,9 +652,8 @@ " # If we have a default, return that if we have no value\n", " if res in (empty,None): res = p.default\n", " # We can cast str and list[str] to types; otherwise just return what we have\n", - " if not isinstance(res, (list,str)) or anno is empty: return res\n", - " anno = _fix_anno(anno)\n", - " try: return anno(res)\n", + " if anno is empty: return res\n", + " try: return _fix_anno(anno, res)\n", " except ValueError: raise HTTPException(404, req.url.path) from None\n", "\n", "async def _wrap_req(req, params):\n", @@ -805,8 +805,7 @@ " if res is empty or res is None: res = p.default\n", " # We can cast str and list[str] to types; otherwise just return what we have\n", " if not isinstance(res, (list,str)) or anno is empty: return res\n", - " anno = _fix_anno(anno)\n", - " return [anno(o) for o in res] if isinstance(res,list) else anno(res)\n", + " return [_fix_anno(anno, o) for o in res] if isinstance(res,list) else _fix_anno(anno, res)\n", "\n", "def _wrap_ws(ws, data, params):\n", " hdrs = {k.lower().replace('-','_'):v for k,v in data.pop('HEADERS', {}).items()}\n", @@ -2233,6 +2232,27 @@ "print(res.text)" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "28a99667", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "200\n", + "content1\n" + ] + } + ], + "source": [ + "res = cli.post('/uploads', files=[files[0]])\n", + "print(res.status_code)\n", + "print(res.text)" + ] + }, { "cell_type": "code", "execution_count": null, @@ -2243,13 +2263,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "Set to 2024-10-16 15:38:25.588198\n" + "Set to 2024-10-19 15:14:43.250650\n" ] }, { "data": { "text/plain": [ - "'Session time: 2024-10-16 15:38:25.588198'" + "'Session time: 2024-10-19 15:14:43.250650'" ] }, "execution_count": null, @@ -2480,7 +2500,7 @@ { "data": { "text/plain": [ - "'Cookie was set at time 15:38:25.962118'" + "'Cookie was set at time 15:14:43.683602'" ] }, "execution_count": null, diff --git a/nbs/api/04_pico.ipynb b/nbs/api/04_pico.ipynb index 279b078b..64fc9285 100644 --- a/nbs/api/04_pico.ipynb +++ b/nbs/api/04_pico.ipynb @@ -73,7 +73,6 @@ "data": { "text/html": [ "\n", - "\n", "\n" ], "text/plain": [