diff --git a/fasthtml/core.py b/fasthtml/core.py index 9de0e5a8..cd130b95 100644 --- a/fasthtml/core.py +++ b/fasthtml/core.py @@ -40,6 +40,19 @@ from .starlette import * +# %% ../nbs/api/00_core.ipynb #2b18bf01 +@patch +def __deepcopy__(self:FT, memo): + "Fast `deepcopy` for `FT`, avoiding the slow generic object fallback. Unknown attr/child value types are delegated to `deepcopy` itself, so a user's own `__deepcopy__` is still respected" + y = self.__class__.__new__(self.__class__) + memo[id(self)] = y + y.tag = self.tag + y.children = tuple(deepcopy(c, memo) for c in self.children) + y.attrs = deepcopy(self.attrs, memo) + y.void_ = self.void_ + y.listeners_ = deepcopy(self.listeners_, memo) + return y + # %% ../nbs/api/00_core.ipynb #19d3f2a7 def _params(f): return signature_ex(f, True).parameters diff --git a/fasthtml/core.pyi b/fasthtml/core.pyi index 3ec037c6..a080d6e3 100644 --- a/fasthtml/core.pyi +++ b/fasthtml/core.pyi @@ -22,6 +22,11 @@ from base64 import b64encode, b64decode from email.utils import format_datetime from .starlette import * +@patch +def __deepcopy__(self: FT, memo): + """Fast `deepcopy` for `FT`, avoiding the slow generic object fallback. Unknown attr/child value types are delegated to `deepcopy` itself, so a user's own `__deepcopy__` is still respected""" + ... + def _params(f): ... empty = Parameter.empty diff --git a/nbs/api/00_core.ipynb b/nbs/api/00_core.ipynb index 6d5161f5..6907c949 100644 --- a/nbs/api/00_core.ipynb +++ b/nbs/api/00_core.ipynb @@ -90,6 +90,106 @@ "from starlette.datastructures import UploadFile" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b18bf01", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "@patch\n", + "def __deepcopy__(self:FT, memo):\n", + " \"Fast `deepcopy` for `FT`, avoiding the slow generic object fallback. Unknown attr/child value types are delegated to `deepcopy` itself, so a user's own `__deepcopy__` is still respected\"\n", + " y = self.__class__.__new__(self.__class__)\n", + " memo[id(self)] = y\n", + " y.tag = self.tag\n", + " y.children = tuple(deepcopy(c, memo) for c in self.children)\n", + " y.attrs = deepcopy(self.attrs, memo)\n", + " y.void_ = self.void_\n", + " y.listeners_ = deepcopy(self.listeners_, memo)\n", + " return y" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d9ecba07", + "metadata": {}, + "outputs": [], + "source": [ + "el = Div(Span('hi', cls='a'), id='x')\n", + "cp = deepcopy(el)\n", + "test_eq(cp, el)\n", + "test_ne(id(cp), id(el))\n", + "test_ne(id(cp.attrs), id(el.attrs))\n", + "test_ne(id(cp.children[0]), id(el.children[0]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b69aa77", + "metadata": {}, + "outputs": [], + "source": [ + "nested = Div(Div(Span('hi', cls='a')))\n", + "cp = deepcopy(nested)\n", + "cp.children[0].children[0].attrs['class'] = 'mutated'\n", + "test_eq(nested.children[0].children[0].attrs['class'], 'a')\n", + "test_eq(cp.children[0].children[0].attrs['class'], 'mutated')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50296a48", + "metadata": {}, + "outputs": [], + "source": [ + "v = Meta(charset='utf-8')\n", + "cp = deepcopy(v)\n", + "test_eq(cp.void_, True)\n", + "test_eq(cp, v)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "69795726", + "metadata": {}, + "outputs": [], + "source": [ + "class Custom:\n", + " def __init__(self, v): self.v = v\n", + " def __deepcopy__(self, memo):\n", + " c = Custom(self.v)\n", + " memo[id(self)] = c\n", + " return c\n", + " def __eq__(self, o): return isinstance(o, Custom) and o.v == self.v\n", + "\n", + "el = Div(x=Custom(5))\n", + "cp = deepcopy(el)\n", + "test_eq(type(cp.attrs['x']), Custom)\n", + "test_ne(id(cp.attrs['x']), id(el.attrs['x']))\n", + "test_eq(cp.attrs['x'].v, 5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fce2d92a", + "metadata": {}, + "outputs": [], + "source": [ + "app, rt = fast_app()\n", + "hdrs, ftrs, htmlkw, bodykw = app.hdrs, app.ftrs, app.htmlkw, app.bodykw\n", + "r = deepcopy((hdrs, ftrs, htmlkw, bodykw))\n", + "test_eq(r, (hdrs, ftrs, htmlkw, bodykw))\n", + "test_ne(id(r[0][0]), id(hdrs[0]))\n", + "test_ne(id(r[0][0].attrs), id(hdrs[0].attrs))" + ] + }, { "cell_type": "code", "execution_count": null, @@ -4989,6 +5089,9 @@ } ], "metadata": { + "language_info": { + "name": "python" + }, "solveit": { "default_code": false, "mode": "concise",