diff --git a/TODO.md b/TODO.md index a0d5482..ffb5dde 100644 --- a/TODO.md +++ b/TODO.md @@ -5,3 +5,4 @@ * pre- and post-scripts that will be run from __main__, either some shipped with pixywerk or project-level. * Library of template modules? ATOM et al. * Some off the shelf website templates and a template manager. +* Live refreshing server thing which maps a pixywerk tree into a web server's memory and updates on change. diff --git a/pixywerk2/__init__.py b/pixywerk2/__init__.py index e69de29..0404d81 100644 --- a/pixywerk2/__init__.py +++ b/pixywerk2/__init__.py @@ -0,0 +1 @@ +__version__ = '0.3.0' diff --git a/pixywerk2/__main__.py b/pixywerk2/__main__.py index ad656e3..72f5cd0 100644 --- a/pixywerk2/__main__.py +++ b/pixywerk2/__main__.py @@ -17,9 +17,16 @@ from .metadata import MetaTree from .processchain import ProcessorChains from .processors.processors import PassthroughException from .pygments import pygments_get_css, pygments_markup_contents_html -from .template_tools import (date_iso8601, file_content, file_list, - file_list_hier, file_metadata, file_name, - file_raw, time_iso8601) +from .template_tools import ( + date_iso8601, + file_content, + file_list, + file_list_hier, + file_metadata, + file_name, + file_raw, + time_iso8601, +) logger = logging.getLogger() @@ -38,12 +45,11 @@ def get_args(args: List[str]) -> argparse.Namespace: "-c", "--clean", help="Remove the target tree before proceeding (by renaming to .bak).", action="store_true" ) parser.add_argument("-s", "--safe", help="Abort if the target directory already exists.", action="store_true") + parser.add_argument("-f", "--follow-links", help="Follow symbolic links in the input tree.", action="store_true") parser.add_argument("-t", "--template", help="The template directory (default: root/templates)", default=None) parser.add_argument("-d", "--dry-run", help="Perform a dry-run.", action="store_true") parser.add_argument("-v", "--verbose", help="Output verbosely.", action="store_true") parser.add_argument("--processors", help="Specify a path to a processor configuration file.", default=None) - # parser.add_argument("--prescript", help="Specify one or more prescripts to run (in order specified) with context of the compile.", default=[], action="append") - # parser.add_argument("--postscript", help="Specify one or more postsscripts to run (in order specified) with context of the compile.", default=[], action="append") result = parser.parse_args(args) # validate arguments @@ -102,7 +108,7 @@ def main() -> int: "pygments_markup_contents_html": pygments_markup_contents_html, } - for root, _, files in os.walk(args.root): + for root, _, files in os.walk(args.root, followlinks=args.follow_links): workroot = os.path.relpath(root, args.root) if workroot == ".": workroot = "" diff --git a/pixywerk2/metadata.py b/pixywerk2/metadata.py index 3549c35..f92e534 100644 --- a/pixywerk2/metadata.py +++ b/pixywerk2/metadata.py @@ -93,7 +93,7 @@ class MetaTree: """Retrieve the metadata for a given path The general procedure is to iterate the tree, at each level -m load .meta (JSON formatted dictionary) for that level, and + load .meta (JSON formatted dictionary) for that level, and then finally load the path.meta, and merge these dictionaries in descendant order. diff --git a/pixywerk2/template_tools.py b/pixywerk2/template_tools.py index 641a698..fff688a 100644 --- a/pixywerk2/template_tools.py +++ b/pixywerk2/template_tools.py @@ -2,7 +2,7 @@ import datetime import glob import itertools import os -from typing import Callable, Dict, Iterable, List, Union, cast +from typing import Callable, Dict, Iterable, List, Union, cast, Tuple import pytz @@ -11,29 +11,32 @@ from .processchain import ProcessorChains def file_list(root: str, listcache: Dict) -> Callable: - def get_file_list(path_glob: str, *, sort_order: str = "ctime", reverse: bool = False, limit: int = 0) -> Iterable: + def get_file_list(path_glob: Union[str, List[str], Tuple[str]], *, sort_order: str = "ctime", reverse: bool = False, limit: int = 0) -> Iterable: stattable = cast(List, []) - if path_glob in listcache: - stattable = listcache[path_glob] - else: - for fil in glob.glob(os.path.join(root, path_glob)): - if os.path.isdir(fil): - continue - if fil.endswith(".meta") or fil.endswith("~"): - continue - st = os.stat(fil) - stattable.append( - { - "file_path": os.path.relpath(fil, root), - "file_name": os.path.split(fil)[-1], - "mtime": st.st_mtime, - "ctime": st.st_ctime, - "size": st.st_size, - "ext": os.path.splitext(fil)[1], - } - ) - listcache[path_glob] = stattable - ret = sorted(stattable, key=lambda x: x[sort_order], reverse=reverse) + if isinstance(path_glob, str): + path_glob = [path_glob] + for pglob in path_glob: + if pglob in listcache: + stattable.extend(listcache[pglob]) + else: + for fil in glob.glob(os.path.join(root, pglob)): + if os.path.isdir(fil): + continue + if fil.endswith(".meta") or fil.endswith("~"): + continue + st = os.stat(fil) + stattable.append( + { + "file_path": os.path.relpath(fil, root), + "file_name": os.path.split(fil)[-1], + "mtime": st.st_mtime, + "ctime": st.st_ctime, + "size": st.st_size, + "ext": os.path.splitext(fil)[1], + } + ) + listcache[pglob] = stattable + ret = sorted(stattable, key=lambda x: x[sort_order], reverse=reverse) if limit > 0: return itertools.islice(ret, limit) return ret @@ -48,9 +51,6 @@ def file_list_hier(root: str, flist: Callable) -> Callable: def get_file_list_hier(path: str, glob: str, *, sort_order: str = "ctime", reverse: bool = False) -> Iterable: output = [] - def collect(pth): - print(arg, pth, files) - for pth in os.walk(os.path.join(root, path)): output.extend( flist( diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e69de29 diff --git a/tox.ini b/tox.ini index 53ca9b0..d6a9161 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py{36,37}-{code-quality, unit} #, py37-sphinx +envlist=py{36,37,38,39}-{code-quality, unit} #, py37-sphinx skipsdist = true [testenv] @@ -17,6 +17,8 @@ commands = basepython = py36: python3.6 py37: python3.7 + py38: python3.8 + py39: python3.9 [flake8] max-line-length = 120