mirror of
https://github.com/ChristianLight/tutor.git
synced 2025-01-26 06:28:24 +00:00
Add config render
command
This is going to be useful for using custom themes with user-defined variables.
This commit is contained in:
parent
c4c12b0ab8
commit
72e23f3f96
@ -2,6 +2,10 @@
|
||||
|
||||
Note: Breaking changes between versions are indicated by "💥".
|
||||
|
||||
## Unreleased
|
||||
|
||||
- [Feature] Add `config render` command
|
||||
|
||||
## 3.11.0 (2020-01-14)
|
||||
|
||||
- [Feature] Add support for simple, YAML-based plugins
|
||||
|
@ -25,6 +25,14 @@ class EnvTests(unittest.TestCase):
|
||||
self.assertIn(template_name, renderer.environment.loader.list_templates())
|
||||
self.assertNotIn(template_name, templates)
|
||||
|
||||
def test_is_binary_file(self):
|
||||
self.assertTrue(env.is_binary_file("/home/somefile.ico"))
|
||||
|
||||
def test_find_path(self):
|
||||
renderer = env.Renderer({}, [env.TEMPLATES_ROOT])
|
||||
path = renderer.find_path("local/docker-compose.yml")
|
||||
self.assertTrue(os.path.exists(path))
|
||||
|
||||
def test_pathjoin(self):
|
||||
self.assertEqual(
|
||||
"/tmp/env/target/dummy", env.pathjoin("/tmp", "target", "dummy")
|
||||
|
@ -59,6 +59,28 @@ def save(context, interactive, set_, unset):
|
||||
env.save(context.root, config)
|
||||
|
||||
|
||||
@click.command(help="Render a template folder with eventual extra configuration files")
|
||||
@click.option(
|
||||
"-x",
|
||||
"--extra-config",
|
||||
"extra_configs",
|
||||
multiple=True,
|
||||
type=click.Path(exists=True, resolve_path=True, dir_okay=False),
|
||||
help="Load extra configuration file (can be used multiple times)",
|
||||
)
|
||||
@click.argument("src", type=click.Path(exists=True, resolve_path=True))
|
||||
@click.argument("dst")
|
||||
@click.pass_obj
|
||||
def render(context, extra_configs, src, dst):
|
||||
config = tutor_config.load(context.root)
|
||||
for extra_config in extra_configs:
|
||||
tutor_config.merge(config, tutor_config.load_file(extra_config), force=True)
|
||||
|
||||
renderer = env.Renderer(config, [src])
|
||||
renderer.render_all_to(dst)
|
||||
fmt.echo_info("Templates rendered to {}".format(dst))
|
||||
|
||||
|
||||
@click.command(help="Print the project root")
|
||||
@click.pass_obj
|
||||
def printroot(context):
|
||||
@ -77,5 +99,6 @@ def printvalue(context, key):
|
||||
|
||||
|
||||
config_command.add_command(save)
|
||||
config_command.add_command(render)
|
||||
config_command.add_command(printroot)
|
||||
config_command.add_command(printvalue)
|
||||
|
@ -56,6 +56,11 @@ def load_defaults():
|
||||
return serialize.load(env.read("config.yml"))
|
||||
|
||||
|
||||
def load_file(path):
|
||||
with open(path) as f:
|
||||
return serialize.load(f.read())
|
||||
|
||||
|
||||
def load_current(root, defaults):
|
||||
"""
|
||||
Load the configuration currently stored on disk.
|
||||
|
36
tutor/env.py
36
tutor/env.py
@ -14,6 +14,7 @@ from .__about__ import __version__
|
||||
|
||||
TEMPLATES_ROOT = pkg_resources.resource_filename("tutor", "templates")
|
||||
VERSION_FILENAME = "version"
|
||||
BIN_FILE_EXTENSIONS = [".ico", ".ttf", ".png", ".jpg"]
|
||||
|
||||
|
||||
class Renderer:
|
||||
@ -38,6 +39,7 @@ class Renderer:
|
||||
|
||||
def __init__(self, config, template_roots):
|
||||
self.config = deepcopy(config)
|
||||
self.template_roots = template_roots
|
||||
|
||||
# Create environment
|
||||
environment = jinja2.Environment(
|
||||
@ -68,6 +70,13 @@ class Renderer:
|
||||
"""
|
||||
yield from self.iter_templates_in(subdir + "/")
|
||||
|
||||
def find_path(self, path):
|
||||
for templates_root in self.template_roots:
|
||||
full_path = os.path.join(templates_root, path)
|
||||
if os.path.exists(full_path):
|
||||
return full_path
|
||||
raise ValueError("Template path does not exist")
|
||||
|
||||
def patch(self, name, separator="\n", suffix=""):
|
||||
"""
|
||||
Render calls to {{ patch("...") }} in environment templates from plugin patches.
|
||||
@ -93,11 +102,17 @@ class Renderer:
|
||||
return self.__render(template)
|
||||
|
||||
def render_file(self, path):
|
||||
if is_binary_file(path):
|
||||
# Don't try to render binary files
|
||||
with open(self.find_path(path), "rb") as f:
|
||||
return f.read()
|
||||
|
||||
try:
|
||||
template = self.environment.get_template(path)
|
||||
except Exception:
|
||||
fmt.echo_error("Error loading template " + path)
|
||||
raise
|
||||
|
||||
try:
|
||||
return self.__render(template)
|
||||
except (jinja2.exceptions.TemplateError, exceptions.TutorError):
|
||||
@ -107,6 +122,12 @@ class Renderer:
|
||||
fmt.echo_error("Unknown error rendering template " + path)
|
||||
raise
|
||||
|
||||
def render_all_to(self, root):
|
||||
for template in self.iter_templates_in():
|
||||
rendered = self.render_file(template)
|
||||
dst = os.path.join(root, template)
|
||||
write_to(rendered, dst)
|
||||
|
||||
def __render(self, template):
|
||||
try:
|
||||
return template.render(**self.config)
|
||||
@ -165,8 +186,14 @@ def save_all_from(prefix, root, config):
|
||||
|
||||
|
||||
def write_to(content, path):
|
||||
"""
|
||||
Content can be either str or bytes.
|
||||
"""
|
||||
open_mode = "w"
|
||||
if isinstance(content, bytes):
|
||||
open_mode += "b"
|
||||
utils.ensure_file_directory_exists(path)
|
||||
with open(path, "w") as of:
|
||||
with open(path, open_mode) as of:
|
||||
of.write(content)
|
||||
|
||||
|
||||
@ -262,10 +289,15 @@ def is_part_of_env(path):
|
||||
is_excluded = False
|
||||
is_excluded = is_excluded or basename.startswith(".") or basename.endswith(".pyc")
|
||||
is_excluded = is_excluded or basename == "__pycache__"
|
||||
is_excluded = is_excluded or "partials" in parts
|
||||
is_excluded = is_excluded or "partials" in parts or ".git" in parts
|
||||
return not is_excluded
|
||||
|
||||
|
||||
def is_binary_file(path):
|
||||
ext = os.path.splitext(path)[1]
|
||||
return ext in BIN_FILE_EXTENSIONS
|
||||
|
||||
|
||||
def template_path(*path, templates_root=TEMPLATES_ROOT):
|
||||
"""
|
||||
Return the template file's absolute path.
|
||||
|
Loading…
x
Reference in New Issue
Block a user