Fix template name separator for Windows users

See: https://github.com/overhangio/tutor/issues/381
This commit is contained in:
Régis Behmo 2020-11-07 16:37:43 +01:00
parent 6848253b8e
commit 998dec7149
4 changed files with 35 additions and 25 deletions

View File

@ -4,6 +4,7 @@ Note: Breaking changes between versions are indicated by "💥".
## Unreleased
- [Bugfix] Fix template rendering for Windows users.
- [Improvement] Switch to `bcrypt` for htpasswd password generation, for better portability on Windows.
- [Improvement] In the openedx production docker image, add some jitter to the gunicorn worker restart process to prevent all workers from restarting at the same time.

View File

@ -28,9 +28,9 @@ class EnvTests(unittest.TestCase):
def test_is_binary_file(self):
self.assertTrue(env.is_binary_file("/home/somefile.ico"))
def test_find_path(self):
def test_find_os_path(self):
renderer = env.Renderer({}, [env.TEMPLATES_ROOT])
path = renderer.find_path("local/docker-compose.yml")
path = renderer.find_os_path("local/docker-compose.yml")
self.assertTrue(os.path.exists(path))
def test_pathjoin(self):

View File

@ -101,8 +101,10 @@ def printvalue(context, key):
try:
# Note that this will incorrectly print None values
fmt.echo(config[key])
except KeyError:
raise exceptions.TutorError("Missing configuration value: {}".format(key))
except KeyError as e:
raise exceptions.TutorError(
"Missing configuration value: {}".format(key)
) from e
config_command.add_command(save)

View File

@ -57,8 +57,11 @@ class Renderer:
environment.globals["TUTOR_VERSION"] = __version__
self.environment = environment
def iter_templates_in(self, *path):
prefix = "/".join(path)
def iter_templates_in(self, *prefix):
"""
The elements of `prefix` must contain only "/", and not os.sep.
"""
prefix = "/".join(prefix)
for template in self.environment.loader.list_templates():
if template.startswith(prefix) and self.is_part_of_env(template):
yield template
@ -88,7 +91,8 @@ class Renderer:
is_excluded = is_excluded or ignore_folder in parts
return not is_excluded
def find_path(self, path):
def find_os_path(self, template_name):
path = template_name.replace("/", os.sep)
for templates_root in self.template_roots:
full_path = os.path.join(templates_root, path)
if os.path.exists(full_path):
@ -119,35 +123,41 @@ class Renderer:
template = self.environment.from_string(text)
return self.__render(template)
def render_file(self, path):
def render_template(self, template_name):
"""
Render a template file. Return the corresponding string. If it's a binary file
(as indicated by its path), return bytes.
The template_name *always* uses "/" separators, and is not os-dependent. Do not pass the result of
os.path.join(...) to this function.
"""
if is_binary_file(path):
if is_binary_file(template_name):
# Don't try to render binary files
with open(self.find_path(path), "rb") as f:
with open(self.find_os_path(template_name), "rb") as f:
return f.read()
try:
template = self.environment.get_template(path)
template = self.environment.get_template(template_name)
except Exception:
fmt.echo_error("Error loading template " + path)
fmt.echo_error("Error loading template " + template_name)
raise
try:
return self.__render(template)
except (jinja2.exceptions.TemplateError, exceptions.TutorError):
fmt.echo_error("Error rendering template " + path)
fmt.echo_error("Error rendering template " + template_name)
raise
except Exception:
fmt.echo_error("Unknown error rendering template " + path)
fmt.echo_error("Unknown error rendering template " + template_name)
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)
def render_all_to(self, root, *prefix):
"""
`prefix` can be used to limit the templates to render.
"""
for template_name in self.iter_templates_in(*prefix):
rendered = self.render_template(template_name)
dst = os.path.join(root, template_name.replace("/", os.sep))
write_to(rendered, dst)
def __render(self, template):
@ -207,13 +217,10 @@ def save_plugin_templates(plugin, root, config):
def save_all_from(prefix, root, config):
"""
Render the templates that start with `prefix` and store them with the same
hierarchy at `root`.
hierarchy at `root`. Here, `prefix` can be the result of os.path.join(...).
"""
renderer = Renderer.instance(config)
for template in renderer.iter_templates_in(prefix):
rendered = renderer.render_file(template)
dst = os.path.join(root, template)
write_to(rendered, dst)
renderer.render_all_to(root, prefix.replace(os.sep, "/"))
def write_to(content, path):
@ -233,8 +240,8 @@ def render_file(config, *path):
Return the rendered contents of a template.
"""
renderer = Renderer.instance(config)
file_path = os.path.join(*path)
return renderer.render_file(file_path)
template_name = "/".join(path)
return renderer.render_template(template_name)
def render_dict(config):