|
@@ -0,0 +1,255 @@
|
|
|
+import json
|
|
|
+import sys
|
|
|
+import tempfile
|
|
|
+from pathlib import Path
|
|
|
+
|
|
|
+from metadocker.cmdline.base import ArgsFromFile
|
|
|
+from metadocker.common.errors import MetadockerError
|
|
|
+from metadocker.common.misc import get_root_dir
|
|
|
+from metadocker.docker import DockerBuilder
|
|
|
+
|
|
|
+NoneType=None.__class__
|
|
|
+
|
|
|
+def get_types(x):
|
|
|
+ if x is None:
|
|
|
+ return tuple()
|
|
|
+ if not isinstance(x, (list, tuple, set)):
|
|
|
+ return (x,)
|
|
|
+ return x
|
|
|
+
|
|
|
+class Var:
|
|
|
+
|
|
|
+ class VarException(MetadockerError):
|
|
|
+ def get_error(self):
|
|
|
+ raise NotImplementedError()
|
|
|
+
|
|
|
+ class RequiredException(VarException):
|
|
|
+ def __init__(self, name):
|
|
|
+ super().__init__(name)
|
|
|
+ self.name = name
|
|
|
+
|
|
|
+ def get_error(self):
|
|
|
+ return f"La variable {self.name} est requise"
|
|
|
+
|
|
|
+ class ValidationException(VarException):
|
|
|
+ def __init__(self, name, err):
|
|
|
+ super().__init__(name, err)
|
|
|
+ self.name = name
|
|
|
+ self.err = err
|
|
|
+
|
|
|
+ def get_error(self):
|
|
|
+ return f"La variable {self.name} est invalide : {self.err}"
|
|
|
+
|
|
|
+ class TypeException(VarException):
|
|
|
+ def __init__(self, name, types, found):
|
|
|
+ super().__init__(name, types)
|
|
|
+ self.name = name
|
|
|
+
|
|
|
+ if isinstance(types, type):
|
|
|
+ types = [types]
|
|
|
+ types = [x.__name__ for x in types]
|
|
|
+ self.types = tuple(types)
|
|
|
+ self.found = found
|
|
|
+
|
|
|
+ def get_error(self):
|
|
|
+ return f"La variable {self.name} doit être de type {'('+', '.join(self.types)+')'}. Type trouvé {self.found.__class__.__name__}"
|
|
|
+
|
|
|
+ def __init__(self, name, default_value=None, required=False, validate=None,
|
|
|
+ expected_types=None, help=None, is_meta=False, null=False):
|
|
|
+ self.name = name
|
|
|
+ self.default_value = default_value
|
|
|
+ self.required = required
|
|
|
+ self._validate = validate if validate else lambda x: x
|
|
|
+ self.expected_types = expected_types
|
|
|
+ self.help = help
|
|
|
+ self.null = null
|
|
|
+ self.is_meta = is_meta
|
|
|
+
|
|
|
+ def validate(self, obj):
|
|
|
+ if not hasattr(obj, self.name):
|
|
|
+ if self.required:
|
|
|
+ raise self.RequiredException(self.name)
|
|
|
+ return self.default_value
|
|
|
+ data = getattr(obj, self.name)
|
|
|
+ if self.expected_types:
|
|
|
+ if data is None and self.null:
|
|
|
+ pass
|
|
|
+ elif not isinstance(data, self.expected_types):
|
|
|
+ raise self.TypeException(self.name, self.expected_types, data)
|
|
|
+
|
|
|
+ data = self._validate(data)
|
|
|
+
|
|
|
+ return data
|
|
|
+
|
|
|
+ def __repr__(self):
|
|
|
+ return f"<{self.__class__.__name__} {self.name}>"
|
|
|
+
|
|
|
+class VarStr(Var):
|
|
|
+ def __init__(self, name, default_value=None, required=False, validate=None, help=None,
|
|
|
+ is_meta=False, expected_types=None, null=False):
|
|
|
+ super().__init__(name, default_value, required, validate, help=help,
|
|
|
+ expected_types=(str, *get_types(expected_types)), is_meta=is_meta, null=null)
|
|
|
+
|
|
|
+class VarPath(Var):
|
|
|
+ def __init__(self, name, default_value=None, required=False, validate=None, help=None,
|
|
|
+ is_meta=False, expected_types=None, null=False):
|
|
|
+ super().__init__(name, default_value, required, help=help, is_meta=is_meta,
|
|
|
+ validate=validate or (lambda x: Path(x)),
|
|
|
+ expected_types=(str, Path, *get_types(expected_types)), null=null)
|
|
|
+
|
|
|
+
|
|
|
+class VarInt(Var):
|
|
|
+ def __init__(self, name, default_value=None, required=False, validate=None, help=None,
|
|
|
+ is_meta=False, expected_types=None, null=False):
|
|
|
+ super().__init__(name, default_value, required, validate, help=help, is_meta=is_meta,
|
|
|
+ expected_types=(int, *get_types(expected_types)), null=null)
|
|
|
+
|
|
|
+class VarNumber(Var):
|
|
|
+ def __init__(self, name, default_value=None, required=False, validate=None, help=None,
|
|
|
+ is_meta=False, expected_types=None, null=False):
|
|
|
+ super().__init__(name, default_value, required, validate, help=help, is_meta=is_meta,
|
|
|
+ expected_types=(float, int, *get_types(expected_types)), null=null)
|
|
|
+
|
|
|
+class VarDict(Var):
|
|
|
+ def __init__(self, name, default_value=None, required=False, validate=None, help=None,
|
|
|
+ is_meta=False, expected_types=None, null=False):
|
|
|
+ super().__init__(name, default_value, required, validate, help=help, is_meta=is_meta,
|
|
|
+ expected_types=(dict, *get_types(expected_types)), null=null)
|
|
|
+
|
|
|
+
|
|
|
+class EnvHolder:
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ self._items = []
|
|
|
+
|
|
|
+ def __getitem__(self, item):
|
|
|
+ return getattr(self, item)
|
|
|
+
|
|
|
+ def __setitem__(self, key, value):
|
|
|
+ if key not in self._items:
|
|
|
+ self._items.append(key)
|
|
|
+
|
|
|
+ return setattr(self, key, value)
|
|
|
+
|
|
|
+ def items(self):
|
|
|
+ return [
|
|
|
+ (k, getattr(self, k)) for k in self._items
|
|
|
+ ]
|
|
|
+
|
|
|
+
|
|
|
+class Env:
|
|
|
+ _current_env = None
|
|
|
+ _vars_ = []
|
|
|
+ _name_ = None
|
|
|
+
|
|
|
+
|
|
|
+ class EnvException(MetadockerError):
|
|
|
+ def __init__(self, errors):
|
|
|
+ super().__init__(errors)
|
|
|
+ self.errors = errors
|
|
|
+
|
|
|
+
|
|
|
+ def get_error(self):
|
|
|
+ return "\n".join(repr(x) for x in self.errors)
|
|
|
+
|
|
|
+ def __init__(self, debug=False, **kwargs):
|
|
|
+ Env._current_env = self
|
|
|
+ self.root = get_root_dir()
|
|
|
+ self.debug = debug
|
|
|
+ for k, v in kwargs.items():
|
|
|
+ setattr(self, k, v)
|
|
|
+
|
|
|
+ def check(self):
|
|
|
+ self.get_env()
|
|
|
+
|
|
|
+ def get_meta(self):
|
|
|
+ data= EnvHolder()
|
|
|
+ errors = []
|
|
|
+ for var in self._vars_:
|
|
|
+ if not var.is_meta: continue
|
|
|
+ try:
|
|
|
+ value = var.validate(self)
|
|
|
+ except Var.VarException as err:
|
|
|
+ errors.append(err)
|
|
|
+ continue
|
|
|
+ data[var.name] = value
|
|
|
+
|
|
|
+ if errors:
|
|
|
+ raise self.EnvException(errors)
|
|
|
+
|
|
|
+ for k,v in data.items():
|
|
|
+ setattr(data, k, v)
|
|
|
+ return data
|
|
|
+
|
|
|
+
|
|
|
+ def get_env(self):
|
|
|
+ data= EnvHolder()
|
|
|
+ errors = []
|
|
|
+ for var in self._vars_:
|
|
|
+ try:
|
|
|
+ value = var.validate(self)
|
|
|
+ except Var.VarException as err:
|
|
|
+ errors.append(err)
|
|
|
+ continue
|
|
|
+ data[var.name] = value
|
|
|
+
|
|
|
+ if errors:
|
|
|
+ raise self.EnvException(errors)
|
|
|
+
|
|
|
+ for k,v in data.items():
|
|
|
+ setattr(data, k, v)
|
|
|
+ return data
|
|
|
+
|
|
|
+ def generate_docker_file(self, docker : DockerBuilder, ):
|
|
|
+ raise NotImplementedError()
|
|
|
+
|
|
|
+
|
|
|
+ def init(self, docker : DockerBuilder):
|
|
|
+ raise NotImplementedError()
|
|
|
+
|
|
|
+
|
|
|
+ def build_docker_file(self, docker : DockerBuilder, output=None):
|
|
|
+ self.init(docker)
|
|
|
+ if not output:
|
|
|
+ temp = tempfile.TemporaryDirectory()
|
|
|
+ output = Path(temp.name) / "Dockerfile"
|
|
|
+ output = Path(output)
|
|
|
+ output.parent.mkdir(exist_ok=True, parents=True)
|
|
|
+
|
|
|
+ self.generate_docker_file(docker)
|
|
|
+ output.write_text(docker.get_file_content())
|
|
|
+
|
|
|
+ def create_empty_config(self):
|
|
|
+ def _value(v):
|
|
|
+ if v is None:
|
|
|
+ return "None"
|
|
|
+ if isinstance(v, (str, list, int, float, dict)):
|
|
|
+ if v == {}: return "{\n}"
|
|
|
+ if v == []: return "[\n]"
|
|
|
+ return json.dumps(v, indent=2)
|
|
|
+ return v
|
|
|
+
|
|
|
+ def _help(v):
|
|
|
+ return v.replace("\n", "\n#")
|
|
|
+
|
|
|
+ data = [
|
|
|
+ "#!/bin/env python3",
|
|
|
+ f"from metadocker.env import {self.__class__.__name__}",
|
|
|
+ f"env = {self.__class__.__name__}()",
|
|
|
+ f""
|
|
|
+ ]
|
|
|
+ for var in self._vars_:
|
|
|
+ if var.help:
|
|
|
+ data.append(f"# {var.help}")
|
|
|
+ if var.required:
|
|
|
+ data.append(f"env.{var.name}={_value(var.default_value)}")
|
|
|
+ else:
|
|
|
+ data.append(f"# env.{var.name}={_value(var.default_value)}")
|
|
|
+ data.append("")
|
|
|
+ data.append("# lancement ")
|
|
|
+ data.append("env.main()")
|
|
|
+ return "\n".join(data)
|
|
|
+
|
|
|
+ def main(self, args=None):
|
|
|
+ ArgsFromFile.main(self, args)
|
|
|
+
|