diff --git a/src/rougail/template.py b/src/rougail/template.py index a38d7e69..a9fce3a0 100644 --- a/src/rougail/template.py +++ b/src/rougail/template.py @@ -9,8 +9,8 @@ from shutil import copy import logging from typing import Dict, Any from subprocess import call -from os import listdir, makedirs -from os.path import dirname, join, isfile, abspath, normpath +from os import listdir, makedirs, getcwd, chdir +from os.path import dirname, join, isfile, abspath, normpath, relpath from Cheetah.Template import Template as ChtTemplate from Cheetah.NameMapper import NotFound as CheetahNotFound @@ -79,7 +79,8 @@ class CheetahTemplate(ChtTemplate): context, eosfunc: Dict, destfilename, - variable): + variable, + ): """Initialize Creole CheetahTemplate """ extra_context = {'is_defined' : IsDefined(context), @@ -93,11 +94,15 @@ class CheetahTemplate(ChtTemplate): searchList=[context, eosfunc, extra_context]) # FORK of Cheetah fonction, do not replace '\\' by '/' - def serverSidePath(self, path=None, + def serverSidePath(self, + path=None, normpath=normpath, abspath=abspath ): + # strange... + if path is None and isinstance(self, str): + path = self if path: return normpath(abspath(path)) # return normpath(abspath(path.replace("\\", '/'))) @@ -107,7 +112,6 @@ class CheetahTemplate(ChtTemplate): return None - class CreoleLeader: def __init__(self, value, follower=None, index=None): """ @@ -302,37 +306,43 @@ class CreoleTemplateEngine: return CreoleExtra(families) def patch_template(self, - filename: str): + filename: str, + tmp_dir: str, + patch_dir: str, + ) -> None: """Apply patch to a template """ - patch_cmd = ['patch', '-d', self.tmp_dir, '-N', '-p1'] + patch_cmd = ['patch', '-d', tmp_dir, '-N', '-p1'] patch_no_debug = ['-s', '-r', '-', '--backup-if-mismatch'] - # patches variante + locaux - for directory in [join(Config['patch_dir'], 'variante'), Config['patch_dir']]: - patch_file = join(directory, f'{filename}.patch') - if isfile(patch_file): - log.info(_("Patching template '{filename}' with '{patch_file}'")) - ret = call(patch_cmd + patch_no_debug + ['-i', patch_file]) - if ret: - patch_cmd_err = ' '.join(patch_cmd + ['-i', patch_file]) - log.error(_(f"Error applying patch: '{patch_file}'\nTo reproduce and fix this error {patch_cmd_err}")) - copy(filename, self.tmp_dir) + patch_file = join(patch_dir, f'{filename}.patch') + if isfile(patch_file): + log.info(_("Patching template '{filename}' with '{patch_file}'")) + rel_patch_file = relpath(patch_file, tmp_dir) + ret = call(patch_cmd + patch_no_debug + ['-i', rel_patch_file]) + if ret: + patch_cmd_err = ' '.join(patch_cmd + ['-i', rel_patch_file]) + log.error(_(f"Error applying patch: '{rel_patch_file}'\nTo reproduce and fix this error {patch_cmd_err}")) + copy(join(self.distrib_dir, filename), tmp_dir) def prepare_template(self, - filename: str): + filename: str, + tmp_dir: str, + patch_dir: str, + ) -> None: """Prepare template source file """ - log.info(_("Copy template: '{filename}' -> '{self.tmp_dir}'")) - copy(filename, self.tmp_dir) - self.patch_template(filename) + log.info(_("Copy template: '{filename}' -> '{tmp_dir}'")) + copy(filename, tmp_dir) + self.patch_template(filename, tmp_dir, patch_dir) def process(self, source: str, true_destfilename: str, destfilename: str, filevar: Dict, - variable: Any): + variable: Any, + ): """Process a cheetah template """ # full path of the destination file @@ -356,7 +366,10 @@ class CreoleTemplateEngine: def instance_file(self, filevar: Dict, - service_name: str) -> None: + service_name: str, + tmp_dir: str, + dest_dir: str, + ) -> None: """Run templatisation on one file """ log.info(_("Instantiating file '{filename}'")) @@ -370,13 +383,13 @@ class CreoleTemplateEngine: if variable: variable = [variable] for idx, filename in enumerate(filenames): - destfilename = join(self.dest_dir, filename[1:]) + destfilename = join(dest_dir, filename[1:]) makedirs(dirname(destfilename), exist_ok=True) if variable: var = variable[idx] else: var = None - source = join(self.tmp_dir, filevar['source']) + source = join(tmp_dir, filevar['source']) if filevar['templating']: self.process(source, filename, @@ -389,6 +402,11 @@ class CreoleTemplateEngine: async def instance_files(self) -> None: """Run templatisation on all files """ + ori_dir = getcwd() + tmp_dir = relpath(self.tmp_dir, self.distrib_dir) + dest_dir = relpath(self.dest_dir, self.distrib_dir) + patch_dir = relpath(Config['patch_dir'], self.distrib_dir) + chdir(self.distrib_dir) for option in await self.config.option.list(type='all'): namespace = await option.option.name() if namespace == Config['variable_namespace']: @@ -397,8 +415,8 @@ class CreoleTemplateEngine: families = await self.load_eole_variables(namespace, option) self.rougail_variables_dict[namespace] = families - for template in listdir(self.distrib_dir): - self.prepare_template(join(self.distrib_dir, template)) + for template in listdir('.'): + self.prepare_template(template, tmp_dir, patch_dir) for service_obj in await self.config.option('services').list('all'): service_name = await service_obj.option.doc() for fills in await service_obj.list('all'): @@ -406,15 +424,17 @@ class CreoleTemplateEngine: for fill_obj in await fills.list('all'): fill = await fill_obj.value.dict() filename = fill['source'] - distib_file = join(self.distrib_dir, filename) - if not isfile(distib_file): - raise FileNotFound(_(f"File {distib_file} does not exist.")) + if not isfile(filename): + raise FileNotFound(_(f"File {filename} does not exist.")) if fill.get('activate', False): self.instance_file(fill, service_name, + tmp_dir, + dest_dir, ) else: log.debug(_("Instantiation of file '{filename}' disabled")) + chdir(ori_dir) async def generate(config: Config, diff --git a/tests/dictionaries/01base_file_include/00-base.xml b/tests/dictionaries/01base_file_include/00-base.xml new file mode 100644 index 00000000..f19fa38e --- /dev/null +++ b/tests/dictionaries/01base_file_include/00-base.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + non + + + + + + diff --git a/tests/dictionaries/01base_file_include/__init__.py b/tests/dictionaries/01base_file_include/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/01base_file_include/makedict/base.json b/tests/dictionaries/01base_file_include/makedict/base.json new file mode 100644 index 00000000..ce547e52 --- /dev/null +++ b/tests/dictionaries/01base_file_include/makedict/base.json @@ -0,0 +1 @@ +{"rougail.general.mode_conteneur_actif": "non", "services.test.files.file.group": "root", "services.test.files.file.mode": "0644", "services.test.files.file.name": "/etc/file", "services.test.files.file.owner": "root", "services.test.files.file.source": "file", "services.test.files.file.templating": true, "services.test.files.file.activate": true} diff --git a/tests/dictionaries/01base_file_include/result/etc/file b/tests/dictionaries/01base_file_include/result/etc/file new file mode 100644 index 00000000..d907505b --- /dev/null +++ b/tests/dictionaries/01base_file_include/result/etc/file @@ -0,0 +1 @@ +non diff --git a/tests/dictionaries/01base_file_include/tiramisu/__init__.py b/tests/dictionaries/01base_file_include/tiramisu/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/01base_file_include/tiramisu/base.py b/tests/dictionaries/01base_file_include/tiramisu/base.py new file mode 100644 index 00000000..95802381 --- /dev/null +++ b/tests/dictionaries/01base_file_include/tiramisu/base.py @@ -0,0 +1,26 @@ +import imp +func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py') +for key, value in dict(locals()).items(): + if key != ['imp', 'func']: + setattr(func, key, value) +try: + from tiramisu3 import * +except: + from tiramisu import * +from rougail.tiramisu import ConvertDynOptionDescription +option_3 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif', doc='Description', multi=False, default='non', values=('oui', 'non')) +option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3]) +option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2]) +option_8 = StrOption(name='group', doc='group', multi=False, default='root') +option_9 = StrOption(name='mode', doc='mode', multi=False, default='0644') +option_10 = StrOption(name='name', doc='name', multi=False, default='/etc/file') +option_11 = StrOption(name='owner', doc='owner', multi=False, default='root') +option_12 = StrOption(name='source', doc='source', multi=False, default='file') +option_13 = BoolOption(name='templating', doc='templating', multi=False, default=True) +option_14 = BoolOption(name='activate', doc='activate', multi=False, default=True) +option_7 = OptionDescription(name='file', doc='file', children=[option_8, option_9, option_10, option_11, option_12, option_13, option_14]) +option_6 = OptionDescription(name='files', doc='files', children=[option_7]) +option_5 = OptionDescription(name='test', doc='test', children=[option_6]) +option_5.impl_set_information("manage", True) +option_4 = OptionDescription(name='services', doc='services', properties=frozenset({'hidden'}), children=[option_5]) +option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1, option_4]) diff --git a/tests/dictionaries/01base_file_include/tmpl/file b/tests/dictionaries/01base_file_include/tmpl/file new file mode 100644 index 00000000..19f93237 --- /dev/null +++ b/tests/dictionaries/01base_file_include/tmpl/file @@ -0,0 +1 @@ +%include "incfile" diff --git a/tests/dictionaries/01base_file_include/tmpl/incfile b/tests/dictionaries/01base_file_include/tmpl/incfile new file mode 100644 index 00000000..a29cfeaf --- /dev/null +++ b/tests/dictionaries/01base_file_include/tmpl/incfile @@ -0,0 +1 @@ +%%mode_conteneur_actif diff --git a/tests/dictionaries/01base_file_patch/00-base.xml b/tests/dictionaries/01base_file_patch/00-base.xml new file mode 100644 index 00000000..f19fa38e --- /dev/null +++ b/tests/dictionaries/01base_file_patch/00-base.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + non + + + + + + diff --git a/tests/dictionaries/01base_file_patch/__init__.py b/tests/dictionaries/01base_file_patch/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/01base_file_patch/makedict/base.json b/tests/dictionaries/01base_file_patch/makedict/base.json new file mode 100644 index 00000000..ce547e52 --- /dev/null +++ b/tests/dictionaries/01base_file_patch/makedict/base.json @@ -0,0 +1 @@ +{"rougail.general.mode_conteneur_actif": "non", "services.test.files.file.group": "root", "services.test.files.file.mode": "0644", "services.test.files.file.name": "/etc/file", "services.test.files.file.owner": "root", "services.test.files.file.source": "file", "services.test.files.file.templating": true, "services.test.files.file.activate": true} diff --git a/tests/dictionaries/01base_file_patch/patches/file.patch b/tests/dictionaries/01base_file_patch/patches/file.patch new file mode 100644 index 00000000..db5b04d1 --- /dev/null +++ b/tests/dictionaries/01base_file_patch/patches/file.patch @@ -0,0 +1,5 @@ +--- tmpl/file 2020-11-20 07:44:38.588472784 +0100 ++++ dest/file 2020-11-20 07:44:54.588536011 +0100 +@@ -1 +1 @@ +-unpatched ++patched diff --git a/tests/dictionaries/01base_file_patch/result/etc/file b/tests/dictionaries/01base_file_patch/result/etc/file new file mode 100644 index 00000000..893adcd3 --- /dev/null +++ b/tests/dictionaries/01base_file_patch/result/etc/file @@ -0,0 +1 @@ +patched diff --git a/tests/dictionaries/01base_file_patch/tiramisu/__init__.py b/tests/dictionaries/01base_file_patch/tiramisu/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/01base_file_patch/tiramisu/base.py b/tests/dictionaries/01base_file_patch/tiramisu/base.py new file mode 100644 index 00000000..95802381 --- /dev/null +++ b/tests/dictionaries/01base_file_patch/tiramisu/base.py @@ -0,0 +1,26 @@ +import imp +func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py') +for key, value in dict(locals()).items(): + if key != ['imp', 'func']: + setattr(func, key, value) +try: + from tiramisu3 import * +except: + from tiramisu import * +from rougail.tiramisu import ConvertDynOptionDescription +option_3 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif', doc='Description', multi=False, default='non', values=('oui', 'non')) +option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3]) +option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2]) +option_8 = StrOption(name='group', doc='group', multi=False, default='root') +option_9 = StrOption(name='mode', doc='mode', multi=False, default='0644') +option_10 = StrOption(name='name', doc='name', multi=False, default='/etc/file') +option_11 = StrOption(name='owner', doc='owner', multi=False, default='root') +option_12 = StrOption(name='source', doc='source', multi=False, default='file') +option_13 = BoolOption(name='templating', doc='templating', multi=False, default=True) +option_14 = BoolOption(name='activate', doc='activate', multi=False, default=True) +option_7 = OptionDescription(name='file', doc='file', children=[option_8, option_9, option_10, option_11, option_12, option_13, option_14]) +option_6 = OptionDescription(name='files', doc='files', children=[option_7]) +option_5 = OptionDescription(name='test', doc='test', children=[option_6]) +option_5.impl_set_information("manage", True) +option_4 = OptionDescription(name='services', doc='services', properties=frozenset({'hidden'}), children=[option_5]) +option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1, option_4]) diff --git a/tests/dictionaries/01base_file_patch/tmpl/file b/tests/dictionaries/01base_file_patch/tmpl/file new file mode 100644 index 00000000..832551d8 --- /dev/null +++ b/tests/dictionaries/01base_file_patch/tmpl/file @@ -0,0 +1 @@ +unpatched diff --git a/tests/test_1_flattener.py b/tests/test_1_flattener.py index 24abbc1f..d6b9f7b4 100644 --- a/tests/test_1_flattener.py +++ b/tests/test_1_flattener.py @@ -1,4 +1,5 @@ from lxml import etree +from os import getcwd from os.path import isfile, join, isdir from pytest import fixture, raises from os import listdir @@ -25,11 +26,14 @@ for test in listdir(dico_dirs): test_raise.add(test) excludes = set([]) +#excludes = set(['01base_file_utfchar']) test_ok -= excludes test_raise -= excludes #test_ok = ['10leadership_autoleader'] #test_raise = [] +ORI_DIR = getcwd() + debug = False #debug = True @@ -80,6 +84,7 @@ def launch_flattener(test_dir, test_ok=False): if isdir(subfolder): eolobj.create_or_populate_from_xml('extra1', [subfolder]) eosfunc = join(dico_dirs, '../eosfunc/test.py') + Config['patch_dir'] = join(test_dir, 'patches') eolobj.space_visitor(eosfunc) tiramisu_objects = eolobj.save() tiramisu_dir = join(test_dir, 'tiramisu') @@ -109,14 +114,18 @@ def teardown_module(module): def test_dictionary(test_dir): + assert getcwd() == ORI_DIR test_dir = join(dico_dirs, test_dir) launch_flattener(test_dir, True) + assert getcwd() == ORI_DIR def test_error_dictionary(test_dir_error): + assert getcwd() == ORI_DIR test_dir = join(dico_dirs, test_dir_error) with raises(DictConsistencyError): launch_flattener(test_dir) + assert getcwd() == ORI_DIR def test_no_dtd(): diff --git a/tests/test_2_makedict.py b/tests/test_2_makedict.py index a823fe23..05b48895 100644 --- a/tests/test_2_makedict.py +++ b/tests/test_2_makedict.py @@ -20,7 +20,7 @@ for test in listdir(dico_dirs): debug = False #debug = True excludes = set([]) -#excludes = set(['70container_services']) +#excludes = set(['01base_file_utfchar']) test_ok -= excludes #test_ok = ['10check_valid_ipnetmask'] diff --git a/tests/test_3_template.py b/tests/test_3_template.py index dbaf974c..bbb0d944 100644 --- a/tests/test_3_template.py +++ b/tests/test_3_template.py @@ -9,7 +9,10 @@ from rougail import template template_dirs = 'tests/dictionaries' -test_ok = [f for f in listdir(template_dirs) if not f.startswith('_') and isdir(join(template_dirs, f, 'tmpl'))] +excludes = set([]) +#excludes = set(['01base_file_utfchar']) +test_ok = {f for f in listdir(template_dirs) if not f.startswith('_') and isdir(join(template_dirs, f, 'tmpl'))} +test_ok -= excludes #test_ok = ['60extra_group'] @@ -55,6 +58,7 @@ async def test_dictionary(test_dir): if isdir(dest_dir): rmtree(dest_dir) mkdir(dest_dir) + template.Config['patch_dir'] = join(test_dir, 'patches') await template.generate(config, funcs_file, distrib_dir,