|
131 | 131 | { |
132 | 132 | "data": { |
133 | 133 | "text/plain": [ |
134 | | - "datetime.datetime(2024, 11, 4, 14, 0)" |
| 134 | + "datetime.datetime(2024, 12, 3, 14, 0)" |
135 | 135 | ] |
136 | 136 | }, |
137 | 137 | "execution_count": null, |
|
1229 | 1229 | { |
1230 | 1230 | "data": { |
1231 | 1231 | "text/plain": [ |
1232 | | - "'77486da1-c613-48be-80c4-9cae89eeec48'" |
| 1232 | + "'7eb16e0e-f1b9-4266-98fa-2d84c34f86fc'" |
1233 | 1233 | ] |
1234 | 1234 | }, |
1235 | 1235 | "execution_count": null, |
|
2423 | 2423 | "name": "stdout", |
2424 | 2424 | "output_type": "stream", |
2425 | 2425 | "text": [ |
2426 | | - "Set to 2024-11-04 15:30:23.038930\n" |
| 2426 | + "Set to 2024-12-03 13:51:41.200169\n" |
2427 | 2427 | ] |
2428 | 2428 | }, |
2429 | 2429 | { |
2430 | 2430 | "data": { |
2431 | 2431 | "text/plain": [ |
2432 | | - "'Session time: 2024-11-04 15:30:23.038930'" |
| 2432 | + "'Session time: 2024-12-03 13:51:41.200169'" |
2433 | 2433 | ] |
2434 | 2434 | }, |
2435 | 2435 | "execution_count": null, |
|
2608 | 2608 | "## APIRouter" |
2609 | 2609 | ] |
2610 | 2610 | }, |
| 2611 | + { |
| 2612 | + "cell_type": "code", |
| 2613 | + "execution_count": null, |
| 2614 | + "id": "d5223a9a", |
| 2615 | + "metadata": {}, |
| 2616 | + "outputs": [], |
| 2617 | + "source": [ |
| 2618 | + "#| export\n", |
| 2619 | + "class RouteFuncs:\n", |
| 2620 | + " def __init__(self): super().__setattr__('_funcs', {})\n", |
| 2621 | + " def __setattr__(self, name, value): self._funcs[name] = value\n", |
| 2622 | + " 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", |
| 2625 | + " def __dir__(self): return list(self._funcs.keys())" |
| 2626 | + ] |
| 2627 | + }, |
2611 | 2628 | { |
2612 | 2629 | "cell_type": "code", |
2613 | 2630 | "execution_count": null, |
|
2618 | 2635 | "#| export\n", |
2619 | 2636 | "class APIRouter:\n", |
2620 | 2637 | " \"Add routes to an app\"\n", |
2621 | | - " def __init__(self): self.routes,self.wss = [],[]\n", |
| 2638 | + " def __init__(self, prefix:str|None=None): \n", |
| 2639 | + " self.routes,self.wss = [],[]\n", |
| 2640 | + " self.rt_funcs = RouteFuncs() # Store wrapped functions for discoverability\n", |
| 2641 | + " self.prefix = prefix if prefix else \"\"\n", |
2622 | 2642 | "\n", |
2623 | | - " def __call__(self:FastHTML, path:str=None, methods=None, name=None, include_in_schema=True, body_wrap=noop_body):\n", |
| 2643 | + " def _wrap_func(self, func, path=None):\n", |
| 2644 | + " name = func.__name__\n", |
| 2645 | + " \n", |
| 2646 | + " class _lf:\n", |
| 2647 | + " def __init__(s): update_wrapper(s, func)\n", |
| 2648 | + " def __call__(s, *args, **kw): return func(*args, **kw)\n", |
| 2649 | + " def to(s, **kw): return qp(path, **kw)\n", |
| 2650 | + " def __str__(s): return path\n", |
| 2651 | + " \n", |
| 2652 | + " wrapped = _lf()\n", |
| 2653 | + " wrapped.__routename__ = name\n", |
| 2654 | + " # If you are using the def get or def post method names, this approach is not supported\n", |
| 2655 | + " if name not in all_meths: setattr(self.rt_funcs, name, wrapped)\n", |
| 2656 | + " return wrapped\n", |
| 2657 | + "\n", |
| 2658 | + " def __call__(self, path:str=None, methods=None, name=None, include_in_schema=True, body_wrap=noop_body):\n", |
2624 | 2659 | " \"Add a route at `path`\"\n", |
2625 | | - " def f(func): return self.routes.append((func, path,methods,name,include_in_schema,body_wrap))\n", |
| 2660 | + " def f(func):\n", |
| 2661 | + " p = self.prefix + (\"/\" + ('' if path.__name__=='index' else func.__name__) if callable(path) else path)\n", |
| 2662 | + " wrapped = self._wrap_func(func, p)\n", |
| 2663 | + " self.routes.append((func, p, methods, name, include_in_schema, body_wrap))\n", |
| 2664 | + " return wrapped\n", |
2626 | 2665 | " return f(path) if callable(path) else f\n", |
2627 | 2666 | "\n", |
2628 | 2667 | " def to_app(self, app):\n", |
2629 | 2668 | " \"Add routes to `app`\"\n", |
2630 | 2669 | " for args in self.routes: app._add_route(*args)\n", |
2631 | | - " for args in self.wss : app._add_ws (*args)\n", |
2632 | | - "\n", |
2633 | | - " def ws(self:FastHTML, path:str, conn=None, disconn=None, name=None, middleware=None):\n", |
| 2670 | + " for args in self.wss: app._add_ws(*args)\n", |
| 2671 | + " \n", |
| 2672 | + " def ws(self, path:str, conn=None, disconn=None, name=None, middleware=None):\n", |
2634 | 2673 | " \"Add a websocket route at `path`\"\n", |
2635 | 2674 | " def f(func=noop): return self.wss.append((func, path, conn, disconn, name, middleware))\n", |
2636 | 2675 | " return f" |
|
2663 | 2702 | "def show_host(req): return req.headers['host']\n", |
2664 | 2703 | "@ar\n", |
2665 | 2704 | "def yoyo(): return 'a yoyo'\n", |
| 2705 | + "@ar\n", |
| 2706 | + "def index(): return \"home page\"\n", |
2666 | 2707 | "\n", |
2667 | 2708 | "@ar.ws(\"/ws\")\n", |
2668 | 2709 | "def ws(self, msg:str): return f\"Message text was: {msg}\"" |
2669 | 2710 | ] |
2670 | 2711 | }, |
| 2712 | + { |
| 2713 | + "cell_type": "code", |
| 2714 | + "execution_count": null, |
| 2715 | + "id": "8c265ff8", |
| 2716 | + "metadata": {}, |
| 2717 | + "outputs": [], |
| 2718 | + "source": [ |
| 2719 | + "assert str(ar.rt_funcs.index) == '/'\n", |
| 2720 | + "assert str(yoyo) == '/yoyo'\n", |
| 2721 | + "assert \"get\" not in ar.rt_funcs._funcs.keys()" |
| 2722 | + ] |
| 2723 | + }, |
2671 | 2724 | { |
2672 | 2725 | "cell_type": "code", |
2673 | 2726 | "execution_count": null, |
|
2708 | 2761 | " assert data == 'Message text was: Hi!'" |
2709 | 2762 | ] |
2710 | 2763 | }, |
| 2764 | + { |
| 2765 | + "cell_type": "code", |
| 2766 | + "execution_count": null, |
| 2767 | + "id": "02a4e649", |
| 2768 | + "metadata": {}, |
| 2769 | + "outputs": [], |
| 2770 | + "source": [ |
| 2771 | + "ar2 = APIRouter(\"/products\")" |
| 2772 | + ] |
| 2773 | + }, |
| 2774 | + { |
| 2775 | + "cell_type": "code", |
| 2776 | + "execution_count": null, |
| 2777 | + "id": "151b9e3c", |
| 2778 | + "metadata": {}, |
| 2779 | + "outputs": [], |
| 2780 | + "source": [ |
| 2781 | + "@ar2(\"/hi\")\n", |
| 2782 | + "def get(): return 'Hi there'\n", |
| 2783 | + "@ar2(\"/hi\")\n", |
| 2784 | + "def post(): return 'Postal'\n", |
| 2785 | + "@ar2\n", |
| 2786 | + "def ho(): return 'Ho ho'\n", |
| 2787 | + "@ar2(\"/hostie\")\n", |
| 2788 | + "def show_host(req): return req.headers['host']\n", |
| 2789 | + "@ar2\n", |
| 2790 | + "def yoyo(): return 'a yoyo'\n", |
| 2791 | + "@ar2\n", |
| 2792 | + "def index(): return \"home page\"\n", |
| 2793 | + "\n", |
| 2794 | + "@ar2.ws(\"/ws\")\n", |
| 2795 | + "def ws(self, msg:str): return f\"Message text was: {msg}\"" |
| 2796 | + ] |
| 2797 | + }, |
| 2798 | + { |
| 2799 | + "cell_type": "code", |
| 2800 | + "execution_count": null, |
| 2801 | + "id": "77ce8548", |
| 2802 | + "metadata": {}, |
| 2803 | + "outputs": [], |
| 2804 | + "source": [ |
| 2805 | + "app,cli,_ = get_cli(FastHTML())\n", |
| 2806 | + "ar2.to_app(app)" |
| 2807 | + ] |
| 2808 | + }, |
| 2809 | + { |
| 2810 | + "cell_type": "code", |
| 2811 | + "execution_count": null, |
| 2812 | + "id": "f265860d", |
| 2813 | + "metadata": {}, |
| 2814 | + "outputs": [], |
| 2815 | + "source": [ |
| 2816 | + "test_eq(cli.get('/products/hi').text, 'Hi there')\n", |
| 2817 | + "test_eq(cli.post('/products/hi').text, 'Postal')\n", |
| 2818 | + "test_eq(cli.get('/products/hostie').text, 'testserver')\n", |
| 2819 | + "test_eq(cli.post('/products/yoyo').text, 'a yoyo')\n", |
| 2820 | + "\n", |
| 2821 | + "test_eq(cli.get('/products/ho').text, 'Ho ho')\n", |
| 2822 | + "test_eq(cli.post('/products/ho').text, 'Ho ho')" |
| 2823 | + ] |
| 2824 | + }, |
| 2825 | + { |
| 2826 | + "cell_type": "code", |
| 2827 | + "execution_count": null, |
| 2828 | + "id": "f1fc8425", |
| 2829 | + "metadata": {}, |
| 2830 | + "outputs": [], |
| 2831 | + "source": [ |
| 2832 | + "assert str(ar2.rt_funcs.index) == '/products/'\n", |
| 2833 | + "assert str(yoyo) == '/products/yoyo'\n", |
| 2834 | + "assert \"get\" not in ar2.rt_funcs._funcs.keys()" |
| 2835 | + ] |
| 2836 | + }, |
2711 | 2837 | { |
2712 | 2838 | "cell_type": "code", |
2713 | 2839 | "execution_count": null, |
|
2731 | 2857 | "@ar.get(\"/hi3\")\n", |
2732 | 2858 | "def _(): return 'Hi there'\n", |
2733 | 2859 | "@ar.post(\"/post2\")\n", |
| 2860 | + "def _(): return 'Postal'\n", |
| 2861 | + "\n", |
| 2862 | + "@ar2.get\n", |
| 2863 | + "def hi2(): return 'Hi there'\n", |
| 2864 | + "@ar2.get(\"/hi3\")\n", |
| 2865 | + "def _(): return 'Hi there'\n", |
| 2866 | + "@ar2.post(\"/post2\")\n", |
2734 | 2867 | "def _(): return 'Postal'" |
2735 | 2868 | ] |
2736 | 2869 | }, |
|
2794 | 2927 | { |
2795 | 2928 | "data": { |
2796 | 2929 | "text/plain": [ |
2797 | | - "'Cookie was set at time 15:30:23.174646'" |
| 2930 | + "'Cookie was set at time 13:51:41.310412'" |
2798 | 2931 | ] |
2799 | 2932 | }, |
2800 | 2933 | "execution_count": null, |
|
0 commit comments