Skip to content

Commit 47bf3cf

Browse files
authored
Merge pull request #274 from ncilfone/optional_fix
Fix: Bug in Optional of GenericType
2 parents af305f4 + 99a893d commit 47bf3cf

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)