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/types.py CHANGED
@@ -2,11 +2,12 @@ import inspect
2
2
  import sys
3
3
  import typing
4
4
  from dataclasses import dataclass
5
+ from functools import partial
5
6
  from typing import Protocol, runtime_checkable
6
7
 
7
- from ovld.utils import UsageError
8
-
9
8
  from .mro import Order, TypeRelationship, subclasscheck, typeorder
9
+ from .typemap import TypeMap
10
+ from .utils import UsageError, clsstring
10
11
 
11
12
  try:
12
13
  from types import UnionType
@@ -14,66 +15,152 @@ except ImportError: # pragma: no cover
14
15
  UnionType = None
15
16
 
16
17
 
17
- def normalize_type(t, fn):
18
- from .dependent import DependentType, Equals
19
-
20
- if isinstance(t, str):
21
- t = eval(t, getattr(fn, "__globals__", {}))
22
-
23
- if t is type:
24
- t = type[object]
25
- elif t is typing.Any:
26
- t = object
27
- elif t is inspect._empty:
28
- t = object
29
- elif isinstance(t, typing._AnnotatedAlias):
30
- t = t.__origin__
31
-
32
- origin = getattr(t, "__origin__", None)
33
- if UnionType and isinstance(t, UnionType):
34
- return normalize_type(t.__args__, fn)
35
- elif origin is type:
36
- return t
37
- elif origin is typing.Union:
38
- return normalize_type(t.__args__, fn)
39
- elif origin is typing.Literal:
40
- return Equals(*t.__args__)
41
- elif origin and not getattr(t, "__args__", None):
42
- return t
43
- elif origin is not None:
44
- raise TypeError(
45
- f"ovld does not accept generic types except type, Union, Optional, Literal, but not: {t}"
46
- )
47
- elif isinstance(t, tuple):
48
- return typing.Union[tuple(normalize_type(t2, fn) for t2 in t)]
49
- elif isinstance(t, DependentType) and not t.bound:
50
- raise UsageError(
51
- f"Dependent type {t} has not been given a type bound. Please use Dependent[<bound>, {t}] instead."
52
- )
53
- else:
54
- return t
18
+ class TypeNormalizer:
19
+ def __init__(self, generic_handlers=None):
20
+ self.generic_handlers = generic_handlers or TypeMap()
21
+
22
+ def register_generic(self, generic, handler=None):
23
+ if handler is None:
24
+ return partial(self.register_generic, generic)
25
+ else:
26
+ self.generic_handlers.register(generic, handler)
27
+
28
+ def __call__(self, t, fn):
29
+ from .dependent import DependentType
30
+
31
+ if isinstance(t, str):
32
+ t = eval(t, getattr(fn, "__globals__", {}))
33
+
34
+ if t is type:
35
+ t = type[object]
36
+ elif t is typing.Any:
37
+ t = object
38
+ elif t is inspect._empty:
39
+ t = object
40
+ elif isinstance(t, typing._AnnotatedAlias):
41
+ t = t.__origin__
42
+
43
+ origin = getattr(t, "__origin__", None)
44
+ if UnionType and isinstance(t, UnionType):
45
+ return self(t.__args__, fn)
46
+ elif origin is type:
47
+ return t
48
+ elif origin and getattr(t, "__args__", None) is None:
49
+ return t
50
+ elif origin is not None:
51
+ try:
52
+ results = self.generic_handlers[origin]
53
+ results = list(results.items())
54
+ results.sort(key=lambda x: x[1], reverse=True)
55
+ assert results
56
+ rval = results[0][0](self, t, fn)
57
+ if isinstance(origin, type) and hasattr(rval, "with_bound"):
58
+ rval = rval.with_bound(origin)
59
+ return rval
60
+ except KeyError: # pragma: no cover
61
+ raise TypeError(f"ovld does not understand generic type {t}")
62
+ elif isinstance(t, tuple):
63
+ return Union[tuple(self(t2, fn) for t2 in t)]
64
+ elif isinstance(t, DependentType) and not t.bound:
65
+ raise UsageError(
66
+ f"Dependent type {t} has not been given a type bound. Please use Dependent[<bound>, {t}] instead."
67
+ )
68
+ else:
69
+ return t
70
+
71
+
72
+ normalize_type = TypeNormalizer()
73
+
74
+
75
+ @normalize_type.register_generic(typing.Union)
76
+ def _(self, t, fn):
77
+ return self(t.__args__, fn)
55
78
 
