-
Notifications
You must be signed in to change notification settings - Fork 313
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for custom Jinja filters #477
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
""" | ||
|
||
from datetime import datetime | ||
import glob | ||
import imp | ||
import logging | ||
import os | ||
|
@@ -37,7 +38,6 @@ class Template(object): | |
|
||
def __init__(self, path, sceptre_user_data): | ||
self.logger = logging.getLogger(__name__) | ||
|
||
self.path = path | ||
self.sceptre_user_data = sceptre_user_data | ||
self.name = os.path.basename(path).split(".")[0] | ||
|
@@ -97,7 +97,7 @@ def _call_sceptre_handler(self): | |
# NB: this is a horrible hack... | ||
relpath = os.path.relpath(self.path, os.getcwd()).split(os.path.sep) | ||
relpaths_to_add = [ | ||
os.path.sep.join(relpath[:i+1]) | ||
os.path.sep.join(relpath[:i + 1]) | ||
for i in range(len(relpath[:-1])) | ||
] | ||
# Add any directory between the current working directory and where | ||
|
@@ -270,8 +270,7 @@ def _create_bucket(self, region, bucket_name, connection_manager): | |
} | ||
) | ||
|
||
@staticmethod | ||
def _render_jinja_template(template_dir, filename, jinja_vars): | ||
def _render_jinja_template(self, template_dir, filename, jinja_vars): | ||
""" | ||
Renders a jinja template. | ||
|
||
|
@@ -291,8 +290,50 @@ def _render_jinja_template(template_dir, filename, jinja_vars): | |
logger.debug("%s Rendering CloudFormation template", filename) | ||
env = jinja2.Environment( | ||
loader=jinja2.FileSystemLoader(template_dir), | ||
undefined=jinja2.StrictUndefined | ||
undefined=jinja2.StrictUndefined, | ||
) | ||
env.filters.update(self.jinja_filters) | ||
template = env.get_template(filename) | ||
body = template.render(**jinja_vars) | ||
return body | ||
|
||
@property | ||
def jinja_filters(self): | ||
""" | ||
Returns cached jinja filters if already computed, or loads/caches | ||
the filters from python files if this is the first time. | ||
|
||
This (optional) feature relies on the environment variable | ||
${SCEPTRE_JINJA_FILTER_ROOT} because we don't have easy access | ||
from here to CLI parsing info or global configuration info. | ||
""" | ||
# give back cached jinja filters if available | ||
if hasattr(self, '_jinja_filters'): | ||
return self._jinja_filters | ||
# load jinja filters if not already cached | ||
env_var_name = 'SCEPTRE_JINJA_FILTER_ROOT' | ||
if env_var_name not in os.environ: | ||
msg = '${} is not set, no extra jinja filters will be loaded' | ||
self.logger.debug(msg.format(env_var_name)) | ||
else: | ||
filter_dir = os.environ[env_var_name] | ||
if not os.path.exists(filter_dir): | ||
err = '${} is set, but directory does not exist!' | ||
raise ValueError(err.format(env_var_name)) | ||
else: | ||
msg = 'loading jinja filters from: {}' | ||
self.logger.debug(msg.format(filter_dir)) | ||
self._jinja_filters = {} | ||
for fpath in glob.glob(os.path.join(filter_dir, '*.py')): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally this would be done using entry points, as per the new hooks, resolves code There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there an example of that in v1, or would this need backport from v2? |
||
self.logger.debug(' loading filter: {}'.format(fpath)) | ||
mod = imp.load_source('dynamic_jinja_filters', fpath) | ||
for name in dir(mod): | ||
# ignore anything like private methods | ||
if name.startswith('_'): | ||
continue | ||
else: | ||
fxn = getattr(mod, name) | ||
# ignore things that aren't callables | ||
if callable(fxn): | ||
self._jinja_filters[name] = fxn | ||
return self._jinja_filters |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should be reaching env_vars directly, can we read this as environment config for consistency
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How exactly should that be done?
Template()
seems to only be instantiated with withscepre_user_data
, notenvironment_config
. See also this related issue.Does a path from environment_config or stack_config actually help here? I think what is really needed is the exact value passed from the CLI argument
--dir
, because even given an environment or stack path, if env folders are nested then one couldn't be sure how far to go back up the directory tree to get to the sceptre root (i.e. the folder that holdsconfig
,template
,hooks
,resolvers
, and by symmetryjinja_filters
).