Skip to content

Commit 99a893d

Browse files
committed
fixed bug in allowing inherited GenericType to be optional. since the typing.Optional annotation is stripped off of the parents we needed to pass on the optional metadata to override the typing check.
1 parent af305f4 commit 99a893d

File tree

5 files changed

+28
-24
lines changed

5 files changed

+28
-24
lines changed

spock/backend/config.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ def _base_attr(cls, kw_only, make_init, dynamic):
3838
bases = ()
3939
base_annotation = {}
4040
base_defaults = {}
41+
base_optional = {}
4142
if len(cls.__mro__[1:-1]) > 0:
4243
# Get bases minus self and python class object
4344
bases = list(cls.__mro__[1:-1])
@@ -64,6 +65,10 @@ def _base_attr(cls, kw_only, make_init, dynamic):
6465
base_annotation.update(
6566
{attribute.name: val.__annotations__[attribute.name]}
6667
)
68+
if "optional" in attribute.metadata:
69+
base_optional.update(
70+
{attribute.name: attribute.metadata["optional"]}
71+
)
6772
base_defaults = {
6873
attribute.name: attribute.default
6974
for val in bases
@@ -120,7 +125,13 @@ def _base_attr(cls, kw_only, make_init, dynamic):
120125
default = base_defaults[k]
121126
else:
122127
default = None
123-
attrs_dict.update({k: katra(typed=v, default=default)})
128+
# If the parent was optional then set the child to optional
129+
optional = False
130+
if k in base_optional:
131+
optional = base_optional[k]
132+
attrs_dict.update(
133+
{k: katra(typed=v, default=default, inherit_optional=optional)}
134+
)
124135
return bases, attrs_dict, merged_annotations
125136

126137

spock/backend/typed.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -561,23 +561,28 @@ def _callable_katra(typed, default=None, optional=False):
561561
return x
562562

563563

564-
def katra(typed, default=None):
564+
def katra(typed, default=None, inherit_optional=False):
565565
"""Public interface to create a katra
566566
567-
A 'katra' is the basic functional unit of `spock`. It defines a parameter using attrs as the backend, type checks
568-
both simple types and subscripted GenericAlias types (e.g. lists and tuples), handles setting default parameters,
567+
A 'katra' is the basic functional unit of `spock`. It defines a parameter using
568+
attrs as the backend, type checks both simple types and subscripted GenericAlias
569+
types (e.g. lists and tuples), handles setting default parameters,
569570
and deals with parameter optionality
570571
571572
Args:
572573
typed: the type of the parameter to define
573574
default: the default value to assign if given
575+
inherit_optional: optionality from inheritance
574576
575577
Returns:
576578
x: Attribute from attrs
577579
578580
"""
579581
# Handle optionals
580582
typed, optional = _handle_optional_typing(typed)
583+
# Since we strip away the optional typing notation for Generics -- we need to
584+
# override with optional coming from the parent
585+
optional = True if (optional or inherit_optional) else False
581586
# Checks for callables via the different Variadic types across versions
582587
if isinstance(typed, _SpockVariadicGenericAlias):
583588
x = _callable_katra(typed=typed, default=default, optional=optional)

spock/builder.py

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from spock.backend.wrappers import Spockspace
2525
from spock.exceptions import _SpockCryptoError, _SpockEvolveError, _SpockValueError
2626
from spock.handlers import YAMLHandler
27+
from spock.helpers import to_dict
2728
from spock.utils import (
2829
_C,
2930
_T,
@@ -635,32 +636,13 @@ def obj_2_dict(self, obj: Union[_C, List[_C], Tuple[_C, ...]]) -> Dict[str, Dict
635636
"""Converts spock classes from a Spockspace into their dictionary representations
636637
637638
Args:
638-
objs: single spock class or an iterable of spock classes
639+
obj: single spock class or an iterable of spock classes
639640
640641
Returns:
641642
dictionary where the class names are keys and the values are the dictionary representations
642643
"""
643-
644-
from spock.helpers import to_dict
645-
646644
return to_dict(obj, self._saver_obj)
647645

648-
# if isinstance(obj, (List, Tuple)):
649-
# obj_dict = {}
650-
# for val in obj:
651-
# if not _is_spock_instance(val):
652-
# raise _SpockValueError(
653-
# f"Object is not a @spock decorated class object -- currently `{type(val)}`"
654-
# )
655-
# obj_dict.update({type(val).__name__: val})
656-
# elif _is_spock_instance(obj):
657-
# obj_dict = {type(obj).__name__: obj}
658-
# else:
659-
# raise _SpockValueError(
660-
# f"Object is not a @spock decorated class object -- currently `{type(obj)}`"
661-
# )
662-
# return self.spockspace_2_dict(Spockspace(**obj_dict))
663-
664646
def evolve(self, *args: _C) -> Spockspace:
665647
"""Function that allows a user to evolve the underlying spock classes with
666648
instantiated spock objects

tests/base/attr_configs_test.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,11 @@ class TypeInherited(TypeConfig, TypeDefaultOptConfig):
393393
pass
394394

395395

396+
@spock
397+
class TypeOptionalInherited(TypeOptConfig):
398+
pass
399+
400+
396401
class Foo:
397402
p: int = 1
398403

tests/base/test_loaders.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def arg_builder(monkeypatch):
6060
m.setattr(sys, "argv", ["", "--config", "./tests/conf/yaml/inherited.yaml"])
6161
config = ConfigArgBuilder(
6262
TypeInherited,
63+
TypeOptionalInherited,
6364
NestedStuff,
6465
NestedStuffOpt,
6566
NestedListStuff,

0 commit comments

Comments
 (0)