Skip to content

Commit 6e7b2a4

Browse files
authored
Merge pull request #598 from AnswerDotAI/apirouter-refactor
Made route functions accesible from an instance of APIRouter as well …
2 parents 72dbc04 + 13b58d8 commit 6e7b2a4

File tree

3 files changed

+32
-10
lines changed

3 files changed

+32
-10
lines changed

fasthtml/_modidx.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
'fasthtml.components.sse_message': ('api/components.html#sse_message', 'fasthtml/components.py')},
2626
'fasthtml.core': { 'fasthtml.core.APIRouter': ('api/core.html#apirouter', 'fasthtml/core.py'),
2727
'fasthtml.core.APIRouter.__call__': ('api/core.html#apirouter.__call__', 'fasthtml/core.py'),
28+
'fasthtml.core.APIRouter.__getattr__': ('api/core.html#apirouter.__getattr__', 'fasthtml/core.py'),
2829
'fasthtml.core.APIRouter.__init__': ('api/core.html#apirouter.__init__', 'fasthtml/core.py'),
2930
'fasthtml.core.APIRouter._wrap_func': ('api/core.html#apirouter._wrap_func', 'fasthtml/core.py'),
3031
'fasthtml.core.APIRouter.to_app': ('api/core.html#apirouter.to_app', 'fasthtml/core.py'),

fasthtml/core.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -658,8 +658,9 @@ class RouteFuncs:
658658
def __init__(self): super().__setattr__('_funcs', {})
659659
def __setattr__(self, name, value): self._funcs[name] = value
660660
def __getattr__(self, name):
661-
if name in all_meths: raise KeyError("Route functions with HTTP Names are not accessible here")
662-
return self._funcs[name]
661+
if name in all_meths: raise AttributeError("Route functions with HTTP Names are not accessible here")
662+
try: return self._funcs[name]
663+
except KeyError: raise AttributeError(f"No route named {name} found in route functions")
663664
def __dir__(self): return list(self._funcs.keys())
664665

665666
# %% ../nbs/api/00_core.ipynb
@@ -686,6 +687,10 @@ def f(func):
686687
self.routes.append((func, p, methods, name, include_in_schema, body_wrap))
687688
return wrapped
688689
return f(path) if callable(path) else f
690+
691+
def __getattr__(self, name):
692+
try: return getattr(self.rt_funcs, name)
693+
except AttributeError: return super().__getattr__(self, name)
689694

690695
def to_app(self, app):
691696
"Add routes to `app`"

nbs/api/00_core.ipynb

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@
131131
{
132132
"data": {
133133
"text/plain": [
134-
"datetime.datetime(2024, 12, 4, 14, 0)"
134+
"datetime.datetime(2024, 12, 10, 14, 0)"
135135
]
136136
},
137137
"execution_count": null,
@@ -2423,13 +2423,13 @@
24232423
"name": "stdout",
24242424
"output_type": "stream",
24252425
"text": [
2426-
"Set to 2024-12-04 09:20:09.428221\n"
2426+
"Set to 2024-12-10 09:54:42.331681\n"
24272427
]
24282428
},
24292429
{
24302430
"data": {
24312431
"text/plain": [
2432-
"'Session time: 2024-12-04 09:20:09.428221'"
2432+
"'Session time: 2024-12-10 09:54:42.331681'"
24332433
]
24342434
},
24352435
"execution_count": null,
@@ -2620,8 +2620,9 @@
26202620
" def __init__(self): super().__setattr__('_funcs', {})\n",
26212621
" def __setattr__(self, name, value): self._funcs[name] = value\n",
26222622
" def __getattr__(self, name): \n",
2623-
" if name in all_meths: raise KeyError(\"Route functions with HTTP Names are not accessible here\")\n",
2624-
" return self._funcs[name]\n",
2623+
" if name in all_meths: raise AttributeError(\"Route functions with HTTP Names are not accessible here\")\n",
2624+
" try: return self._funcs[name]\n",
2625+
" except KeyError: raise AttributeError(f\"No route named {name} found in route functions\")\n",
26252626
" def __dir__(self): return list(self._funcs.keys())"
26262627
]
26272628
},
@@ -2656,6 +2657,10 @@
26562657
" self.routes.append((func, p, methods, name, include_in_schema, body_wrap))\n",
26572658
" return wrapped\n",
26582659
" return f(path) if callable(path) else f\n",
2660+
" \n",
2661+
" def __getattr__(self, name):\n",
2662+
" try: return getattr(self.rt_funcs, name)\n",
2663+
" except AttributeError: return super().__getattr__(self, name)\n",
26592664
"\n",
26602665
" def to_app(self, app):\n",
26612666
" \"Add routes to `app`\"\n",
@@ -2720,8 +2725,14 @@
27202725
"metadata": {},
27212726
"outputs": [],
27222727
"source": [
2723-
"assert str(ar.rt_funcs.index) == '/'\n",
27242728
"assert str(yoyo) == '/yoyo'\n",
2729+
"# ensure route functions are properly discoverable on `APIRouter` and `APIRouter.rt_funcs`\n",
2730+
"assert ar.prefix == ''\n",
2731+
"assert str(ar.rt_funcs.index) == '/'\n",
2732+
"assert str(ar.index) == '/'\n",
2733+
"with ExceptionExpected(): ar.blah()\n",
2734+
"with ExceptionExpected(): ar.rt_funcs.blah()\n",
2735+
"# ensure any route functions named using an HTTPMethod are not discoverable via `rt_funcs`\n",
27252736
"assert \"get\" not in ar.rt_funcs._funcs.keys()"
27262737
]
27272738
},
@@ -2806,8 +2817,13 @@
28062817
"metadata": {},
28072818
"outputs": [],
28082819
"source": [
2809-
"assert str(ar2.rt_funcs.index) == '/products/'\n",
28102820
"assert str(yoyo) == '/products/yoyo'\n",
2821+
"assert ar2.prefix == '/products'\n",
2822+
"assert str(ar2.rt_funcs.index) == '/products/'\n",
2823+
"assert str(ar2.index) == '/products/'\n",
2824+
"assert str(ar.index) == '/'\n",
2825+
"with ExceptionExpected(): ar2.blah()\n",
2826+
"with ExceptionExpected(): ar2.rt_funcs.blah()\n",
28112827
"assert \"get\" not in ar2.rt_funcs._funcs.keys()"
28122828
]
28132829
},
@@ -2933,7 +2949,7 @@
29332949
{
29342950
"data": {
29352951
"text/plain": [
2936-
"'Cookie was set at time 09:20:09.542569'"
2952+
"'Cookie was set at time 09:54:43.255286'"
29372953
]
29382954
},
29392955
"execution_count": null,

0 commit comments

Comments
 (0)