|
53 | 53 | "from datetime import datetime\n", |
54 | 54 | "from dataclasses import dataclass,fields,is_dataclass,MISSING,asdict\n", |
55 | 55 | "from collections import namedtuple\n", |
56 | | - "from inspect import isfunction,ismethod,signature,Parameter,get_annotations\n", |
| 56 | + "from inspect import isfunction,ismethod,Parameter,get_annotations\n", |
57 | 57 | "from functools import wraps, partialmethod\n", |
58 | 58 | "from http import cookies\n", |
59 | 59 | "from copy import copy,deepcopy\n", |
|
96 | 96 | "id": "19d3f2a7", |
97 | 97 | "metadata": {}, |
98 | 98 | "outputs": [], |
| 99 | + "source": [ |
| 100 | + "#| export\n", |
| 101 | + "def _sig(f): return signature_ex(f, True)" |
| 102 | + ] |
| 103 | + }, |
| 104 | + { |
| 105 | + "cell_type": "code", |
| 106 | + "execution_count": null, |
| 107 | + "id": "3d01cb33", |
| 108 | + "metadata": {}, |
| 109 | + "outputs": [], |
99 | 110 | "source": [ |
100 | 111 | "#| export\n", |
101 | 112 | "def is_typeddict(cls:type)->bool:\n", |
|
635 | 646 | "source": [ |
636 | 647 | "async def f(req):\n", |
637 | 648 | " def _f(p:HttpHeader): ...\n", |
638 | | - " p = first(signature(_f).parameters.values())\n", |
| 649 | + " p = first(_sig(_f).parameters.values())\n", |
639 | 650 | " result = await _from_body(req, p)\n", |
640 | 651 | " return JSONResponse(result.__dict__)\n", |
641 | 652 | "\n", |
|
715 | 726 | "def g(req, this:Starlette, a:str, b:HttpHeader): ...\n", |
716 | 727 | "\n", |
717 | 728 | "async def f(req):\n", |
718 | | - " a = await _wrap_req(req, signature(g).parameters)\n", |
| 729 | + " a = await _wrap_req(req, _sig(g).parameters)\n", |
719 | 730 | " return Response(str(a))\n", |
720 | 731 | "\n", |
721 | 732 | "app = Starlette(routes=[Route('/', f, methods=['POST'])])\n", |
|
826 | 837 | " cls = type('WS_Endp', (WebSocketEndpoint,), {\"encoding\":\"text\"})\n", |
827 | 838 | " \n", |
828 | 839 | " async def _generic_handler(handler, ws, data=None):\n", |
829 | | - " wd = _wrap_ws(ws, loads(data) if data else {}, signature(handler).parameters)\n", |
| 840 | + " wd = _wrap_ws(ws, loads(data) if data else {}, _sig(handler).parameters)\n", |
830 | 841 | " resp = handler(*wd)\n", |
831 | 842 | " if resp:\n", |
832 | 843 | " if is_async_callable(handler): resp = await resp\n", |
|
953 | 964 | "class RouteX(Route):\n", |
954 | 965 | " def __init__(self, path:str, endpoint, *, methods=None, name=None, include_in_schema=True, middleware=None,\n", |
955 | 966 | " hdrs=None, ftrs=None, before=None, after=None, htmlkw=None, **bodykw):\n", |
956 | | - " self.sig = signature(endpoint)\n", |
| 967 | + " self.sig = _sig(endpoint)\n", |
957 | 968 | " self.f,self.hdrs,self.ftrs,self.before,self.after,self.htmlkw,self.bodykw = endpoint,hdrs,ftrs,before,after,htmlkw,bodykw\n", |
958 | 969 | " super().__init__(path, self._endp, methods=methods, name=name, include_in_schema=include_in_schema, middleware=middleware)\n", |
959 | 970 | "\n", |
|
967 | 978 | " if isinstance(b, Beforeware): bf,skip = b.f,b.skip\n", |
968 | 979 | " else: bf,skip = b,[]\n", |
969 | 980 | " if not any(re.match(r, req.url.path) for r in skip):\n", |
970 | | - " resp = await _wrap_call(bf, req, signature(bf).parameters)\n", |
| 981 | + " resp = await _wrap_call(bf, req, _sig(bf).parameters)\n", |
971 | 982 | " if not resp: resp = await _wrap_call(self.f, req, self.sig.parameters)\n", |
972 | 983 | " for a in self.after:\n", |
973 | | - " _,*wreq = await _wrap_req(req, signature(a).parameters)\n", |
| 984 | + " _,*wreq = await _wrap_req(req, _sig(a).parameters)\n", |
974 | 985 | " nr = a(resp, *wreq)\n", |
975 | 986 | " if nr: resp = nr\n", |
976 | 987 | " return _resp(req, resp, self.sig.return_annotation)" |
|
1598 | 1609 | { |
1599 | 1610 | "data": { |
1600 | 1611 | "text/plain": [ |
1601 | | - "'Cookie was set at time 13:26:44.481873'" |
| 1612 | + "'Cookie was set at time 23:01:53.591425'" |
1602 | 1613 | ] |
1603 | 1614 | }, |
1604 | 1615 | "execution_count": null, |
|
1639 | 1650 | "name": "stdout", |
1640 | 1651 | "output_type": "stream", |
1641 | 1652 | "text": [ |
1642 | | - "Set to 2024-08-05 13:26:44.529004\n" |
| 1653 | + "Set to 2024-08-05 23:01:53.638504\n" |
1643 | 1654 | ] |
1644 | 1655 | }, |
1645 | 1656 | { |
1646 | 1657 | "data": { |
1647 | 1658 | "text/plain": [ |
1648 | | - "'Session time: 13:26:44.529004'" |
| 1659 | + "'Session time: 23:01:53.638504'" |
1649 | 1660 | ] |
1650 | 1661 | }, |
1651 | 1662 | "execution_count": null, |
|
1673 | 1684 | "\n", |
1674 | 1685 | "<!-- do not remove -->\n", |
1675 | 1686 | "\n", |
1676 | | - "## 0.2.1\n", |
| 1687 | + "## 0.2.3\n", |
| 1688 | + "\n", |
| 1689 | + "### New Features\n", |
| 1690 | + "\n", |
| 1691 | + "- Add `attr\n" |
| 1692 | + ] |
| 1693 | + } |
| 1694 | + ], |
| 1695 | + "source": [ |
| 1696 | + "@rt(\"/upload\")\n", |
| 1697 | + "async def post(uploadfile:UploadFile): return (await uploadfile.read()).decode()\n", |
| 1698 | + "\n", |
| 1699 | + "fn = '../../CHANGELOG.md'\n", |
| 1700 | + "data = {'message': 'Hello, world!'}\n", |
| 1701 | + "with open(fn, 'rb') as f:\n", |
| 1702 | + " print(cli.post('/upload', files={'uploadfile': f}, data=data).text[:80])" |
| 1703 | + ] |
| 1704 | + }, |
| 1705 | + { |
| 1706 | + "cell_type": "code", |
| 1707 | + "execution_count": null, |
| 1708 | + "id": "4d17ab9c", |
| 1709 | + "metadata": {}, |
| 1710 | + "outputs": [ |
| 1711 | + { |
| 1712 | + "name": "stdout", |
| 1713 | + "output_type": "stream", |
| 1714 | + "text": [ |
| 1715 | + "# Release notes\n", |
| 1716 | + "\n", |
| 1717 | + "<!-- do not remove -->\n", |
1677 | 1718 | "\n", |
| 1719 | + "## 0.2.3\n", |
1678 | 1720 | "\n", |
1679 | | - "### Bugs Squashed\n", |
| 1721 | + "### New Features\n", |
1680 | 1722 | "\n", |
1681 | | - "- railway\n" |
| 1723 | + "- Add `attr\n" |
1682 | 1724 | ] |
1683 | 1725 | } |
1684 | 1726 | ], |
|
1692 | 1734 | " print(cli.post('/upload', files={'uploadfile': f}, data=data).text[:80])" |
1693 | 1735 | ] |
1694 | 1736 | }, |
| 1737 | + { |
| 1738 | + "cell_type": "code", |
| 1739 | + "execution_count": null, |
| 1740 | + "id": "9c1863bc", |
| 1741 | + "metadata": {}, |
| 1742 | + "outputs": [], |
| 1743 | + "source": [ |
| 1744 | + "@rt(\"/{fname:path}.{ext:static}\")\n", |
| 1745 | + "async def get(fname:str, ext:str): return FileResponse(f'{fname}.{ext}')" |
| 1746 | + ] |
| 1747 | + }, |
| 1748 | + { |
| 1749 | + "cell_type": "code", |
| 1750 | + "execution_count": null, |
| 1751 | + "id": "9281360c", |
| 1752 | + "metadata": {}, |
| 1753 | + "outputs": [], |
| 1754 | + "source": [ |
| 1755 | + "assert 'These are the source notebooks for FastHTML' in cli.get('/README.txt').text" |
| 1756 | + ] |
| 1757 | + }, |
1695 | 1758 | { |
1696 | 1759 | "cell_type": "code", |
1697 | 1760 | "execution_count": null, |
|
0 commit comments