56
79
 
57
80
  class MetaMC(type):
58
- def __new__(T, name, order):
59
- return super().__new__(T, name, (), {"order": order})
81
+ def __new__(T, name, handler):
82
+ return super().__new__(T, name, (), {"_handler": handler})
60
83
 
61
- def __init__(cls, name, order):
62
- pass
84
+ def __init__(cls, name, handler):
85
+ cls.__args__ = getattr(handler, "__args__", ())
63
86
 
64
- def __typeorder__(cls, other):
65
- order = cls.order(other)
66
- if isinstance(order, bool):
67
- return NotImplemented
68
- else:
69
- return order
87
+ def codegen(cls):
88
+ return cls._handler.codegen()
89
+
90
+ def __type_order__(cls, other):
91
+ return cls._handler.__type_order__(other)
92
+
93
+ def __is_supertype__(cls, other):
94
+ return cls._handler.__is_supertype__(other)
95
+
96
+ def __is_subtype__(cls, other): # pragma: no cover
97
+ return cls._handler.__is_subtype__(other)
70
98
 
71
99
  def __subclasscheck__(cls, sub):
72
- result = cls.order(sub)
73
- if isinstance(result, TypeRelationship):
74
- return result.order in (Order.MORE, Order.SAME)
100
+ return cls._handler.__subclasscheck__(sub)
101
+
102
+ def __instancecheck__(cls, obj):
103
+ return cls._handler.__instancecheck__(obj)
104
+
105
+ def __eq__(cls, other):
106
+ return (
107
+ type(cls) is type(other)
108
+ and type(cls._handler) is type(other._handler)
109
+ and cls._handler == other._handler
110
+ )
111
+
112
+ def __hash__(cls):
113
+ return hash(cls._handler)
114
+
115
+ def __and__(cls, other):
116
+ return Intersection[cls, other]
117
+
118
+ def __rand__(cls, other):
119
+ return Intersection[other, cls]
120
+
121
+ def __str__(self):
122
+ return str(self._handler)
123
+
124
+ __repr__ = __str__
125
+
126
+
127
+ class SingleFunctionHandler:
128
+ def __init__(self, handler, args):
129
+ self.handler = handler
130
+ self.args = self.__args__ = args
131
+
132
+ def __type_order__(self, other):
133
+ results = self.handler(other, *self.args)
134
+ if isinstance(results, TypeRelationship):
135
+ return results.order
75
136
  else:
76
- return result
137
+ return NotImplemented
138
+
139
+ def __is_supertype__(self, other):
140
+ results = self.handler(other, *self.args)
141
+ if isinstance(results, bool):
142
+ return results
143
+ elif isinstance(results, TypeRelationship):
144
+ return results.supertype
145
+ else: # pragma: no cover
146
+ return NotImplemented
147
+
148
+ def __is_subtype__(self, other): # pragma: no cover
149
+ results = self.handler(other, *self.args)
150
+ if isinstance(results, TypeRelationship):
151
+ return results.subtype
152
+ else:
153
+ return NotImplemented
154
+
155
+ def __subclasscheck__(self, sub):
156
+ return self.__is_supertype__(sub)
157
+
158
+ def __instancecheck__(self, obj):
159
+ return issubclass(type(obj), self)
160
+
161
+ def __str__(self):
162
+ args = ", ".join(map(clsstring, self.__args__))
163
+ return f"{self.handler.__name__}[{args}]"
77
164
 
78
165
 
79
166
  def class_check(condition):
@@ -87,7 +174,7 @@ def class_check(condition):
87
174
  condition: A function that takes a class as an argument and returns
88
175
  True or False depending on whether it matches some condition.
