def parse(cls, spec): if isinstance(spec, cls): return spec if not _is_param_format(spec): deprecation.warn( "Support for non-ParameterSpec format for `spec` will be" " removed in a future version.") spec = {"type": spec} _validate_param_spec(spec) return cls(**spec)
def make(self, cls, key=None, inputs=None, spec=None): """Create object of specified class via factory. This method delegates the object creation to the `Manufacturer` of specified `cls` with the given arguments. Args: cls: Class or type of instance to be created. key: String key specifying the factory to call. If `None`, the default factory is used. inputs: Keyword argument `dict` for the factory. This `dict` is expected to map each factory parameter to its manifest. See `TypeSpec` for detailed explanation on manifests. If `None`, the default inputs for for the factory is used. spec: Deprecated. `dict`. Either `{key: inputs}` or `{"key": key, "inputs": inputs}` is accepted. If specified, `key` and `inputs` are ignored, for compatibility. Returns: A instance of `cls` created by specified factory and inputs. Raises: ArgumentError: - Invalid inputs for any involved factory call. (e.g. missing required arguments, or invalid argument exists) GraphError: - This `Manufacturer` not attached to `Broker` when calling factories where other classes are referenced. InvalidFormatError: - `inputs` contains arguments not conforming to the corresponding type spec. KeyError: - Factory with `key` not found. ValueError: - `key` and `default` both unspecified. TypeError: - `inputs` is not a kwargs dictionary. - Specified factory does not return an instance of the target class. """ mfr = self.get_or_create(cls) if spec is not None: deprecation.warn("Parameter `spec` is deprecated. " "Specify `key` and `inputs` directly.") else: spec = {"key": key, "inputs": inputs} obj_spec = obj_.ObjectSpec.parse(spec) return mfr.make(**obj_spec.as_dict())
def merge_all(self, mfr_dict, **kwargs): """Merges multiple manufacturers. Args: mfr_dict: A dictionary mapping each root to either of the following: 1. A `Manufacturer` instance with compatible target 2. A zero-argument function that returns (1) above 3. An iterable of either of (1, 2) above 4. A zero-argument function that returns either of (1, 3) above **kwargs: Merge options: * override: If True, overwrites the original factory registration in case of key collision. Defaults to False. See `raise_on_collision` for collision handling if not `override`. * ignore_collision: If True, the factory key collision is ignored when `override` is False, and the original factory is preserved. Otherwise, an error would be raised. Defaults to False. * sep: Separator between root and method name in the resulting factory key. Defaults to "/". See `Manufacturer.merge` for details. * force: Deprecated. Use `override` instead. If specified, this overrides `override` for backwards compatibility. Raises: KeyConflictError: - Any of the resulting keys has been registered when `override` and `ignore_collision` are both False TypeError: - Any values of `mfr_dict` is not one of the described ones above. - Target of any `Manufacturer` that is not a subclass of this target. """ if "force" in kwargs: deprecation.warn( "Parameter `force` is deprecated. Use `override` instead.") kwargs["override"] = kwargs.pop("force") mfr_dict_validated = collections.defaultdict(list) for root, mfrs in mfr_dict.items(): if callable(mfrs): mfrs = mfrs() if not hasattr(mfrs, "__iter__"): mfrs = [mfrs] for mfr in mfrs: mfr = fn_util.maybe_call(mfr, Manufacturer) self._validate_merge(mfr, root, **kwargs) mfr_dict_validated[root].append(mfr) for root, mfrs in mfr_dict_validated.items(): [self._merge(root, mfr, **kwargs) for mfr in mfrs]
def _prep_reg_args(key, registrant): if callable(registrant): registrant = registrant() if isinstance(registrant, dict): registrant = dict(registrant) elif isinstance(registrant, (tuple, list)): registrant = {kw: arg for kw, arg in zip(_reg_params, registrant)} else: raise TypeError("Invalid registrant. Expected a list, tuple or dict. " "Given: {} ".format(registrant)) if "sig" in registrant: deprecation.warn( "Parameter `sig` is deprecated. Use `signature` instead.") registrant["signature"] = registrant.pop("sig") if any(p not in set(_reg_params) for p in registrant): invalids = sorted(set(registrant) - set(_reg_params)) raise errors.ArgumentError( "Invalid parameters - key: {}, args: {}".format(key, invalids)) registrant["key"] = key return registrant
def add_factory(self, cls, key, factory, signature, **kwargs): """Register factory to `Manufacturer` with specified target class. This method is a shorthand to the following: ```python mfr = broker.get_or_create(cls) mfr.register(key, factory, signature, ...) ``` Args: cls: Target class of `Manufacturer` key: A string key that identifies the factory. factory: The factory to be registered. signature: The signature of the input parameters. It is a dict mapping each factory parameter to its parameter specification. **kwargs: Register options: * defaults: (Optional) `dict` of default arguments for this factory. * descriptions: (Optional) Descriptions of this factory. * override: If `True`, overrides the existing factory (if any). If `False`, an error is raised in case of key collision. Defaults to `False`. """ if "force" in kwargs: deprecation.warn( "Parameter `force` is deprecated. Use `override` instead.") kwargs["override"] = kwargs.pop("force") if "params" in kwargs: deprecation.warn( "Parameter `params` is deprecated. Use `defaults` instead.") kwargs["defaults"] = kwargs.pop("params") if "create_mfr" in kwargs: deprecation.warn("Parameter `create_mfr` is not used anymore.") kwargs.pop("create_mfr") self._add_factory(cls, key, factory, signature, **kwargs)
def export_markdown(self, export_dir, cls_dir_fn=None, cls_desc_name=None): if (cls_dir_fn, cls_desc_name) != (None, None): deprecation.warn( "Parameters `cls_dir_fn` and `cls_desc_name` are not used anymore." ) return self.export_docs(export_dir)
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]