Skip to content

Commit cf0d2a1

Browse files
Interoperability with Django and Jinja using __html__() protocol
1 parent 69b8508 commit cf0d2a1

File tree

2 files changed

+49
-4
lines changed

2 files changed

+49
-4
lines changed

fastcore/xml.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def Html(*c, doctype=True, **kwargs)->FT:
7676
return (ft('!DOCTYPE', html=True, void_=True), res)
7777

7878
# %% ../nbs/11_xml.ipynb
79-
def _escape(s): return '' if s is None else escape(s) if isinstance(s, str) else s
79+
def _escape(s): return '' if s is None else s.__html__() if hasattr(s, '__html__') else escape(s) if isinstance(s, str) else s
8080

8181
# %% ../nbs/11_xml.ipynb
8282
def _to_attr(k,v):
@@ -115,6 +115,8 @@ def to_xml(elm, lvl=0):
115115
if not isvoid: res += f'{sp}{cltag}\n'
116116
return res
117117

118+
FT.__html__ = to_xml
119+
118120
# %% ../nbs/11_xml.ipynb
119121
def highlight(s, lang='xml'):
120122
"Markdown to syntax-highlight `s` in language `lang`"

nbs/11_xml.ipynb

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@
264264
"outputs": [],
265265
"source": [
266266
"#| export\n",
267-
"def _escape(s): return '' if s is None else escape(s) if isinstance(s, str) else s"
267+
"def _escape(s): return '' if s is None else s.__html__() if hasattr(s, '__html__') else escape(s) if isinstance(s, str) else s"
268268
]
269269
},
270270
{
@@ -317,7 +317,9 @@
317317
" res = f'{sp}<{stag}>\\n'\n",
318318
" res += ''.join(to_xml(c, lvl=lvl+2) for c in cs)\n",
319319
" if not isvoid: res += f'{sp}{cltag}\\n'\n",
320-
" return res"
320+
" return res\n",
321+
"\n",
322+
"FT.__html__ = to_xml"
321323
]
322324
},
323325
{
@@ -353,11 +355,52 @@
353355
"print(h)"
354356
]
355357
},
358+
{
359+
"cell_type": "markdown",
360+
"id": "4713bd8d",
361+
"metadata": {},
362+
"source": [
363+
"Interoperability both directions with Django and Jinja using the [__html__() protocol](https://jinja.palletsprojects.com/en/3.1.x/templates/#jinja-filters.escape)."
364+
]
365+
},
356366
{
357367
"cell_type": "code",
358-
"execution_count": null,
368+
"execution_count": 107,
359369
"id": "798ae1d2",
360370
"metadata": {},
371+
"outputs": [
372+
{
373+
"name": "stdout",
374+
"output_type": "stream",
375+
"text": [
376+
"<div><b>Hello from Django</b></div>\n",
377+
"\n",
378+
"<div>\n",
379+
" <p>Hello from fastcore &lt;3</p>\n",
380+
"</div>\n",
381+
"\n"
382+
]
383+
}
384+
],
385+
"source": [
386+
"class MockDjangoSafeString(str):\n",
387+
" def __html__(self):\n",
388+
" return self\n",
389+
"\n",
390+
"def mock_django_conditional_escape(s):\n",
391+
" return s.__html__() if hasattr(s, '__html__') else MockDjangoSafeString(escape(s))\n",
392+
"\n",
393+
"html_string_coming_from_django = MockDjangoSafeString('<b>Hello from Django</b>')\n",
394+
"print(to_xml(Div(html_string_coming_from_django)))\n",
395+
"\n",
396+
"print(mock_django_conditional_escape(Div(P('Hello from fastcore <3'))))"
397+
]
398+
},
399+
{
400+
"cell_type": "code",
401+
"execution_count": 108,
402+
"id": "5f0e91e0",
403+
"metadata": {},
361404
"outputs": [],
362405
"source": [
363406
"#| export\n",

0 commit comments

Comments
 (0)