Skip to content

Commit 9550ff9

Browse files
committed
factor out types
1 parent 0e10134 commit 9550ff9

File tree

3 files changed

+105
-105
lines changed

3 files changed

+105
-105
lines changed

src/midgy/_magics.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
from IPython.core.magic import Magics, magics_class, line_magic, cell_magic
99
from jinja2 import Environment
1010

11-
from midgy.language.python import Python, HTML, Css, Script, Markdown
11+
from midgy.language.python import Python
12+
from midgy.types import HTML, Css, Script, Markdown
1213

1314
from ._ipython import run_ipython
1415

src/midgy/language/python.py

+17-104
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,15 @@
44
from collections import deque
55
from dataclasses import dataclass, field
66
from functools import wraps
7+
from importlib.metadata import EntryPoint
78
from io import StringIO
89
from itertools import pairwise, zip_longest
910
from re import sub
1011
from subprocess import check_output
1112
from textwrap import dedent
1213
from ..tangle import Markdown, SP
1314

14-
YAML = "yaml" # yaml library we use, users may have different preferences.
15-
TOML = "tomli" # toml library we use, users may have different preferences.
16-
17-
18-
def _shell_out(body):
19-
return check_output(body, shell=True).decode()
15+
LOAD_FENCE = """__import__("importlib").metadata.EntryPoint(None, "{}", None).load()"""
2016

2117

