Exemple #1
0
 def _init_database(_name: str, _uri: str):
     for dbms in self._DBMS:
         if _uri.startswith(dbms):
             self.engines[_name] = self._DBMS[dbms](_uri)
             break
     else:
         raise InitError(f'Unsupported database system for URI "{_uri}"')
Exemple #2
0
 def collect_by_name(self, collection: str, name: str) -> type[Any]:
     if (
         collection not in self._types_by_name
         or name not in self._types_by_name[collection]
     ):
         raise InitError(f"{collection}.{name} does not exist in registered types")
     return self._types_by_name[collection][name]
Exemple #3
0
 def _load_from_file(self, file_name):
     try:
         with open(self._cwd_path("env", file_name), "r") as f:
             d = yaml.safe_load(f)
             if not isinstance(d, dict):
                 raise yaml.YAMLError()
             return d
     except FileNotFoundError:
         return {}
     except yaml.YAMLError:
         raise InitError(f'File "{file_name}" does not contain valid YAML syntax')
Exemple #4
0
 def _get_url_keys(self, key: str | list[str] | None, *,
                   route: str) -> list[str]:
     if key is None:
         entity_key = self.service.model.__props__.entity_key
         if entity_key is None:
             raise InitError(
                 f'Default route "{route}" for controller "{self.service.repo.model.__blnt__.name}" '
                 f"must define a key or set entity_key=True in one or more columns in the model"
             )
         key = [m.name for m in entity_key]
     if not isinstance(key, list):
         key = [key]
     return key
Exemple #5
0
    def _parse_middleware_options(self,
                                  mdw: str,
                                  context: BolinetteContext,
                                  system: bool = False):
        name, *args = mdw.split("|")
        if name.startswith("!"):
            self.middlewares = list(
                filter(lambda m: m.__blnt__.name != name[1:],
                       self.middlewares))
            return
        find = list(filter(lambda m: m.__blnt__.name == name,
                           self.middlewares))
        if len(find) > 0:
            middleware = find[0]
            middleware.options = {}
        else:
            middleware = context.inject.instantiate_type(
                self.web_ctx.ext.cache.collect_by_name("middleware", name))

        if not middleware.__blnt__.loadable and not system:
            raise InitError(f"[{type(self.controller).__name__}] Middleware "
                            f'"{mdw}" cannot be loaded in a controller')

        parsed_args: dict[str, Any] = {}
        for arg in args:
            arg_n, *arg_v = arg.split("=", maxsplit=1)
            parsed_args[arg_n] = arg_v[0] if len(arg_v) else True
        def_options = middleware.define_options()
        for opt_name, option in def_options.items():
            if opt_name in parsed_args:
                middleware.options[opt_name] = option.validate(
                    parsed_args[opt_name])
            elif option.required:
                raise InitError(
                    f'[{type(self.controller).__name__}] Middleware "{mdw}" '
                    f'option "{opt_name}" is missing from declaration string')
            else:
                middleware.options[opt_name] = option.default
        self.middlewares.append(middleware)
Exemple #6
0
 def _parse_commands(self):
     command_tree = {}
     for _, command in self.commands.items():
         cur_node = command_tree
         path = command.path.split(" ")
         for elem in path[:-1]:
             if elem not in cur_node:
                 cur_node[elem] = {}
             cur_node = cur_node[elem]
         elem = path[-1]
         if elem in cur_node:
             raise InitError(f'Conflict with "{command.name}" command')
         cur_node[elem] = command
     return command_tree
Exemple #7
0
 def _init_reference(_col: Column,
                     _attr: InstantiableAttribute[Reference]):
     _target_name = _attr.pop("model_name")
     _target_model = context.inject.require("model",
                                            _target_name,
                                            immediate=True)
     _target_col_name = _attr.pop("column_name")
     _target_column = getattr(_target_model, _target_col_name, None)
     if _target_column is None:
         raise InitError(
             f"{model.__blnt__.name}.{_col.name}: "
             f'no "{_target_col_name}" column in "{_target_name}" model'
         )
     return _attr.instantiate(
         model=model,
         column=_col,
         target_model=_target_model,
         target_column=_target_column,
     )
Exemple #8
0
    def _init_databases(self):
        def _init_database(_name: str, _uri: str):
            for dbms in self._DBMS:
                if _uri.startswith(dbms):
                    self.engines[_name] = self._DBMS[dbms](_uri)
                    break
            else:
                raise InitError(f'Unsupported database system for URI "{_uri}"')

        try:
            env_key_prefix = "database."
            conf = self.context.env.get_all(startswith=env_key_prefix)
            if len(conf) <= 0:
                raise ValueError()
            for db_name, ctn_str in conf.items():
                if not isinstance(ctn_str, str):
                    raise ValueError()
                db_name = db_name[len(env_key_prefix) :] or "default"
                _init_database(db_name, ctn_str)
        except ValueError:
            raise InitError("Bad database configuration in env files")