89
176
  """
90
- return MetaMC(condition.__name__, condition)
177
+ return MetaMC(condition.__name__, SingleFunctionHandler(condition, ()))
91
178
 
92
179
 
93
180
  def parametrized_class_check(fn):
@@ -106,14 +193,10 @@ def parametrized_class_check(fn):
106
193
  if not isinstance(arg, tuple):
107
194
  arg = (arg,)
108
195
 
109
- def arg_to_str(x):
110
- if isinstance(x, type):
111
- return x.__name__
112
- else:
113
- return repr(x)
114
-
115
- name = f"{fn.__name__}[{', '.join(map(arg_to_str, arg))}]"
116
- return MetaMC(name, lambda sub: fn(sub, *arg))
196
+ if isinstance(fn, type):
197
+ return MetaMC(fn.__name__, fn(*arg))
198
+ else:
199
+ return MetaMC(fn.__name__, SingleFunctionHandler(fn, arg))
117
200
 
118
201
  _C.__name__ = fn.__name__
119
202
  _C.__qualname__ = fn.__qualname__
@@ -128,6 +211,56 @@ def _getcls(ref):
128
211
  return curr
129
212
 
130
213
 
214
+ class AllMC(type):
215
+ def __type_order__(self, other):
216
+ return Order.MORE
217
+
218
+ def __is_subtype__(self, other):
219
+ return True
220
+
221
+ def __is_supertype__(self, other):
222
+ return False
223
+
224
+ def __subclasscheck__(self, other): # pragma: no cover
225
+ return False
226
+
227
+ def __isinstance__(self, other): # pragma: no cover
228
+ return False
229
+
230
+
231
+ class All(metaclass=AllMC):
232
+ """All is the empty/void/bottom type -- it acts as a subtype of all types.
233
+
234
+ It is basically the opposite of Any: nothing is an instance of All. The main
235
+ place you want to use All is as a wildcard in contravariant settings, e.g.
236
+ all 2-argument functions are instances of Callable[[All, All], Any] because
237
+ the arguments are contravariant.
238
+ """
239
+
240
+
241
+ class WhateverMC(AllMC):
242
+ def __is_supertype__(self, other):
243
+ return True
244
+
245
+ def __subclasscheck__(self, other): # pragma: no cover
246
+ return True
247
+
248
+ def __isinstance__(self, other): # pragma: no cover
249
+ return True
250
+
251
+
252
+ class Whatever(metaclass=WhateverMC):
253
+ """This type is a superclass and a subclass of everything.
254
+
255
+ It's not a coherent type, more like a convenience.
256
+
257
+ It'll match anything anywhere, so you can write e.g.
258
+ Callable[[Whatever, Whatever], Whatever] to match any function of
259
+ two arguments. Any only works in covariant settings, and All only
260
+ works in contravariant settings.
261
+ """
262
+
263
+
131
264
  class Deferred:
132
265
  """Represent a class from an external module without importing it.
133
266
 
@@ -156,7 +289,7 @@ class Deferred:
156
289
  else:
157
290
  return False
158
291
 
159
- return MetaMC(f"Deferred[{ref}]", check)
292
+ return MetaMC(f"Deferred[{ref}]", SingleFunctionHandler(check, ()))
160
293
 
161
294
 
162
295
  @parametrized_class_check
@@ -164,7 +297,7 @@ def Exactly(cls, base_cls):
164
297
  """Match the class but not its subclasses."""
165
298
  return TypeRelationship(
166
299
  order=Order.LESS if cls is base_cls else typeorder(base_cls, cls),
167
- matches=cls is base_cls,
300
+ supertype=cls is base_cls,
168
301
  )
169
302
 
170
303
 
@@ -179,16 +312,105 @@ def StrictSubclass(cls, base_cls):
179
312
 
180
313
 
181
314
  @parametrized_class_check
