ovld 0.4.2__py3-none-any.whl → 0.4.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ovld/__init__.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
+ from . import abc # noqa: F401
3
4
  from .core import (
4
5
  Ovld,
5
6
  OvldBase,
@@ -15,6 +16,11 @@ from .dependent import (
15
16
  ParametrizedDependentType,
16
17
  dependent_check,
17
18
  )
19
+ from .mro import (
20
+ TypeRelationship,
21
+ subclasscheck,
22
+ typeorder,
23
+ )
18
24
  from .recode import call_next, recurse
19
25
  from .typemap import (
20
26
  MultiTypeMap,
@@ -66,6 +72,9 @@ __all__ = [
66
72
  "Intersection",
67
73
  "StrictSubclass",
68
74
  "class_check",
75
+ "subclasscheck",
76
+ "typeorder",
77
+ "TypeRelationship",
69
78
  "parametrized_class_check",
70
79
  "keyword_decorator",
71
80
  "call_next",
ovld/abc.py ADDED
@@ -0,0 +1,48 @@
1
+ import typing
2
+ from collections.abc import Callable, Collection, Mapping, Sequence
3
+
4
+ from .dependent import Callable as OvldCallable
5
+ from .dependent import (
6
+ CollectionFastCheck,
7
+ Equals,
8
+ MappingFastCheck,
9
+ ProductType,
10
+ SequenceFastCheck,
11
+ )
12
+ from .types import normalize_type
13
+
14
+
15
+ @normalize_type.register_generic(typing.Literal)
16
+ def _(self, t, fn):
17
+ return Equals[t.__args__]
18
+
19
+
20
+ @normalize_type.register_generic(tuple)
21
+ def _(self, t, fn):
22
+ args = tuple(self(arg, fn) for arg in t.__args__)
23
+ return ProductType[args]
24
+
25
+
26
+ @normalize_type.register_generic(Sequence)
27
+ def _(self, t, fn):
28
+ args = tuple(self(arg, fn) for arg in t.__args__)
29
+ return SequenceFastCheck[args]
30
+
31
+
32
+ @normalize_type.register_generic(Collection)
33
+ def _(self, t, fn):
34
+ args = tuple(self(arg, fn) for arg in t.__args__)
35
+ return CollectionFastCheck[args]
36
+
37
+
38
+ @normalize_type.register_generic(Mapping)
39
+ def _(self, t, fn):
40
+ args = tuple(self(arg, fn) for arg in t.__args__)
41
+ return MappingFastCheck[args]
42
+
43
+
44
+ @normalize_type.register_generic(Callable)
45
+ def _(self, t, fn):
46
+ *at, rt = t.__args__
47
+ at = tuple(self(arg, fn) for arg in at)
48
+ return OvldCallable[at, self(rt, fn)]
ovld/core.py CHANGED
@@ -16,9 +16,9 @@ from .recode import (
16
16
  generate_dispatch,
17
17
  rename_function,
18
18
  )
19
- from .typemap import MultiTypeMap, is_type_of_type
20
- from .types import normalize_type
21
- from .utils import UsageError, keyword_decorator
19
+ from .typemap import MultiTypeMap
20
+ from .types import clsstring, normalize_type
21
+ from .utils import UsageError, keyword_decorator, subtler_type
22
22
 
23
23
  _current_id = itertools.count()
24
24
 
@@ -80,6 +80,7 @@ class Arginfo:
80
80
  @dataclass(frozen=True)
81
81
  class Signature:
82
82
  types: tuple
83
+ return_type: type
83
84
  req_pos: int
84
85
  max_pos: int
85
86
  req_names: frozenset
@@ -139,6 +140,7 @@ class Signature:
139
140
 
140
141
  return cls(
141
142
  types=tuple(typelist),
143
+ return_type=normalize_type(sig.return_annotation, fn),
142
144
  req_pos=req_pos,
143
145
  max_pos=max_pos,
144
146
  req_names=frozenset(req_names),
@@ -149,28 +151,16 @@ class Signature:
149
151
  )
150
152
 
151
153
 
152
- def clsstring(cls):
153
- if cls is object:
154
- return "*"
155
- elif isinstance(cls, tuple):
154
+ def typemap_entry_string(cls):
155
+ if isinstance(cls, tuple):
156
156
  key, typ = cls
157
157
  return f"{key}: {clsstring(typ)}"
158
- elif is_type_of_type(cls):
159
- arg = clsstring(cls.__args__[0])
160
- return f"type[{arg}]"
161
- elif hasattr(cls, "__origin__"):
162
- if cls.__origin__ is typing.Union:
163
- return "|".join(map(clsstring, cls.__args__))
164
- else:
165
- return repr(cls)
166
- elif hasattr(cls, "__name__"):
167
- return cls.__name__
168
158
  else:
169
- return repr(cls)
159
+ return clsstring(cls)
170
160
 
171
161
 
172
162
  def sigstring(types):
173
- return ", ".join(map(clsstring, types))
163
+ return ", ".join(map(typemap_entry_string, types))
174
164
 
175
165
 
176
166
  class ArgumentAnalyzer:
@@ -277,9 +267,7 @@ class ArgumentAnalyzer:
277
267
  )
278
268
 
279
269
  def lookup_for(self, key):
280
- return (
281
- "self.map.transform" if key in self.complex_transforms else "type"
282
- )
270
+ return subtler_type if key in self.complex_transforms else type
283
271
 
284
272
 
285
273
  class _Ovld:
@@ -473,7 +461,7 @@ class _Ovld:
473
461
  @_compile_first
474
462
  def resolve(self, *args):
475
463
  """Find the correct method to call for the given arguments."""
476
- return self.map[tuple(map(self.map.transform, args))]
464
+ return self.map[tuple(map(subtler_type, args))]
477
465
 
478
466
  def register_signature(self, sig, orig_fn):
479
467
  """Register a function for the given signature."""
@@ -577,7 +565,7 @@ class _Ovld:
577
565
 
578
566
  This should be replaced by an auto-generated function.
579
567
  """
580
- key = tuple(map(self.map.transform, args))
568
+ key = tuple(map(subtler_type, args))
581
569
  method = self.map[key]
582
570
  return method(*args)
583
571
 
@@ -586,7 +574,7 @@ class _Ovld:
586
574
  def next(self, *args):
587
575
  """Call the next matching method after the caller, in terms of priority or specificity."""
588
576
  fr = sys._getframe(1)
589
- key = (fr.f_code, *map(self.map.transform, args))
577
+ key = (fr.f_code, *map(subtler_type, args))
590
578
  method = self.map[key]
591
579
  return method(*args)
592
580
 
@@ -631,20 +619,20 @@ class OvldCall:
631
619
  def next(self, *args):
632
620
  """Call the next matching method after the caller, in terms of priority or specificity."""
633
621
  fr = sys._getframe(1)
634
- key = (fr.f_code, *map(self.map.transform, args))
622
+ key = (fr.f_code, *map(subtler_type, args))
635
623
  method = self.map[key]
636
624
  return method(self.obj, *args)
637
625
 
638
626
  def resolve(self, *args):
639
627
  """Find the right method to call for the given arguments."""
640
- return self.map[tuple(map(self.map.transform, args))].__get__(self.obj)
628
+ return self.map[tuple(map(subtler_type, args))].__get__(self.obj)
641
629
 
642
630
  def __call__(self, *args): # pragma: no cover
643
631
  """Call this overloaded function.
644
632
 
645
633
  This should be replaced by an auto-generated function.
646
634
  """
647
- key = tuple(map(self.map.transform, args))
635
+ key = tuple(map(subtler_type, args))
648
636
  method = self.map[key]
649
637
  return method(self.obj, *args)
650
638
 
ovld/dependent.py CHANGED
@@ -1,13 +1,21 @@
1
1
  import inspect
2
2
  import re
3
+ from collections.abc import Callable as _Callable
4
+ from collections.abc import Mapping, Sequence
3
5
  from dataclasses import dataclass
6
+ from functools import partial
4
7
  from itertools import count
5
- from typing import TYPE_CHECKING, Any, Mapping, TypeVar, Union
8
+ from typing import (
9
+ TYPE_CHECKING,
10
+ Any,
11
+ Collection,
12
+ TypeVar,
13
+ )
6
14
 
7
15
  from .types import (
8
16
  Intersection,
9
17
  Order,
10
- TypeRelationship,
18
+ clsstring,
11
19
  normalize_type,
12
20
  subclasscheck,
13
21
  typeorder,
@@ -16,6 +24,13 @@ from .types import (
16
24
  _current = count()
17
25
 
18
26
 
27
+ def generate_checking_code(typ):
28
+ if hasattr(typ, "codegen"):
29
+ return typ.codegen()
30
+ else:
31
+ return CodeGen("isinstance({arg}, {this})", {"this": typ})
32
+
33
+
19
34
  @dataclass
20
35
  class CodeGen:
21
36
  template: str
@@ -47,9 +62,22 @@ def combine(master_template, args):
47
62
  return CodeGen(master_template.format(*fmts), subs)
48
63
 
49
64
 
50
- class DependentType:
65
+ def is_dependent(t):
66
+ if isinstance(t, DependentType):
67
+ return True
68
+ elif any(is_dependent(subt) for subt in getattr(t, "__args__", ())):
69
+ return True
70
+ return False
71
+
72
+
73
+ class DependentType(type):
51
74
  exclusive_type = False
52
75
  keyable_type = False
76
+ bound_is_name = False
77
+
78
+ def __new__(cls, *args, **kwargs):
79
+ value = super().__new__(cls, cls.__name__, (), {})
80
+ return value
53
81
 
54
82
  def __init__(self, bound):
55
83
  self.bound = bound
@@ -67,39 +95,48 @@ class DependentType:
67
95
  def codegen(self):
68
96
  return CodeGen("{this}.check({arg})", {"this": self})
69
97
 
70
- def __typeorder__(self, other):
98
+ def __type_order__(self, other):
71
99
  if isinstance(other, DependentType):
72
100
  order = typeorder(self.bound, other.bound)
73
101
  if order is Order.SAME:
74
102
  # It isn't fully deterministic which of these will be called
75
103
  # because of set ordering between the types we compare
76
104
  if self < other: # pragma: no cover
77
- return TypeRelationship(Order.LESS, matches=False)
105
+ return Order.LESS
78
106
  elif other < self: # pragma: no cover
79
- return TypeRelationship(Order.MORE, matches=False)
107
+ return Order.MORE
80
108
  else:
81
- return TypeRelationship(Order.NONE, matches=False)
109
+ return Order.NONE
82
110
  else: # pragma: no cover
83
- return TypeRelationship(order, matches=False)
84
- elif (matches := subclasscheck(other, self.bound)) or subclasscheck(
111
+ return order
112
+ elif subclasscheck(other, self.bound) or subclasscheck(
85
113
  self.bound, other
86
114
  ):
87
- return TypeRelationship(Order.LESS, matches=matches)
115
+ return Order.LESS
88
116
  else:
89
- return TypeRelationship(Order.NONE, matches=False)
117
+ return Order.NONE
118
+
119
+ def __is_supertype__(self, other):
120
+ if isinstance(other, DependentType):
121
+ return False
122
+ elif subclasscheck(other, self.bound):
123
+ return True
124
+ else:
125
+ return False
126
+
127
+ def __instancecheck__(self, other):
128
+ return isinstance(other, self.bound) and self.check(other)
90
129
 
91
130
  def __lt__(self, other):
92
131
  return False
93
132
 
94
- def __or__(self, other):
95
- if not isinstance(other, DependentType):
96
- return NotImplemented
97
- return Or(self, other)
98
-
99
133
  def __and__(self, other):
100
- if not isinstance(other, DependentType):
101
- return NotImplemented
102
- return And(self, other)
134
+ return Intersection[self, other]
135
+
136
+ def __rand__(self, other):
137
+ return Intersection[other, self]
138
+
139
+ __repr__ = __str__ = clsstring
103
140
 
104
141
 
105
142
  class ParametrizedDependentType(DependentType):
@@ -107,7 +144,7 @@ class ParametrizedDependentType(DependentType):
107
144
  super().__init__(
108
145
  self.default_bound(*parameters) if bound is None else bound
109
146
  )
110
- self.parameters = parameters
147
+ self.__args__ = self.parameters = parameters
111
148
 
112
149
  @property
113
150
  def parameter(self):
@@ -130,8 +167,17 @@ class ParametrizedDependentType(DependentType):
130
167
  return hash(self.parameters) ^ hash(self.bound)
131
168
 
132
169
  def __str__(self):
133
- params = ", ".join(map(repr, self.parameters))
134
- return f"{type(self).__name__}({params})"
170
+ if self.bound_is_name:
171
+ origin = self.bound
172
+ bound = ""
173
+ else:
174
+ origin = self
175
+ if self.bound != self.default_bound(*self.parameters):
176
+ bound = f" < {clsstring(self.bound)}"
177
+ else:
178
+ bound = ""
179
+ args = ", ".join(map(clsstring, self.__args__))
180
+ return f"{origin.__name__}[{args}]{bound}"
135
181
 
136
182
  __repr__ = __str__
137
183
 
@@ -160,8 +206,14 @@ class FuncDependentType(ParametrizedDependentType):
160
206
  return type(self).func(value, *self.parameters)
161
207
 
162
208
 
163
- def dependent_check(fn):
164
- t = type(fn.__name__, (FuncDependentType,), {"func": fn})
209
+ def dependent_check(fn=None, bound_is_name=False):
210
+ if fn is None:
211
+ return partial(dependent_check, bound_is_name=bound_is_name)
212
+ t = type(
213
+ fn.__name__,
214
+ (FuncDependentType,),
215
+ {"func": fn, "bound_is_name": bound_is_name},
216
+ )
165
217
  if len(inspect.signature(fn).parameters) == 1:
166
218
  t = t()
167
219
  return t
@@ -190,8 +242,77 @@ class Equals(ParametrizedDependentType):
190
242
  return CodeGen("({arg} in {ps})", {"ps": self.parameters})
191
243
 
192
244
 
245
+ class ProductType(ParametrizedDependentType):
246
+ bound_is_name = True
247
+
248
+ def default_bound(self, *subtypes):
249
+ return tuple
250
+
251
+ def check(self, value):
252
+ return (
253
+ isinstance(value, tuple)
254
+ and len(value) == len(self.parameters)
255
+ and all(isinstance(x, t) for x, t in zip(value, self.parameters))
256
+ )
257
+
258
+ def codegen(self):
259
+ checks = ["len({arg}) == {n}"]
260
+ params = {"n": len(self.parameters)}
261
+ for i, p in enumerate(self.parameters):
262
+ checks.append(f"isinstance({{arg}}[{i}], {{p{i}}})")
263
+ params[f"p{i}"] = p
264
+ return CodeGen(" and ".join(checks), params)
265
+
266
+ def __type_order__(self, other):
267
+ if isinstance(other, ProductType):
268
+ if len(other.parameters) == len(self.parameters):
269
+ return Order.merge(
270
+ typeorder(a, b)
271
+ for a, b in zip(self.parameters, other.parameters)
272
+ )
273
+ else:
274
+ return Order.NONE
275
+ else:
276
+ return NotImplemented
277
+
278
+
279
+ @dependent_check(bound_is_name=True)
280
+ def SequenceFastCheck(value: Sequence, typ):
281
+ return not value or isinstance(value[0], typ)
282
+
283
+
284
+ @dependent_check(bound_is_name=True)
285
+ def CollectionFastCheck(value: Collection, typ):
286
+ for x in value:
287
+ return isinstance(x, typ)
288
+ else:
289
+ return True
290
+
291
+
292
+ @dependent_check(bound_is_name=True)
293
+ def MappingFastCheck(value: Mapping, kt, vt):
294
+ if not value:
295
+ return True
296
+ for k in value:
297
+ break
298
+ return isinstance(k, kt) and isinstance(value[k], vt)
299
+
300
+
301
+ @dependent_check
302
+ def Callable(fn: _Callable, argt, rett):
303
+ from .core import Signature
304
+
305
+ sig = Signature.extract(fn)
306
+ return (
307
+ sig.max_pos >= len(argt) >= sig.req_pos
308
+ and not sig.req_names
309
+ and all(subclasscheck(t1, t2) for t1, t2 in zip(argt, sig.types))
310
+ and subclasscheck(sig.return_type, rett)
311
+ )
312
+
313
+
193
314
  @dependent_check
194
- def HasKeys(value: Mapping, *keys):
315
+ def HasKey(value: Mapping, *keys):
195
316
  return all(k in value for k in keys)
196
317
 
197
318
 
@@ -218,44 +339,6 @@ class Dependent:
218
339
  return dt.with_bound(bound)
219
340
 
220
341
 
221
- class Or(DependentType):
222
- def __init__(self, *types, bound=None):
223
- self.types = types
224
- super().__init__(bound or self.default_bound())
225
-
226
- def default_bound(self):
227
- return Union[tuple([t.bound for t in self.types])]
228
-
229
- def codegen(self):
230
- template = " or ".join("{}" for t in self.types)
231
- return combine(template, [t.codegen() for t in self.types])
232
-
233
- def check(self, value):
234
- return any(t.check(value) for t in self.types)
235
-
236
-
237
- class And(DependentType):
238
- def __init__(self, *types, bound=None):
239
- self.types = types
240
- super().__init__(bound or self.default_bound())
241
-
242
- def default_bound(self):
243
- bounds = frozenset(t.bound for t in self.types)
244
- return Intersection[tuple(bounds)]
245
-
246
- def codegen(self):
247
- template = " and ".join("{}" for t in self.types)
248
- return combine(template, [t.codegen() for t in self.types])
249
-
250
- def check(self, value):
251
- return all(t.check(value) for t in self.types)
252
-
253
- def __str__(self):
254
- return " & ".join(map(repr, self.types))
255
-
256
- __repr__ = __str__
257
-
258
-
259
342
  if TYPE_CHECKING: # pragma: no cover
260
343
  from typing import Annotated, TypeAlias
261
344
 
@@ -268,7 +351,7 @@ __all__ = [
268
351
  "Dependent",
269
352
  "DependentType",
270
353
  "Equals",
271
- "HasKeys",
354
+ "HasKey",
272
355
  "StartsWith",
273
356
  "EndsWith",
274
357
  "dependent_check",
ovld/mro.py CHANGED
@@ -1,7 +1,7 @@
1
- import typing
2
1
  from dataclasses import dataclass
3
2
  from enum import Enum
4
3
  from graphlib import TopologicalSorter
4
+ from typing import get_args, get_origin
5
5
 
6
6
 
7
7
  class Order(Enum):
@@ -18,21 +18,24 @@ class Order(Enum):
18
18
  else:
19
19
  return self
20
20
 
21
+ @staticmethod
22
+ def merge(orders):
23
+ orders = set(orders)
24
+ if orders == {Order.SAME}:
25
+ return Order.SAME
26
+ elif not (orders - {Order.LESS, Order.SAME}):
27
+ return Order.LESS
28
+ elif not (orders - {Order.MORE, Order.SAME}):
29
+ return Order.MORE
30
+ else:
31
+ return Order.NONE
32
+
21
33
 
22
34
  @dataclass
23
35
  class TypeRelationship:
24
36
  order: Order
25
- matches: bool = None
26
-
27
-
28
- def _issubclass(t1, t2):
29
- try:
30
- return issubclass(t1, t2)
31
- except TypeError:
32
- try:
33
- return isinstance(t1, t2)
34
- except TypeError: # pragma: no cover
35
- return False
37
+ supertype: bool = NotImplemented
38
+ subtype: bool = NotImplemented
36
39
 
37
40
 
38
41
  def typeorder(t1, t2):
@@ -48,44 +51,25 @@ def typeorder(t1, t2):
48
51
  if t1 == t2:
49
52
  return Order.SAME
50
53
 
51
- t1 = getattr(t1, "__proxy_for__", t1)
52
- t2 = getattr(t2, "__proxy_for__", t2)
53
-
54
54
  if (
55
- hasattr(t1, "__typeorder__")
56
- and (result := t1.__typeorder__(t2)) is not NotImplemented
55
+ hasattr(t1, "__type_order__")
56
+ and (result := t1.__type_order__(t2)) is not NotImplemented
57
57
  ):
58
- return result.order
58
+ return result
59
59
  elif (
60
- hasattr(t2, "__typeorder__")
61
- and (result := t2.__typeorder__(t1)) is not NotImplemented
60
+ hasattr(t2, "__type_order__")
61
+ and (result := t2.__type_order__(t1)) is not NotImplemented
62
62
  ):
63
- return result.order.opposite()
63
+ return result.opposite()
64
64
 
65
- o1 = getattr(t1, "__origin__", None)
66
- o2 = getattr(t2, "__origin__", None)
67
-
68
- if o2 is typing.Union:
69
- if t1 is typing.Union:
70
- return Order.MORE
71
- compare = [
72
- x for t in t2.__args__ if (x := typeorder(t1, t)) is not Order.NONE
73
- ]
74
- if not compare:
75
- return Order.NONE
76
- elif any(x is Order.LESS or x is Order.SAME for x in compare):
77
- return Order.LESS
78
- else:
79
- return Order.MORE
80
-
81
- if o1 is typing.Union:
82
- return typeorder(t2, t1).opposite()
65
+ o1 = get_origin(t1)
66
+ o2 = get_origin(t2)
83
67
 
84
68
  if o2 and not o1:
85
69
  return typeorder(t2, t1).opposite()
86
70
 
87
71
  if o1:
88
- if not o2: # or getattr(t2, "__args__", None) is None:
72
+ if not o2:
89
73
  order = typeorder(o1, t2)
90
74
  if order is order.SAME:
91
75
  order = order.LESS
@@ -94,8 +78,8 @@ def typeorder(t1, t2):
94
78
  if (order := typeorder(o1, o2)) is not Order.SAME:
95
79
  return order
96
80
 
97
- args1 = getattr(t1, "__args__", ())
98
- args2 = getattr(t2, "__args__", ())
81
+ args1 = get_args(t1)
82
+ args2 = get_args(t2)
99
83
 
100
84
  if args1 and not args2:
101
85
  return Order.LESS
@@ -105,20 +89,10 @@ def typeorder(t1, t2):
105
89
  return Order.NONE
106
90
 
107
91
  ords = [typeorder(a1, a2) for a1, a2 in zip(args1, args2)]
108
- if Order.MORE in ords and Order.LESS in ords:
109
- return Order.NONE
110
- elif Order.NONE in ords:
111
- return Order.NONE
112
- elif Order.MORE in ords:
113
- return Order.MORE
114
- elif Order.LESS in ords:
115
- return Order.LESS
116
- else: # pragma: no cover
117
- # Not sure when t1 != t2 and that happens
118
- return Order.SAME
92
+ return Order.merge(ords)
119
93
 
120
- sx = _issubclass(t1, t2)
121
- sy = _issubclass(t2, t1)
94
+ sx = issubclass(t1, t2)
95
+ sy = issubclass(t2, t1)
122
96
  if sx and sy: # pragma: no cover
123
97
  # Not sure when t1 != t2 and that happens
124
98
  return Order.SAME
@@ -135,26 +109,20 @@ def subclasscheck(t1, t2):
135
109
  if t1 == t2:
136
110
  return True
137
111
 
138
- t1 = getattr(t1, "__proxy_for__", t1)
139
- t2 = getattr(t2, "__proxy_for__", t2)
140
-
141
112
  if (
142
- hasattr(t2, "__typeorder__")
143
- and (result := t2.__typeorder__(t1)) is not NotImplemented
113
+ hasattr(t2, "__is_supertype__")
114
+ and (result := t2.__is_supertype__(t1)) is not NotImplemented
144
115
  ):
145
- return result.matches
116
+ return result
146
117
 
147
- o1 = getattr(t1, "__origin__", None)
148
- o2 = getattr(t2, "__origin__", None)
118
+ if (
119
+ hasattr(t1, "__is_subtype__")
120
+ and (result := t1.__is_subtype__(t2)) is not NotImplemented
121
+ ):
122
+ return result
149
123
 
150
- if o2 is typing.Union:
151
- return t1 is typing.Union or any(
152
- subclasscheck(t1, t) for t in t2.__args__
153
- )
154
- elif o1 is typing.Union:
155
- return t2 is typing.Union or all(
156
- subclasscheck(t, t2) for t in t1.__args__
157
- )
124
+ o1 = get_origin(t1)
125
+ o2 = get_origin(t2)
158
126
 
159
127
  if not isinstance(o1, type):
160
128
  o1 = None
@@ -164,12 +132,12 @@ def subclasscheck(t1, t2):
164
132
  if o1 or o2:
165
133
  o1 = o1 or t1
166
134
  o2 = o2 or t2
167
- if _issubclass(o1, o2):
135
+ if issubclass(o1, o2):
168
136
  if o2 is t2: # pragma: no cover
169
137
  return True
170
138
  else:
171
- args1 = getattr(t1, "__args__", ())
172
- args2 = getattr(t2, "__args__", ())
139
+ args1 = get_args(t1)
140
+ args2 = get_args(t2)
173
141
  if len(args1) != len(args2):
174
142
  return False
175
143
  return all(
@@ -178,7 +146,10 @@ def subclasscheck(t1, t2):
178
146
  else:
179
147
  return False
180
148
  else:
181
- return _issubclass(t1, t2)
149
+ try:
150
+ return issubclass(t1, t2)
151
+ except TypeError:
152
+ return False
182
153
 
183
154
 
184
155
  def sort_types(cls, avail):