def _register(self, mfr, override=False): validate.is_type(mfr, Manufacturer, "mfr") cls = mfr.cls with self._lock: if cls in self._mfrs: if not override: raise errors.KeyConflictError( "Manufacturer with target class `{}` exists.".format( misc.qualname(cls))) self._mfrs.pop(cls)._bind = None mfr._bind(self) # pylint: disable=protected-access self._mfrs[cls] = mfr
def _register(self, key, factory, signature, defaults=None, descriptions=None, override=False, _builtins=False): key_is_reserved = misc.is_reserved(key) if _builtins and not key_is_reserved: key = misc.join_reserved(key) elif not _builtins and key_is_reserved: pass # Retrieve registry registry = self._builtin_fcts if _builtins else self._user_fcts # Key conflict short-circuiting if not override and key in registry: raise errors.KeyConflictError(key) if signature is None: # This block is for Manufacturer merges if not isinstance(factory, fct_lib.Factory): raise errors.InputError() else: # Validate arguments # 1. Validate `key` validate.is_type(key, str, "key") # 2. Validate `factory` and `signature` with self._exc_proxy(prefix="[key: {}] ".format(key)): validate.is_callable(factory, "factory") validate.is_type(signature, dict, "signature") defaults = defaults or {} validate.is_kwargs(defaults, "defaults") # 3. Create `Factory` factory = fct_lib.Factory(self._cls, factory, signature, descriptions=descriptions, defaults=defaults) # Register factory. with self._lock: if not override and key in registry: raise errors.KeyConflictError(key) registry[key] = factory
def _validate_merge(self, mfr, key, override=None, ignore_collision=None, sep="/"): validate.is_type(key, str, "key") if not issubclass(mfr.cls, self.cls): raise TypeError( "The target of `mfr` must be a subclass of {}. Given: {}". format(misc.qualname(self.cls), misc.qualname(mfr.cls))) if override or ignore_collision: return merged_names = set(_merged_name(key, n, sep=sep) for n in mfr.keys()) collisions = merged_names.intersection(self._user_fcts) if collisions: raise errors.KeyConflictError( "Factory key conflicts detected, " "please use another key for merge instead: {}".format( sorted(collisions)))
def _validate_param_spec(spec): assert isinstance(spec, dict) if "description" in spec: validate.is_type(spec["description"], str, "description") if "required" in spec: validate.is_type(spec["required"], bool, "required") if "forced" in spec: validate.is_type(spec["forced"], bool, "forced")
def register_dict(self, registrants, keyword_mode=None): """Registers factories from dictionary. This method allows a dictionary of factories to be registered at once. The argument is expected to be a dictionary that maps factory keys to arguments for the `Manufacturer.register` call, either in tuple or dictionary, or a zero-argument function that returns either of them. For instance, the value can be a tuple: (factory, signature, defaults (optional), descriptions (optional), override (optional)) Or a dictionary: {"factory": factory, # required "signature": signature, # required "defaults": defaults, # optional "descriptions": descriptions, # optional "override": override} # optional Or a zero-argument function that returns either of the above. A typical pattern one would encounter is to have a dictionary as a registry to manage a group of factories as "plugin"s, so that whenever a new factory is implemented it can be made available for the manufacturer to call by adding it in the dictionary. For example: `registry.py` ```python from . import factories as fct REGISTRY = { "create": fct.create.get_create, "load": fct.load.get_load, } ``` `factories/create.py`: ```python def create(arg_1, arg_2, arg_3=True): ... return obj def get_create(): sig = { "arg_1": { "type": str, "description": "Some string", "required": True, }, "arg_2": { "type": [int], "description": "List of ints", "required": True, }, "arg_3": { "type": bool, "description": "Some boolean", "required": False, }, } return create, sig, {"arg_1": "Hello World!", "arg_2": [1, -1, 0]} ``` `factories/load.py`: ```python def load(filename, mode): ... return obj def get_load(): sig = { "filename": str, "mode": bool } return load, sig # The default parameter is optional. ``` The `REGISTRY` can then be passed to its manufacturer for registration. Args: registrants: A dictionary that maps each factory key to its `Manufacturer.register` call arguments (or zero-argument function that returns it). keyword_mode: Deprecated. Has no effect at all. Raises: ArgumentError: - Invalid inputs for `Manufacturer.register` call. TypeError: - `registrants` is not a `dict` """ if keyword_mode is not None: deprecation.warn("Parameter `keyword_mode` is not used anymore.") validate.is_type(registrants, dict, "registrants") reg_dicts = [_prep_reg_args(k, r) for k, r in registrants.items()] [self.register(**kwargs) for kwargs in reg_dicts]
def default(self, key): validate.is_type(key, str, "key") if key is not None and key not in self: raise KeyError("Factory not found: {}".format(key)) self._default = key