Skip to content
This repository has been archived by the owner on Feb 4, 2021. It is now read-only.

Commit

Permalink
render_as
Browse files Browse the repository at this point in the history
  • Loading branch information
jpsca committed Oct 25, 2019
1 parent 3f7ee32 commit 4200ff5
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 66 deletions.
22 changes: 9 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,7 @@ copy('gl:jpscaletti/hecto.git', 'path/to/destination')

## How it works

The content of the files inside the project template are copied to the destination
without changes, **unless are suffixed with the extension '.tmpl'.** (you can change
that with the `render` setting). In that case, the templating engine is used to
render them.
The content of the files inside the project template are copied to the destination without changes, **unless are suffixed with the extension '.tmpl'.** (you can customize that with the `render_as` setting). In that case, the templating engine is used to render them.

A slightly customized Jinja2 templates are used. The main difference is
that variables are referenced with ``[[ name ]]`` instead of
Expand All @@ -61,11 +58,11 @@ hecto.copy(

data=DEFAULT_DATA,
*,
render=DEFAULT_RENDER,
exclude=DEFAULT_EXCLUDE,
include=[],
skip_if_exists=[],
envops={},
render_as=DEFAULT_RENDER_AS,

pretend=False,
force=False,
Expand All @@ -87,10 +84,6 @@ Uses the template in `src_path` to generate a new project at `dst_path`.
- **data** (dict):<br>
Optional. Data to be passed to the templates.

- **render** (list of str):<br>
A list of names or shell-style patterns matching files that must be rendered
with Jinja. `["*.tmpl"]` by default.

- **exclude** (list of str):<br>
Optional. A list of names or shell-style patterns matching files or folders
that must not be copied.
Expand All @@ -106,6 +99,13 @@ Uses the template in `src_path` to generate a new project at `dst_path`.
- **envops** (dict):<br>
Optional. Extra options for the Jinja template environment.

- **render_as** (function):<br>
An optional hook that takes the relative source path and the relative destination path of a file as arguments.

It should return `None` if the file must be copied as-is or the proposed destination path (that could be the same as the one given).

By default all the files with the `.tmpl` postfix are rendered and saved without that postfix. Eg: `readme.md.tmpl` becomes `readme.md`.

- **pretend** (bool):<br>
Optional. Run but do not make any changes

Expand All @@ -126,10 +126,6 @@ If a YAML file named `hecto.yaml` is found in the root of the project, it will b
Note that they become just _the defaults_, so any explicitly-passed argument will overwrite them.

```yaml
# Shell-style patterns files/folders that must be rendered.
render:
- "*.tmpl"

# Shell-style patterns files/folders that must not be copied.
exclude:
- "*.bar"
Expand Down
96 changes: 47 additions & 49 deletions hecto/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,36 +20,17 @@

__all__ = ("copy", "copy_local")

GLOBAL_DEFAULTS = {
"render": ["*.tmpl"],
"exclude": [
"~*",
"*.py[co]",
"__pycache__",
"__pycache__/*",
".git",
".git/*",
".DS_Store",
".svn",
".hg",
],
"include": [],
"skip_if_exists": [],
}

DEFAULT_DATA = {"now": datetime.datetime.utcnow}


def copy(
src_path,
dst_path,
data=None,
*,
render=None,
exclude=None,
include=None,
skip_if_exists=None,
envops=None,
render_as=None,
pretend=False,
force=False,
skip=False,
Expand All @@ -70,10 +51,6 @@ def copy(
Optional. Data to be passed to the templates in addtion to the user data from
a `hecto.json`.
- render (list):
A list of names or shell-style patterns matching files that must be rendered
with Jinja. `["*.tmpl"]` by default.
- exclude (list):
A list of names or shell-style patterns matching files or folders
that must not be copied.
Expand All @@ -91,6 +68,16 @@ def copy(
- envops (dict):
Extra options for the Jinja template environment.
- render_as (function):
An optional hook that takes the relative source path and the relative
destination path of a file as arguments.
It should return `None` if the file must be copied as-is or the proposed
destination path (that could be the same as the one given).
By default all the files with the `.tmpl` postfix are rendered and saved
without that postfix. Eg: `readme.md.tmpl` becomes `readme.md`.
- pretend (bool):
Run but do not make any changes
Expand All @@ -108,19 +95,16 @@ def copy(
if repo:
src_path = vcs.clone(repo)

_data = DEFAULT_DATA.copy()
_data.update(data or {})

try:
copy_local(
src_path,
dst_path,
data=_data,
render=render,
data=data,
exclude=exclude,
include=include,
skip_if_exists=skip_if_exists,
envops=envops,
render_as=render_as,
pretend=pretend,
force=force,
skip=skip,
Expand All @@ -131,9 +115,21 @@ def copy(
shutil.rmtree(src_path)


GLOBAL_DEFAULTS = {
"exclude": ["~*", "~*/*", ".*", ".*/*", "__pycache__", "__pycache__/*"],
"include": [".gitignore", ".gittouch", ".touch"],
"skip_if_exists": [],
}

DEFAULT_DATA = {"now": datetime.datetime.utcnow}
RE_TMPL = re.compile(r"\.tmpl$", re.IGNORECASE)


def default_render_as(src_path, dst_path):
if dst_path.suffix == ".tmpl":
return Path(re.sub(RE_TMPL, "", str(dst_path)))


def resolve_source_path(src_path):
src_path = Path(src_path).resolve()
if not src_path.exists():
Expand All @@ -148,20 +144,20 @@ def resolve_source_path(src_path):
def copy_local(
src_path,
dst_path,
data,
data=None,
*,
render=None,
exclude=None,
include=None,
skip_if_exists=None,
envops=None,
render_as=None,
**flags,
):
src_path = resolve_source_path(src_path)
dst_path = Path(dst_path).resolve()
render_as = render_as or default_render_as

user_settings = {
"render": render,
"exclude": exclude,
"include": include,
"skip_if_exists": skip_if_exists,
Expand All @@ -174,17 +170,18 @@ def copy_local(
envops.setdefault("block_end_string", "%]")
envops.setdefault("variable_start_string", "[[")
envops.setdefault("variable_end_string", "]]")
data.setdefault("folder_name", dst_path.name)
jrender = JinjaRender(src_path, data, envops)

render_patterns = [jrender.string(pattern) for pattern in config["render"]]
exclude_patterns = [jrender.string(pattern) for pattern in config["exclude"]]
include_patterns = [jrender.string(pattern) for pattern in config["include"]]
_data = DEFAULT_DATA.copy()
_data.update(data or {})
_data.setdefault("folder_name", dst_path.name)
render = JinjaRender(src_path, _data, envops)

exclude_patterns = [render.string(pattern) for pattern in config["exclude"]]
include_patterns = [render.string(pattern) for pattern in config["include"]]
skip_if_exists_patterns = [
jrender.string(pattern) for pattern in config["skip_if_exists"]
render.string(pattern) for pattern in config["skip_if_exists"]
]

must_render = make_matcher(render_patterns)
must_exclude = make_matcher(exclude_patterns)
must_include = make_matcher(include_patterns)
must_filter = make_filter(must_exclude, must_include)
Expand All @@ -195,7 +192,7 @@ def copy_local(

for folder, _, files in os.walk(str(src_path)):
rel_folder = folder.replace(str(src_path), "", 1).lstrip(os.path.sep)
rel_folder = jrender.string(rel_folder)
rel_folder = render.string(rel_folder)
rel_folder = rel_folder.replace("." + os.path.sep, ".", 1)

if must_filter(rel_folder):
Expand All @@ -206,15 +203,15 @@ def copy_local(

render_folder(dst_path, rel_folder, flags)

source_paths = get_source_paths(folder, rel_folder, files, jrender, must_filter)
source_paths = get_source_paths(folder, rel_folder, files, render, must_filter)

for source_path, rel_path in source_paths:
render_file(
dst_path,
rel_path,
source_path,
jrender,
must_render,
render,
render_as,
must_skip_if_exists,
flags,
)
Expand All @@ -234,11 +231,10 @@ def get_config(user_settings, src_path, flags):
return GLOBAL_DEFAULTS


def get_source_paths(folder, rel_folder, files, jrender, must_filter):
def get_source_paths(folder, rel_folder, files, render, must_filter):
source_paths = []
for src_name in files:
dst_name = re.sub(RE_TMPL, "", src_name)
dst_name = jrender.string(dst_name)
dst_name = render.string(src_name)
rel_path = rel_folder / dst_name

if must_filter(rel_path):
Expand All @@ -264,12 +260,14 @@ def render_folder(dst_path, rel_folder, flags):


def render_file(
dst_path, rel_path, source_path, jrender, must_render, must_skip_if_exists, flags
dst_path, rel_path, source_path, render, render_as, must_skip_if_exists, flags
):
"""Process or copy a file of the skeleton.
"""
if must_render(source_path):
content = jrender(source_path)
render_to = render_as(source_path, rel_path)
if render_to:
content = render(source_path)
rel_path = render_to
else:
content = None

Expand Down
2 changes: 1 addition & 1 deletion hecto/utils/load_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,5 @@ def _load_user_defaults(src_files):
path = Path(path)
if not path.exists():
continue
return yaml.safe_load(path.read_text())
return yaml.safe_load(path.read_text()) or {}
return {}
2 changes: 1 addition & 1 deletion mm.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"title": "Hecto",
"name": "hecto",
"pypi_name": "hecto",
"version": "1.1.0",
"version": "1.191024",
"author": "Juan-Pablo Scaletti",
"author_email": "juanpablo@jpscaletti.com",
"description": "(graph).",
Expand Down
3 changes: 1 addition & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = hecto
version= 1.1.0
version= 1.191024
author = Juan-Pablo Scaletti
author_email = juanpablo@jpscaletti.com
description = (graph).
Expand Down Expand Up @@ -41,7 +41,6 @@ dev =
tox



[flake8]
max-complexity = 10
max-line-length = 88
Expand Down

0 comments on commit 4200ff5

Please sign in to comment.