8000 Added must subclass feature · CarliJoy/intersection_examples@aeb8a01 · GitHub
[go: up one dir, main page]

Skip to content

Commit aeb8a01

Browse files
committed
Added must subclass feature
1 parent 73efed2 commit aeb8a01

File tree

10 files changed

+72
-22
lines changed

10 files changed

+72
-22
lines changed

examples/any_intersect.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ def foo(self) -> int:
88
...
99

1010

11-
test_new = Intersection[A, Any]
12-
print(test_new)
13-
print("foo", test_new.foo)
14-
print("x", test_new.x)
11+
test = Intersection[A, Any]
12+
print(test)
13+
print("foo", test.foo)
14+
print("x", test.x)
15+
print(test.must_subclass)
1516
print()
16-
test_new = Intersection[Any, A]
17-
print(test_new)
18-
print("foo", test_new.foo)
19-
print("x", test_new.x)
17+
test = Intersection[Any, A]
18+
print(test)
19+
print("foo", test.foo)
20+
print("x", test.x)
21+
print(test.must_subclass)

examples/base_types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
print(test)
55
print(test.strip)
66
print(test.numerator)
7+
print(test.must_subclass)

examples/basic.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ def foo(self, x: int) -> int:
1919
print(test)
2020
print(test.test)
2121
print(test.foo)
22+
print(test.must_subclass)
2223
print()
2324
test = Intersection[B, A]
2425
print(test)
2526
print(test.test)
2627
print(test.foo)
28+
print(test.must_subclass)

examples/callable_and_proto.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ def __call__(self, x: int, /) -> str:
1010

1111
test = Intersection[Callable[[int, str], int], Proto]
1212
print(test.__call__)
13-
13+
print(test.must_subclass)
14+
print()
1415
test = Intersection[Proto, Callable[[int], int]]
1516
print(test.__call__)
17+
print(test.must_subclass)

examples/callables.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
from intersection_examples import Intersection
44

5-
# test = Intersection[Callable[[int], int], Callable[[str], str]]
6-
# print(test)
7-
# print(test.__call__)
8-
9-
x = Callable[[int], int]
5+
test = Intersection[Callable[[int], int], Callable[[int], str]]
6+
print(test.__call__)
7+
print(test.must_subclass)

examples/clashing_callables.py

Lines changed: 0 additions & 6 deletions
This file was deleted.

examples/clashing_overloads.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ def foo(self, x: str) -> int:
1313

1414
test = Intersection[A, C]
1515
print(test.foo)
16+
print(test.must_subclass)

examples/overloaded_already.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ def foo(self, x: int | str) -> int | str:
3232
test = Intersection[A, B]
3333
print(test)
3434
print(test.foo)
35+
print(test.must_subclass)

examples/overloads.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ def foo(self, x: int) -> int:
1414
test = Intersection[A, B]
1515
print(test)
1616
print(test.foo)
17+
print(test.must_subclass)

intersection_examples/__init__.py

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,19 @@
1313
get_args,
1414
get_origin,
1515
get_overloads,
16+
is_typeddict,
1617
)
18+
from typing_extensions import is_protocol
1719

1820

19-
excluded_methods = ["__class__", "__init_subclass__", "__subclasshook__", "__new__"]
2021
get_attribute_excludes = [
2122
"__intersects__",
2223
"__callables__",
24+
"__class__",
25+
"__init_subclass__",
26+
"__subclasshook__",
27+
"__new__",
28+
"must_subclass",
2329
]
2430

2531

@@ -34,6 +40,34 @@ def get_possible_methods(method: Callable) -> Sequence[Callable]:
3440
return overloads
3541

3642

43+
def has_origin(cls: object) -> bool:
44+
try:
45+ orig = get_origin(cls)
46+
if orig is not None:
47+
return True
48+
else:
49+
return False
50+
except:
51+
return False
52+
53+
54+
def is_non_structural(cls: object) -> bool:
55+
"""
56+
We wish to determine if the given class is to be considered structural,
57+
and be excluded from the inheritance restriction.
58+
Args:
59+
cls (object): Class to be determined
60+
61+
Returns:
62+
bool: True is the class is non-structural
63+
"""
64+
if (
65+
cls == Any or is_protocol(cls) or is_typeddict(cls) or has_origin(cls)
66+
): # type:ignore
67+
return False
68+
return True
69+
70+
3771
# Inheritance from Any added to allow type checking to be enabled - attributes of
3872
# this class are unknown to the type checker.
3973
class Intersection(Any):
@@ -104,7 +138,7 @@ def __getattribute__(self, name: str):
104138
)
105139
else:
106140
method = getattr(i, name)
107-
if callable(method) and name not in excluded_methods:
141+
if callable(method):
108142
sig_funcs = [sig_func(i) for i in get_possible_methods(method)]
109143
if len(sig_funcs) == 1:
110144
return sig_funcs[0]
@@ -120,3 +154,17 @@ def __getattribute__(self, name: str):
120154
pass
121155

122156
raise AttributeError(f"Attribute not found on type {self}")
157+
158+
@property
159+
def must_subclass(self) -> tuple[object, ...]:
160+
"""
161+
Returns the classes that this intersection must subclass
162+
163+
Returns:
164+
tuple[object,...]: Classes that must be subclassed in this order
165+
"""
166+
out = []
167+
for i in self.__intersects__:
168+
if is_non_structural(i):
169+
out.append(i)
170+
return tuple(out)

0 commit comments

Comments
 (0)
0