Skip to content

Commit dd148cf

Browse files
authored
Merge pull request #589 from collinanderson/interop-django
Interoperability with Django and Jinja using __html__() protocol
2 parents 9ff049d + cf0d2a1 commit dd148cf

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
@@ -81,7 +81,7 @@ def Html(*c, doctype=True, **kwargs)->FT:
8181
return (ft('!DOCTYPE', html=True, void_=True), res)
8282

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

8686
# %% ../nbs/11_xml.ipynb
8787
def _to_attr(k,v):
@@ -120,6 +120,8 @@ def to_xml(elm, lvl=0):
120120
if not isvoid: res += f'{sp}{cltag}\n'
121121
return res
122122

123+
FT.__html__ = to_xml
124+
123125
# %% ../nbs/11_xml.ipynb
124126
def highlight(s, lang='xml'):
125127
"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
@@ -269,7 +269,7 @@
269269
"outputs": [],
270270
"source": [
271271
"#| export\n",
272-
"def _escape(s): return '' if s is None else escape(s) if isinstance(s, str) else s"
272+
"def _escape(s): return '' if s is None else s.__html__() if hasattr(s, '__html__') else escape(s) if isinstance(s, str) else s"
273273
]
274274
},
275275
{
@@ -322,7 +322,9 @@
322322
" res = f'{sp}<{stag}>\\n'\n",
323323
" res += ''.join(to_xml(c, lvl=lvl+2) for c in cs)\n",
324324
" if not isvoid: res += f'{sp}{cltag}\\n'\n",
325-
" return res"
325+
" return res\n",
326+
"\n",
327+
"FT.__html__ = to_xml"
326328
]
327329
},
328330
{
@@ -358,11 +360,52 @@
358360
"print(h)"
359361
]
360362
},
363+
{
364+
"cell_type": "markdown",
365+
"id": "4713bd8d",
366+
"metadata": {},
367+
"source": [
368+
"Interoperability both directions with Django and Jinja using the [__html__() protocol](https://jinja.palletsprojects.com/en/3.1.x/templates/#jinja-filters.escape)."
369+
]
370+
},
361371
{
362372
"cell_type": "code",
363-
"execution_count": null,
373+
"execution_count": 107,
364374
"id": "798ae1d2",
365375
"metadata": {},
376+
"outputs": [
377+
{
378+
"name": "stdout",
379+
"output_type": "stream",
380+
"text": [
381+
"<div><b>Hello from Django</b></div>\n",
382+
"\n",
383+
"<div>\n",
384+
" <p>Hello from fastcore &lt;3</p>\n",
385+
"</div>\n",
386+
"\n"
387+
]
388+
}
389+
],
390+
"source": [
391+
"class MockDjangoSafeString(str):\n",
392+
" def __html__(self):\n",
393+
" return self\n",
394+
"\n",
395+
"def mock_django_conditional_escape(s):\n",
396+
" return s.__html__() if hasattr(s, '__html__') else MockDjangoSafeString(escape(s))\n",
397+
"\n",
398+
"html_string_coming_from_django = MockDjangoSafeString('<b>Hello from Django</b>')\n",
399+
"print(to_xml(Div(html_string_coming_from_django)))\n",
400+
"\n",
401+
"print(mock_django_conditional_escape(Div(P('Hello from fastcore <3'))))"
402+
]
403+
},
404+
{
405+
"cell_type": "code",
406+
"execution_count": 108,
407+
"id": "5f0e91e0",
408+
"metadata": {},
366409
"outputs": [],
367410
"source": [
368411
"#| export\n",

0 commit comments

Comments
 (0)