Compare commits

..

7 commits

47 changed files with 301 additions and 156 deletions

View file

@ -30,7 +30,7 @@ Rougail est un bibliothèque python3 qui permet de charger des dictionnaires (fi
### Les contraintes
- [Les calcules automatiques](fill/README.md)
- [Les calculs automatiques](fill/README.md)
- [Les vérifications des valeurs](check/README.md)
- [Les conditions](condition/README.md)

View file

@ -111,7 +111,7 @@ $ python3 script.py
## Templatisons un fichier
Un template est un fichier dans laquelle on va remplacer les valeurs attendus par le nom des variables.
Un [template](../template/README.md) est un fichier dans lequel on va remplacer les valeurs attendues par le nom des variables.
Premièrement déclarons dans un dictionnaire complémentaire notre template dict/00-template.yml :
@ -146,7 +146,6 @@ async def main():
RougailConfig['extra_dictionaries']['example'] = ['extras/']
RougailConfig['functions_file'] = 'funcs/functions.py'
rougail = Rougail()
config = await rougail.get_config()
await rougail.template()
run(main())
@ -162,7 +161,7 @@ The extra value: my_value_extra
## Créons une fonction personnalisé
Nous créons un dictionnaire complémentaire pour ajouter un calcul à la variable "my_variable" dans dict/00-fill.yml :
Nous créons le dictionnaire complémentaire dict/00-fill.yml pour que la variable "my_variable" soit [calculée](fill/README.md) :
```yml
version: '0.10'
@ -190,5 +189,31 @@ The extra value: my_value_extra
La valeur de la variable "my_variable" est bien calculé à partir de la fonction "return_no".
## Template et systemd
#FIXME systemd
Rougail peut également généré automatiquement le fichier [tmpfiles.d](https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html) pour installer automatiquement les fichiers de configuration au démarrage de la machine.
Pour générer le fichier tmpfiles.d, ajouter l'argument "systemd" à la methode "template" :
```python
from rougail import Rougail, RougailConfig
from asyncio import run
async def main():
RougailConfig['dictionaries_dir'] = ['dict']
RougailConfig['templates_dir'] = ['tmpl']
RougailConfig['tmp_dir'] = 'tmp'
RougailConfig['destinations_dir'] = 'dest'
RougailConfig['extra_dictionaries']['example'] = ['extras/']
RougailConfig['functions_file'] = 'funcs/functions.py'
rougail = Rougail()
await rougail.template('systemd')
run(main())
```
Ainsi le fichier supplémentaire "dest/tmpfiles.d/0rougail.conf" sera créé avec le contenu :
```
C /etc/example.conf 0644 root root - /usr/local/lib/etc/example.conf
```

View file

@ -7,9 +7,9 @@
viewBox="0 0 183.75807 175.69795"
version="1.1"
id="svg1281"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="schema.svg"
inkscape:export-filename="/home/gnunux/git/risotto/rougail/doc/schema.png"
inkscape:export-filename="schema.png"
inkscape:export-xdpi="149.25999"
inkscape:export-ydpi="149.25999"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
@ -28,14 +28,16 @@
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.7786055"
inkscape:cx="353.19555"
inkscape:cy="355.76425"
inkscape:window-width="1033"
inkscape:window-height="1063"
inkscape:window-x="26"
inkscape:window-y="23"
inkscape:window-maximized="0"
inkscape:current-layer="layer1" />
inkscape:cx="-26.329123"
inkscape:cy="357.0486"
inkscape:window-width="1920"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
inkscape:showpageshadow="2"
inkscape:deskcolor="#d1d1d1" />
<defs
id="defs1278">
<marker

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View file

@ -151,11 +151,11 @@ services:
servicelist: test
variables:
- variable:
name: condition
- name: condition
type: boolean
constraints:
- condition:
name: disabled_if_in
- name: disabled_if_in
source: condition
param:
- text: false

View file

@ -58,9 +58,11 @@ def get_annotators(annotators, module_name):
annotators[module_name] = []
for pathobj in importlib.resources.files(module_name).iterdir():
path = str(pathobj)
if not path.endswith('__') and not path.endswith('__.py'):
if path.endswith('__') or path.endswith('__.py'):
continue
module = load_modules(path)
if 'Annotator' in dir(module):
if 'Annotator' not in dir(module):
continue
annotators[module_name].append(module.Annotator)
@ -69,25 +71,26 @@ class SpaceAnnotator: # pylint: disable=R0903
"""
def __init__(self,
objectspace,
eosfunc_files,
):
global ANNOTATORS
if ANNOTATORS is None:
ANNOTATORS = {}
get_annotators(ANNOTATORS, 'rougail.annotator')
for extra_annotator in objectspace.rougailconfig['extra_annotators']:
get_annotators(ANNOTATORS, extra_annotator)
for extra_annotator in objectspace.rougailconfig['extra_annotators']:
if extra_annotator not in ANNOTATORS:
if extra_annotator in ANNOTATORS:
continue
get_annotators(ANNOTATORS, extra_annotator)
annotators = ANNOTATORS['rougail.annotator'].copy()
for extra_annotator in objectspace.rougailconfig['extra_annotators']:
annotators.extend(ANNOTATORS[extra_annotator])
annotators = sorted(annotators, key=get_level)
functions = []
for eosfunc_file in eosfunc_files:
if isfile(eosfunc_file):
functions.extend(dir(load_modules(eosfunc_file)))
functions_files = objectspace.rougailconfig['functions_file']
if not isinstance(functions_files, list):
functions_files = [functions_files]
for functions_file in functions_files:
if isfile(functions_file):
functions.extend(dir(load_modules(functions_file)))
for annotator in annotators:
annotator(objectspace,
functions,

View file

@ -72,7 +72,7 @@ class Annotator(TargetAnnotator, ParamAnnotator):
"""
remove_indexes = []
for check_idx, check in enumerate(constraints.check):
if not check.name in self.functions:
if not check.name in self.functions and not self.objectspace.just_doc:
msg = _(f'cannot find check function "{check.name}"')
raise DictConsistencyError(msg, 1, check.xmlfiles)
if hasattr(check, 'param') and check.param == []:

View file

@ -38,6 +38,11 @@ from rougail.annotator.param import ParamAnnotator
from rougail.annotator.variable import Walk
class FakeVariable:
def __getattr__(self, name):
return None
class Annotator(TargetAnnotator, ParamAnnotator, Walk):
"""Annotate condition
"""
@ -222,7 +227,7 @@ class Annotator(TargetAnnotator, ParamAnnotator, Walk):
listvars,
fills,
)
elif not target.optional:
elif not target.optional and self.objectspace.just_doc is False:
msg = f'cannot found target "{target.type}" "{target.name}"'
raise DictConsistencyError(_(msg), 2, target.xmlfiles)
remove_targets.append(target_idx)
@ -323,6 +328,9 @@ class Annotator(TargetAnnotator, ParamAnnotator, Walk):
add_path_prefix=True,
)
except DictConsistencyError as err:
if self.objectspace.just_doc:
condition.source = FakeVariable()
continue
if err.errno == 36:
msg = _(f'the source "{condition.source}" in condition cannot be a dynamic '
f'variable')

View file

@ -76,7 +76,7 @@ class Annotator(TargetAnnotator, ParamAnnotator):
"""
for fill in constraints.fill:
# test if the function exists
if fill.name not in self.functions:
if not self.objectspace.just_doc and fill.name not in self.functions:
msg = _(f'cannot find fill function "{fill.name}"')
raise DictConsistencyError(msg, 25, fill.xmlfiles)
for target in fill.target:

View file

@ -90,7 +90,7 @@ class ParamAnnotator:
path_prefix,
)
except DictConsistencyError as err:
if err.errno != 42 or not param.optional:
if not self.objectspace.just_doc and (err.errno != 42 or not param.optional):
raise err
param_to_delete.append(param_idx)
elif param.type == 'function':
@ -118,7 +118,7 @@ class ParamAnnotator:
raise DictConsistencyError(msg, 53, obj.xmlfiles)
elif param.type == 'index':
for target in obj.target:
if not self.objectspace.paths.is_follower(target.name):
if not self.objectspace.just_doc and not self.objectspace.paths.is_follower(target.name):
msg = _(f'"{param.type}" parameter cannot be set with target '
f'"{target.name.name}" which is not a follower variable')
raise DictConsistencyError(msg, 60, obj.xmlfiles)

View file

@ -91,7 +91,7 @@ class TargetAnnotator(Walk):
if err.errno != 42:
raise err
# for optional variable
if not target.optional:
if not target.optional and not self.objectspace.just_doc:
msg = f'cannot found target "{target.type}" "{target.name}"'
raise DictConsistencyError(_(msg), 12, target.xmlfiles) from err
remove_targets.append(index)

View file

@ -63,6 +63,7 @@ class RougailConvert:
"""
def __init__(self,
rougailconfig: RougailConfig=None,
just_doc: bool=False,
) -> None:
if rougailconfig is None:
rougailconfig = RougailConfig
@ -70,11 +71,9 @@ class RougailConvert:
xmlreflector = Reflector(self.rougailconfig)
self.rougailobjspace = RougailObjSpace(xmlreflector,
self.rougailconfig,
just_doc,
)
self.internal_functions = self.rougailconfig['internal_functions']
self.functions_file = self.rougailconfig['functions_file']
if not isinstance(self.functions_file, list):
self.functions_file = [self.functions_file]
self.dictionaries = False
self.annotator = False
self.reflector = None
@ -111,7 +110,7 @@ class RougailConvert:
path_prefix: str,
namespace_description: str=None,
) -> List[str]:
for xmlfile, document in xmlreflector.load_dictionaries_from_folders(xmlfolders):
for xmlfile, document in xmlreflector.load_dictionaries_from_folders(xmlfolders, self.rougailobjspace.just_doc):
self.rougailobjspace.xml_parse_document(xmlfile,
document,
namespace,
@ -122,9 +121,7 @@ class RougailConvert:
def annotate(self):
if self.annotator:
raise DictConsistencyError(_('Cannot execute annotate multiple time'), 85, None)
SpaceAnnotator(self.rougailobjspace,
self.functions_file,
)
SpaceAnnotator(self.rougailobjspace)
self.annotator = True
def reflexion(self,
@ -136,7 +133,10 @@ class RougailConvert:
self.annotate()
if self.reflector:
raise DictConsistencyError(_('Cannot execute reflexion multiple time'), 86, None)
functions_file = [func for func in self.functions_file if func not in exclude_imports]
functions_file = self.rougailconfig['functions_file']
if not isinstance(functions_file, list):
functions_file = [functions_file]
functions_file = [func for func in functions_file if func not in exclude_imports]
self.reflector = TiramisuReflector(self.rougailobjspace,
functions_file,
self.internal_functions,

View file

@ -43,7 +43,7 @@ DEFAULT_LANG = os.environ.get('LANG', '').split(':')
DEFAULT_LANG += ['en_US']
languages = []
lc, encoding = locale.getdefaultlocale()
lc, encoding = locale.getlocale()
if lc:
languages = [lc]

View file

@ -28,7 +28,7 @@ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
from typing import Optional
from typing import Optional, List
from .i18n import _
from .reflector import Reflector
@ -106,7 +106,9 @@ class RougailObjSpace:
def __init__(self,
xmlreflector: Reflector,
rougailconfig: 'RougailConfig',
just_doc: bool,
) -> None:
self.just_doc = just_doc
self.space = ObjSpace()
self.paths = Path(rougailconfig)
@ -201,6 +203,7 @@ class RougailObjSpace:
namespace_description,
redefine_variables,
False,
True,
)
def _xml_parse(self, # pylint: disable=R0913
@ -211,6 +214,7 @@ class RougailObjSpace:
namespace_description,
redefine_variables,
is_dynamic,
first_level,
) -> None:
# var to check unique family name in a XML file
family_names = []
@ -218,6 +222,8 @@ class RougailObjSpace:
if not isinstance(child.tag, str):
# doesn't proceed the XML commentaries
continue
if first_level and self.just_doc and child.tag == 'services':
continue
if is_dynamic:
is_sub_dynamic = True
else:
@ -246,6 +252,7 @@ class RougailObjSpace:
child,
variableobj,
)
if not self.just_doc:
self.remove(child,
variableobj,
redefine_variables,
@ -276,6 +283,7 @@ class RougailObjSpace:
namespace_description,
redefine_variables,
is_sub_dynamic,
False,
)
def get_variableobj(self,
@ -370,7 +378,7 @@ class RougailObjSpace:
# manage object only if already exists, so cancel
raise SpaceObjShallNotBeUpdated()
redefine = convert_boolean(subspace.get('redefine', False))
if redefine is True:
if redefine is True and not self.just_doc:
# cannot redefine an inexistant object
msg = _(f'Redefined object: "{name}" does not exist yet')
raise DictConsistencyError(msg, 46, [xmlfile])
@ -432,9 +440,10 @@ class RougailObjSpace:
redefine = convert_boolean(child.attrib.get('redefine', False))
if redefine and child.tag == 'variable':
# delete old values
has_value = hasattr(variableobj, 'value')
if has_value and len(child) != 0:
if hasattr(variableobj, 'value') and len(child) != 0:
del variableobj.value
if self.just_doc:
variableobj.multi = len(child) > 1
for attr, val in child.attrib.items():
if attr == 'text' and child.tag in self.forced_text_elts_as_name:
continue
@ -469,12 +478,12 @@ class RougailObjSpace:
if child.attrib.get('remove_condition', False):
self.remove_condition(variableobj.name)
if child.attrib.get('remove_fill', False):
self.remove_fill(variableobj.name)
self.remove_fill(variableobj.name, variableobj.xmlfiles)
elif child.tag == 'fill':
for target in child:
if target.tag == 'target' and \
self.paths.get_path(target.text, namespace) in redefine_variables:
self.remove_fill(target.text)
self.remove_fill(target.text, variableobj.xmlfiles)
def remove_check(self, name):
"""Remove a check with a specified target
@ -515,11 +524,15 @@ class RougailObjSpace:
def remove_fill(self,
name: str,
xmlfiles: List[str],
) -> None:
"""Remove a fill with a specified target
"""
remove_fills = []
constraints = self.get_constraints()
if not hasattr(constraints, 'fill'):
msg = _(f'Cannot remove fill to "{name}", the variable has no fill yet')
raise DictConsistencyError(msg, 89, xmlfiles)
for idx, fill in enumerate(constraints.fill): # pylint: disable=E1101
for target in fill.target:
if target.name == name:
@ -579,6 +592,8 @@ class RougailObjSpace:
def get_variables(objectspace):
"""Iter all variables from the objectspace
"""
if not hasattr(objectspace.space, 'variables'):
return
for family in objectspace.space.variables.values():
yield from _get_variables(family, objectspace.family)

View file

@ -71,6 +71,7 @@ class Reflector:
def load_dictionaries_from_folders(self,
folders: List[str],
just_doc: bool,
):
"""Loads all the dictionary files located in the folders' list
@ -90,8 +91,8 @@ class Reflector:
if filename in filenames:
raise DictConsistencyError(_(f'duplicate dictionary file name {filename}'), 78, [filenames[filename], full_filename])
filenames[filename] = (ext, full_filename)
if not filenames:
raise DictConsistencyError(_('there is no dictionary file'), 77, [folder])
if not filenames and not just_doc:
raise DictConsistencyError(_('there is no dictionary file'), 77, folders)
file_names = list(filenames.keys())
file_names.sort()
for filename in file_names:

View file

@ -62,7 +62,7 @@ log.addHandler(logging.NullHandler())
INFORMATIONS = {'files': ['source', 'mode', 'engine', 'included'],
'overrides': ['name', 'source', 'engine'],
'service_names': ['doc', 'engine', 'type'],
'service_names': ['doc', 'engine', 'type', 'target', 'undisable'],
}
DEFAULT = {'files': ['owner', 'group'],
'overrides': [],
@ -178,6 +178,10 @@ class RougailLeader:
def index(self, value):
return self._value.index(value)
def __str__(self):
followers_name = list(self._follower)
return f'RougailLeader({followers_name[0]}) => {followers_name[1:]}'
class RougailExtra:
"""Object that implement access to extra variable
@ -212,7 +216,7 @@ class RougailExtra:
return self._suboption.items()
def __str__(self):
return self._name
return f'RougailExtra("{self._name}") => {self._suboption}'
class RougailBaseTemplate:
@ -233,7 +237,12 @@ class RougailBaseTemplate:
templates_dir = [templates_dir]
for templ_dir in templates_dir:
self.templates_dir.append(abspath(templ_dir))
self.patches_dir = abspath(rougailconfig['patches_dir'])
patches_dir = rougailconfig['patches_dir']
if not isinstance(patches_dir, list):
patches_dir = [patches_dir]
self.patches_dir = []
for p_dir in patches_dir:
self.patches_dir.append(abspath(p_dir))
eos = {}
functions_file = rougailconfig['functions_file']
if not isinstance(functions_file, list):
@ -259,7 +268,8 @@ class RougailBaseTemplate:
patch_cmd = ['patch', '-d', self.tmp_dir, '-N', '-p1', '-f']
patch_no_debug = ['-s', '-r', '-', '--backup-if-mismatch']
patch_file = join(self.patches_dir, f'{filename}.patch')
for patches_dir in self.patches_dir:
patch_file = join(patches_dir, f'{filename}.patch')
if isfile(patch_file):
self.log.info(_("Patching template '{filename}' with '{patch_file}'"))
ret = call(patch_cmd + patch_no_debug + ['-i', patch_file])
@ -362,6 +372,7 @@ class RougailBaseTemplate:
except FileNotFoundError:
ori_dir = None
chdir(self.tmp_dir)
try:
if not self.rougail_variables_dict:
await self.load_variables()
for templates_dir in self.templates_dir:
@ -421,6 +432,10 @@ class RougailBaseTemplate:
break
rmdir(parent)
self.post_instance()
except Exception as err:
if ori_dir is not None:
chdir(ori_dir)
raise err
if ori_dir is not None:
chdir(ori_dir)
@ -443,11 +458,20 @@ class RougailBaseTemplate:
obj: 'Option',
) -> None:
for key in INFORMATIONS.get(type_, []):
if key == 'target':
default_value = None
elif key == 'undisable':
default_value = False
elif key == 'engine' and type_ == 'service_names':
default_value = None
else:
default_key = f'default_{type_}_{key}'
if default_key in RougailConfig:
default_value = RougailConfig[default_key]
else:
default_value = undefined
value = await obj.information.get(key, default_value)
if key not in ['target', 'undisable'] or value != default_value:
dico[key] = await obj.information.get(key, default_value)
def desactive_service(self,
@ -547,7 +571,7 @@ class RougailBaseTemplate:
await suboption.option.name(),
path,
)
variables[leadership_name] = RougailExtra(await optiondescription.option.name(), {leader_name: leader}, await optiondescription.option.path())
variables[leadership_name] = RougailExtra(await option.option.name(), {leader_name: leader}, await option.option.path())
else:
if is_service_namespace == 'root':
new_is_service_namespace = 'service_name'
@ -567,15 +591,6 @@ class RougailBaseTemplate:
if is_variable_namespace:
value = await option.value.get()
self.rougail_variables_dict[await option.option.name()] = value
if await option.option.issymlinkoption() and await option.option.isfollower():
value = []
if isinstance(self.config, TiramisuOption):
path = (await option.option.path())[len_root_path:]
else:
path = await option.option.path()
for index in range(await option.value.len()):
value.append(await self.config.option(path, index).value.get())
else:
value = await option.value.get()
variables[await option.option.name()] = value
if isinstance(is_service_namespace, str) and is_service_namespace + 's' in INFORMATIONS:

View file

@ -29,7 +29,7 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
from json import dumps
from os.path import isfile
from os.path import isfile, basename
from .i18n import _
from .annotator import CONVERT_OPTION
@ -44,6 +44,12 @@ class BaseElt: # pylint: disable=R0903
path = '.'
def sorted_func_name(func_name):
s_func_name = func_name.split('/')
s_func_name.reverse()
return '/'.join(s_func_name)
class TiramisuReflector:
"""Convert object to tiramisu representation
"""
@ -76,7 +82,7 @@ class TiramisuReflector:
" continue",
" setattr(func, function, getattr(func_, function))",
])
for funcs_path in funcs_paths:
for funcs_path in sorted(funcs_paths, key=sorted_func_name):
if not isfile(funcs_path):
continue
self.text['header'].append(f"_load_functions('{funcs_path}')")
@ -295,7 +301,6 @@ class Common:
continue
if isinstance(value, str):
value = self.convert_str(value)
#pouet self.text['optiondescription'].append(f"{self.option_name}.impl_set_information('{key}', {value})")
self.text['option'].append(f"{self.option_name}.impl_set_information('{key}', {value})")
def populate_param(self,

View file

View file

View file

@ -0,0 +1,13 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail version="0.10">
<variables>
<family name="general">
<variable name="mode_conteneur_actif" type="string" description="No change">
<value>oui</value>
</variable>
<variable name="mode_conteneur_actif1" type="string" description="No change">
<value>non</value>
</variable>
</family>
</variables>
</rougail>

View file

@ -0,0 +1,8 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail version="0.10">
<variables>
<family name="general">
<variable name="mode_conteneur_actif" redefine="True" remove_fill="True"/>
</family>
</variables>
</rougail>

View file

@ -0,0 +1,16 @@
version: '0.10'
variables:
- family:
- name: general
variables:
- variable:
- name: mode_conteneur_actif
type: string
description: No change
value:
- text: oui
- name: mode_conteneur_actif1
type: string
description: No change
value:
- text: non

View file

@ -0,0 +1,9 @@
version: '0.10'
variables:
- family:
- name: general
variables:
- variable:
- name: mode_conteneur_actif
redefine: true
remove_fill: true

View file

@ -1,11 +1,11 @@
from os import listdir, mkdir, readlink
from os.path import join, isdir, isfile, islink
from shutil import rmtree
from pytest import fixture, mark
from pytest import fixture, mark, raises
from lxml.etree import parse
from tiramisu import Config
from rougail import RougailConfig, RougailSystemdTemplate
from rougail import RougailConfig, RougailBaseTemplate, RougailSystemdTemplate
template_dirs = 'tests/dictionaries'
@ -39,7 +39,7 @@ def find_files(dirname: str,
files.add(join(*root_file))
async def template(test_dir, filename, root):
async def template(test_dir, filename, root, engine_name):
test_dir = join(template_dirs, test_dir)
tmp_dir = join(test_dir, '..', 'tmp')
@ -71,6 +71,9 @@ async def template(test_dir, filename, root):
RougailConfig['tmpfile_dest_dir'] = '/test/new/file'
else:
RougailConfig['tmpfile_dest_dir'] = '/usr/local/lib'
if engine_name == 'base':
engine = RougailBaseTemplate(config)
else:
engine = RougailSystemdTemplate(config)
await engine.instance_files()
list_templates = set()
@ -85,6 +88,8 @@ async def template(test_dir, filename, root):
[],
list_results,
)
if engine_name == 'base' and 'tmpfiles.d/0rougail.conf' in list_results:
list_results.remove('tmpfiles.d/0rougail.conf')
assert list_templates == list_results, f'error with {test_dir}:'
for result in list_results:
template_file = join(dest_dir, result)
@ -99,20 +104,40 @@ async def template(test_dir, filename, root):
with open(template_file, 'r') as fh:
generated_file = fh.read()
assert result_file == generated_file, f'{template_file} content : \n{generated_file}\nexpected: \nresult_file\n'
if isdir(dest_dir):
rmtree(dest_dir)
if isdir(tmp_dir):
rmtree(tmp_dir)
@mark.asyncio
async def test_template(test_dir):
await template(test_dir, 'base', '1')
for engine in ['base', 'systemd']:
not_file = join(template_dirs, test_dir, 'no_' + engine)
if isfile(not_file):
with raises(Exception) as err:
await template(test_dir, 'base', '1', engine)
else:
await template(test_dir, 'base', '1', engine)
@mark.asyncio
async def test_template_multi_1(test_dir):
await template(test_dir, 'multi', '1')
for engine in ['base', 'systemd']:
not_file = join(template_dirs, test_dir, 'no_' + engine)
if isfile(not_file):
with raises(Exception) as err:
await template(test_dir, 'multi', '1', engine)
else:
await template(test_dir, 'multi', '1', engine)
@mark.asyncio
async def test_template_multi_2(test_dir):
await template(test_dir, 'multi', '1')
for engine in ['base', 'systemd']:
not_file = join(template_dirs, test_dir, 'no_' + engine)
if isfile(not_file):
with raises(Exception) as err:
await template(test_dir, 'multi', '1', engine)
else:
await template(test_dir, 'multi', '1', engine)