Skip to content

Commit f0b4eac

Browse files
committed
fixes #585
1 parent 005ffd9 commit f0b4eac

File tree

5 files changed

+116
-34
lines changed

5 files changed

+116
-34
lines changed

fastcore/_modidx.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
'fastcore.basics.Str': ('basics.html#str', 'fastcore/basics.py'),
6262
'fastcore.basics.StrEnum': ('basics.html#strenum', 'fastcore/basics.py'),
6363
'fastcore.basics.StrEnum.__str__': ('basics.html#strenum.__str__', 'fastcore/basics.py'),
64+
'fastcore.basics.ValEnum': ('basics.html#valenum', 'fastcore/basics.py'),
65+
'fastcore.basics.ValEnum.__str__': ('basics.html#valenum.__str__', 'fastcore/basics.py'),
6466
'fastcore.basics._Arg': ('basics.html#_arg', 'fastcore/basics.py'),
6567
'fastcore.basics._Arg.__init__': ('basics.html#_arg.__init__', 'fastcore/basics.py'),
6668
'fastcore.basics._InfMeta': ('basics.html#_infmeta', 'fastcore/basics.py'),
@@ -558,7 +560,8 @@
558560
'fastcore.xdg.xdg_data_home': ('xdg.html#xdg_data_home', 'fastcore/xdg.py'),
559561
'fastcore.xdg.xdg_runtime_dir': ('xdg.html#xdg_runtime_dir', 'fastcore/xdg.py'),
560562
'fastcore.xdg.xdg_state_home': ('xdg.html#xdg_state_home', 'fastcore/xdg.py')},
561-
'fastcore.xml': { 'fastcore.xml.XT': ('xml.html#xt', 'fastcore/xml.py'),
563+
'fastcore.xml': { 'fastcore.xml.Html': ('xml.html#html', 'fastcore/xml.py'),
564+
'fastcore.xml.XT': ('xml.html#xt', 'fastcore/xml.py'),
562565
'fastcore.xml.XT.__getattr__': ('xml.html#xt.__getattr__', 'fastcore/xml.py'),
563566
'fastcore.xml.XT.__init__': ('xml.html#xt.__init__', 'fastcore/xml.py'),
564567
'fastcore.xml.XT.__setattr__': ('xml.html#xt.__setattr__', 'fastcore/xml.py'),

fastcore/basics.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
'only', 'nested_attr', 'nested_setdefault', 'nested_callable', 'nested_idx', 'set_nested_idx', 'val2idx',
1515
'uniqueify', 'loop_first_last', 'loop_first', 'loop_last', 'first_match', 'last_match', 'fastuple', 'bind',
1616
'mapt', 'map_ex', 'compose', 'maps', 'partialler', 'instantiate', 'using_attr', 'copy_func', 'patch_to',
17-
'patch', 'patch_property', 'compile_re', 'ImportEnum', 'StrEnum', 'str_enum', 'Stateful', 'NotStr',
18-
'PrettyString', 'even_mults', 'num_cpus', 'add_props', 'typed', 'exec_new', 'exec_import', 'str2bool', 'lt',
19-
'gt', 'le', 'ge', 'eq', 'ne', 'add', 'sub', 'mul', 'truediv', 'is_', 'is_not', 'mod']
17+
'patch', 'patch_property', 'compile_re', 'ImportEnum', 'StrEnum', 'str_enum', 'ValEnum', 'Stateful',
18+
'NotStr', 'PrettyString', 'even_mults', 'num_cpus', 'add_props', 'typed', 'exec_new', 'exec_import',
19+
'str2bool', 'lt', 'gt', 'le', 'ge', 'eq', 'ne', 'add', 'sub', 'mul', 'truediv', 'is_', 'is_not', 'mod']
2020

2121
# %% ../nbs/01_basics.ipynb 1
2222
from .imports import *
@@ -1038,7 +1038,12 @@ def str_enum(name, *vals):
10381038
"Simplified creation of `StrEnum` types"
10391039
return StrEnum(name, {o:o for o in vals})
10401040

1041-
# %% ../nbs/01_basics.ipynb 415
1041+
# %% ../nbs/01_basics.ipynb 414
1042+
class ValEnum(str,ImportEnum):
1043+
"An `ImportEnum` that stringifies using values"
1044+
def __str__(self): return self.value
1045+
1046+
# %% ../nbs/01_basics.ipynb 417
10421047
class Stateful:
10431048
"A base class/mixin for objects that should not serialize all their state"
10441049
_stateattrs=()
@@ -1058,7 +1063,7 @@ def _init_state(self):
10581063
"Override for custom init and deserialization logic"
10591064
self._state = {}
10601065

