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": [