Core Types & Functions#
The Fiddle configuration system builds on a core data model and functions.
Config#
- class fiddle.Config(fn_or_cls, /, *args, **kwargs)[source]#
A mutable representation of a function or class’s parameters.
This class represents the configuration for a given function or class, exposing configured parameters as mutable attributes. For example, given a class:
class SampleClass: '''Example class for demonstration purposes.''' def __init__(self, arg, kwarg=None): self.arg = arg self.kwarg = kwarg
a configuration may (for instance) be accomplished via:
class_config = Config(SampleClass, 1, kwarg='kwarg')
or via:
class_config = Config(SampleClass) class_config.arg = 1 class_config.kwarg = 'kwarg'
A function can be configured in the same ways:
def test_function(arg, kwarg=None): return arg, kwarg fn_config = Config(test_function, 1) fn_config.kwarg = 'kwarg'
If the class/function has positional arguments, they can be accessed through the [] syntax:
def test_function(a, b, /, c, *args): return locals() fn_config = Config(test_function, 1, 2, 3, 4, 5) # Read assert fn_config[0] == 1 assert fn_config[:] == [1, 2, 3, 4, 5] # Modify fn_config[0] = 'a' fn_config.c = 'c' # `fdl.VARARGS` represents the start of variadic positional args (*args) fn_config[fdl.VARARGS:] = ['x', 'y'] assert fn_config[:] == [1, 2, 3, 'x', 'y'] # Delete del fn_config[0] del fn_config[fdl.VARARGS:] assert fn_config[:] == [fdl.NO_VALUE, 2, 3]
NOTE: Directly calling list methods like append and extend is not supported, and will not mutate the config. Like with Python lists, slice operations on Configs effectively create a copy of the underlying sequence.
NOTE: If using slice as key for modifying the config, and the slice spans over positional-only or positional-or-keyword arguments, the provided value must have the same length as that of the slice range.
fn_config[2:4] = [‘a’, ‘b’] # OK fn_config[2:4] = [‘m’] # Not OK. Will raise an error!
A
Configinstance may be transformed into instances and function outputs by passing it to thebuildfunction. Thebuildfunction invokes each function or class in the configuration tree (appropriately propagating the built outputs from nestedConfig’s). For example, using theSampleClassconfig from above:instance = build(class_config) assert instance.arg == 1 assert instance.kwarg == 'kwarg'
If the same
Configinstance is used in multiple places within the configuration tree, its function or class is invoked only once duringbuild, and the result shared across all occurrences of theConfiginstance. (Seebuilddocumentation for further details.) To create a new instance of aConfigwith the same parameter settings that will yield a separate instance duringbuild,copy.copy()orcopy.deepcopy()may be used.
Partial#
- class fiddle.Partial(fn_or_cls, /, *args, **kwargs)[source]#
A
Partialconfig creates a partial function or class when built.In some cases, it may be desired to leave a function or class uninvoked, and instead output a corresponding
functools.partialobject. Where theConfigbase class calls its underlying__fn_or_cls__when built, thisPartialinstead results in a partially bound function or class:partial_config = Partial(SampleClass) partial_config.arg = 1 partial_config.kwarg = 'kwarg' partial_class = build(partial_config) instance = partial_class(arg=2) # Keyword arguments can be overridden. assert instance.arg == 2 assert instance.kwarg == 'kwarg'
A
Partialcan also be created from an existingConfig, by using thefdl.cast()function. This results in a shallow copy that is decoupled from theConfigused to create it. In the example below, any further changes topartial_configare not reflected byclass_config(and vice versa):partial_config = fdl.cast(Partial, class_config) class_config.arg = 'new value' # Further modification to `class_config`. partial_class = build(partial_config) instance = partial_class() assert instance.arg == 'arg' # The instance config is still 'arg'.
Partialalso supports configuring function/class with positional arguments. Please see docstring ofConfigclass for details.
build#
- fiddle.build(buildable)[source]#
Builds
buildable, recursively building nestedBuildableinstances.This is the core function for turning a
Buildableinto a usable object. It recursively walks throughbuildable’s parameters, building any nestedConfiginstances. Depending on the specificBuildabletype passed (ConfigorPartial), the result is either the result of callingconfig.__fn_or_cls__with the configured parameters, or a partial function or class with those parameters bound.If the same
Buildableinstance is seen multiple times during traversal of the configuration tree,buildis called only once (for the first instance encountered), and the result is reused for subsequent copies of the instance. This is achieved via thememodictionary (similar todeepcopy). This has the effect that for configured class instances, each separate config instance is in one-to-one correspondence with an actual instance of the configured class after callingbuild(shared config instances <=> shared class instances).- Parameters:
buildable – A
Buildableinstance to build, or a nested structure ofBuildableobjects.- Returns:
The built version of
buildable.
Buildable Manipulation Functions#
- fiddle.get_callable(buildable)[source]#
Returns the callable of a Buildable.
- Return type:
Union[Callable[...,TypeVar(T)],Type[TypeVar(T)]]
- fiddle.update_callable(buildable, new_callable, drop_invalid_args=False)[source]#
Updates
configto buildnew_callableinstead.When extending a base configuration, it can often be useful to swap one class for another. For example, an experiment may want to swap in a subclass that has augmented functionality.
update_callableupdatesconfigin-place (preserving argument history).- Parameters:
buildable (
Buildable) – ABuildable(e.g. afdl.Config) to mutate.new_callable (
Union[Callable[...,TypeVar(T)],Type[TypeVar(T)]]) – The new callableconfigshould call when built.drop_invalid_args (
bool) – If True, arguments that don’t exist in the new callable will be removed from buildable. If False, raise an exception for such arguments.
- Raises:
TypeError – if
new_callablehas varargs, or if there are arguments set onconfigthat are invalid to pass tonew_callable.- Return type:
None
- fiddle.assign(buildable, /, **kwargs)[source]#
Assigns multiple arguments to
buildable.Although this function does not enable a caller to do something they can’t already do with other syntax, this helper function can be useful when manipulating deeply nested configs. Example:
cfg = # ... fdl.assign(cfg.my.deeply.nested.child.object, arg_a=1, arg_b='b')
The above code snippet is equivalent to:
cfg = # ... cfg.my.deeply.nested.child.object.arg_a = 1 cfg.my.deeply.nested.child.object.arg_b = 'b'
- Parameters:
buildable (
Buildable) – ABuildable(e.g. afdl.Config) to set values upon.**kwargs – The arguments and values to assign.
- fiddle.copy_with(buildable, **kwargs)[source]#
Returns a shallow copy of
buildablewith updates to arguments.
- fiddle.deepcopy_with(buildable, **kwargs)[source]#
Returns a deep copy of
buildablewith updates to arguments.Note: if any
Config’s insidebuildableare shared withConfig’s outside ofbuildable, then they will no longer be shared in the returned value. E.g., ifcfg1.x.y is cfg2, thenfdl.deepcopy_with(cfg1, ...).x.y is cfg2will beFalse.- Parameters:
buildable (
Buildable) – ABuildable(e.g. afdl.Config) to copy and mutate.**kwargs – The arguments and values to assign.
ArgFactory#
- class fiddle.ArgFactory(fn_or_cls, /, *args, **kwargs)[source]#
A configuration that creates an argument factory when built.
When an
ArgFactoryis used as a parameter for afdl.Partial, the partial function built from thatfdl.Partialwill construct a new value for the parameter each time it is called. For example:>>> def f(x, noise): return x + noise >>> cfg = fdl.Partial(f, noise=fdl.ArgFactory(random.random)) >>> p = fdl.build(cfg) >>> p(5) == p(5) # noise has a different value for each call to `p`. False
In contrast, if we replaced
fdl.ArgFactorywithfdl.Configin the above example, then the same noise value would be added each timepis called, sincerandom.randomwould be called whenfdl.build(cfg)is called.ArgFactory’s can also be nested inside containers that are parameter values for aPartial. In this case, the partial function will construct the parameter value by copying the containers and replacing anyArgFactorywith the result of calling its factory. Only the containers that (directly or indirectly) containArgFactory’s are copied; any elements of the containers that do not containArgFactory’s are not copied.ArgFactorycan also be used as the parameter for anotherArgFactory, in which case a new value will be constructed for the child argument each time the parent argument is created.ArgFactoryshould not be used as a top-level configuration object, or as the argument to afdl.Config.
Advanced Functionality#
Buildable#
- class fiddle.Buildable(fn_or_cls, /, *args, **kwargs)[source]#
Base class for buildable types (
ConfigandPartial).Buildable types implement a
__build__method that is called duringfdl.build()with arguments set on theBuildableto get output for the corresponding instance.Arguments are stored in the __arguments__ dict with the following “canonical storage format”: Positional-only and variadic-positional arguments use the position index of the argument as a key (of type int); all other arguments use the name of the argument as a key (of type str).
- abstractmethod __build__(*args, **kwargs)[source]#
Builds output for this instance; see subclasses for details.
- __copy__()[source]#
Shallowly copies this
Buildableinstance.This copy implementation ensures that setting parameters on a copy of a
Buildablewon’t affect the original instance. However, the copy is shallow, so parameter values will still refer to the same instances of objects after the copy.- Returns:
A shallow copy of this
Buildable.
- __dir__()[source]#
Provide a useful list of attribute names, optimized for Jupyter/Colab.
__dir__is often implicitly called by tooling such as Jupyter/Colab to provide autocomplete suggestions. This implementation of__dir__makes it easy to see what are valid attributes to get, set, or delete.- Return type:
Collection[str]- Returns:
A list of useful attribute names corresponding to set or unset parameters.
- __eq__(other)[source]#
Returns true iff self and other contain the same argument values.
This compares the specific types of
selfandother, the function or class being configured, and then checks for equality in the configured arguments.Default argument values are considered in this comparison: If one
Buildablehas an argument explicitly set to its default value while another does not, the two will still be considered equal (motivated by the fact that calls to the function or class being configured will be the same).Argument history is not compared (i.e., it doesn’t matter how the
Buildable’s being compared reached their current state).- Parameters:
other – The other value to compare
selfto.- Returns:
Trueifselfequalsother,Falseif not.
- __getstate__()[source]#
Gets pickle serialization state, removing some fields.
For now, we discard the
__signature_info__.signature(which can be recalculated), because these tend to contain values which cannot be serialized.- Returns:
Dict of serialized state.
- __init__(fn_or_cls, /, *args, **kwargs)[source]#
Initialize for
fn_or_cls, optionally specifying parameters.- Parameters:
fn_or_cls (
Union[Buildable[TypeVar(T)],Callable[...,TypeVar(T)],Type[TypeVar(T)]]) – A callable to configure. The signature of this callable will determine the parameters thisBuidlablecan be used to configure.*args – Any positional arguments to configure for
fn_or_cls.**kwargs – Any keyword arguments to configure for
fn_or_cls.