4
4
from collections import deque
5
5
from dataclasses import dataclass , field
6
6
from functools import wraps
7
+ from importlib .metadata import EntryPoint
7
8
from io import StringIO
8
9
from itertools import pairwise , zip_longest
9
10
from re import sub
10
11
from subprocess import check_output
11
12
from textwrap import dedent
12
13
from ..tangle import Markdown , SP
13
14
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()"""
20
16
21
17
22
18
@dataclass
@@ -25,6 +21,7 @@ class Python(Markdown, type="text/x-python", language="ipython3"):
25
21
26
22
* a line-for-line transformation from md to py
27
23
* characters are added, not removed **
24
+ * the doctest literate programming style is supported for source code or testing
28
25
29
26
**: doctest must modify source to work in midgy
30
27
"""
@@ -33,6 +30,8 @@ class Python(Markdown, type="text/x-python", language="ipython3"):
33
30
STRING_MARKER = ['"""' , '"""' ]
34
31
CONTINUE_MARKER = "\\ "
35
32
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.
36
35
37
36
# these entry point style references map to functiosn that code load string syntaxes
38
37
fence_methods = dict (
@@ -44,16 +43,13 @@ class Python(Markdown, type="text/x-python", language="ipython3"):
44
43
yml = f"{ YAML } :safe_load" ,
45
44
toml = f"{ TOML } :loads" ,
46
45
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" ,
57
53
)
58
54
fenced_code_blocks : list = field (
59
55
default_factory = ["python" , "python3" , "ipython3" , "ipython" , "" ].copy
@@ -95,7 +91,7 @@ def code_doctest(self, token, env):
95
91
96
92
block = self .generate_block_lines (env , token .meta ["input" ][1 ])
97
93
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 ">>> ", "... "
99
95
block = self .generate_dedent_block (block , token .meta ["min_indent" ] + 4 )
100
96
indent = SP * self .get_indent (env )
101
97
@@ -184,6 +180,9 @@ def fence_code(self, token, env):
184
180
yield self .COMMENT_MARKER
185
181
yield from self .generate_block_lines (env , token .map [1 ])
186
182
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
187
186
env .update (quoted = False , continued = False )
188
187
189
188
def fence_doctest (self , token , env ):
@@ -305,9 +304,7 @@ def get_fence_method(self, token):
305
304
lang = self .get_lang (token )
306
305
method = self .fence_methods .get (lang , lang )
307
306
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 )
311
308
return ""
312
309
313
310
def get_indent (self , env ):
@@ -478,87 +475,3 @@ def is_urls(tokens):
478
475
continue
479
476
return False
480
477
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
0 commit comments