Skip to content

Commit

Permalink
Merge pull request #9 from ccpgames/feature/dir_renderer
Browse files Browse the repository at this point in the history
Version 0.7.0 - Directory Rendering
  • Loading branch information
CCP-Zeulix authored May 30, 2024
2 parents 77812f0 + c58eda4 commit 2ee878b
Show file tree
Hide file tree
Showing 12 changed files with 319 additions and 8 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.7.0] - 2024-05-30

## Added

- A new `DirectoryRenderer` and `DirectoryTemplate` to render entire directories
of templates in one go
- Support for `{% skip_if %}` and `{% setfilename %}` tags to control directory
names and skipping by adding them to a file called `__stencil_meta__` placed in
any directory in a `DirectoryTemplate`
- The ability to run directory rendering via command line with the `-d` flag


## [0.6.0] - 2024-05-30

Expand Down
6 changes: 5 additions & 1 deletion _sandbox_input.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@ name: Bob
age: 7
colors:
favorite: Blue
weakness: Yellow
weakness: Yellow
foo:
skip_module: false
module:
name: yomoma
2 changes: 1 addition & 1 deletion ccpstencil/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = '0.6.1'
__version__ = '0.7.0'

__author__ = 'Thordur Matthiasson <thordurm@ccpgames.com>'
__license__ = 'MIT License'
Expand Down
22 changes: 18 additions & 4 deletions ccpstencil/cli/ccp_stencil/_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
]
import os
import sys
from ccptools.structs import *
from ccpstencil.structs import *
from ccpstencil.stencils import *
import logging
log = logging.getLogger(__file__)
Expand All @@ -15,12 +15,14 @@ def __init__(self):

self.template: Optional[str] = None
self.string_template: Optional[str] = None
self.directory_template: Optional[str] = None

self.input: Optional[str] = None
self.additional_arg_list: List[str] = []

self.output: Optional[str] = None
self.no_overwrite: bool = False
self.no_purge: bool = False

def _make_cwd_importable(self):
cwd = os.getcwd()
Expand All @@ -36,10 +38,16 @@ def run(self):
rnd.context = self.get_context()
res = rnd.render()
if self.output:
print(f'Wrote file: {res}')
if isinstance(res, List):
for f in res:
print(f'Wrote file: {f}')
else:
print(f'Wrote file: {res}')

def get_template(self) -> ITemplate:
if self.template:
if self.directory_template:
return DirectoryTemplate(self.directory_template)
elif self.template:
return FileTemplate(self.template)
else:
return StringTemplate(self.string_template)
Expand All @@ -60,7 +68,13 @@ def get_context(self) -> IContext:
return ctx

def get_renderer(self) -> IRenderer:
if self.output:
if self.directory_template:
if not self.output:
raise RenderError('Must specify output directory for directory rendering')
return DirectoryRenderer(output_path=self.output,
overwrite=not self.no_overwrite,
purge=not self.no_purge)
elif self.output:
return FileRenderer(self.output,
overwrite=not self.no_overwrite)
else:
Expand Down
5 changes: 5 additions & 0 deletions ccpstencil/cli/ccp_stencil/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@ def main():
' If this is a path (ends with /), the name of the rendered file will be the same as the input template.',
default='', nargs='?')
parser.add_argument('--no-overwrite', action="store_true", help='Makes sore existing output files are not overwritten')
parser.add_argument('--no-purge', action="store_true", help='Skips purging the output path when rendering directories')

input_group = parser.add_mutually_exclusive_group(required=True)
input_group.add_argument('-t', '--template', default='',
help='Template file to render')
input_group.add_argument('-s', '--string-template', default='',
help='Supply a string directly from the command line to use as a template instead of a file')
input_group.add_argument('-d', '--directory-template', default='',
help='Root directory of files and directories to render. This requires the output argument to be a directory')

args = parser.parse_args()

Expand All @@ -43,8 +46,10 @@ def main():
runner.input = args.input or None
runner.output = args.output or None
runner.no_overwrite = args.no_overwrite or False
runner.no_purge = args.no_purge or False
runner.template = args.template or None
runner.string_template = args.string_template or None
runner.directory_template = args.directory_template or None
if args.arg:
for arg in args.arg:
runner.additional_arg_list.append(arg)
Expand Down
1 change: 0 additions & 1 deletion ccpstencil/context/_alviss.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,4 @@ def nested_update(self, key_tuple: Union[str, Tuple[str]], value: Any):
self._data.update(**iters.nest_dict(list(key_tuple), value)) # noqa

