Skip to content

Commit

Permalink
Merge pull request #274 from ncilfone/optional_fix
Browse files Browse the repository at this point in the history
Fix: Bug in Optional of GenericType
  • Loading branch information
mmalouane committed Jan 25, 2023
2 parents af305f4 + 99a893d commit 47bf3cf
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 24 deletions.
13 changes: 12 additions & 1 deletion spock/backend/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def _base_attr(cls, kw_only, make_init, dynamic):
bases = ()
base_annotation = {}
base_defaults = {}
base_optional = {}
if len(cls.__mro__[1:-1]) > 0:
# Get bases minus self and python class object
bases = list(cls.__mro__[1:-1])
Expand All @@ -64,6 +65,10 @@ def _base_attr(cls, kw_only, make_init, dynamic):
base_annotation.update(
{attribute.name: val.__annotations__[attribute.name]}
)
if "optional" in attribute.metadata:
base_optional.update(
{attribute.name: attribute.metadata["optional"]}
)
base_defaults = {
attribute.name: attribute.default
for val in bases
Expand Down Expand Up @@ -120,7 +125,13 @@ def _base_attr(cls, kw_only, make_init, dynamic):
default = base_defaults[k]
else:
default = None
attrs_dict.update({k: katra(typed=v, default=default)})
# If the parent was optional then set the child to optional
optional = False
if k in base_optional:
optional = base_optional[k]
attrs_dict.update(
{k: katra(typed=v, default=default, inherit_optional=optional)}
)
return bases, attrs_dict, merged_annotations


Expand Down
11 changes: 8 additions & 3 deletions spock/backend/typed.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,23 +561,28 @@ def _callable_katra(typed, default=None, optional=False):
return x


def katra(typed, default=None):
def katra(typed, default=None, inherit_optional=False):
"""Public interface to create a katra
A 'katra' is the basic functional unit of `spock`. It defines a parameter using attrs as the backend, type checks
both simple types and subscripted GenericAlias types (e.g. lists and tuples), handles setting default parameters,
A 'katra' is the basic functional unit of `spock`. It defines a parameter using
attrs as the backend, type checks both simple types and subscripted GenericAlias
types (e.g. lists and tuples), handles setting default parameters,
and deals with parameter optionality
Args:
typed: the type of the parameter to define
default: the default value to assign if given
inherit_optional: optionality from inheritance
Returns:
x: Attribute from attrs
"""
# Handle optionals
typed, optional = _handle_optional_typing(typed)
# Since we strip away the optional typing notation for Generics -- we need to
# override with optional coming from the parent
optional = True if (optional or inherit_optional) else False
# Checks for callables via the different Variadic types across versions
if isinstance(typed, _SpockVariadicGenericAlias):
x = _callable_katra(typed=typed, default=default, optional=optional)
Expand Down
22 changes: 2 additions & 20 deletions spock/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from spock.backend.wrappers import Spockspace
from spock.exceptions import _SpockCryptoError, _SpockEvolveError, _SpockValueError
from spock.handlers import YAMLHandler
from spock.helpers import to_dict
from spock.utils import (
_C,
_T,
Expand Down Expand Up @@ -635,32 +636,13 @@ def obj_2_dict(self, obj: Union[_C, List[_C], Tuple[_C, ...]]) -> Dict[str, Dict
"""Converts spock classes from a Spockspace into their dictionary representations
Args:
objs: single spock class or an iterable of spock classes
obj: single spock class or an iterable of spock classes
Returns:
dictionary where the class names are keys and the values are the dictionary representations
"""

from spock.helpers import to_dict

return to_dict(obj, self._saver_obj)

# if isinstance(obj, (List, Tuple)):
# obj_dict = {}
# for val in obj:
# if not _is_spock_instance(val):
# raise _SpockValueError(
# f"Object is not a @spock decorated class object -- currently `{type(val)}`"
# )
# obj_dict.update({type(val).__name__: val})
# elif _is_spock_instance(obj):
# obj_dict = {type(obj).__name__: obj}
# else:
# raise _SpockValueError(
# f"Object is not a @spock decorated class object -- currently `{type(obj)}`"
# )
# return self.spockspace_2_dict(Spockspace(**obj_dict))

def evolve(self, *args: _C) -> Spockspace:
"""Function that allows a user to evolve the underlying spock classes with
instantiated spock objects
Expand Down
5 changes: 5 additions & 0 deletions tests/base/attr_configs_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,11 @@ class TypeInherited(TypeConfig, TypeDefaultOptConfig):
pass


@spock
class TypeOptionalInherited(TypeOptConfig):
pass


class Foo:
p: int = 1

Expand Down
1 change: 1 addition & 0 deletions tests/base/test_loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def arg_builder(monkeypatch):
m.setattr(sys, "argv", ["", "--config", "./tests/conf/yaml/inherited.yaml"])
config = ConfigArgBuilder(
TypeInherited,
TypeOptionalInherited,
NestedStuff,
NestedStuffOpt,
NestedListStuff,
Expand Down

0 comments on commit 47bf3cf

Please sign in to comment.