Commit 41d90e26 authored by Kirill Smelkov's avatar Kirill Smelkov

software/ors-amarisoft: slapos-render-config: Remove duplicated code from slapos.recipe.template

For its operation slapos-render-config mimics jinja2 templates rendering
done for real in slapos. But to do so it currently duplicates the code
from slapos.recipe.template.

We can do the same by reusing slapos.recipe.template instead of
duplicating code from there.

-> Do it.

The output of generated gnb.cfg is the same before and after this patch.

/cc @xavier_thompson, @Daetalus
/reviewed-by @jhuge, @lu.xu, @tomo
/reviewed-on nexedi/slapos!1462
parent dcc326cd
Pipeline #30926 passed with stage
in 0 seconds
......@@ -3,6 +3,37 @@
# It mimics the way config files are generated during the build but runs much
# faster compared to full `slapos node software` + `slapos node instance` runs.
import zc.buildout.buildout # XXX workaround for https://lab.nexedi.com/nexedi/slapos.recipe.template/merge_requests/9
from slapos.recipe.template import jinja2_template
import json
# j2render renders config/<cfg>.jinja2.cfg into config/<cfg>.cfg with provided json parameters.
def j2render(cfg, jcfg):
ctx = json.loads(jcfg)
textctx = ''
for k, v in ctx.items():
textctx += 'json %s %s\n' % (k, json.dumps(v))
buildout = None # stub
r = jinja2_template.Recipe(buildout, "recipe", {
'extensions': 'jinja2.ext.do',
'url': 'config/{}.jinja2.cfg'.format(cfg),
'output': 'config/{}.cfg'.format(cfg),
'context': textctx,
})
#print(r.context)
# avoid dependency on zc.buildout.download and the need to use non-stub buildout section
def _read(url, *args):
with open(url, *args) as f:
return f.read()
r._read = _read
with open('config/{}.cfg'.format(cfg), 'w+') as f:
f.write(r._render().decode())
config = "gnb"
json_params_empty = """{
"rf_mode": 'fdd',
......@@ -52,190 +83,5 @@ json_params = """{
"tdd_ul_dl_config": "5ms 8UL 1DL 2/10 (maximum uplink)"
}
}"""
import os
import json
from jinja2 import Environment, StrictUndefined, \
BaseLoader, TemplateNotFound, PrefixLoader
import six
DEFAULT_CONTEXT = {x.__name__: x for x in (
abs, all, any, bin, bool, bytes, callable, chr, complex, dict, divmod,
enumerate, filter, float, format, frozenset, hex, int,
isinstance, iter, len, list, map, max, min, next, oct, ord, pow,
range, repr, reversed, round, set, six, sorted, str, sum, tuple, zip)}
if six.PY2:
import itertools
DEFAULT_CONTEXT.update(
filter=itertools.ifilter,
map=itertools.imap,
range=xrange,
zip=itertools.izip,
)
def _assert(x, *args):
if x:
return ""
raise AssertionError(*args)
DEFAULT_CONTEXT['assert'] = _assert
DUMPS_KEY = 'dumps'
DEFAULT_IMPORT_DELIMITER = '/'
def getKey(expression, buildout, _, options):
section, entry = expression.split(':')
if section:
return buildout[section][entry]
else:
return options[entry]
def getJsonKey(expression, buildout, _, __):
return json.loads(getKey(expression, buildout, _, __))
EXPRESSION_HANDLER = {
'raw': (lambda expression, _, __, ___: expression),
'key': getKey,
'json': (lambda expression, _, __, ___: json.loads(expression)),
'jsonkey': getJsonKey,
'import': (lambda expression, _, __, ___:
__import__(expression, fromlist=['*'], level=0)),
'section': (lambda expression, buildout, _, __: dict(
buildout[expression])),
}
class RelaxedPrefixLoader(PrefixLoader):
"""
Same as PrefixLoader, but accepts imports lacking separator.
"""
def get_loader(self, template):
if self.delimiter not in template:
template += self.delimiter
return super(RelaxedPrefixLoader, self).get_loader(template)
class RecipeBaseLoader(BaseLoader):
"""
Base class for import classes altering import path.
"""
def __init__(self, path, delimiter, encoding):
self.base = os.path.normpath(path)
self.delimiter = delimiter
self.encoding = encoding
def get_source(self, environment, template):
path = self._getPath(template)
# Code adapted from jinja2's doc on BaseLoader.
if path is None or not os.path.exists(path):
raise TemplateNotFound(template)
mtime = os.path.getmtime(path)
with open(path, 'rb') as f:
source = f.read().decode(self.encoding)
return source, path, lambda: mtime == os.path.getmtime(path)
def _getPath(self, template):
raise NotImplementedError
class FileLoader(RecipeBaseLoader):
"""
Single-path loader.
"""
def _getPath(self, template):
if template:
return None
return self.base
class FolderLoader(RecipeBaseLoader):
"""
Multi-path loader (to allow importing a folder's content).
"""
def _getPath(self, template):
path = os.path.normpath(os.path.join(
self.base,
*template.split(self.delimiter)
))
if path.startswith(self.base):
return path
return None
LOADER_TYPE_DICT = {
'rawfile': (FileLoader, EXPRESSION_HANDLER['raw']),
'file': (FileLoader, getKey),
'rawfolder': (FolderLoader, EXPRESSION_HANDLER['raw']),
'folder': (FolderLoader, getKey),
}
compiled_source_cache = {}
class Recipe():
def _init(self, name, options):
self.once = options.get('once')
self.encoding = options.get('encoding', 'utf-8')
self._update = True
import_delimiter = options.get('import-delimiter',
DEFAULT_IMPORT_DELIMITER)
import_dict = {}
for line in options.get('import-list', '').splitlines(False):
if not line:
continue
expression_type, alias, expression = line.split(None, 2)
if alias in import_dict:
raise ValueError('Duplicate import-list entry %r' % alias)
loader_type, expression_handler = LOADER_TYPE_DICT[
expression_type]
import_dict[alias] = loader_type(
expression_handler(expression, *args),
import_delimiter, self.encoding,
)
if import_dict:
loader = RelaxedPrefixLoader(import_dict,
delimiter=import_delimiter)
else:
loader = None
self.template = options['url']
extension_list = [x for x in (y.strip()
for y in options.get('extensions', '').split()) if x]
self.context = options['context']
self.context.update(DEFAULT_CONTEXT.copy())
self.env = Environment(
extensions=extension_list,
undefined=StrictUndefined,
loader=loader)
def _render(self):
env = self.env
template = self.template
with open(template, 'rb') as f:
source = f.read().decode(self.encoding)
compiled_source_cache[template] = compiled_source = \
env.compile(source, filename=template)
template_object = env.template_class.from_code(env,
compiled_source,
env.make_globals(None), None)
print(self.context)
return template_object.render(**self.context).encode(self.encoding)
def install(self):
once = self.once
if once and os.path.exists(once):
return
installed = super(Recipe, self).install()
if once:
open(once, 'ab').close()
return
return installed
def update(self):
if self._update:
self.install()
else:
super(Recipe, self).update()
r = Recipe()
r._init("recipe", {
'extensions': 'jinja2.ext.do',
'url': 'config/{}.jinja2.cfg'.format(config),
'output': 'config/{}.cfg'.format(config),
'context': json.loads(json_params),
})
with open('config/{}.cfg'.format(config), 'w+') as f:
f.write(r._render().decode())
j2render(config, json_params)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment