Skip to content

Commit 581374d

Browse files
Make NullContext an alias of ContextBase
1 parent fc64400 commit 581374d

File tree

2 files changed

+27
-16
lines changed

2 files changed

+27
-16
lines changed

ccflow/tests/test_callable.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
CallableModelGenericType,
1212
ContextBase,
1313
ContextType,
14-
DateContext,
1514
Flow,
1615
GenericResult,
1716
GraphDepList,
@@ -33,6 +32,10 @@ class MyExtendedContext(MyContext):
3332
c: bool
3433

3534

35+
class MyOtherContext(ContextBase):
36+
a: int
37+
38+
3639
class ListContext(ContextBase):
3740
ll: List[str] = []
3841

@@ -51,12 +54,6 @@ def __call__(self, context: MyContext) -> MyResult:
5154
return MyResult(x=self.i, y=context.a)
5255

5356

54-
class MyCallableNullContext(CallableModel):
55-
@Flow.call
56-
def __call__(self, context: NullContext) -> MyResult:
57-
return MyResult(x=1, y="default")
58-
59-
6057
class MyCallableOptionalContext(CallableModel):
6158
@Flow.call
6259
def __call__(self, context: Optional[MyContext] = None) -> MyResult:
@@ -159,7 +156,7 @@ class BadModelMismatchedContextAndCall(CallableModel):
159156

160157
@property
161158
def context_type(self):
162-
return DateContext
159+
return MyOtherContext
163160

164161
@property
165162
def result_type(self):
@@ -170,7 +167,7 @@ def __call__(self, context: MyContext) -> MyResult:
170167
return context
171168

172169

173-
class BadModelGenericMismatchedContextAndCall(CallableModelGenericType[DateContext, MyResult]):
170+
class BadModelGenericMismatchedContextAndCall(CallableModelGenericType[MyOtherContext, MyResult]):
174171
"""Model with mismatched context_type and __call__ annotation"""
175172

176173
@Flow.call
@@ -412,11 +409,6 @@ def test_signature(self):
412409
self.assertRaises(TypeError, m, context, a="foo")
413410
self.assertRaises(TypeError, m, context=context, a="foo")
414411

415-
def test_nullable_context(self):
416-
m = MyCallableNullContext()
417-
context = MyContext(a="foo")
418-
self.assertEqual(m(context=context), m(None))
419-
420412
def test_signature_optional_context(self):
421413
m = MyCallableOptionalContext()
422414
context = MyContext(a="foo")
@@ -472,7 +464,7 @@ def test_types(self):
472464
error = "__call__ method must take a single argument, named 'context'"
473465
self.assertRaisesRegex(ValueError, error, BadModelDoubleContextArg)
474466

475-
error = "The context_type <class 'ccflow.context.DateContext'> must match the type of the context accepted by __call__ <class 'ccflow.tests.test_callable.MyContext'>"
467+
error = "The context_type <class 'ccflow.tests.test_callable.MyOtherContext'> must match the type of the context accepted by __call__ <class 'ccflow.tests.test_callable.MyContext'>"
476468
self.assertRaisesRegex(ValueError, error, BadModelMismatchedContextAndCall)
477469

478470
error = "The result_type <class 'ccflow.result.generic.GenericResult'> must match the return type of __call__ <class 'ccflow.tests.test_callable.MyResult'>"
@@ -654,7 +646,7 @@ def __call__(self, context: NullContext) -> GenericResult[float]:
654646
MyCallable()
655647

656648
def test_types_generic(self):
657-
error = "Context type annotation <class 'ccflow.tests.test_callable.MyContext'> on __call__ does not match context_type <class 'ccflow.context.DateContext'> defined by CallableModelGenericType"
649+
error = "Context type annotation <class 'ccflow.tests.test_callable.MyContext'> on __call__ does not match context_type <class 'ccflow.tests.test_callable.MyOtherContext'> defined by CallableModelGenericType"
658650
self.assertRaisesRegex(TypeError, error, BadModelGenericMismatchedContextAndCall)
659651

660652
error = "Return type annotation <class 'ccflow.tests.test_callable.MyResult'> on __call__ does not match result_type <class 'ccflow.result.generic.GenericResult'> defined by CallableModelGenericType"

ccflow/tests/test_context.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@
3030
from ccflow.result import GenericResult
3131

3232

33+
class MyDefaultContext(ContextBase):
34+
b: float = 3.14
35+
c: bool = False
36+
37+
3338
class TestContexts(TestCase):
3439
def test_null_context(self):
3540
n1 = NullContext()
@@ -38,11 +43,25 @@ def test_null_context(self):
3843
self.assertEqual(hash(n1), hash(n2))
3944

4045
def test_null_context_validation(self):
46+
# Context creation is based on two main assumptions:
47+
# 1. If there is enough information to create a context, it should be created.
48+
# 2. Since NullContext has no required fields, it can be created from None,
49+
# empty containers ({} or []), or any other context.
4150
self.assertEqual(NullContext.model_validate([]), NullContext())
4251
self.assertEqual(NullContext.model_validate({}), NullContext())
4352
self.assertEqual(NullContext.model_validate(None), NullContext())
53+
self.assertIsInstance(NullContext.model_validate(DateContext(date="0d")), NullContext)
4454
self.assertRaises(ValueError, NullContext.model_validate, [True])
4555

56+
def test_context_with_defaults(self):
57+
# Contexts may define default values. Extending the assumptions above:
58+
# Any context inherits the behavior from NullContext, and can be
59+
# created as long as all required fields (if any) are satisfied.
60+
self.assertEqual(TypeAdapter(MyDefaultContext).validate_python(None), MyDefaultContext(b=3.14, c=False))
61+
self.assertEqual(TypeAdapter(MyDefaultContext).validate_python({}), MyDefaultContext(b=3.14, c=False))
62+
self.assertEqual(TypeAdapter(MyDefaultContext).validate_python([]), MyDefaultContext(b=3.14, c=False))
63+
self.assertEqual(TypeAdapter(MyDefaultContext).validate_python({"b": 10.0}), MyDefaultContext(b=10.0, c=False))
64+
4665
def test_date_validation(self):
4766
c = DateContext(date=date.today())
4867
self.assertEqual(DateContext(date=str(date.today())), c)

0 commit comments

Comments
 (0)