def as_dict(self) -> Dict:
log.debug(f'as_dict() YAML dump:\n{self._data.as_yaml(unmaksed=True)}')
return self._data.as_dict(unmaksed=True)
1 change: 1 addition & 0 deletions ccpstencil/renderer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
from ._string import *
from ._stdout import *
from ._file import *
from ._dir import *
2 changes: 1 addition & 1 deletion ccpstencil/renderer/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def render(self):
rendered_string = self._render_as_string()
return self._output_rendered_results(rendered_string)
except CancelRendering:
log.info(f'Rendering cancelled by skip_if tag')
log.debug(f'Rendering cancelled by skip_if tag')
return None

@abc.abstractmethod
Expand Down
119 changes: 119 additions & 0 deletions ccpstencil/renderer/_dir.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
__all__ = [
'DirectoryRenderer',
]

from ccpstencil.structs import *
from pathlib import Path

from . import FileRenderer
from ._base import *
from ccpstencil.template import DirectoryTemplate
import shutil
import logging

from ..template import FileTemplate

log = logging.getLogger(__file__)


class DirectoryRenderer(_BaseRenderer):
_VALID_TEMPLATES = (DirectoryTemplate,)

def __init__(self, output_path: T_PATH,
context: Optional[IContext] = None,
template: Optional[DirectoryTemplate] = None,
overwrite: bool = True,
purge: bool = True,
**kwargs):
self._overwrite = overwrite
self._purge = purge
self._output_path = output_path
self._rendered: List[str] = []
if isinstance(self._output_path, str):
self._output_path = Path(self._output_path)
super().__init__(context, template, **kwargs)

def _ensure_root(self):
self._do_purge()
if not self._output_path.exists():
log.debug(f'Creating target output root path: {self._output_path}...')
self._output_path.mkdir(parents=True, exist_ok=True)

def _do_purge(self):
if self._purge:
if self._output_path.exists():
if not self._overwrite:
raise OutputFileExistsError(f'Purge is enabled but the target output path already exists and overwriting is disabled: {self._output_path}')
log.debug(f'Purging output path: {self._output_path}...')
shutil.rmtree(self._output_path.absolute(), ignore_errors=True)

def _make_dir(self, dir_template: DirectoryTemplate) -> bool:
full_path = self._output_path / dir_template.target_path
if dir_template.skip_me:
log.debug(f'Skipping due to skip_if tag: {full_path}...')
return False
else:
log.debug(f'Creating relative directory: {full_path}...')
full_path.mkdir(exist_ok=True)
return True

def _build_tree(self):
def _build_tree_inner(dir_template: DirectoryTemplate):
for dt in dir_template.directories:
if self._make_dir(dt):
_build_tree_inner(dt)
_build_tree_inner(self.template) # noqa

def _render_file(self, template: FileTemplate, target_path: Optional[Path] = None):
log.debug(f'Rendering {template}...')
if target_path:
p = self._output_path / target_path / template.file_name
else:
p = self._output_path / template.file_name
r = FileRenderer(output_path=p,
context=self.context,
template=template,
overwrite=self._overwrite)
res = r.render()
if target_path:
rp = target_path / template.file_name
else:
rp = template.file_name
if res is None:
log.debug(f'Skipped rendering {rp} due to skip_if tag...')
else:
log.debug(f'Rendered: {rp}')
self._rendered.append(str(p))

def _build_files(self):
def _build_files_inner(dir_template: DirectoryTemplate):
if dir_template.skip_me:
return
for f in dir_template.files:
self._render_file(f, None if dir_template.is_root else dir_template.target_path)
for dt in dir_template.directories:
_build_files_inner(dt)

_build_files_inner(self.template) # noqa

def render(self) -> List[str]:
self._pre_flight()
self._ensure_root()
self._build_tree()
self._build_files()
return self._rendered

def _output_rendered_results(self, rendered_string: str) -> str:
pass

def _render_as_string(self) -> str:
pass

@property
def output_file_name(self) -> Optional[str]:
pass

@output_file_name.setter
def output_file_name(self, value: str):
pass

1 change: 1 addition & 0 deletions ccpstencil/template/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
from ._base import *
from ._string import *
from ._file import *
from ._dir import *
Loading

0 comments on commit 2ee878b

Please sign in to comment.