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}"')
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]
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')
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
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)
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
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, )
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")
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))
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)