def _is_attribute_in_config_arguments(self, attr_space: AttributeSpace, builder_space: BuilderSpace): """Checks if an attribute is in the configuration file or keyword arguments dictionary Will recurse spock classes as dependencies might be defined in the configs class Args: attr_space: holds information about a single attribute that is mapped to a ConfigSpace builder_space: map of the read/cmd-line parameter dictionary to general or class level arguments Returns: boolean if in dictionary """ # Instances might have other instances that might be defined in the configs # Recurse to try and catch all config defs # Only map if default is not None -- do so by evolving the attribute if (_is_spock_instance(attr_space.attribute.type) and attr_space.attribute.default is not None): attr_space.field, special_keys = RegisterSpockCls( ).recurse_generate(attr_space.attribute.type, builder_space) attr_space.attribute = attr_space.attribute.evolve( default=attr_space.field) builder_space.spock_space[ attr_space.attribute.type.__name__] = attr_space.field self.special_keys.update(special_keys) return (attr_space.config_space.name in builder_space.arguments and attr_space.attribute.name in builder_space.arguments[attr_space.config_space.name])
def handle_attribute_from_config(self, attr_space: AttributeSpace, builder_space: BuilderSpace): """Handles setting a simple attribute when it is a spock class type Args: attr_space: holds information about a single attribute that is mapped to a ConfigSpace builder_space: named_tuple containing the arguments and spock_space Returns: """ typed = attr_space.attribute.metadata["type"] out = None # Handle List Types if (_get_name_py_version(typed) == "List" or _get_name_py_version(typed) == "Tuple"): out = [] for v in builder_space.arguments[attr_space.config_space.name][ attr_space.attribute.name]: out.append(_recurse_callables(v, _str_2_callable)) # Handle Dict Types elif _get_name_py_version(typed) == "Dict": out = {} for k, v in builder_space.arguments[attr_space.config_space.name][ attr_space.attribute.name].items(): out.update({k: _recurse_callables(v, _str_2_callable)}) attr_space.field = out
def handle_optional_attribute_value(self, attr_space: AttributeSpace, builder_space: BuilderSpace): """Handles setting an optional value with its default Args: attr_space: holds information about a single attribute that is mapped to a ConfigSpace builder_space: named_tuple containing the arguments and spock_space Returns: """ attr_space.field = attr_space.attribute.default
def handle_attribute_from_config(self, attr_space: AttributeSpace, builder_space: BuilderSpace): """Handles when the spock tune class is made up of spock classes Args: attr_space: holds information about a single attribute that is mapped to a ConfigSpace builder_space: named_tuple containing the arguments and spock_space Returns: """ attr_type = self._attr_type(attr_space) attr_space.field = attr_type(**builder_space.arguments[ attr_space.config_space.name][attr_space.attribute.name])
def handle_attribute_from_config(self, attr_space: AttributeSpace, builder_space: BuilderSpace): """Handles setting a simple attribute when it is a spock class type Args: attr_space: holds information about a single attribute that is mapped to a ConfigSpace builder_space: named_tuple containing the arguments and spock_space Returns: """ attr_space.field = builder_space.arguments[ attr_space.config_space.name][attr_space.attribute.name] self.register_special_key(attr_space)
def handle_attribute_from_config(self, attr_space: AttributeSpace, builder_space: BuilderSpace): """Handles setting a simple attribute when it is a spock class type Args: attr_space: holds information about a single attribute that is mapped to a ConfigSpace builder_space: named_tuple containing the arguments and spock_space Returns: """ # These should always be strings str_field = builder_space.arguments[attr_space.config_space.name][ attr_space.attribute.name] call_ref = _str_2_callable(str_field) attr_space.field = call_ref
def _handle_and_register_enum(self, enum_cls, attr_space: AttributeSpace, builder_space: BuilderSpace): """Recurses the enum in case there are nested type definitions Args: enum_cls: current enum class attr_space: holds information about a single attribute that is mapped to a ConfigSpace builder_space: named_tuple containing the arguments and spock_space Returns: """ attr_space.field, special_keys = RegisterSpockCls.recurse_generate( enum_cls, builder_space) self.special_keys.update(special_keys) builder_space.spock_space[enum_cls.__name__] = attr_space.field
def handle_optional_attribute_value(self, attr_space: AttributeSpace, builder_space: BuilderSpace): """Handles setting the value for an optional basic attribute Args: attr_space: holds information about a single attribute that is mapped to a ConfigSpace builder_space: named_tuple containing the arguments and spock_space Returns: """ super().handle_optional_attribute_value(attr_space, builder_space) if attr_space.field is not None: list_item_spock_class = attr_space.field # Here we need to catch the possibility of repeated lists via coded defaults if _is_spock_instance( attr_space.attribute.metadata["type"].__args__[0]): spock_cls = attr_space.attribute.metadata["type"].__args__[0] # Fall back to configs if present if spock_cls.__name__ in builder_space.arguments: attr_space.field = self._process_list( spock_cls, builder_space) # Here we need to attempt to instantiate any class references that still exist try: attr_space.field = [ val() if type(val) is type else val for val in attr_space.field ] except Exception as e: raise _SpockInstantiationError( f"Spock class `{spock_cls.__name__}` could not be instantiated -- attrs message: {e}" ) builder_space.spock_space[ spock_cls.__name__] = attr_space.field else: builder_space.spock_space[ list_item_spock_class.__name__] = attr_space.field
def handle_optional_attribute_type(self, attr_space: AttributeSpace, builder_space: BuilderSpace): """Handles a list of spock config classes (aka repeated classes) if it is optional Args: attr_space: holds information about a single attribute that is mapped to a ConfigSpace builder_space: named_tuple containing the arguments and spock_space Returns: """ list_item_spock_class = attr_space.attribute.default attr_space.field = self._process_list(list_item_spock_class, builder_space) builder_space.spock_space[ list_item_spock_class.__name__] = attr_space.field
def handle_attribute_from_config(self, attr_space: AttributeSpace, builder_space: BuilderSpace): """Handles when the attribute is made up of a spock class or classes Calls the recurse_generate function which handles nesting of spock classes Args: attr_space: holds information about a single attribute that is mapped to a ConfigSpace builder_space: named_tuple containing the arguments and spock_space Returns: """ attr_type = self._attr_type(attr_space) attr_space.field, special_keys = self.recurse_generate( attr_type, builder_space) builder_space.spock_space[attr_type.__name__] = attr_space.field self.special_keys.update(special_keys)
def handle_optional_attribute_type(self, attr_space: AttributeSpace, builder_space: BuilderSpace): """Handles when the falling back onto the default for the attribute of spock class type Calls the recurse_generate function which handles nesting of spock classes -- to make sure the attr_space.field value is defined Args: attr_space: holds information about a single attribute that is mapped to a ConfigSpace builder_space: named_tuple containing the arguments and spock_space Returns: """ attr_space.field, special_keys = RegisterSpockCls.recurse_generate( self._attr_type(attr_space), builder_space) self.special_keys.update(special_keys) builder_space.spock_space[self._attr_type( attr_space).__name__] = attr_space.field
def recurse_generate(cls, spock_cls: _C, builder_space: BuilderSpace): """Call on a spock classes to iterate through the attrs attributes and handle each based on type and optionality Triggers a recursive call when an attribute refers to another spock classes Args: spock_cls: current spock class that is being handled builder_space: named_tuple containing the arguments and spock_space Returns: tuple of the instantiated spock class and the dictionary of special keys """ # Empty dits for storing info special_keys = {} fields = {} # Init the ConfigSpace for this spock class config_space = ConfigSpace(spock_cls, fields) # Iterate through the attrs within the spock class for attribute in spock_cls.__attrs_attrs__: attr_space = AttributeSpace(attribute, config_space) # Logic to handle the underlying type to call the correct Register* class # Lists of repeated values if ((attribute.type is list) or (attribute.type is List)) and _is_spock_instance( attribute.metadata["type"].__args__[0]): handler = RegisterList() # Dict/List of Callables elif ((attribute.type is list) or (attribute.type is List) or (attribute.type is dict) or (attribute.type is Dict) or (attribute.type is tuple) or (attribute.type is Tuple)) and cls._find_callables( attribute.metadata["type"]): # handler = RegisterListCallableField() handler = RegisterGenericAliasCallableField() # Enums elif isinstance(attribute.type, EnumMeta) and _check_iterable( attribute.type): handler = RegisterEnum() # References to other spock classes elif _is_spock_instance(attribute.type): handler = RegisterSpockCls() # References to tuner classes elif _is_spock_tune_instance(attribute.type): handler = RegisterTuneCls() # References to callables elif isinstance(attribute.type, _SpockVariadicGenericAlias): handler = RegisterCallableField() # Basic field else: handler = RegisterSimpleField() handler(attr_space, builder_space) special_keys.update(handler.special_keys) # Try except on the class since it might not be successful -- throw the attrs message as it will know the # error on instantiation try: spock_instance = spock_cls(**fields) except Exception as e: raise _SpockInstantiationError( f"Spock class `{spock_cls.__name__}` could not be instantiated -- attrs message: {e}" ) return spock_instance, special_keys