예제 #1
0
    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)
예제 #2
0
    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())
예제 #3
0
    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]
예제 #4
0
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
예제 #5
0
    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)
예제 #6
0
 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)
예제 #7
0
    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]