Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Byte-compiled / optimized / DLL files
__jinja__/
__pycache__/
*.py[cod]
*$py.class
Expand Down
Empty file added mig/assets/templates/.gitkeep
Empty file.
Empty file added mig/lib/__init__.py
Empty file.
138 changes: 138 additions & 0 deletions mig/lib/templates/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# --- BEGIN_HEADER ---
#
# base - shared base helper functions
# Copyright (C) 2003-2024 The MiG Project by the Science HPC Center at UCPH
#
# This file is part of MiG.
#
# MiG is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# MiG is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

Check warning on line 23 in mig/lib/templates/__init__.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

line too long (81 > 80 characters)

Check warning on line 23 in mig/lib/templates/__init__.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

line too long (81 > 80 characters)
#
# -- END_HEADER ---
#

import errno
from collections import ChainMap
from jinja2 import meta as jinja2_meta, select_autoescape, Environment, \
FileSystemLoader, FileSystemBytecodeCache
import os
import weakref

from mig.shared.compat import PY2
from mig.shared.defaults import MIG_BASE


class _BonundTemplate:
def __init__(self, template, template_args):
self.tmpl = template
self.args = template_args


def render(self):

Check warning on line 45 in mig/lib/templates/__init__.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

too many blank lines (2)

Check warning on line 45 in mig/lib/templates/__init__.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

too many blank lines (2)
return self.tmpl.render(**self.args)


class _FormatContext:
def __init__(self, configuration):
self.output_format = None
self.configuration = configuration
self.script_map = {}
self.style_map = {}

def __getitem__(self, key):
return self.__dict__[key]

def __iter__(self):
return iter(self.__dict__)

def extend(self, template, template_args):
return _BonundTemplate(template, ChainMap(template_args, self))


class TemplateStore:
def __init__(self, template_dirs, cache_dir=None, extra_globals=None):
assert cache_dir is not None

self._cache_dir = cache_dir
self._template_globals = extra_globals
self._template_environment = Environment(
loader=FileSystemLoader(template_dirs),
bytecode_cache=FileSystemBytecodeCache(cache_dir, '%s'),
autoescape=select_autoescape()
)

@property
def cache_dir(self):
return self._cache_dir

@property
def context(self):
return self._template_globals

def _get_template(self, template_fqname):
return self._template_environment.get_template(template_fqname)

def grab_template(self, template_name, template_group, output_format, template_globals=None, **kwargs):

Check warning on line 89 in mig/lib/templates/__init__.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

line too long (107 > 80 characters)

Check warning on line 89 in mig/lib/templates/__init__.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

line too long (107 > 80 characters)
template_fqname = "%s_%s.%s.jinja" % (
template_group, template_name, output_format)
return self._template_environment.get_template(template_fqname, globals=template_globals)

Check warning on line 92 in mig/lib/templates/__init__.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

line too long (97 > 80 characters)

Check warning on line 92 in mig/lib/templates/__init__.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

line too long (97 > 80 characters)

def list_templates(self):
return [t for t in self._template_environment.list_templates() if t.endswith('.jinja')]

Check warning on line 95 in mig/lib/templates/__init__.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

line too long (95 > 80 characters)

Check warning on line 95 in mig/lib/templates/__init__.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

line too long (95 > 80 characters)

def extract_variables(self, template_fqname):
template = self._template_environment.get_template(template_fqname)
with open(template.filename) as f:
template_source = f.read()
ast = self._template_environment.parse(template_source)
return jinja2_meta.find_undeclared_variables(ast)

@staticmethod
def populated(template_dirs, cache_dir=None, context=None):
assert cache_dir is not None

try:
os.mkdir(cache_dir)
except OSError as direxc:
if direxc.errno != errno.EEXIST: # FileExistsError
raise

store = TemplateStore(
template_dirs, cache_dir=cache_dir, extra_globals=context)

for template_fqname in store.list_templates():
store._get_template(template_fqname)

return store


def init_global_templates(configuration):
template_division = configuration.division(section_name="TEMPLATES")

_context = configuration.context()

try:
return configuration.context(namespace='templates')
except KeyError as exc:
pass

store = TemplateStore.populated(
[template_division.source_dir],
cache_dir=template_division.cache_dir,
context=_FormatContext(configuration)
)
return configuration.context_set(store, namespace='templates')
78 changes: 78 additions & 0 deletions mig/lib/templates/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# --- BEGIN_HEADER ---
#
# base - shared base helper functions
# Copyright (C) 2003-2024 The MiG Project by the Science HPC Center at UCPH
#
# This file is part of MiG.
#
# MiG is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# MiG is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

Check warning on line 23 in mig/lib/templates/__main__.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

line too long (81 > 80 characters)

Check warning on line 23 in mig/lib/templates/__main__.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

line too long (81 > 80 characters)
#
# -- END_HEADER ---
#

from types import SimpleNamespace
import os
import sys

from mig.lib.templates import init_global_templates
from mig.shared.conf import get_configuration_object


def warn(message):
print(message, file=sys.stderr, flush=True)


def main(args, _print=print):
configuration = get_configuration_object(config_file=args.config_file)
template_store = init_global_templates(configuration)

command = args.command
if command == 'show':
print(template_store.list_templates())
elif command == 'prime':
try:
os.mkdir(template_store.cache_dir)
except FileExistsError:
pass

for template_fqname in template_store.list_templates():
template_store._get_template(template_fqname)
elif command == 'vars':
for template_ref in template_store.list_templates():
_print("<%s>" % (template_ref,))
for var in template_store.extract_variables(template_ref):
_print(" {{%s}}" % (var,))
_print("</%s>" % (template_ref,))
else:
raise RuntimeError("unknown command: %s" % (command,))


if __name__ == '__main__':
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-c', dest='config_file', default=None)
parser.add_argument('command')
args = parser.parse_args()

try:
main(args)
sys.exit(0)
except Exception as exc:
warn(str(exc))
sys.exit(1)
4 changes: 2 additions & 2 deletions mig/shared/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import os
import sys

from mig.shared.configuration import RuntimeConfiguration
from mig.shared.defaults import MIG_ENV
from mig.shared.fileio import unpickle

Expand All @@ -42,7 +43,6 @@ def get_configuration_object(config_file=None, skip_log=False,
and disable_auth_log arguments are passed on to allow skipping the default
log initialization and disabling auth log for unit tests.
"""
from mig.shared.configuration import Configuration
if config_file:
_config_file = config_file
elif os.environ.get('MIG_CONF', None):
Expand All @@ -63,7 +63,7 @@ def get_configuration_object(config_file=None, skip_log=False,
skip_log = True
disable_auth_log = True

configuration = Configuration(_config_file, False, skip_log,
configuration = RuntimeConfiguration(_config_file, False, skip_log,
disable_auth_log)
return configuration

Expand Down
Loading
Loading