]> git.rocketbowman.com Git - krass.git/commitdiff
Add render_files and tests.
authorKyle Bowman <kylebowman14@gmail.com>
Mon, 15 Apr 2024 02:11:11 +0000 (22:11 -0400)
committerKyle Bowman <kylebowman14@gmail.com>
Mon, 15 Apr 2024 02:11:11 +0000 (22:11 -0400)
pyproject.toml
src/krass/__init__ [new file with mode: 0644]
src/krass/render.py [new file with mode: 0644]
tests/data/test.md [new file with mode: 0644]
tests/test_render.py [new file with mode: 0644]
todo.md [new file with mode: 0644]

index 9de00c2124c14c890935b7e288bccb6ae95aa066..08dd4e85552233f5348fa3f39f44c4405ba1df23 100644 (file)
@@ -18,7 +18,7 @@ classifiers = [
 ]
 
 [project.scripts]
-render = "render:main"
+render = "krass.render:main"
 
 [project.urls]
 Homepage = "None"
diff --git a/src/krass/__init__ b/src/krass/__init__
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/krass/render.py b/src/krass/render.py
new file mode 100644 (file)
index 0000000..c58dcc7
--- /dev/null
@@ -0,0 +1,44 @@
+import os
+from pathlib import Path
+from typing import Optional
+
+from markdown_it import MarkdownIt
+
+from proto import command, get_argspecs
+from proto.io import Stdio
+
+
+def render_contents(contents: str)->str:
+    return MarkdownIt().render(contents)
+
+@command
+def render_file(infile: Optional[Path]=None, outfile: Optional[Path]=None):
+    file_contents = Stdio.read(infile)
+    output = render_contents(file_contents)
+    Stdio.write(output, outfile)
+
+@get_argspecs.register
+def bool_argspecs(annotation: bool)->dict:
+    """ Implements get_argspecs for Booleans. """
+    return {'action':'store_true'}
+
+@get_argspecs.register
+def list_argspecs(annotation: list)->dict:
+    """ Implements argspecs for a list of one or more items."""
+    return {'nargs':'+', 'type':Path}
+
+@command
+def render_files(files: list, norecursion: bool = False):
+    for path in files:
+        if path.exists and path.is_file():
+            outfile= Path(path.parent).joinpath(path.stem + ".html")
+            render_file(infile=path, outfile=outfile)             
+        elif path.exists and path.is_dir() and not norecursion:
+            children = [path.joinpath(child) for child in os.listdir(path)]
+            render_files(files=children)
+        else:
+            raise Exception(f"The specified path does not exist or is not a file: {path}.")
+
+def main():
+    render_files.parse()
+    render_files.run()
diff --git a/tests/data/test.md b/tests/data/test.md
new file mode 100644 (file)
index 0000000..cc1c075
--- /dev/null
@@ -0,0 +1,26 @@
+# Title
+
+This is a paragraph.
+
+* Unordered item one
+* Unordered item two
+
+``` python
+print("This is code")
+```
+
+## Subtitle
+
+This line has *emphasized* text.
+
+This line has **strong** text.
+
+This line has `inline code`.
+
+1. Enumerated one
+2. Enumerated two
+
+| Column One | Column Two |
+| ---------- | ---------- |
+| Entry 1, 1 | Entry 1, 2 |
+| Entry 2, 1 | Entry 2, 2 |
\ No newline at end of file
diff --git a/tests/test_render.py b/tests/test_render.py
new file mode 100644 (file)
index 0000000..3e14bd1
--- /dev/null
@@ -0,0 +1,37 @@
+import os
+import shlex
+import shutil
+from pathlib import Path
+
+import pytest
+
+from krass.render import render_file, render_files
+
+DATA = Path.cwd().joinpath("tests/data/test.md")
+
+# TODO: When you create outdir option, rewrite tests to write to tmp rather than copying.
+# TODO: Test --norecursion
+
+def test_single_file(tmp_path):
+    infile = Path(shutil.copy(DATA, tmp_path))
+    outfile = Path(tmp_path).joinpath(infile.stem + ".html")
+    render_file.parse(args=shlex.split(f"--infile {infile} --outfile={outfile}"))
+    render_file.run()
+    contents = os.listdir(tmp_path)
+    assert outfile.name in contents
+
+def test_two_files(tmp_path):
+    infile1 = Path(shutil.copy(DATA, tmp_path))
+    infile2 = Path(shutil.copy(DATA, tmp_path.joinpath("test2.md")))
+    outfile1 = Path(tmp_path).joinpath(infile1.stem + ".html")
+    outfile2 = Path(tmp_path).joinpath(infile2.stem + ".html")
+    render_files.parse(args=shlex.split(f"{infile1} {infile2}"))
+    render_files.run()
+    contents = os.listdir(tmp_path)
+    assert outfile1.name in contents
+    assert outfile2.name in contents
+
+def test_non_file():
+    with pytest.raises(Exception):
+        render_files.parse(args=shlex.split("/not/a/path"))
+        render_files.run()
diff --git a/todo.md b/todo.md
new file mode 100644 (file)
index 0000000..bc6143f
--- /dev/null
+++ b/todo.md
@@ -0,0 +1,27 @@
+# TODO: 
+
+## Krass
+* Add --outdir to render_files to separate A
+* With --outdir, clean up tests
+* BUG: In render_files: list should be list[Path] See proto.infer._parse_type bug
+    * Note: It doesn't look like you can dispatch on list[T] for various T.
+    * Per https://stackoverflow.com/questions/68381197/is-it-possible-to-use-functools-singledispatch-with-composite-nested-container-t
+    * I wonder if you can get around that by singledispatch currying
+    * e.g. dispatch on list to a generic function that is itself dispatched on T.
+* Clean up Exception raised in render_files. Make specific and push upstream.
+
+## Proto
+* Refine Stdio stuff. (Binding infile to content, define hook in command decorator)
+    * This will probably be done under the hood with functools.partial.
+    * Turn this into a context manager?
+    ``` python
+    with Stdio:
+        do_command(*args, **kwargs)
+    ```
+    * Still need a way to bind infile_contents -> do_command argument and otherwise pass args.
+    * `@command(io=Stdio, infile="contents")`
+    * Alt: `@command(io=Stdio(infile="contents"))`
+* Continue work with subparsers branch
+* Consider defining walk(predicate, function) as a way to render files.
+* Add Stdio check for Path.exists and Path.is_file
+* Add Stdio tests?
\ No newline at end of file