Skip to content

Commit

Permalink
factor out types
Browse files Browse the repository at this point in the history
  • Loading branch information
tonyfast committed Aug 23, 2024
1 parent 0e10134 commit 9550ff9
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 105 deletions.
3 changes: 2 additions & 1 deletion src/midgy/_magics.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
from IPython.core.magic import Magics, magics_class, line_magic, cell_magic
from jinja2 import Environment

from midgy.language.python import Python, HTML, Css, Script, Markdown
from midgy.language.python import Python
from midgy.types import HTML, Css, Script, Markdown

from ._ipython import run_ipython

Expand Down
121 changes: 17 additions & 104 deletions src/midgy/language/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,15 @@
from collections import deque
from dataclasses import dataclass, field
from functools import wraps
from importlib.metadata import EntryPoint
from io import StringIO
from itertools import pairwise, zip_longest
from re import sub
from subprocess import check_output
from textwrap import dedent
from ..tangle import Markdown, SP

YAML = "yaml" # yaml library we use, users may have different preferences.
TOML = "tomli" # toml library we use, users may have different preferences.


def _shell_out(body):
return check_output(body, shell=True).decode()
LOAD_FENCE = """__import__("importlib").metadata.EntryPoint(None, "{}", None).load()"""


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

# these entry point style references map to functiosn that code load string syntaxes
fence_methods = dict(
Expand All @@ -44,16 +43,13 @@ class Python(Markdown, type="text/x-python", language="ipython3"):
yml=f"{YAML}:safe_load",
toml=f"{TOML}:loads",
front_matter="midgy.front_matter:load",
css="midgy.language.python:Css",
html="midgy.language.python:HTML",
markdown="midgy.language.python:Markdown",
javascript="midgy.language.python:Script",
graphviz="midgy.language.python:DOT",
dot="midgy.language.python:DOT",
md="midgy.language.python:Markdown",
**{
"!": "midgy.language.python:_shell_out",
},
css="midgy.types:Css",
html="midgy.types:HTML",
markdown="midgy.types:Markdown",
javascript="midgy.types:Script",
graphviz="midgy.types:DOT",
dot="midgy.types:DOT",
md="midgy.types:Markdown",
)
fenced_code_blocks: list = field(
default_factory=["python", "python3", "ipython3", "ipython", ""].copy
Expand Down Expand Up @@ -95,7 +91,7 @@ def code_doctest(self, token, env):

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

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

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

def fence_doctest(self, token, env):
Expand Down Expand Up @@ -305,9 +304,7 @@ def get_fence_method(self, token):
lang = self.get_lang(token)
method = self.fence_methods.get(lang, lang)
if ":" in method:
# decode entry point style methods into importlib expressions
module, method = method.partition(":")[::2]
return f"""__import__("importlib").import_module("{module}").{method}"""
return LOAD_FENCE.format(method)
return ""

def get_indent(self, env):
Expand Down Expand Up @@ -478,87 +475,3 @@ def is_urls(tokens):
continue
return False
return True


def enforce_cls(callable):
@wraps(callable)
def main(self, *args, **kwargs):
return type(self)(callable(self, *args, **kwargs))

return main


class String(str):
@property
def data(self):
return self

__add__ = enforce_cls(str.__add__)
__mul__ = enforce_cls(str.__mul__)
__rmul__ = enforce_cls(str.__rmul__)
capitalize = enforce_cls(str.capitalize)
format = enforce_cls(str.format)
removeprefix = enforce_cls(str.removeprefix)
removesuffix = enforce_cls(str.removesuffix)
replace = enforce_cls(str.replace)
strip = enforce_cls(str.strip)
lstrip = enforce_cls(str.lstrip)
rstrip = enforce_cls(str.rstrip)
upper = enforce_cls(str.upper)
lower = enforce_cls(str.lower)

@enforce_cls
def render(self, *args, **kwargs):
from IPython import get_ipython

shell = get_ipython()
if shell:
if shell.has_trait("environment"):
return shell.environment.from_string(self).render(*args, **kwargs)
object.__getattribute__(self, "render")


class HTML(String):
tag = ""

def _repr_html_(self):
html = ""
if self.tag:
html += f"<{self.tag}>"
html += self
if self.tag:
html += f"</{self.tag}>"
return html


class Css(HTML):
tag = "style"


class Script(HTML):
tag = "script"


class Markdown(str):
def _repr_markdown_(self):
return self


class SVG(HTML):
def _repr_svg_(self):
return self


class DOT(String):
def graphviz(
self,
):
from graphviz import Source

return Source(self)

def _repr_svg_(self):
try:
return self.graphviz()._repr_image_svg_xml()
except (ModuleNotFoundError, ImportError):
pass
86 changes: 86 additions & 0 deletions src/midgy/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from functools import wraps


def enforce_cls(callable):
@wraps(callable)
def main(self, *args, **kwargs):
return type(self)(callable(self, *args, **kwargs))

return main


class String(str):
@property
def data(self):
return self

__add__ = enforce_cls(str.__add__)
__mul__ = enforce_cls(str.__mul__)
__rmul__ = enforce_cls(str.__rmul__)
capitalize = enforce_cls(str.capitalize)
format = enforce_cls(str.format)
removeprefix = enforce_cls(str.removeprefix)
removesuffix = enforce_cls(str.removesuffix)
replace = enforce_cls(str.replace)
strip = enforce_cls(str.strip)
lstrip = enforce_cls(str.lstrip)
rstrip = enforce_cls(str.rstrip)
upper = enforce_cls(str.upper)
lower = enforce_cls(str.lower)

@enforce_cls
def render(self, *args, **kwargs):
from IPython import get_ipython

shell = get_ipython()
if shell:
from midgy._magics import get_environment

return get_environment().from_string(self).render(*args, **kwargs)
object.__getattribute__(self, "render")


class HTML(String):
tag = ""

def _repr_html_(self):
html = ""
if self.tag:
html += f"<{self.tag}>"
html += self
if self.tag:
html += f"</{self.tag}>"
return html


class Css(HTML):
tag = "style"


class Script(HTML):
tag = "script"


class Markdown(str):
def _repr_markdown_(self):
return self


class SVG(HTML):
def _repr_svg_(self):
return self


class DOT(String):
def graphviz(
self,
):
from graphviz import Source

return Source(self)

def _repr_svg_(self):
try:
return self.graphviz()._repr_image_svg_xml()
except (ModuleNotFoundError, ImportError):
pass

0 comments on commit 9550ff9

Please sign in to comment.