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",