7
0
mirror of https://github.com/ChristianLight/tutor.git synced 2024-05-31 13:20:48 +00:00
tutor/tutor/env.py
Régis Behmo 07b3d113d4 Simplify environment generation
Environment is no longer generated separately for each target, but only
once the configuration is saved.

Note that the environment is automatically updated during
re-configuration, based on a "version" file stored in the environment.
2019-03-18 18:34:42 +01:00

135 lines
3.6 KiB
Python

import codecs
import os
import shutil
import jinja2
from . import exceptions
from . import utils
from .__about__ import __version__
TEMPLATES_ROOT = os.path.join(os.path.dirname(__file__), "templates")
VERSION_FILENAME = "version"
def render_full(root, config):
"""
Render the full environment, including version information.
"""
for target in ["apps", "k8s", "local", "webui"]:
render_target(root, config, target)
copy_target(root, "build")
with open(pathjoin(root, VERSION_FILENAME), 'w') as f:
f.write(__version__)
def render_target(root, config, target):
"""
Render the templates located in `target` and store them with the same
hierarchy at `root`.
"""
for src, dst in walk_templates(root, target):
if is_part_of_env(src):
with codecs.open(src, encoding='utf-8') as fi:
substituted = render_str(fi.read(), config)
with open(dst, "w") as of:
of.write(substituted)
def render_dict(config):
"""
Render the values from the dict. This is useful for rendering the default
values from config.yml.
Args:
config (dict)
"""
rendered = {}
for key, value in config.items():
if isinstance(value, str):
rendered[key] = render_str(value, config)
else:
rendered[key] = value
for k, v in rendered.items():
config[k] = v
pass
def render_str(text, config):
"""
Args:
text (str)
config (dict)
Return:
substituted (str)
"""
template = jinja2.Template(text, undefined=jinja2.StrictUndefined)
try:
return template.render(
RAND8=utils.random_string(8),
RAND24=utils.random_string(24),
**config
)
except jinja2.exceptions.UndefinedError as e:
raise exceptions.TutorError("Missing configuration value: {}".format(e.args[0]))
def copy_target(root, target):
"""
Copy the templates located in `path` and store them with the same hierarchy
at `root`.
"""
for src, dst in walk_templates(root, target):
if is_part_of_env(src):
shutil.copy(src, dst)
def is_part_of_env(path):
return not os.path.basename(path).startswith(".")
def is_up_to_date(root):
return version(root) == __version__
def version(root):
"""
Return the current environment version.
"""
path = pathjoin(root, VERSION_FILENAME)
if not os.path.exists(path):
return "0"
return open(path).read().strip()
def read(*path):
"""
Read template content located at `path`.
"""
src = os.path.join(TEMPLATES_ROOT, *path)
with codecs.open(src, encoding='utf-8') as fi:
return fi.read()
def walk_templates(root, target):
"""
Iterate on the template files from `templates/target`.
Yield:
src: template path
dst: destination path inside root
"""
target_root = os.path.join(TEMPLATES_ROOT, target)
for dirpath, _, filenames in os.walk(os.path.join(TEMPLATES_ROOT, target)):
dst_dir = pathjoin(
root, target,
os.path.relpath(dirpath, target_root)
)
if not os.path.exists(dst_dir):
os.makedirs(dst_dir)
for filename in filenames:
src = os.path.join(dirpath, filename)
dst = os.path.join(dst_dir, filename)
yield src, dst
def data_path(root, *path):
return os.path.join(os.path.abspath(root), "data", *path)
def pathjoin(root, target, *path):
return os.path.join(base_dir(root), target, *path)
def base_dir(root):
return os.path.join(root, "env")