Skip to content

Commit f55aade

Browse files
committed
fixes #549
1 parent ec5751c commit f55aade

File tree

3 files changed

+312
-0
lines changed

3 files changed

+312
-0
lines changed

fastcore/_modidx.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,7 @@
534534
'fastcore.xdg.xdg_runtime_dir': ('xdg.html#xdg_runtime_dir', 'fastcore/xdg.py'),
535535
'fastcore.xdg.xdg_state_home': ('xdg.html#xdg_state_home', 'fastcore/xdg.py')},
536536
'fastcore.xml': { 'fastcore.xml._attrmap': ('xml.html#_attrmap', 'fastcore/xml.py'),
537+
'fastcore.xml.to_xml': ('xml.html#to_xml', 'fastcore/xml.py'),
537538
'fastcore.xml.xt': ('xml.html#xt', 'fastcore/xml.py')},
538539
'fastcore.xtras': { 'fastcore.xtras.ContextManagers': ('xtras.html#contextmanagers', 'fastcore/xtras.py'),
539540
'fastcore.xtras.ContextManagers.__enter__': ('xtras.html#contextmanagers.__enter__', 'fastcore/xtras.py'),

fastcore/xml.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/11_xml.ipynb.
2+
3+
# %% auto 0
4+
__all__ = ['xt', 'to_xml', 'Html', 'Head', 'Title', 'Meta', 'Link', 'Style', 'Body', 'Pre', 'Code', 'Div', 'Span', 'P', 'H1',
5+
'H2', 'H3', 'H4', 'H5', 'H6', 'Strong', 'Em', 'B', 'I', 'U', 'S', 'Strike', 'Sub', 'Sup', 'Hr', 'Br', 'Img',
6+
'A', 'Nav', 'Ul', 'Ol', 'Li', 'Dl', 'Dt', 'Dd', 'Table', 'Thead', 'Tbody', 'Tfoot', 'Tr', 'Th', 'Td',
7+
'Caption', 'Col', 'Colgroup', 'Form', 'Input', 'Textarea', 'Button', 'Select', 'Option', 'Label', 'Fieldset',
8+
'Legend', 'Details', 'Summary', 'Main', 'Header', 'Footer', 'Section', 'Article', 'Aside', 'Figure',
9+
'Figcaption', 'Mark', 'Small', 'Iframe', 'Object', 'Embed', 'Param', 'Video', 'Audio', 'Source', 'Canvas',
10+
'Svg', 'Math', 'Script', 'Noscript', 'Template', 'Slot']
11+
12+
# %% ../nbs/11_xml.ipynb 2
13+
from .utils import *
14+
15+
import types
16+
from dataclasses import dataclass
17+
from functools import partial
18+
from html import escape
19+
20+
# %% ../nbs/11_xml.ipynb 4
21+
def _attrmap(o):
22+
return dict(cls='class', klass='class', fr='for').get(o, o)
23+
24+
# %% ../nbs/11_xml.ipynb 5
25+
def xt(tag:str, *c, **kw):
26+
"Create an XML tag structure `[tag,children,attrs]` for `toxml()`"
27+
if len(c)==1 and isinstance(c[0], types.GeneratorType): c = tuple(c[0])
28+
kw = {_attrmap(k.lstrip('_')):str(v) for k,v in kw.items()}
29+
return [tag.lower(),c,kw]
30+
31+
# %% ../nbs/11_xml.ipynb 6
32+
_g = globals()
33+
_all_ = ['Html', 'Head', 'Title', 'Meta', 'Link', 'Style', 'Body', 'Pre', 'Code',
34+
'Div', 'Span', 'P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Strong', 'Em', 'B',
35+
'I', 'U', 'S', 'Strike', 'Sub', 'Sup', 'Hr', 'Br', 'Img', 'A', 'Link', 'Nav',
36+
'Ul', 'Ol', 'Li', 'Dl', 'Dt', 'Dd', 'Table', 'Thead', 'Tbody', 'Tfoot', 'Tr',
37+
'Th', 'Td', 'Caption', 'Col', 'Colgroup', 'Form', 'Input', 'Textarea',
38+
'Button', 'Select', 'Option', 'Label', 'Fieldset', 'Legend', 'Details',
39+
'Summary', 'Main', 'Header', 'Footer', 'Section', 'Article', 'Aside', 'Figure',
40+
'Figcaption', 'Mark', 'Small', 'Iframe', 'Object', 'Embed', 'Param', 'Video',
41+
'Audio', 'Source', 'Canvas', 'Svg', 'Math', 'Script', 'Noscript', 'Template', 'Slot']
42+
43+
for o in _all_: _g[o] = partial(xt, o.lower())
44+
45+
# %% ../nbs/11_xml.ipynb 9
46+
def to_xml(elm, lvl=0):
47+
"Convert `xt` element tree into an XML string"
48+
sp = ' ' * lvl
49+
if not isinstance(elm, list):
50+
if isinstance(elm, str): elm = escape(elm)
51+
return f'{elm}\n'
52+
53+
tag,cs,attrs = elm
54+
stag = tag
55+
if attrs:
56+
sattrs = (f'{k}="{escape(str(v))}"' for k,v in attrs.items())
57+
stag += ' ' + ' '.join(sattrs)
58+
59+
if not cs: return f'{sp}<{stag}></{tag}>\n'
60+
res = f'{sp}<{stag}>\n'
61+
res += ''.join(to_xml(c, lvl+2) for c in cs)
62+
res += f'{sp}</{tag}>\n'
63+
return res

nbs/11_xml.ipynb

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"id": "d0c84aa5",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"#| default_exp xml"
11+
]
12+
},
13+
{
14+
"cell_type": "markdown",
15+
"id": "61bf8203",
16+
"metadata": {},
17+
"source": [
18+
"# XML\n",
19+
"\n",
20+
"> Concise generation of XML."
21+
]
22+
},
23+
{
24+
"cell_type": "code",
25+
"execution_count": null,
26+
"id": "55944805",
27+
"metadata": {},
28+
"outputs": [],
29+
"source": [
30+
"#| export\n",
31+
"from fastcore.utils import *\n",
32+
"\n",
33+
"import types\n",
34+
"from dataclasses import dataclass\n",
35+
"from functools import partial\n",
36+
"from html import escape"
37+
]
38+
},
39+
{
40+
"cell_type": "code",
41+
"execution_count": null,
42+
"id": "42d18e5c",
43+
"metadata": {},
44+
"outputs": [],
45+
"source": [
46+
"from IPython.display import Markdown"
47+
]
48+
},
49+
{
50+
"cell_type": "code",
51+
"execution_count": null,
52+
"id": "4dbb9a59",
53+
"metadata": {},
54+
"outputs": [],
55+
"source": [
56+
"#| export\n",
57+
"def _attrmap(o):\n",
58+
" return dict(cls='class', klass='class', fr='for').get(o, o)"
59+
]
60+
},
61+
{
62+
"cell_type": "code",
63+
"execution_count": null,
64+
"id": "06718948",
65+
"metadata": {},
66+
"outputs": [],
67+
"source": [
68+
"#| export\n",
69+
"def xt(tag:str, *c, **kw):\n",
70+
" \"Create an XML tag structure `[tag,children,attrs]` for `toxml()`\"\n",
71+
" if len(c)==1 and isinstance(c[0], types.GeneratorType): c = tuple(c[0])\n",
72+
" kw = {_attrmap(k.lstrip('_')):str(v) for k,v in kw.items()}\n",
73+
" return [tag.lower(),c,kw]"
74+
]
75+
},
76+
{
77+
"cell_type": "code",
78+
"execution_count": null,
79+
"id": "45489975",
80+
"metadata": {},
81+
"outputs": [],
82+
"source": [
83+
"#| export\n",
84+
"_g = globals()\n",
85+
"_all_ = ['Html', 'Head', 'Title', 'Meta', 'Link', 'Style', 'Body', 'Pre', 'Code',\n",
86+
"'Div', 'Span', 'P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Strong', 'Em', 'B',\n",
87+
"'I', 'U', 'S', 'Strike', 'Sub', 'Sup', 'Hr', 'Br', 'Img', 'A', 'Link', 'Nav',\n",
88+
"'Ul', 'Ol', 'Li', 'Dl', 'Dt', 'Dd', 'Table', 'Thead', 'Tbody', 'Tfoot', 'Tr',\n",
89+
"'Th', 'Td', 'Caption', 'Col', 'Colgroup', 'Form', 'Input', 'Textarea',\n",
90+
"'Button', 'Select', 'Option', 'Label', 'Fieldset', 'Legend', 'Details',\n",
91+
"'Summary', 'Main', 'Header', 'Footer', 'Section', 'Article', 'Aside', 'Figure',\n",
92+
"'Figcaption', 'Mark', 'Small', 'Iframe', 'Object', 'Embed', 'Param', 'Video',\n",
93+
"'Audio', 'Source', 'Canvas', 'Svg', 'Math', 'Script', 'Noscript', 'Template', 'Slot']\n",
94+
"\n",
95+
"for o in _all_: _g[o] = partial(xt, o.lower())"
96+
]
97+
},
98+
{
99+
"cell_type": "markdown",
100+
"id": "732e44ab",
101+
"metadata": {},
102+
"source": [
103+
"The main HTML tags are exported as `xt` partials.\n",
104+
"\n",
105+
"Attributes are passed as keywords. Use 'klass' and 'fr' instead of 'class' and 'for', to avoid Python reserved word clashes."
106+
]
107+
},
108+
{
109+
"cell_type": "code",
110+
"execution_count": null,
111+
"id": "9a8b4ddb",
112+
"metadata": {},
113+
"outputs": [
114+
{
115+
"data": {
116+
"text/plain": [
117+
"['html',\n",
118+
" (['head', (['title', ('Some page',), {}],), {}],\n",
119+
" ['body',\n",
120+
" (['div',\n",
121+
" (['p', ('Some text',), {}], ['img', (), {'src': 'filename'}]),\n",
122+
" {'class': 'myclass'}],),\n",
123+
" {}]),\n",
124+
" {}]"
125+
]
126+
},
127+
"execution_count": null,
128+
"metadata": {},
129+
"output_type": "execute_result"
130+
}
131+
],
132+
"source": [
133+
"samp = Html(\n",
134+
" Head(Title('Some page')),\n",
135+
" Body(Div(P('Some text'), Img(src=\"filename\"), klass='myclass'))\n",
136+
")\n",
137+
"samp"
138+
]
139+
},
140+
{
141+
"cell_type": "code",
142+
"execution_count": null,
143+
"id": "b89d088a",
144+
"metadata": {},
145+
"outputs": [],
146+
"source": [
147+
"#| export\n",
148+
"def to_xml(elm, lvl=0):\n",
149+
" \"Convert `xt` element tree into an XML string\"\n",
150+
" sp = ' ' * lvl\n",
151+
" if not isinstance(elm, list):\n",
152+
" if isinstance(elm, str): elm = escape(elm)\n",
153+
" return f'{elm}\\n'\n",
154+
"\n",
155+
" tag,cs,attrs = elm\n",
156+
" stag = tag\n",
157+
" if attrs:\n",
158+
" sattrs = (f'{k}=\"{escape(str(v))}\"' for k,v in attrs.items())\n",
159+
" stag += ' ' + ' '.join(sattrs)\n",
160+
" \n",
161+
" if not cs: return f'{sp}<{stag}></{tag}>\\n'\n",
162+
" res = f'{sp}<{stag}>\\n'\n",
163+
" res += ''.join(to_xml(c, lvl+2) for c in cs)\n",
164+
" res += f'{sp}</{tag}>\\n'\n",
165+
" return res"
166+
]
167+
},
168+
{
169+
"cell_type": "code",
170+
"execution_count": null,
171+
"id": "d3d23c48",
172+
"metadata": {},
173+
"outputs": [
174+
{
175+
"data": {
176+
"text/markdown": [
177+
"```html\n",
178+
"<html>\n",
179+
" <head>\n",
180+
" <title>\n",
181+
"Some page\n",
182+
" </title>\n",
183+
" </head>\n",
184+
" <body>\n",
185+
" <div class=\"myclass\">\n",
186+
" <p>\n",
187+
"Some text\n",
188+
" </p>\n",
189+
" <img src=\"filename\"></img>\n",
190+
" </div>\n",
191+
" </body>\n",
192+
"</html>\n",
193+
"\n",
194+
"```"
195+
],
196+
"text/plain": [
197+
"<IPython.core.display.Markdown object>"
198+
]
199+
},
200+
"execution_count": null,
201+
"metadata": {},
202+
"output_type": "execute_result"
203+
}
204+
],
205+
"source": [
206+
"Markdown(f'''```html\n",
207+
"{to_xml(samp)}\n",
208+
"```''')"
209+
]
210+
},
211+
{
212+
"cell_type": "markdown",
213+
"id": "df973d4e",
214+
"metadata": {},
215+
"source": [
216+
"# Export -"
217+
]
218+
},
219+
{
220+
"cell_type": "code",
221+
"execution_count": null,
222+
"id": "ad32b076",
223+
"metadata": {},
224+
"outputs": [],
225+
"source": [
226+
"#|hide\n",
227+
"import nbdev; nbdev.nbdev_export()"
228+
]
229+
},
230+
{
231+
"cell_type": "code",
232+
"execution_count": null,
233+
"id": "84fe290c",
234+
"metadata": {},
235+
"outputs": [],
236+
"source": []
237+
}
238+
],
239+
"metadata": {
240+
"kernelspec": {
241+
"display_name": "python3",
242+
"language": "python",
243+
"name": "python3"
244+
}
245+
},
246+
"nbformat": 4,
247+
"nbformat_minor": 5
248+
}

0 commit comments

Comments
 (0)