HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Jinja2 template loader

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
loadertemplatejinja2

Problem

I am playing around with building my own back end with python 2.7 and jinja2/cgi. Originally I was defining some of my environment and loader variables at the top but figured this was not the best practice. I then defined functions for each, but they'd only get called once or twice depending on which route it decided to execute. I came up with this class idea instead that I pass into my dispatch function, which in turn passes it into whatever rendering function happens to be being executed. Is this the proper 'pythonic' way to approach having a template loader, and if not would anyone have any suggestions?

class Template(object):
    def __init__(self):
        self._environment = None

    def __call__(self, temp_name):
        return self.get_env.get_template(temp_name)

    @property
    def get_env(self):
        return self._environment

    @get_env.getter
    def get_env(self):
        if not self._environment:
            self._loader = jinja2.FileSystemLoader(searchpath=os.path.join(
              os.path.dirname(os.path.abspath(__file__)), '../templates/madlib/'))
            self._environment = jinja2.Environment(loader = self._loader)

        return self._environment


This class then allows me to just call template('templateFileName.j2') and be returned that jinja template object. If the environment is not created, the getter will create one for me.

Solution

For starter, I'd rewrite get_env a bit and introduce intermediate variables for readability:

def get_env(self):
    if not self._environment:
        cwd = os.path.dirname(os.path.abspath(__file__))
        madlib_dir = os.path.join(cwd, '..', 'templates', 'madlib')
        loader = jinja2.FileSystemLoader(searchpath=madlib_dir)
        self._environment = jinja2.Environment(loader=loader)
    return self._environment


Next I would cleanup your @property usage; since the raw decorator already defines the getter property, you can get rid of the first method:

class Template(object):
    def __init__(self):
        self._environment = None

    def __call__(self, temp_name):
        return self.get_env.get_template(temp_name)

    @property
    def get_env(self):
        if not self._environment:
            cwd = os.path.dirname(os.path.abspath(__file__))
            madlib_dir = os.path.join(cwd, '..', 'templates', 'madlib')
            loader = jinja2.FileSystemLoader(searchpath=madlib_dir)
            self._environment = jinja2.Environment(loader=loader)
        return self._environment


Lastly, since the initialization of _environment is meant to be done (and thus checked) only once, why not take advantage of attribute access that already offer us this kind of flexibility:

class Template(object):
    def __call__(self, temp_name):
        return self.environment.get_template(temp_name)

    def __getattr__(self, attribute):
        if attribute == 'environment':
            cwd = os.path.dirname(os.path.abspath(__file__))
            madlib_dir = os.path.join(cwd, '..', 'templates', 'madlib')
            loader = jinja2.FileSystemLoader(searchpath=madlib_dir)
            self.environment = env = jinja2.Environment(loader=loader)
            return env
        return super(Template, self).__getattr__(attribute)


This way, __getattr__ is only called the first time self.environment is accessed and never again. But... Hold on... The only usefull method of this class is __call__... Better write a function instead; this will bring the check back at each call but will make the intent clearer:

def load_template(template_name):
    if not hasattr(load_template, 'environment'):
        cwd = os.path.dirname(os.path.abspath(__file__))
        madlib_dir = os.path.join(cwd, '..', 'templates', 'madlib')
        loader = jinja2.FileSystemLoader(searchpath=madlib_dir)
        load_template.environment = jinja2.Environment(loader=loader)
   return load_template.environment.get_template(template_name)


So now, no need to pass an object around everywhere, just call the function when you need to.

And if you want to, EAFP can also help reduce the overhead of the hasattr test:

def load_template(template_name):
    try:
        environment = load_template.environment
    except AttributeError:
        cwd = os.path.dirname(os.path.abspath(__file__))
        madlib_dir = os.path.join(cwd, '..', 'templates', 'madlib')
        loader = jinja2.FileSystemLoader(searchpath=madlib_dir)
        load_template.environment = environment = jinja2.Environment(loader=loader)
   return environment.get_template(template_name)

Code Snippets

def get_env(self):
    if not self._environment:
        cwd = os.path.dirname(os.path.abspath(__file__))
        madlib_dir = os.path.join(cwd, '..', 'templates', 'madlib')
        loader = jinja2.FileSystemLoader(searchpath=madlib_dir)
        self._environment = jinja2.Environment(loader=loader)
    return self._environment
class Template(object):
    def __init__(self):
        self._environment = None

    def __call__(self, temp_name):
        return self.get_env.get_template(temp_name)

    @property
    def get_env(self):
        if not self._environment:
            cwd = os.path.dirname(os.path.abspath(__file__))
            madlib_dir = os.path.join(cwd, '..', 'templates', 'madlib')
            loader = jinja2.FileSystemLoader(searchpath=madlib_dir)
            self._environment = jinja2.Environment(loader=loader)
        return self._environment
class Template(object):
    def __call__(self, temp_name):
        return self.environment.get_template(temp_name)

    def __getattr__(self, attribute):
        if attribute == 'environment':
            cwd = os.path.dirname(os.path.abspath(__file__))
            madlib_dir = os.path.join(cwd, '..', 'templates', 'madlib')
            loader = jinja2.FileSystemLoader(searchpath=madlib_dir)
            self.environment = env = jinja2.Environment(loader=loader)
            return env
        return super(Template, self).__getattr__(attribute)
def load_template(template_name):
    if not hasattr(load_template, 'environment'):
        cwd = os.path.dirname(os.path.abspath(__file__))
        madlib_dir = os.path.join(cwd, '..', 'templates', 'madlib')
        loader = jinja2.FileSystemLoader(searchpath=madlib_dir)
        load_template.environment = jinja2.Environment(loader=loader)
   return load_template.environment.get_template(template_name)
def load_template(template_name):
    try:
        environment = load_template.environment
    except AttributeError:
        cwd = os.path.dirname(os.path.abspath(__file__))
        madlib_dir = os.path.join(cwd, '..', 'templates', 'madlib')
        loader = jinja2.FileSystemLoader(searchpath=madlib_dir)
        load_template.environment = environment = jinja2.Environment(loader=loader)
   return environment.get_template(template_name)

Context

StackExchange Code Review Q#157058, answer score: 2

Revisions (0)

No revisions yet.