2218
@dataclass
@@ -25,6 +21,7 @@ class Python(Markdown, type="text/x-python", language="ipython3"):
2521
2622
* a line-for-line transformation from md to py
2723
* characters are added, not removed **
24+
* the doctest literate programming style is supported for source code or testing
2825
2926
**: doctest must modify source to work in midgy
3027
"""
@@ -33,6 +30,8 @@ class Python(Markdown, type="text/x-python", language="ipython3"):
3330
STRING_MARKER = ['"""', '"""']
3431
CONTINUE_MARKER = "\\"
3532
TERMINAL_MARKER = ";"
33+
YAML = "yaml" # yaml library we use, users may have different preferences.
34+
TOML = "tomli" # toml library we use, users may have different preferences.
3635

3736
# these entry point style references map to functiosn that code load string syntaxes
3837
fence_methods = dict(
@@ -44,16 +43,13 @@ class Python(Markdown, type="text/x-python", language="ipython3"):
4443
yml=f"{YAML}:safe_load",
4544
toml=f"{TOML}:loads",
4645
front_matter="midgy.front_matter:load",
47-
css="midgy.language.python:Css",
48-
html="midgy.language.python:HTML",
49-
markdown="midgy.language.python:Markdown",
50-
javascript="midgy.language.python:Script",
51-
graphviz="midgy.language.python:DOT",
52-
dot="midgy.language.python:DOT",
53-
md="midgy.language.python:Markdown",
54-
**{
55-
"!": "midgy.language.python:_shell_out",
56-
},
46+
css="midgy.types:Css",
47+
html="midgy.types:HTML",
48+
markdown="midgy.types:Markdown",
49+
javascript="midgy.types:Script",
50+
graphviz="midgy.types:DOT",
51+
dot="midgy.types:DOT",
52+
md="midgy.types:Markdown",
5753
)
5854
fenced_code_blocks: list = field(
5955
default_factory=["python", "python3", "ipython3", "ipython", ""].copy
@@ -95,7 +91,7 @@ def code_doctest(self, token, env):
9591

9692
block = self.generate_block_lines(env, token.meta["input"][1])
9793

98-
# normalize the input statement STRING_MARby dedenting & removing the 4 prefixes chars ">>> ", "... "
94+
# normalize the input statement by dedenting & removing the 4 prefixes chars ">>> ", "... "
9995
block = self.generate_dedent_block(block, token.meta["min_indent"] + 4)
10096
indent = SP * self.get_indent(env)
10197

@@ -184,6 +180,9 @@ def fence_code(self, token, env):
184180
yield self.COMMENT_MARKER
185181
yield from self.generate_block_lines(env, token.map[1])
186182
self.update_env(token, env)
183+
# we don't allow for continued blocks or explicit quotes with code fences.
184+
# these affordances are only possible with indented code blocks.
185+
# continutation can be acheived using parenthesis continuation
187186
env.update(quoted=False, continued=False)
188187

189188
def fence_doctest(self, token, env):
@@ -305,9 +304,7 @@ def get_fence_method(self, token):
305304
lang = self.get_lang(token)
306305
method = self.fence_methods.get(lang, lang)
307306
if ":" in method:
308-
# decode entry point style methods into importlib expressions
309-
module, method = method.partition(":")[::2]
310-
return f"""__import__("importlib").import_module("{module}").{method}"""
307+
return LOAD_FENCE.format(method)
311308
return ""
312309

313310
def get_indent(self, env):
@@ -478,87 +475,3 @@ def is_urls(tokens):
478475
continue
479476
return False
480477
return True
481-
482-
483-
def enforce_cls(callable):
484-
@wraps(callable)
485-
def main(self, *args, **kwargs):
486-
return type(self)(callable(self, *args, **kwargs))
487-
488-
return main
489-
490-
491-
class String(str):
492-
@property
493-
def data(self):
494-
return self
495-
496-
__add__ = enforce_cls(str.__add__)
497-
__mul__ = enforce_cls(str.__mul__)
498-
__rmul__ = enforce_cls(str.__rmul__)
499-
capitalize = enforce_cls(str.capitalize)
500-
format = enforce_cls(str.format)
501-
removeprefix = enforce_cls(str.removeprefix)
502-
removesuffix = enforce_cls(str.removesuffix)
503-
replace = enforce_cls(str.replace)
504-
strip = enforce_cls(str.strip)
505-
lstrip = enforce_cls(str.lstrip)
506-
rstrip = enforce_cls(str.rstrip)
507-
upper = enforce_cls(str.upper)
508-
lower = enforce_cls(str.lower)
509-
510-
@enforce_cls
511-
def render(self, *args, **kwargs):
512-
from IPython import get_ipython
513-
514-
shell = get_ipython()
515-
if shell:
516-
if shell.has_trait("environment"):
517-
return shell.environment.from_string(self).render(*args, **kwargs)
518-
object.__getattribute__(self, "render")
519-
520-
521-
class HTML(String):
522-
tag = ""
523-
524-
def _repr_html_(self):
525-
html = ""
526-
if self.tag:
527-
html += f"<{self.tag}>"
528-
html += self
529-
if self.tag:
530-
html += f"</{self.tag}>"
531-
return html
532-
533-
534-
class Css(HTML):
535-
tag = "style"
536-
537-
538-
class Script(HTML):
539-
tag = "script"
540-
541-
542-
class Markdown(str):
543-
def _repr_markdown_(self):
544-
return self
545-
546-
547-
class SVG(HTML):
548-
def _repr_svg_(self):
549-
return self
550-
551-
552-
class DOT(String):
553-
def graphviz(
554-
self,
555-
):
556-
from graphviz import Source
557-
558-
return Source(self)
559-
560-
def _repr_svg_(self):
561-
try:
562-
return self.graphviz()._repr_image_svg_xml()
563-
except (ModuleNotFoundError, ImportError):
564-
pass

src/midgy/types.py

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
from functools import wraps
2+
3+
4+
def enforce_cls(callable):
5+
@wraps(callable)
6+
def main(self, *args, **kwargs):
7+
return type(self)(callable(self, *args, **kwargs))
8+
9+
return main
10+
11+
12+
class String(str):
13+
@property
14+
def data(self):
15+
return self
16+
17+
__add__ = enforce_cls(str.__add__)
18+
__mul__ = enforce_cls(str.__mul__)
19+
__rmul__ = enforce_cls(str.__rmul__)
20+
capitalize = enforce_cls(str.capitalize)
21+
format = enforce_cls(str.format)
22+
removeprefix = enforce_cls(str.removeprefix)
23+
removesuffix = enforce_cls(str.removesuffix)
24+
replace = enforce_cls(str.replace)
25+
strip = enforce_cls(str.strip)
26+
lstrip = enforce_cls(str.lstrip)
27+
rstrip = enforce_cls(str.rstrip)
28+
upper = enforce_cls(str.upper)
29+
lower = enforce_cls(str.lower)
30+
31+
@enforce_cls
32+
def render(self, *args, **kwargs):
33+
from IPython import get_ipython
34+
35+
shell = get_ipython()
36+
if shell:
37+
from midgy._magics import get_environment
38+
39+
return get_environment().from_string(self).render(*args, **kwargs)
40+
object.__getattribute__(self, "render")
41+
42+
43+
class HTML(String):
44+
tag = ""
45+
46+
def _repr_html_(self):
47+
html = ""
48+
if self.tag:
49+
html += f"<{self.tag}>"
50+
html += self
51+
if self.tag:
52+
html += f"</{self.tag}>"
53+
return html
54+
55+
56+
class Css(HTML):
57+
tag = "style"
58+
59+
60+
class Script(HTML):
61+
tag = "script"
62+
63+
64+
class Markdown(str):
65+
def _repr_markdown_(self):
66+
return self
67+
68+
69+
class SVG(HTML):
70+
def _repr_svg_(self):
71+
return self
72+
73+
74+
class DOT(String):
75+
def graphviz(
76+
self,
77+
):
78+
from graphviz import Source
79+
80+
return Source(self)
81+
82+
def _repr_svg_(self):
83+
try:
84+
return self.graphviz()._repr_image_svg_xml()
85+
except (ModuleNotFoundError, ImportError):
86+
pass

0 commit comments

Comments
 (0)