182
- def Intersection(cls, *classes):
183
- """Match all classes."""
184
- matches = all(subclasscheck(cls, t) for t in classes)
185
- compare = [x for t in classes if (x := typeorder(t, cls)) is not Order.NONE]
186
- if not compare:
187
- return TypeRelationship(Order.NONE, matches=matches)
188
- elif any(x is Order.LESS or x is Order.SAME for x in compare):
189
- return TypeRelationship(Order.LESS, matches=matches)
190
- else:
191
- return TypeRelationship(Order.MORE, matches=matches)
315
+ class Union:
316
+ def __init__(self, *types):
317
+ self.__args__ = self.types = types
318
+
319
+ def codegen(self):
320
+ from .dependent import combine, generate_checking_code
321
+
322
+ template = " or ".join("{}" for t in self.types)
323
+ return combine(
324
+ template, [generate_checking_code(t) for t in self.types]
325
+ )
326
+
327
+ def __type_order__(self, other):
328
+ if other is Union:
329
+ return Order.LESS
330
+ classes = self.types
331
+ compare = [
332
+ x for t in classes if (x := typeorder(t, other)) is not Order.NONE
333
+ ]
334
+ if not compare:
335
+ return Order.NONE
336
+ elif any(x is Order.MORE or x is Order.SAME for x in compare):
337
+ return Order.MORE
338
+ else:
339
+ return Order.LESS
340
+
341
+ def __is_supertype__(self, other):
342
+ return any(subclasscheck(other, t) for t in self.types)
343
+
344
+ def __is_subtype__(self, other):
345
+ if other is Union:
346
+ return True
347
+ return NotImplemented # pragma: no cover
348
+
349
+ def __subclasscheck__(self, sub):
350
+ return self.__is_supertype__(sub)
351
+
352
+ def __instancecheck__(self, obj):
353
+ return any(isinstance(obj, t) for t in self.types)
354
+
355
+ def __eq__(self, other):
356
+ return self.__args__ == other.__args__
357
+
358
+ def __hash__(self):
359
+ return hash(self.__args__)
360
+
361
+ def __str__(self):
362
+ return " | ".join(map(clsstring, self.__args__))
363
+
364
+
365
+ @parametrized_class_check
366
+ class Intersection:
367
+ def __init__(self, *types):
368
+ self.__args__ = self.types = types
369
+
370
+ def codegen(self):
371
+ from .dependent import combine, generate_checking_code
372
+
373
+ template = " and ".join("{}" for t in self.types)
374
+ return combine(
375
+ template, [generate_checking_code(t) for t in self.types]
376
+ )
377
+
378
+ def __type_order__(self, other):
379
+ if other is Intersection:
380
+ return Order.LESS
381
+ classes = self.types
382
+ compare = [
383
+ x for t in classes if (x := typeorder(t, other)) is not Order.NONE
384
+ ]
385
+ if not compare:
386
+ return Order.NONE
387
+ elif any(x is Order.LESS or x is Order.SAME for x in compare):
388
+ return Order.LESS
389
+ else:
390
+ return Order.MORE
391
+
392
+ def __is_supertype__(self, other):
393
+ return all(subclasscheck(other, t) for t in self.types)
394
+
395
+ def __is_subtype__(self, other): # pragma: no cover
396
+ if other is Intersection:
397
+ return True
398
+ return NotImplemented
399
+
400
+ def __subclasscheck__(self, sub):
401
+ return self.__is_supertype__(sub)
402
+
403
+ def __instancecheck__(self, obj):
404
+ return all(isinstance(obj, t) for t in self.types)
405
+
406
+ def __eq__(self, other):
407
+ return self.__args__ == other.__args__
408
+
409
+ def __hash__(self):
410
+ return hash(self.__args__)
411
+
412
+ def __str__(self):
413
+ return " & ".join(map(clsstring, self.__args__))
192
414
 
193
415
 
194
416
  @parametrized_class_check
ovld/utils.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """Miscellaneous utilities."""
2
2
 
3
3
  import functools
4
+ import typing
4
5
 
5
6
 
6
7
  class Named:
@@ -60,6 +61,41 @@ class Unusable:
60
61
  raise UsageError(self.__message)
61
62
 
62
63
 
