|
1 |
| -from typing import Union |
| 1 | +from typing import Type, TypeVar, Union |
2 | 2 |
|
3 | 3 | import beartype.door
|
4 | 4 | from beartype.roar import BeartypeDoorNonpepException
|
|
13 | 13 | "CovariantMeta",
|
14 | 14 | "parametric",
|
15 | 15 | "type_parameter",
|
| 16 | + "type_unparametrized", |
16 | 17 | "kind",
|
17 | 18 | "Kind",
|
18 | 19 | "Val",
|
19 | 20 | ]
|
20 | 21 |
|
| 22 | +T = TypeVar("T") |
| 23 | + |
21 | 24 |
|
22 | 25 | _dispatch = Dispatcher()
|
23 | 26 |
|
@@ -274,11 +277,82 @@ def class_new(cls, *args, **kw_args):
|
274 | 277 | cls.__new__ = class_new
|
275 | 278 | super(original_class, cls).__init_subclass__(**kw_args)
|
276 | 279 |
|
| 280 | + def __class_nonparametric__(cls): |
| 281 | + """Return the non-parametric type of an object. |
| 282 | +
|
| 283 | + :mod:`plum.parametric` produces parametric subtypes of classes. This |
| 284 | + method can be used to get the non-parametric type of an object. |
| 285 | +
|
| 286 | + Examples |
| 287 | + -------- |
| 288 | + >>> from plum import parametric |
| 289 | + >>> @parametric |
| 290 | + ... class Obj: |
| 291 | + ... @classmethod |
| 292 | + ... def __infer_type_parameter__(cls, *arg): |
| 293 | + ... return type(arg[0]) |
| 294 | + ... def __init__(self, x): |
| 295 | + ... self.x = x |
| 296 | + ... def __repr__(self): |
| 297 | + ... return f"Obj({self.x})" |
| 298 | +
|
| 299 | + >>> obj = Obj(1) |
| 300 | + >>> obj |
| 301 | + Obj(1) |
| 302 | +
|
| 303 | + >>> type(obj).mro() |
| 304 | + [Obj[int], Obj, object] |
| 305 | +
|
| 306 | + >>> obj.__class_nonparametric__().mro() |
| 307 | + [Obj, object] |
| 308 | + """ |
| 309 | + return original_class |
| 310 | + |
| 311 | + def __class_unparametrized__(cls): |
| 312 | + """Return the unparametrized type of an object. |
| 313 | +
|
| 314 | + :mod:`plum.parametric` produces parametric subtypes of classes. This |
| 315 | + method can be used to get the un-parametrized type of an object. |
| 316 | +
|
| 317 | + Examples |
| 318 | + -------- |
| 319 | + >>> from plum import parametric |
| 320 | + >>> @parametric |
| 321 | + ... class Obj: |
| 322 | + ... @classmethod |
| 323 | + ... def __infer_type_parameter__(cls, *arg): |
| 324 | + ... return type(arg[0]) |
| 325 | + ... def __init__(self, x): |
| 326 | + ... self.x = x |
| 327 | + ... def __repr__(self): |
| 328 | + ... return f"Obj({self.x})" |
| 329 | +
|
| 330 | + >>> obj = Obj(1) |
| 331 | + >>> obj |
| 332 | + Obj(1) |
| 333 | +
|
| 334 | + >>> type(obj).__name__ |
| 335 | + Obj[int] |
| 336 | +
|
| 337 | + >>> obj.__class_unparametrized__().mro() |
| 338 | + [Obj, Obj, object] |
| 339 | +
|
| 340 | + Note that this is still NOT the 'original' non-`parametric`-wrapped |
| 341 | + type. This is the type that is wrapped by :mod:`plum.parametric`, but |
| 342 | + without the inferred type parameter(s). |
| 343 | + """ |
| 344 | + return parametric_class |
| 345 | + |
277 | 346 | # Create parametric class.
|
278 | 347 | parametric_class = meta(
|
279 | 348 | original_class.__name__,
|
280 | 349 | (original_class,),
|
281 |
| - {"__new__": __new__, "__init_subclass__": __init_subclass__}, |
| 350 | + { |
| 351 | + "__new__": __new__, |
| 352 | + "__init_subclass__": __init_subclass__, |
| 353 | + "__class_nonparametric__": __class_nonparametric__, |
| 354 | + "__class_unparametrized__": __class_unparametrized__, |
| 355 | + }, |
282 | 356 | )
|
283 | 357 | parametric_class._parametric = True
|
284 | 358 | parametric_class._concrete = False
|
@@ -356,6 +430,44 @@ def type_parameter(x):
|
356 | 430 | )
|
357 | 431 |
|
358 | 432 |
|
| 433 | +def type_unparametrized(q: T) -> Type[T]: |
| 434 | + """Return the unparametrized type of an object. |
| 435 | +
|
| 436 | + :mod:`plum.parametric` produces parametric subtypes of classes. This |
| 437 | + function can be used to get the un-parametrized type of an object. |
| 438 | + This function also works for normal, :mod:`plum.parametric`-wrapped classes. |
| 439 | +
|
| 440 | + Examples |
| 441 | + -------- |
| 442 | + >>> from plum import parametric |
| 443 | + >>> @parametric |
| 444 | + ... class Obj: |
| 445 | + ... @classmethod |
| 446 | + ... def __infer_type_parameter__(cls, *arg): |
| 447 | + ... return type(arg[0]) |
| 448 | + ... def __init__(self, x): |
| 449 | + ... self.x = x |
| 450 | + ... def __repr__(self): |
| 451 | + ... return f"Obj({self.x})" |
| 452 | +
|
| 453 | + >>> obj = Obj(1) |
| 454 | + >>> obj |
| 455 | + Obj(1) |
| 456 | +
|
| 457 | + >>> type(obj).__name__ |
| 458 | + Obj[int] |
| 459 | +
|
| 460 | + >>> type_unparametrized(obj).__name__ |
| 461 | + Obj |
| 462 | +
|
| 463 | + Note that this is still NOT the 'original' non-`parametric`-wrapped type. |
| 464 | + This is the type that is wrapped by :mod:`plum.parametric`, but without the |
| 465 | + inferred type parameter(s). |
| 466 | + """ |
| 467 | + typ = type(q) |
| 468 | + return q.__class_unparametrized__() if isinstance(typ, ParametricTypeMeta) else typ |
| 469 | + |
| 470 | + |
359 | 471 | def kind(SuperClass=object):
|
360 | 472 | """Create a parametric wrapper type for dispatch purposes.
|
361 | 473 |
|
|
0 commit comments