Skip to content

Commit

Permalink
added more tests that should cover new graph and inheritance laziness
Browse files Browse the repository at this point in the history
  • Loading branch information
ncilfone committed Jan 18, 2022
1 parent cd39c46 commit 857563f
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 36 deletions.
2 changes: 1 addition & 1 deletion spock/backend/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def _base_attr(cls, kw_only, make_init, dynamic):
if not _is_spock_instance(base_cls) and not dynamic:
raise _SpockUndecoratedClass(
f"Class `{base_cls.__name__}` was not decorated with the @spock decorator "
f"and `dynamic={dynamic}` was set for child class `{cls.__name__}`"
f"and `dynamic={dynamic}` was set for child class `{cls.__name__}` -- Please remedy one of these"
)
elif not _is_spock_instance(base_cls) and dynamic:
bases[idx] = _process_class(base_cls, kw_only, make_init, dynamic)
Expand Down
33 changes: 13 additions & 20 deletions spock/backend/field_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,10 @@

from spock.args import SpockArguments
from spock.backend.spaces import AttributeSpace, BuilderSpace, ConfigSpace
from spock.exceptions import _SpockInstantiationError, _SpockNotOptionalError
from spock.utils import _check_iterable, _is_spock_instance, _is_spock_tune_instance


class SpockInstantiationError(Exception):
"""Custom exception for when the spock class cannot be instantiated correctly"""

pass


class SpockNotOptionalError(Exception):
"""Custom exception for missing value"""

pass


class RegisterFieldTemplate(ABC):
"""Base class for handing different field types
Expand Down Expand Up @@ -216,7 +205,7 @@ def handle_optional_attribute_value(
val() if type(val) is type else val for val in attr_space.field
]
except Exception as e:
raise SpockInstantiationError(
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
Expand Down Expand Up @@ -370,10 +359,14 @@ def handle_optional_attribute_type(
builder_space: named_tuple containing the arguments and spock_space
Raises:
SpockNotOptionalError
_SpockNotOptionalError
"""
raise SpockNotOptionalError()
raise _SpockNotOptionalError(
f"Parameter `{attr_space.attribute.name}` within `{attr_space.config_space.name}` is of "
f"type `{type(attr_space.attribute.type)}` which seems to be unsupported -- "
f"are you missing an @spock decorator on a base python class?"
)