Exemple #9
0
    def _init_model(model: Model):
        def _init_column(_name: str, _attr: InstantiableAttribute[Column]):
            return _attr.instantiate(name=_name, model=model)

        def _init_relationship(_name: str,
                               _attr: InstantiableAttribute[Relationship]):
            _target_name = _attr.pop("model_name")
            _target_model = context.inject.require("model",
                                                   _target_name,
                                                   immediate=True)
            _fk_name = _attr.pop("foreign_key")
            _foreign_key = None
            if _fk_name is not None:
                _foreign_key = getattr(model, _fk_name, None)
            _remote_name = _attr.pop("remote_side")
            _remote_side = None
            _secondary_name = _attr.pop("secondary")
            _secondary = None
            if _secondary_name is not None:
                _secondary = context.inject.require("model",
                                                    _secondary_name,
                                                    immediate=True)
            if _remote_name is not None:
                _remote_side = getattr(model, _remote_name)
            return _attr.instantiate(
                name=_name,
                model=model,
                target_model=_target_model,
                secondary=_secondary,
                foreign_key=_foreign_key,
                remote_side=_remote_side,
            )

        def _init_reference(_col: Column,
                            _attr: InstantiableAttribute[Reference]):
            _target_name = _attr.pop("model_name")
            _target_model = context.inject.require("model",
                                                   _target_name,
                                                   immediate=True)
            _target_col_name = _attr.pop("column_name")
            _target_column = getattr(_target_model, _target_col_name, None)
            if _target_column is None:
                raise InitError(
                    f"{model.__blnt__.name}.{_col.name}: "
                    f'no "{_target_col_name}" column in "{_target_name}" model'
                )
            return _attr.instantiate(
                model=model,
                column=_col,
                target_model=_target_model,
                target_column=_target_column,
            )

        # Instantiate mixins
        for mixin_name in model.__blnt__.mixins:
            model.__props__.mixins[mixin_name] = ext.cache.collect_by_name(
                "mixin", mixin_name)()

        # Instantiate columns
        for col_name, attr_col in model.__props__.get_instantiable(Column):
            setattr(model, col_name, _init_column(col_name, attr_col))
        # Add mixin columns
        for _, mixin in model.__props__.mixins.items():
            for col_name, attr_col in mixin.columns().items():
                setattr(model, col_name, _init_column(col_name, attr_col))

        # Process auto generated primary keys
        primary = [
            c for _, c in model.__props__.get_columns() if c.primary_key
        ]
        if not primary:
            model.__props__.primary = None
        else:
            if len(primary) == 1 and primary[0].auto_increment is None:
                primary[0].auto_increment = True
            model.__props__.primary = primary
        for column in primary:
            column.nullable = False
        # Find entity key
        entity_key = [
            c for _, c in model.__props__.get_columns() if c.entity_key
        ]
        if not entity_key:
            model.__props__.entity_key = model.__props__.primary
        else:
            model.__props__.entity_key = entity_key
        if model.__props__.entity_key is None:
            raise InitError(
                f'No entity key defined for "{model.__blnt__.name}" model. '
                "Mark one column or more as entity_key=True.")

        # Process relationships
        for rel_name, attr_rel in model.__props__.get_instantiable(
                Relationship):
            setattr(model, rel_name, _init_relationship(rel_name, attr_rel))
        # Add mixin relationships
        for _, mixin in model.__props__.mixins.items():
            for rel_name, attr_rel in mixin.relationships().items():
                setattr(model, rel_name,
                        _init_relationship(rel_name, attr_rel))

        # Instantiate references
        for col_name, col in model.__props__.get_columns():
            if isinstance(col.reference, InstantiableAttribute):
                col.reference = _init_reference(col, col.reference)
        # Instantiate back references
        added_back_refs: dict[Model, list[ColumnList]] = {}
        for rel_name, rel in model.__props__.get_relationships():
            if isinstance(rel.backref, InstantiableAttribute):
                rel.backref = rel.backref.instantiate(model=model,
                                                      relationship=rel)
                if rel.backref.key not in added_back_refs:
                    added_back_refs[rel.target_model] = []
                added_back_refs[rel.target_model].append(
                    ColumnList(rel.backref.key, rel.target_model, model))
Exemple #10
0
 def load(self, ext: BolinetteExtension):
     for e in ext.dependencies:
         self.load(e)
     if ext in self._extensions:
         raise InitError(f"Extension {ext} is already loaded")
     self._extensions.append(ext)