description.py 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. from typing import Any, Tuple, Union
  2. __all__ = [
  3. "Description",
  4. "is_description",
  5. "register_description",
  6. "unregister_description",
  7. ]
  8. class Description:
  9. """Type checker for human readable descriptions.
  10. By default, only ordinary strings are accepted as descriptions,
  11. but you can register() other classes that will also be allowed,
  12. e.g. to support lazy string objects that are evaluated only at runtime.
  13. If you register(object), any object will be allowed as description.
  14. """
  15. bases: Union[type, Tuple[type, ...]] = str
  16. @classmethod
  17. def isinstance(cls, obj: Any) -> bool:
  18. return isinstance(obj, cls.bases)
  19. @classmethod
  20. def register(cls, base: type) -> None:
  21. """Register a class that shall be accepted as a description."""
  22. if not isinstance(base, type):
  23. raise TypeError("Only types can be registered.")
  24. if base is object:
  25. cls.bases = object
  26. elif cls.bases is object:
  27. cls.bases = base
  28. elif not isinstance(cls.bases, tuple):
  29. if base is not cls.bases:
  30. cls.bases = (cls.bases, base)
  31. elif base not in cls.bases:
  32. cls.bases += (base,)
  33. @classmethod
  34. def unregister(cls, base: type) -> None:
  35. """Unregister a class that shall no more be accepted as a description."""
  36. if not isinstance(base, type):
  37. raise TypeError("Only types can be unregistered.")
  38. if isinstance(cls.bases, tuple):
  39. if base in cls.bases:
  40. cls.bases = tuple(b for b in cls.bases if b is not base)
  41. if not cls.bases:
  42. cls.bases = object
  43. elif len(cls.bases) == 1:
  44. cls.bases = cls.bases[0]
  45. elif cls.bases is base:
  46. cls.bases = object
  47. is_description = Description.isinstance
  48. register_description = Description.register
  49. unregister_description = Description.unregister