You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

170 lines
5.1 KiB

4 years ago
  1. """
  2. Commonly useful validators.
  3. """
  4. from __future__ import absolute_import, division, print_function
  5. from ._make import _AndValidator, and_, attrib, attrs
  6. __all__ = ["and_", "in_", "instance_of", "optional", "provides"]
  7. @attrs(repr=False, slots=True, hash=True)
  8. class _InstanceOfValidator(object):
  9. type = attrib()
  10. def __call__(self, inst, attr, value):
  11. """
  12. We use a callable class to be able to change the ``__repr__``.
  13. """
  14. if not isinstance(value, self.type):
  15. raise TypeError(
  16. "'{name}' must be {type!r} (got {value!r} that is a "
  17. "{actual!r}).".format(
  18. name=attr.name,
  19. type=self.type,
  20. actual=value.__class__,
  21. value=value,
  22. ),
  23. attr,
  24. self.type,
  25. value,
  26. )
  27. def __repr__(self):
  28. return "<instance_of validator for type {type!r}>".format(
  29. type=self.type
  30. )
  31. def instance_of(type):
  32. """
  33. A validator that raises a :exc:`TypeError` if the initializer is called
  34. with a wrong type for this particular attribute (checks are performed using
  35. :func:`isinstance` therefore it's also valid to pass a tuple of types).
  36. :param type: The type to check for.
  37. :type type: type or tuple of types
  38. :raises TypeError: With a human readable error message, the attribute
  39. (of type :class:`attr.Attribute`), the expected type, and the value it
  40. got.
  41. """
  42. return _InstanceOfValidator(type)
  43. @attrs(repr=False, slots=True, hash=True)
  44. class _ProvidesValidator(object):
  45. interface = attrib()
  46. def __call__(self, inst, attr, value):
  47. """
  48. We use a callable class to be able to change the ``__repr__``.
  49. """
  50. if not self.interface.providedBy(value):
  51. raise TypeError(
  52. "'{name}' must provide {interface!r} which {value!r} "
  53. "doesn't.".format(
  54. name=attr.name, interface=self.interface, value=value
  55. ),
  56. attr,
  57. self.interface,
  58. value,
  59. )
  60. def __repr__(self):
  61. return "<provides validator for interface {interface!r}>".format(
  62. interface=self.interface
  63. )
  64. def provides(interface):
  65. """
  66. A validator that raises a :exc:`TypeError` if the initializer is called
  67. with an object that does not provide the requested *interface* (checks are
  68. performed using ``interface.providedBy(value)`` (see `zope.interface
  69. <https://zopeinterface.readthedocs.io/en/latest/>`_).
  70. :param zope.interface.Interface interface: The interface to check for.
  71. :raises TypeError: With a human readable error message, the attribute
  72. (of type :class:`attr.Attribute`), the expected interface, and the
  73. value it got.
  74. """
  75. return _ProvidesValidator(interface)
  76. @attrs(repr=False, slots=True, hash=True)
  77. class _OptionalValidator(object):
  78. validator = attrib()
  79. def __call__(self, inst, attr, value):
  80. if value is None:
  81. return
  82. self.validator(inst, attr, value)
  83. def __repr__(self):
  84. return "<optional validator for {what} or None>".format(
  85. what=repr(self.validator)
  86. )
  87. def optional(validator):
  88. """
  89. A validator that makes an attribute optional. An optional attribute is one
  90. which can be set to ``None`` in addition to satisfying the requirements of
  91. the sub-validator.
  92. :param validator: A validator (or a list of validators) that is used for
  93. non-``None`` values.
  94. :type validator: callable or :class:`list` of callables.
  95. .. versionadded:: 15.1.0
  96. .. versionchanged:: 17.1.0 *validator* can be a list of validators.
  97. """
  98. if isinstance(validator, list):
  99. return _OptionalValidator(_AndValidator(validator))
  100. return _OptionalValidator(validator)
  101. @attrs(repr=False, slots=True, hash=True)
  102. class _InValidator(object):
  103. options = attrib()
  104. def __call__(self, inst, attr, value):
  105. try:
  106. in_options = value in self.options
  107. except TypeError as e: # e.g. `1 in "abc"`
  108. in_options = False
  109. if not in_options:
  110. raise ValueError(
  111. "'{name}' must be in {options!r} (got {value!r})".format(
  112. name=attr.name, options=self.options, value=value
  113. )
  114. )
  115. def __repr__(self):
  116. return "<in_ validator with options {options!r}>".format(
  117. options=self.options
  118. )
  119. def in_(options):
  120. """
  121. A validator that raises a :exc:`ValueError` if the initializer is called
  122. with a value that does not belong in the options provided. The check is
  123. performed using ``value in options``.
  124. :param options: Allowed options.
  125. :type options: list, tuple, :class:`enum.Enum`, ...
  126. :raises ValueError: With a human readable error message, the attribute (of
  127. type :class:`attr.Attribute`), the expected options, and the value it
  128. got.
  129. .. versionadded:: 17.1.0
  130. """
  131. return _InValidator(options)