1061-
# %% ../nbs/01_basics.ipynb 421
1066+
# %% ../nbs/01_basics.ipynb 423
10621067
class NotStr(GetAttr):
10631068
"Behaves like a `str`, but isn't an instance of one"
10641069
_default = 's'
@@ -1075,37 +1080,37 @@ def __bool__(self): return bool(self.s)
10751080
def __contains__(self, b): return b in self.s
10761081
def __iter__(self): return iter(self.s)
10771082

1078-
# %% ../nbs/01_basics.ipynb 423
1083+
# %% ../nbs/01_basics.ipynb 425
10791084
class PrettyString(str):
10801085
"Little hack to get strings to show properly in Jupyter."
10811086
def __repr__(self): return self
10821087

1083-
# %% ../nbs/01_basics.ipynb 429
1088+
# %% ../nbs/01_basics.ipynb 431
10841089
def even_mults(start, stop, n):
10851090
"Build log-stepped array from `start` to `stop` in `n` steps."
10861091
if n==1: return stop
10871092
mult = stop/start
10881093
step = mult**(1/(n-1))
10891094
return [start*(step**i) for i in range(n)]
10901095

1091-
# %% ../nbs/01_basics.ipynb 431
1096+
# %% ../nbs/01_basics.ipynb 433
10921097
def num_cpus():
10931098
"Get number of cpus"
10941099
try: return len(os.sched_getaffinity(0))
10951100
except AttributeError: return os.cpu_count()
10961101

10971102
defaults.cpus = num_cpus()
10981103

1099-
# %% ../nbs/01_basics.ipynb 433
1104+
# %% ../nbs/01_basics.ipynb 435
11001105
def add_props(f, g=None, n=2):
11011106
"Create properties passing each of `range(n)` to f"
11021107
if g is None: return (property(partial(f,i)) for i in range(n))
11031108
return (property(partial(f,i), partial(g,i)) for i in range(n))
11041109

1105-
# %% ../nbs/01_basics.ipynb 436
1110+
# %% ../nbs/01_basics.ipynb 438
11061111
def _typeerr(arg, val, typ): return TypeError(f"{arg}=={val} not {typ}")
11071112

1108-
# %% ../nbs/01_basics.ipynb 437
1113+
# %% ../nbs/01_basics.ipynb 439
11091114
def typed(f):
11101115
"Decorator to check param and return types at runtime"
11111116
names = f.__code__.co_varnames
@@ -1122,21 +1127,21 @@ def _f(*args,**kwargs):
11221127
return res
11231128
return functools.update_wrapper(_f, f)
11241129

1125-
# %% ../nbs/01_basics.ipynb 445
1130+
# %% ../nbs/01_basics.ipynb 447
11261131
def exec_new(code):
11271132
"Execute `code` in a new environment and return it"
11281133
pkg = None if __name__=='__main__' else Path().cwd().name
11291134
g = {'__name__': __name__, '__package__': pkg}
11301135
exec(code, g)
11311136
return g
11321137

1133-
# %% ../nbs/01_basics.ipynb 447
1138+
# %% ../nbs/01_basics.ipynb 449
11341139
def exec_import(mod, sym):
11351140
"Import `sym` from `mod` in a new environment"
11361141
# pref = '' if __name__=='__main__' or mod[0]=='.' else '.'
11371142
return exec_new(f'from {mod} import {sym}')
11381143

1139-
# %% ../nbs/01_basics.ipynb 448
1144+
# %% ../nbs/01_basics.ipynb 450
11401145
def str2bool(s):
11411146
"Case-insensitive convert string `s` too a bool (`y`,`yes`,`t`,`true`,`on`,`1`->`True`)"
11421147
if not isinstance(s,str): return bool(s)

fastcore/xml.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/11_xml.ipynb.
22