64
+ class GenericAliasMC(type):
65
+ def __instancecheck__(cls, obj):
66
+ return hasattr(obj, "__origin__")
67
+
68
+
69
+ class GenericAlias(metaclass=GenericAliasMC):
70
+ pass
71
+
72
+
73
+ def clsstring(cls):
74
+ if cls is object:
75
+ return "*"
76
+ elif args := typing.get_args(cls):
77
+ origin = typing.get_origin(cls) or cls
78
+ args = ", ".join(map(clsstring, args))
79
+ return f"{origin.__name__}[{args}]"
80
+ else:
81
+ r = repr(cls)
82
+ if r.startswith("<class "):
83
+ return cls.__name__
84
+ else:
85
+ return r
86
+
87
+
88
+ def subtler_type(obj):
89
+ if isinstance(obj, GenericAlias):
90
+ return type[obj]
91
+ elif obj is typing.Any:
92
+ return type[object]
93
+ elif isinstance(obj, type):
94
+ return type[obj]
95
+ else:
96
+ return type(obj)
97
+
98
+
63
99
  __all__ = [
64
100
  "BOOTSTRAP",
65
101
  "MISSING",
ovld/version.py CHANGED
@@ -1 +1 @@
1
- version = "0.4.2"
1
+ version = "0.4.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ovld
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Overloading Python functions
5
5
  Project-URL: Homepage, https://ovld.readthedocs.io/en/latest/
6
6
  Project-URL: Documentation, https://ovld.readthedocs.io/en/latest/
@@ -0,0 +1,14 @@
1
+ ovld/__init__.py,sha256=GEhOdG64GPUY5Y5BMnaik7UMgcq12mWVhxN9quIonYw,1476
2
+ ovld/abc.py,sha256=4qpZyYwI8dWgY1Oiv5FhdKg2uzNcyWxIpGmGJVcjXrs,1177
3
+ ovld/core.py,sha256=vzrYgUdagsQH6gbw32kzYzd-MvnCE3O9LjUyc-vVk1M,25366
4
+ ovld/dependent.py,sha256=vvWjjOHNtw8PegZVDj1fRKpyp6SCg1WdOWoXNxZSVrk,9288
5
+ ovld/mro.py,sha256=0cJK_v-POCiuwjluwf8fWeQ3G-2chEUv3KYe57GC61Q,4552
6
+ ovld/recode.py,sha256=NWAM0qyOQqXgVCZxtmhtnkygWe-NV5FRwdXGtqikGTg,18494
7
+ ovld/typemap.py,sha256=PH5dy8ddVCcqj2TkQbgsM7fmCdHsJT9WGXFPn4JZsCA,13131
8
+ ovld/types.py,sha256=EZNv8pThbo47KBULYM3R0R3sM2C5LC4DWraXZImkiNs,12877
9
+ ovld/utils.py,sha256=BsE7MmW5sfnO4Ym8hcEL8JNZZ1JIX_pgVRAAidh_8_w,2085
10
+ ovld/version.py,sha256=Qk6wsgzl7cSMWWZ902odoQ-wBV5Fda2ShX82NN7uHzM,18
11
+ ovld-0.4.4.dist-info/METADATA,sha256=wUsEDZyoQPooCN7P7q4MyObKAeSjg7DqpR-vRne5BW8,7713
12
+ ovld-0.4.4.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
13
+ ovld-0.4.4.dist-info/licenses/LICENSE,sha256=cSwNTIzd1cbI89xt3PeZZYJP2y3j8Zus4bXgo4svpX8,1066
14
+ ovld-0.4.4.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- ovld/__init__.py,sha256=Vp9wbIy_opFmJx-vCcToQcIP3cWFIvgbfYHITyiwPLs,1305
2
- ovld/core.py,sha256=glexOiRU0ajoxFAQN6d69ToL6_cPvyv05Z7LEHqrJj8,25701
3
- ovld/dependent.py,sha256=QITsWu2uCwqkHE0tunETy8Jqwc272uoG5YM0I5yy0m4,7303
4
- ovld/mro.py,sha256=rS0pCLLWwLUI0NdthpoJSrdvWR4-XgRfx1pLAvJde14,5586
5
- ovld/recode.py,sha256=zxhyotuc73qzuN4NEN_rKxaqoA0SOH7KFtE2l_CRBFs,17963
6
- ovld/typemap.py,sha256=gw8XvB-N_15Dr0-am0v17x1seNjYK5AxvUT9L36mWic,13630
7
- ovld/types.py,sha256=Zeb7xhHbL4T7qIRHI-I_cjG61UIWrfZv_EwjwqhB-rY,6381
8
- ovld/utils.py,sha256=V6Y8oZ6ojq8JaODL1rMZbU5L9QG0YSqHNYmpIFiwy3M,1294
9
- ovld/version.py,sha256=OHlqRD04Kvw_CFYmiIVx7UuOJRTQKtAx1Bl4rkUYHJE,18
10
- ovld-0.4.2.dist-info/METADATA,sha256=fV3vxTKRAjCUkRVNmzTW14VajGSl7P0b9NnG06cjUJk,7713
11
- ovld-0.4.2.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
12
- ovld-0.4.2.dist-info/licenses/LICENSE,sha256=cSwNTIzd1cbI89xt3PeZZYJP2y3j8Zus4bXgo4svpX8,1066
13
- ovld-0.4.2.dist-info/RECORD,,
File without changes