mirror of
https://github.com/ChristianLight/tutor.git
synced 2025-01-27 15:08:23 +00:00
Env module refactoring
Better naming and tests.
This commit is contained in:
parent
a97453135e
commit
68d23c5dd5
24
tests/test_env.py
Normal file
24
tests/test_env.py
Normal file
@ -0,0 +1,24 @@
|
||||
import unittest
|
||||
|
||||
from tutor import env
|
||||
from tutor import exceptions
|
||||
|
||||
|
||||
class EnvTests(unittest.TestCase):
|
||||
def test_walk_templates(self):
|
||||
templates = list(env.walk_templates("local"))
|
||||
self.assertIn("local/docker-compose.yml", templates)
|
||||
|
||||
def test_pathjoin(self):
|
||||
self.assertEqual(
|
||||
"/tmp/env/target/dummy", env.pathjoin("/tmp", "target", "dummy")
|
||||
)
|
||||
self.assertEqual("/tmp/env/dummy", env.pathjoin("/tmp", "dummy"))
|
||||
|
||||
def test_render_str(self):
|
||||
self.assertEqual(
|
||||
"hello world", env.render_str({"name": "world"}, "hello {{ name }}")
|
||||
)
|
||||
|
||||
def test_render_str_missing_configuration(self):
|
||||
self.assertRaises(exceptions.TutorError, env.render_str, {}, "hello {{ name }}")
|
133
tutor/env.py
133
tutor/env.py
@ -13,32 +13,74 @@ TEMPLATES_ROOT = os.path.join(os.path.dirname(__file__), "templates")
|
||||
VERSION_FILENAME = "version"
|
||||
|
||||
|
||||
class Renderer:
|
||||
ENVIRONMENT = None
|
||||
|
||||
@classmethod
|
||||
def environment(cls):
|
||||
if not cls.ENVIRONMENT:
|
||||
cls.ENVIRONMENT = jinja2.Environment(
|
||||
loader=jinja2.FileSystemLoader(TEMPLATES_ROOT),
|
||||
undefined=jinja2.StrictUndefined,
|
||||
)
|
||||
return cls.ENVIRONMENT
|
||||
|
||||
@classmethod
|
||||
def render_str(cls, config, text):
|
||||
template_globals = dict(
|
||||
RAND8=utils.random_string(8), RAND24=utils.random_string(24), **config
|
||||
)
|
||||
template = cls.environment().from_string(text, globals=template_globals)
|
||||
try:
|
||||
return template.render()
|
||||
except jinja2.exceptions.UndefinedError as e:
|
||||
raise exceptions.TutorError(
|
||||
"Missing configuration value: {}".format(e.args[0])
|
||||
)
|
||||
|
||||
|
||||
def render_full(root, config):
|
||||
"""
|
||||
Render the full environment, including version information.
|
||||
"""
|
||||
for target in ["android", "apps", "k8s", "local", "webui"]:
|
||||
render_target(root, config, target)
|
||||
copy_target(root, "build")
|
||||
for subdir in ["android", "apps", "k8s", "local", "webui"]:
|
||||
render_subdir(subdir, root, config)
|
||||
copy_subdir("build", root)
|
||||
with open(pathjoin(root, VERSION_FILENAME), "w") as f:
|
||||
f.write(__version__)
|
||||
|
||||
|
||||
def render_target(root, config, target):
|
||||
def render_subdir(subdir, root, config):
|
||||
"""
|
||||
Render the templates located in `target` and store them with the same
|
||||
Render the templates located in `subdir` and store them with the same
|
||||
hierarchy at `root`.
|
||||
"""
|
||||
for src, dst in walk_templates(root, target):
|
||||
rendered = render_file(config, src)
|
||||
for path in walk_templates(subdir):
|
||||
dst = pathjoin(root, path)
|
||||
rendered = render_file(config, path)
|
||||
ensure_file_directory_exists(dst)
|
||||
with open(dst, "w") as of:
|
||||
of.write(rendered)
|
||||
|
||||
|
||||
def ensure_file_directory_exists(path):
|
||||
"""
|
||||
Create file's base directory if it does not exist.
|
||||
"""
|
||||
directory = os.path.dirname(path)
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
|
||||
def render_file(config, path):
|
||||
"""
|
||||
Return the rendered contents of a template.
|
||||
"""
|
||||
with codecs.open(path, encoding="utf-8") as fi:
|
||||
try:
|
||||
return render_str(config, fi.read())
|
||||
except jinja2.exceptions.UndefinedError:
|
||||
raise
|
||||
except jinja2.exceptions.TemplateError:
|
||||
print("Error rendering template", path)
|
||||
raise
|
||||
@ -74,31 +116,32 @@ def render_str(config, text):
|
||||
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]))
|
||||
return Renderer.render_str(config, text)
|
||||
|
||||
|
||||
def copy_target(root, target):
|
||||
def copy_subdir(subdir, root):
|
||||
"""
|
||||
Copy the templates located in `path` and store them with the same hierarchy
|
||||
at `root`.
|
||||
Copy the templates located in `subdir` and store them with the same hierarchy
|
||||
at `root`. No rendering is done here.
|
||||
"""
|
||||
for src, dst in walk_templates(root, target):
|
||||
for path in walk_templates(subdir):
|
||||
src = os.path.join(TEMPLATES_ROOT, path)
|
||||
dst = pathjoin(root, path)
|
||||
ensure_file_directory_exists(dst)
|
||||
shutil.copy(src, dst)
|
||||
|
||||
|
||||
def is_up_to_date(root):
|
||||
"""
|
||||
Check if the currently rendered version is equal to the current tutor version.
|
||||
"""
|
||||
return version(root) == __version__
|
||||
|
||||
|
||||
def version(root):
|
||||
"""
|
||||
Return the current environment version.
|
||||
Return the current environment version. If the current environment has no version,
|
||||
return "0".
|
||||
"""
|
||||
path = pathjoin(root, VERSION_FILENAME)
|
||||
if not os.path.exists(path):
|
||||
@ -108,36 +151,33 @@ def version(root):
|
||||
|
||||
def read(*path):
|
||||
"""
|
||||
Read template content located at `path`.
|
||||
Read raw content of template located at `path`.
|
||||
"""
|
||||
src = template_path(*path)
|
||||
with codecs.open(src, encoding="utf-8") as fi:
|
||||
return fi.read()
|
||||
|
||||
|
||||
def walk_templates(root, target):
|
||||
def walk_templates(subdir):
|
||||
"""
|
||||
Iterate on the template files from `templates/target`.
|
||||
Iterate on the template files from `templates/<subdir>`.
|
||||
|
||||
Yield:
|
||||
src: template path
|
||||
dst: destination path inside root
|
||||
path: template path relative to the template root
|
||||
"""
|
||||
target_root = template_path(target)
|
||||
for dirpath, _, filenames in os.walk(target_root):
|
||||
for dirpath, _, filenames in os.walk(template_path(subdir)):
|
||||
if not is_part_of_env(dirpath):
|
||||
continue
|
||||
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)
|
||||
if is_part_of_env(src):
|
||||
yield src, dst
|
||||
path = os.path.join(os.path.relpath(dirpath, TEMPLATES_ROOT), filename)
|
||||
if is_part_of_env(path):
|
||||
yield path
|
||||
|
||||
|
||||
def is_part_of_env(path):
|
||||
"""
|
||||
Determines whether a file should be rendered or not.
|
||||
"""
|
||||
basename = os.path.basename(path)
|
||||
return not (
|
||||
basename.startswith(".")
|
||||
@ -147,16 +187,35 @@ def is_part_of_env(path):
|
||||
|
||||
|
||||
def template_path(*path):
|
||||
"""
|
||||
Return the template file's absolute path.
|
||||
"""
|
||||
return os.path.join(TEMPLATES_ROOT, *path)
|
||||
|
||||
|
||||
def data_path(root, *path):
|
||||
return os.path.join(os.path.abspath(root), "data", *path)
|
||||
"""
|
||||
Return the file's absolute path inside the data directory.
|
||||
"""
|
||||
return os.path.join(root_dir(root), "data", *path)
|
||||
|
||||
|
||||
def pathjoin(root, target, *path):
|
||||
return os.path.join(base_dir(root), target, *path)
|
||||
def pathjoin(root, *path):
|
||||
"""
|
||||
Return the file's absolute path inside the environment.
|
||||
"""
|
||||
return os.path.join(base_dir(root), *path)
|
||||
|
||||
|
||||
def base_dir(root):
|
||||
return os.path.join(root, "env")
|
||||
"""
|
||||
Return the environment base directory.
|
||||
"""
|
||||
return os.path.join(root_dir(root), "env")
|
||||
|
||||
|
||||
def root_dir(root):
|
||||
"""
|
||||
Return the project root directory.
|
||||
"""
|
||||
return os.path.abspath(root)
|
||||
|
Loading…
x
Reference in New Issue
Block a user