33
# %% auto 0
4-
__all__ = ['XT', 'xt', 'to_xml', 'highlight', 'showtags', 'Html', 'Head', 'Title', 'Meta', 'Link', 'Style', 'Body', 'Pre', 'Code',
4+
__all__ = ['XT', 'xt', 'Html', 'to_xml', 'highlight', 'showtags', 'Head', 'Title', 'Meta', 'Link', 'Style', 'Body', 'Pre', 'Code',
55
'Div', 'Span', 'P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Strong', 'Em', 'B', 'I', 'U', 'S', 'Strike', 'Sub',
66
'Sup', 'Hr', 'Br', 'Img', 'A', 'Nav', 'Ul', 'Ol', 'Li', 'Dl', 'Dt', 'Dd', 'Table', 'Thead', 'Tbody', 'Tfoot',
77
'Tr', 'Th', 'Td', 'Caption', 'Col', 'Colgroup', 'Form', 'Input', 'Textarea', 'Button', 'Select', 'Option',
@@ -55,7 +55,7 @@ def xt(tag:str, *c, void_=False, **kw):
5555

5656
# %% ../nbs/11_xml.ipynb 7
5757
_g = globals()
58-
_all_ = ['Html', 'Head', 'Title', 'Meta', 'Link', 'Style', 'Body', 'Pre', 'Code',
58+
_all_ = ['Head', 'Title', 'Meta', 'Link', 'Style', 'Body', 'Pre', 'Code',
5959
'Div', 'Span', 'P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Strong', 'Em', 'B',
6060
'I', 'U', 'S', 'Strike', 'Sub', 'Sup', 'Hr', 'Br', 'Img', 'A', 'Link', 'Nav',
6161
'Ul', 'Ol', 'Li', 'Dl', 'Dt', 'Dd', 'Table', 'Thead', 'Tbody', 'Tfoot', 'Tr',
@@ -67,10 +67,17 @@ def xt(tag:str, *c, void_=False, **kw):
6767

6868
for o in _all_: _g[o] = partial(xt, o.lower())
6969

70-
# %% ../nbs/11_xml.ipynb 14
71-
def _escape(s): return '' if s is None else escape(s) if isinstance(s, str) else s
70+
# %% ../nbs/11_xml.ipynb 9
71+
def Html(*c, doctype=True, **kwargs)->XT:
72+
"An HTML tag, optionally preceeded by `!DOCTYPE HTML`"
73+
res = xt('html', *c, **kwargs)
74+
if not doctype: return res
75+
return (xt('!DOCTYPE', html=True, void_=True), res)
7276

7377
# %% ../nbs/11_xml.ipynb 15
78+
def _escape(s): return '' if s is None else escape(s) if isinstance(s, str) else s
79+
80+
# %% ../nbs/11_xml.ipynb 16
7481
def _to_attr(k,v):
7582
if isinstance(v,bool):
7683
if v==True : return str(k)
@@ -82,7 +89,7 @@ def _to_attr(k,v):
8289
if qt in v: qt = "'"
8390
return f'{k}={qt}{v}{qt}'
8491

85-
# %% ../nbs/11_xml.ipynb 16
92+
# %% ../nbs/11_xml.ipynb 17
8693
def to_xml(elm, lvl=0):
8794
"Convert `xt` element tree into an XML string"
8895
if elm is None: return ''
@@ -107,20 +114,20 @@ def to_xml(elm, lvl=0):
107114
if not isvoid: res += f'{sp}{cltag}\n'
108115
return res
109116

110-
# %% ../nbs/11_xml.ipynb 18
117+
# %% ../nbs/11_xml.ipynb 19
111118
def highlight(s, lang='xml'):
112119
"Markdown to syntax-highlight `s` in language `lang`"
113120
return f'```{lang}\n{to_xml(s)}\n```'
114121

115-
# %% ../nbs/11_xml.ipynb 19
122+
# %% ../nbs/11_xml.ipynb 20
116123
def showtags(s):
117124
return f"""<code><pre>
118125
{escape(to_xml(s))}
119126
</code></pre>"""
120127

121128
XT._repr_markdown_ = highlight
122129

123-
# %% ../nbs/11_xml.ipynb 20
130+
# %% ../nbs/11_xml.ipynb 21
124131
def __getattr__(tag):
125132
if tag.startswith('_') or tag[0].islower(): raise AttributeError
126133
def _f(*c, target_id=None, **kwargs): return xt(tag, *c, target_id=target_id, **kwargs)

nbs/01_basics.ipynb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5678,6 +5678,55 @@
56785678
" return StrEnum(name, {o:o for o in vals})"
56795679
]
56805680
},
5681+
{
5682+
"cell_type": "code",
5683+
"execution_count": null,
5684+
"metadata": {},
5685+
"outputs": [],
5686+
"source": [
5687+
"#| export\n",
5688+
"class ValEnum(str,ImportEnum):\n",
5689+
" \"An `ImportEnum` that stringifies using values\"\n",
5690+
" def __str__(self): return self.value"
5691+
]
5692+
},
5693+
{
5694+
"cell_type": "code",
5695+
"execution_count": null,
5696+
"metadata": {},
5697+
"outputs": [
5698+
{
5699+
"data": {
5700+
"text/markdown": [
5701+
"---\n",
5702+
"\n",
5703+
"#### ValEnum\n",
5704+
"\n",
5705+
"> ValEnum (value, names=None, module=None, qualname=None, type=None,\n",
5706+
"> start=1, boundary=None)\n",
5707+
"\n",
5708+
"*An `ImportEnum` that stringifies using values*"
5709+
],
5710+
"text/plain": [
5711+
"---\n",
5712+
"\n",
5713+
"#### ValEnum\n",
5714+
"\n",
5715+
"> ValEnum (value, names=None, module=None, qualname=None, type=None,\n",
5716+
"> start=1, boundary=None)\n",
5717+
"\n",
5718+
"*An `ImportEnum` that stringifies using values*"
5719+
]
5720+
},
5721+
"execution_count": null,
5722+
"metadata": {},
5723+
"output_type": "execute_result"
5724+
}
5725+
],
5726+
"source": [
5727+
"show_doc(ValEnum, title_level=4)"
5728+
]
5729+
},
56815730
{
56825731
"cell_type": "code",
56835732
"execution_count": null,

nbs/11_xml.ipynb

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@
116116
"source": [
117117
"#| export\n",
118118
"_g = globals()\n",
119-
"_all_ = ['Html', 'Head', 'Title', 'Meta', 'Link', 'Style', 'Body', 'Pre', 'Code',\n",
119+
"_all_ = ['Head', 'Title', 'Meta', 'Link', 'Style', 'Body', 'Pre', 'Code',\n",
120120
" 'Div', 'Span', 'P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Strong', 'Em', 'B',\n",
121121
" 'I', 'U', 'S', 'Strike', 'Sub', 'Sup', 'Hr', 'Br', 'Img', 'A', 'Link', 'Nav',\n",
122122
" 'Ul', 'Ol', 'Li', 'Dl', 'Dt', 'Dd', 'Table', 'Thead', 'Tbody', 'Tfoot', 'Tr',\n",
@@ -139,6 +139,21 @@
139139
"Attributes are passed as keywords. Use 'klass' and 'fr' instead of 'class' and 'for', to avoid Python reserved word clashes."
140140
]
141141
},
142+
{
143+
"cell_type": "code",
144+
"execution_count": null,
145+
"id": "39834fcb",
146+
"metadata": {},
147+
"outputs": [],
148+
"source": [
149+
"#| export\n",
150+
"def Html(*c, doctype=True, **kwargs)->XT:\n",
151+
" \"An HTML tag, optionally preceeded by `!DOCTYPE HTML`\"\n",
152+
" res = xt('html', *c, **kwargs)\n",
153+
" if not doctype: return res\n",
154+
" return (xt('!DOCTYPE', html=True, void_=True), res)"
155+
]
156+
},
142157
{
143158
"cell_type": "code",
144159
"execution_count": null,
@@ -149,16 +164,17 @@
149164
"name": "stdout",
150165
"output_type": "stream",
151166
"text": [
152-
"['html',\n",
153-
" (['head', (['title', ('Some page',), {}],), {}],\n",
154-
" ['body',\n",
155-
" (['div',\n",
156-
" ('Some text',\n",
157-
" ['input', (), {'name': 'me'}],\n",
158-
" ['img', (), {'data': 1, 'src': 'filename'}]),\n",
159-
" {'class': 'myclass'}],),\n",
160-
" {}]),\n",
161-
" {}]\n"
167+
"(['!doctype', (), {'html': True}],\n",
168+
" ['html',\n",
169+
" (['head', (['title', ('Some page',), {}],), {}],\n",
170+
" ['body',\n",
171+
" (['div',\n",
172+
" ('Some text',\n",
173+
" ['input', (), {'name': 'me'}],\n",
174+
" ['img', (), {'data': 1, 'src': 'filename'}]),\n",
175+
" {'class': 'myclass'}],),\n",
176+
" {}]),\n",
177+
" {}])\n"
162178
]
163179
}
164180
],
@@ -313,6 +329,8 @@
313329
"name": "stdout",
314330
"output_type": "stream",
315331
"text": [
332+
"<!doctype html>\n",
333+
"\n",
316334
"<html>\n",
317335
" <head>\n",
318336
" <title>Some page</title>\n",

0 commit comments

Comments
 (0)