def handle_optional_attribute_value(
self, attr_space: AttributeSpace, builder_space: BuilderSpace
Expand Down Expand Up @@ -463,10 +456,10 @@ def handle_optional_attribute_value(
builder_space: named_tuple containing the arguments and spock_space
Raises:
SpockNotOptionalError
_SpockNotOptionalError
"""
raise SpockNotOptionalError()
raise _SpockNotOptionalError()

def handle_optional_attribute_type(
self, attr_space: AttributeSpace, builder_space: BuilderSpace
Expand All @@ -478,10 +471,10 @@ def handle_optional_attribute_type(
builder_space: named_tuple containing the arguments and spock_space
Raises:
SpockNotOptionalError
_SpockNotOptionalError
"""
raise SpockNotOptionalError()
raise _SpockNotOptionalError()


class RegisterSpockCls(RegisterFieldTemplate):
Expand Down Expand Up @@ -625,7 +618,7 @@ def recurse_generate(cls, spock_cls, builder_space: BuilderSpace):
try:
spock_instance = spock_cls(**fields)
except Exception as e:
raise SpockInstantiationError(
raise _SpockInstantiationError(
f"Spock class `{spock_cls.__name__}` could not be instantiated -- attrs message: {e}"
)
return spock_instance, special_keys
12 changes: 12 additions & 0 deletions spock/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,15 @@ class _SpockUndecoratedClass(Exception):
"""Custom exception type for non spock decorated classes and not dynamic"""

pass


class _SpockInstantiationError(Exception):
"""Custom exception for when the spock class cannot be instantiated correctly"""

pass


class _SpockNotOptionalError(Exception):
"""Custom exception for missing value"""

pass
21 changes: 11 additions & 10 deletions spock/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import sys
from typing import Type
from warnings import warn

from spock.utils import _find_all_spock_classes

Expand All @@ -32,10 +31,11 @@ def __init__(self, input_classes, lazy: bool):
self._input_classes = input_classes
self._lazy = lazy
# Maybe find classes lazily -- roll them into the input class tuple
# make sure to cast as a set first since the lazy search might find duplicate references
if self._lazy:
self._input_classes = (
*self._input_classes,
*self._lazily_find_classes(self._input_classes),
*set(self._lazily_find_classes(self._input_classes)),
)
# Build -- post lazy eval
self._dag = self._build()
Expand Down Expand Up @@ -101,7 +101,7 @@ def _lazily_find_classes(self, classes):
lazy_classes = []
for _, v in self._yield_class_deps(classes):
if hasattr(sys.modules["spock"].backend.config, v):
warn(
print(
f"Lazy evaluation found a @spock decorated class named `{v}` within the registered types of "
f"sys.modules['spock'].backend.config -- Attempting to use the class "
f"`{getattr(sys.modules['spock'].backend.config, v)}`..."
Expand All @@ -114,12 +114,12 @@ def _lazily_find_classes(self, classes):
if len(dependent_lazy_classes) > 0:
lazy_classes.extend(dependent_lazy_classes)
lazy_classes.append(lazy_class)
else:
raise ValueError(
f"Missing @spock decorated class -- `{v}` was not passed as an *arg to "
f"ConfigArgBuilder and lazy evaluation could not find it within "
f"sys.modules['spock'].backend.config"
)
# else:
# raise ValueError(
# f"Missing @spock decorated class -- `{v}` was not passed as an *arg to "
# f"ConfigArgBuilder and lazy evaluation could not find it within "
# f"sys.modules['spock'].backend.config"
# )
return tuple(lazy_classes)

def _build(self):
Expand All @@ -136,7 +136,8 @@ def _build(self):
if v not in self.node_names:
raise ValueError(
f"Missing @spock decorated class -- `{v}` was not passed as an *arg to "
f"ConfigArgBuilder and/or could not be found via lazy evaluation"
f"ConfigArgBuilder and/or could not be found via lazy evaluation (currently lazy=`{self._lazy}`) "
f"within sys.modules['spock'].backend.config"
)
nodes.get(v).append(input_class)
nodes = {key: set(val) for key, val in nodes.items()}
Expand Down
19 changes: 18 additions & 1 deletion tests/base/attr_configs_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,4 +334,21 @@ class TestConfigDynamicDefaults(Foo, Bar):
FirstDoubleNestedConfig,
SecondDoubleNestedConfig,
NestedStuffOpt
]
]


@spock
class OtherBar:
hello: str = 'goodbye'


@spock
class RaiseNotFlagged:
test: int = 1
other: OtherBar = OtherBar


@spock
class RaiseNotDecorated:
test: int = 1
other: Bar = Bar
60 changes: 60 additions & 0 deletions tests/base/test_config_arg_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import pytest

from spock.builder import ConfigArgBuilder
from spock.exceptions import _SpockUndecoratedClass, _SpockNotOptionalError
from tests.base.attr_configs_test import *
from tests.base.base_asserts_test import *

Expand Down Expand Up @@ -206,3 +207,62 @@ def arg_builder(monkeypatch):
m.setattr(sys, "argv", [""])
config = ConfigArgBuilder(TestConfigDynamicDefaults)
return config.generate()


class TestBasicLazy(AllTypes):
"""Testing basic lazy evaluation"""
@staticmethod
@pytest.fixture
def arg_builder(monkeypatch):
with monkeypatch.context() as m:
m.setattr(sys, "argv", ["", "--config", "./tests/conf/yaml/test.yaml"])
config = ConfigArgBuilder(TypeConfig, TypeOptConfig, lazy=True)
return config.generate()


class TestLazyNotFlagged:
"""Testing failed lazy evaluation"""
def test_lazy_raise(self, monkeypatch):
with monkeypatch.context() as m:
m.setattr(sys, "argv", [""])
with pytest.raises(ValueError):
config = ConfigArgBuilder(RaiseNotFlagged, lazy=False)
config.generate()


class TestLazyNotDecorated:
"""Testing failed lazy evaluation"""
def test_lazy_raise(self, monkeypatch):
with monkeypatch.context() as m:
m.setattr(sys, "argv", [""])
with pytest.raises(_SpockNotOptionalError):
config = ConfigArgBuilder(RaiseNotDecorated, lazy=False)
config.generate()


class TestDynamic(AllDynamic):
"""Testing basic dynamic inheritance"""
@staticmethod
@pytest.fixture
def arg_builder(monkeypatch):
with monkeypatch.context() as m:
m.setattr(sys, "argv", [""])
config = ConfigArgBuilder(TestConfigDynamicDefaults)
return config.generate()


class TestDynamicRaise:
"""Testing dynamic raise fail"""
def test_dynamic_raise(self, monkeypatch):
with monkeypatch.context() as m:
m.setattr(sys, "argv", [""])
with pytest.raises(_SpockUndecoratedClass):

@spock
class TestConfigDefaultsFail(Foo, Bar):
x: int = 235
y: str = 'yarghhh'
z: List[int] = [10, 20]

config = ConfigArgBuilder(TestConfigDefaultsFail)
return config.generate()
8 changes: 4 additions & 4 deletions tests/base/test_type_specific.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import pytest

from spock.builder import ConfigArgBuilder
from spock.backend.field_handlers import SpockInstantiationError
from spock.exceptions import _SpockInstantiationError
from tests.base.attr_configs_test import *


Expand All @@ -14,7 +14,7 @@ class TestChoiceRaises:
def test_choice_raise(self, monkeypatch):
with monkeypatch.context() as m:
m.setattr(sys, "argv", ["", "--config", "./tests/conf/yaml/choice.yaml"])
with pytest.raises(SpockInstantiationError):
with pytest.raises(_SpockInstantiationError):
ConfigArgBuilder(ChoiceFail, desc="Test Builder")


Expand All @@ -23,7 +23,7 @@ class TestOptionalRaises:
def test_coptional_raise(self, monkeypatch):
with monkeypatch.context() as m:
# m.setattr(sys, "argv", ["", "--config", "./tests/conf/yaml/empty.yaml"])
with pytest.raises(SpockInstantiationError):
with pytest.raises(_SpockInstantiationError):
ConfigArgBuilder(OptionalFail, desc="Test Builder", configs=[], no_cmd_line=True)


Expand Down Expand Up @@ -108,6 +108,6 @@ def test_repeated_defs_fail(self, monkeypatch):
"argv",
[""],
)
with pytest.raises(SpockInstantiationError):
with pytest.raises(_SpockInstantiationError):
config = ConfigArgBuilder(RepeatedDefsFailConfig, NestedListStuff, desc="Test Builder")
config.generate()

0 comments on commit 857563f

Please sign in to comment.