Closed perkinslr closed 1 day ago
Thanks for creating an issue @perkinslr.
I do not see an immediate clean fix for this issue as well. The reason we do immediate checking for subclasses at initialization instead of at function call time is for performance reasons.
You could always push the validation to run time using a custom constraint. I might be able to generalize this with a few caveats.
In your Foo class definition, you could do:
import type_enforced
from type_enforced.utils import WithSubclasses, GenericConstraint
class Bar:
pass
# Using a generic constraint runs the constraint at call time
BarWithSubclass = GenericConstraint({
'bar_with_subclass':lambda x: type(x) in WithSubclasses(Bar)
})
@type_enforced.Enforcer
def foo(x: BarWithSubclass) -> None:
pass
The challenge with this is that Constraints are not validated in the same way as types. First types are validated and then constraints are validated. If you want to pass other type checks with this constraint for foo(x) it may have other implications.
You could also define your foo
function after importing baz
which would then work as is. I understand that this is not necessarily ideal so the above custom constraint would be a more generic solution.
Delayed the callable until inital check in 1.7.0
Closing with: https://github.com/connor-makowski/type_enforced/commit/34e1dd297eb7a5b1e781d03c47e8c9908c1e0f3a
Create a function like
Then in a later imported file, create a subclass of the target type
If you try to call
foo(baz())
you get an error. This is becauseWithSubclasses
immediately looks through the list of subclasses, rather than doing so when the function in question is called.Additionally, abstract base classes are unsupported because it uses this approach. A simpler approach would be to make WithSubclasses be a higher priority check that uses
isinstance
to defer the actual check to at runtime. Unfortunately I don't see a trivial way to fix this.