hydra_zen.builds#
- hydra_zen.builds(hydra_target, /, *pos_args, zen_partial=None, zen_wrappers=(), zen_meta=None, populate_full_signature=False, zen_exclude=(), hydra_recursive=None, hydra_convert=None, hydra_defaults=None, builds_bases=(), zen_dataclass=None, **kwargs_for_target)#
builds(target, *args, **kw)
returns a Hydra-compatible config that, when instantiated, returnstarget(*args, **kw)
.I.e.,
instantiate(builds(target, *args, **kw)) == target(*args, **kw)
Consult the Notes section for more details, and the Examples section to see the various features of
builds
in action.- Parameters:
- hydra_targetT (Callable)
The target object to be configured. This is a required, positional-only argument.
- *pos_argsSupportedPrimitive
Positional arguments passed as
<hydra_target>(*pos_args, ...)
upon instantiation.Arguments specified positionally are not included in the config’s signature and are stored as a tuple bound to in the
_args_
field.- **kwargs_for_targetSupportedPrimitive
The keyword arguments passed as
<hydra_target>(..., **kwargs_for_target)
upon instantiation.The arguments specified here determine the signature of the resulting config, unless
populate_full_signature=True
is specified (see below).Named parameters of the forms that have the prefixes
hydra_
,zen_
or_zen_
are reserved to ensure future-compatibility, and thus cannot be specified by the user.- zen_partialOptional[bool]
If
True
, then the resulting config will instantiate asfunctools.partial(<hydra_target>, *pos_args, **kwargs_for_target)
. Thus this enables the partial-configuration of objects.Specifying
zen_partial=True
andpopulate_full_signature=True
together will populate the config’s signature only with parameters that: are explicitly specified by the user, or that have default values specified in the target’s signature. I.e. it is presumed that un-specified parameters that have no default values are to be excluded from the config.- zen_wrappersNone | Callable | Builds | InterpStr | Sequence[None | Callable | Builds | InterpStr]
One or more wrappers, which will wrap
hydra_target
prior to instantiation. E.g. specifying the wrappers[f1, f2, f3]
will instantiate as:f3(f2(f1(<hydra_target>)))(*args, **kwargs)
Wrappers can also be specified as interpolated strings [2] or targeted configs.
- zen_metaOptional[Mapping[str, SupportedPrimitive]]
Specifies field-names and corresponding values that will be included in the resulting config, but that will not be used to instantiate
<hydra_target>
. These are called “meta” fields.- populate_full_signaturebool, optional (default=False)
If
True
, then the resulting config’s signature and fields will be populated according to the signature of<hydra_target>
; values also specified in**kwargs_for_target
take precedent.This option is not available for objects with inaccessible signatures, such as NumPy’s various ufuncs.
- zen_excludeCollection[str | int] | Callable[[str], bool], optional (default=[])
Specifies parameter names and/or indices, or a function for checking names, to exclude those parameters from the config-creation process.
- Note that inherited fields cannot be excluded.
- zen_convertOptional[ZenConvert]
A dictionary that modifies hydra-zen’s value and type conversion behavior. Consists of the following optional key-value pairs (hydra_zen.typing.ZenConvert):
- builds_basesTuple[Type[DataClass], …]
Specifies a tuple of parent classes that the resulting config inherits from.
- hydra_recursiveOptional[bool], optional (default=True)
If
True
, then Hydra will recursively instantiate all other hydra-config objects nested within this config [3].If
None
, the_recursive_
attribute is not set on the resulting config.- hydra_convertOptional[Literal[“none”, “partial”, “all”, “object”]], optional (default=”none”)
Determines how Hydra handles the non-primitive, omegaconf-specific objects passed to
<hydra_target>
[4]."none"
: No conversion occurs; omegaconf containers are passed through (Default)"partial"
:DictConfig
andListConfig
objects converted todict
and
list
, respectively. Structured configs and their fields are passed without conversion. -"all"
: All passed objects are converted to dicts, lists, and primitives, without a trace of OmegaConf containers. -"object"
: Passed objects are converted to dict and list. Structured Configs are converted to instances of the backing dataclass / attr class.If
None
, the_convert_
attribute is not set on the resulting config.- hydra_defaultsNone | list[str | dict[str, str | list[str] | None ]], optional (default = None)
A list in an input config that instructs Hydra how to build the output config [6] [7]. Each input config can have a Defaults List as a top level element. The Defaults List itself is not a part of output config.
- zen_dataclassOptional[DataclassOptions]
A dictionary that can specify any option that is supported by
dataclasses.make_dataclass()
other thanfields
. The default value forunsafe_hash
isTrue
.target
can be specified as a string to override the_target_
field set on the dataclass type returned bybuilds
.The
module
field can be specified to enable pickle compatibility. Seehydra_zen.typing.DataclassOptions
for details.- frozenbool, optional (default=False)
Deprecated since version 0.9.0:
frozen
will be removed in hydra-zen 0.10.0. It is replaced byzen_dataclass={'frozen': <bool>}
.If
True
, the resulting config will create frozen (i.e. immutable) instances. I.e. setting/deleting an attribute of an instance will raisedataclasses.FrozenInstanceError
at runtime.- dataclass_nameOptional[str]
Deprecated since version 0.9.0:
dataclass_name
will be removed in hydra-zen 0.10.0. It is replaced byzen_dataclass={'cls_name': <str>}
.If specified, determines the name of the returned class object.
- Returns:
- ConfigType[Builds[Type[T]]] | Type[PartialBuilds[Type[T]]]
A dynamically-generated structured config (i.e. a dataclass type) that describes how to build
hydra_target
.
- Raises:
- hydra_zen.errors.HydraZenUnsupportedPrimitiveError
The provided configured value cannot be serialized by Hydra, nor does hydra-zen provide specialized support for it. See Configuration-Value Types Supported by Hydra and hydra-zen for more details.
See also
instantiate
Instantiates a configuration created by
builds
, returning the instantiated target.make_custom_builds_fn
Returns a new
builds
function with customized default values.make_config
Creates a general config with customized field names, default values, and annotations.
get_target
Returns the target-object from a targeted structured config.
just
Produces a config that, when instantiated by Hydra, “just” returns the un-instantiated target-object.
to_yaml
Serialize a config as a yaml-formatted string.
Notes
The following pseudo code conveys the core functionality of
builds
:from dataclasses import make_dataclass def builds(self,target, populate_full_signature=False, **kw): # Dynamically defines a Hydra-compatible dataclass type. # Akin to doing: # # @dataclass # class Builds_thing: # _target_: str = get_import_path(target) # # etc. _target_ = get_import_path(target) if populate_full_signature: sig = get_signature(target) kw = {**sig, **kw} # merge w/ preference for kw type_annots = [get_hints(target)[k] for k in kw] fields = [("_target_", str, _target_)] fields += [ ( field_name, hydra_compat_type_annot(hint), hydra_compat_val(v), ) for hint, (field_name, v) in zip(type_annots, kw.items()) ] Config = make_dataclass(f"Builds_{target}", fields) return Config
The resulting “config” is a dynamically-generated dataclass type [5] with Hydra-specific attributes attached to it [1]. It possesses a
_target_
attribute that indicates the import path to the configured target as a string.Using any of the
zen_xx
features will result in a config that depends explicitly on hydra-zen. I.e. hydra-zen must be installed in order to instantiate the resulting config, including its yaml version.For details of the annotation
SupportedPrimitive
, see Configuration-Value Types Supported by Hydra and hydra-zen.Type annotations are inferred from the target’s signature and are only retained if they are compatible with Hydra’s limited set of supported annotations; otherwise an annotation is automatically ‘broadened’ until it is made compatible with Hydra.
builds
provides runtime validation of user-specified arguments against the target’s signature. E.g. specifying mis-named arguments or too many arguments will causebuilds
to raise.References
Examples
These examples describe:
Basic usage
Creating a partial config
Auto-populating parameters
Composing configs via inheritance
Runtime validation performed by builds
Using meta-fields
Using zen-wrappers
Creating a pickle-compatible config
Creating a frozen config
Support for partial’d targets
A helpful utility for printing examples
>>> from hydra_zen import builds, instantiate, to_yaml >>> def pyaml(x): ... # for pretty printing configs ... print(to_yaml(x))
Basic Usage
Lets create a basic config that describes how to ‘build’ a particular dictionary.
>>> Conf = builds(dict, a=1, b='x')
The resulting config is a dataclass with the following signature and attributes:
>>> Conf # signature: Conf(a: Any = 1, b: Any = 'x') <class 'types.Builds_dict'>
>>> pyaml(Conf) _target_: builtins.dict a: 1 b: x
The
instantiate
function is used to enact this build – to create the dictionary.>>> instantiate(Conf) # calls: `dict(a=1, b='x')` {'a': 1, 'b': 'x'}
The default parameters that we provided can be overridden.
>>> new_conf = Conf(a=10, b="hi") # an instance of our dataclass >>> instantiate(new_conf) # calls: `dict(a=10, b='hi')` {'a': 10, 'b': 'hi'}
Positional arguments are supported.
>>> Conf = builds(len, [1, 2, 3]) >>> Conf._args_ # type: ignore [1, 2, 3] >>> instantiate(Conf) 3
Creating a Partial Config
builds
can be used to partially-configure a target. Let’s create a config for the following functionsuch that we only configure the parameter
x
.>>> PartialConf = builds(a_two_tuple, x=1, zen_partial=True) # configures only `x` >>> pyaml(PartialConf) _target_: __main__.a_two_tuple _partial_: true x: 1
Instantiating this config will return
functools.partial(a_two_tuple, x=1)
.>>> partial_func = instantiate(PartialConf) >>> partial_func functools.partial(<function a_two_tuple at 0x00000220A7820EE0>, x=1)
And thus the remaining parameter can be provided post-instantiation.
>>> partial_func(y=22.0) # providing the remaining parameter (1, 22.0)
Auto-populating parameters
The configurable parameters of a target can be auto-populated in our config. Suppose we want to configure the following function.
The following config will have a signature that matches
bar
; the annotations and default values of the parameters ofbar
are explicitly incorporated into the config.>>> # signature: `Builds_bar(x: bool, y: str = 'foo')` >>> Conf = builds(bar, populate_full_signature=True) >>> pyaml(Conf) _target_: __main__.bar x: ??? 'y': foo
zen_exclude
can be used to name parameter to be excluded from the auto-population process:>>> Conf2 = builds(bar, populate_full_signature=True, zen_exclude=["y"]) >>> pyaml(Conf2) _target_: __main__.bar x: ???
to exclude parameters by index:
>>> Conf2 = builds(bar, populate_full_signature=True, zen_exclude=[-1]) >>> pyaml(Conf2) _target_: __main__.bar x: ???
or to specify a pattern - via a function - for excluding parameters:
>>> Conf3 = builds(bar, populate_full_signature=True, ... zen_exclude=lambda name: name.startswith("x")) >>> pyaml(Conf3) _target_: __main__.bar 'y': foo
Annotations will be used by Hydra to provide limited runtime type-checking during instantiation. Here, we’ll pass a float for
x
, which expects a boolean value.>>> instantiate(Conf(x=10.0)) # type: ignore ValidationError: Value '10.0' is not a valid bool (type float) full_key: x object_type=Builds_func
Composing configs via inheritance
Because a config produced via
builds
is simply a class-object, we can compose configs via class inheritance.>>> ParentConf = builds(dict, a=1, b=2) >>> ChildConf = builds(dict, b=-2, c=-3, builds_bases=(ParentConf,)) >>> instantiate(ChildConf) {'a': 1, 'b': -2, 'c': -3} >>> issubclass(ChildConf, ParentConf) True
Runtime validation performed by builds
Misspelled parameter names and other invalid configurations for the target’s signature will be caught by
builds
so that such errors are caught prior to instantiation.>>> def func(a_number: int): pass
>>> builds(func, a_nmbr=2) # misspelled parameter name TypeError: Building: func ..
>>> builds(func, 1, 2) # too many arguments TypeError: Building: func ..
>>> BaseConf = builds(func, a_number=2) >>> builds(func, 1, builds_bases=(BaseConf,)) # too many args (via inheritance) TypeError: Building: func ..
Using meta-fields
Meta-fields are fields that are included in a config but are excluded by the instantiation process. Thus arbitrary metadata can be attached to a config.
Let’s create a config whose fields reference a meta-field via relative-interpolation [2].
Using zen-wrappers
Zen-wrappers enables us to make arbitrary changes to
<hydra_target>
, its inputs, and/or its outputs during the instantiation process.Let’s use a wrapper to add a unit-conversion step to a config. We’ll modify a config that builds a function, which converts a temperature in Fahrenheit to Celsius, and add a wrapper to it so that it will convert from Fahrenheit to Kelvin instead.
>>> def faren_to_celsius(temp_f): # our target ... return ((temp_f - 32) * 5) / 9
>>> def change_celcius_to_kelvin(celc_func): # our wrapper ... def wraps(*args, **kwargs): ... return 273.15 + celc_func(*args, **kwargs) ... return wraps
>>> AsCelcius = builds(faren_to_celsius) >>> AsKelvin = builds(faren_to_celsius, zen_wrappers=change_celcius_to_kelvin) >>> instantiate(AsCelcius, temp_f=32) 0.0 >>> instantiate(AsKelvin, temp_f=32) 273.15
Creating a pickle-compatible config
The dynamically-generated classes created by
builds
can be made pickle-compatible by specifying the name of the symbol that it is assigned to and the module in which it was defined.Creating a frozen config
Let’s create a config object whose instances will by “frozen” (i.e., immutable).
>>> RouterConfig = builds(dict, ip_address=None, zen_dataclass={'frozen': True}) >>> my_router = RouterConfig(ip_address="192.168.56.1") # an immutable instance
Attempting to overwrite the attributes of
my_router
will raise.>>> my_router.ip_address = "148.109.37.2" FrozenInstanceError: cannot assign to field 'ip_address'
Support for partial’d targets
Specifying
builds(functools.partial(<target>, ...), ...)
is supported;builds
will automatically “unpack” a partial’d object that is passed as its target.>>> import functools >>> partiald_dict = functools.partial(dict, a=1, b=2) >>> Conf = builds(partiald_dict) # signature: (a = 1, b = 2) >>> instantiate(Conf) # equivalent to calling: `partiald_dict()` {'a': 1, 'b': 2} >>> instantiate(Conf(a=-4)) # equivalent to calling: `partiald_dict(a=-4)` {'a': -4, 'b': 2}