utils.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. # This file is dual licensed under the terms of the Apache License, Version
  2. # 2.0, and the BSD License. See the LICENSE file in the root of this repository
  3. # for complete details.
  4. from __future__ import annotations
  5. import enum
  6. import sys
  7. import types
  8. import typing
  9. import warnings
  10. from collections.abc import Callable, Sequence
  11. # We use a UserWarning subclass, instead of DeprecationWarning, because CPython
  12. # decided deprecation warnings should be invisible by default.
  13. class CryptographyDeprecationWarning(UserWarning):
  14. pass
  15. # Several APIs were deprecated with no specific end-of-life date because of the
  16. # ubiquity of their use. They should not be removed until we agree on when that
  17. # cycle ends.
  18. DeprecatedIn36 = CryptographyDeprecationWarning
  19. DeprecatedIn40 = CryptographyDeprecationWarning
  20. DeprecatedIn41 = CryptographyDeprecationWarning
  21. DeprecatedIn42 = CryptographyDeprecationWarning
  22. DeprecatedIn43 = CryptographyDeprecationWarning
  23. DeprecatedIn46 = CryptographyDeprecationWarning
  24. # If you're wondering why we don't use `Buffer`, it's because `Buffer` would
  25. # be more accurately named: Bufferable. It means something which has an
  26. # `__buffer__`. Which means you can't actually treat the result as a buffer
  27. # (and do things like take a `len()`).
  28. if sys.version_info >= (3, 9):
  29. Buffer = typing.Union[bytes, bytearray, memoryview]
  30. else:
  31. Buffer = typing.ByteString
  32. def _check_bytes(name: str, value: bytes) -> None:
  33. if not isinstance(value, bytes):
  34. raise TypeError(f"{name} must be bytes")
  35. def _check_byteslike(name: str, value: Buffer) -> None:
  36. try:
  37. memoryview(value)
  38. except TypeError:
  39. raise TypeError(f"{name} must be bytes-like")
  40. def int_to_bytes(integer: int, length: int | None = None) -> bytes:
  41. if length == 0:
  42. raise ValueError("length argument can't be 0")
  43. return integer.to_bytes(
  44. length or (integer.bit_length() + 7) // 8 or 1, "big"
  45. )
  46. class InterfaceNotImplemented(Exception):
  47. pass
  48. class _DeprecatedValue:
  49. def __init__(self, value: object, message: str, warning_class):
  50. self.value = value
  51. self.message = message
  52. self.warning_class = warning_class
  53. class _ModuleWithDeprecations(types.ModuleType):
  54. def __init__(self, module: types.ModuleType):
  55. super().__init__(module.__name__)
  56. self.__dict__["_module"] = module
  57. def __getattr__(self, attr: str) -> object:
  58. obj = getattr(self._module, attr)
  59. if isinstance(obj, _DeprecatedValue):
  60. warnings.warn(obj.message, obj.warning_class, stacklevel=2)
  61. obj = obj.value
  62. return obj
  63. def __setattr__(self, attr: str, value: object) -> None:
  64. setattr(self._module, attr, value)
  65. def __delattr__(self, attr: str) -> None:
  66. obj = getattr(self._module, attr)
  67. if isinstance(obj, _DeprecatedValue):
  68. warnings.warn(obj.message, obj.warning_class, stacklevel=2)
  69. delattr(self._module, attr)
  70. def __dir__(self) -> Sequence[str]:
  71. return ["_module", *dir(self._module)]
  72. def deprecated(
  73. value: object,
  74. module_name: str,
  75. message: str,
  76. warning_class: type[Warning],
  77. name: str | None = None,
  78. ) -> _DeprecatedValue:
  79. module = sys.modules[module_name]
  80. if not isinstance(module, _ModuleWithDeprecations):
  81. sys.modules[module_name] = module = _ModuleWithDeprecations(module)
  82. dv = _DeprecatedValue(value, message, warning_class)
  83. # Maintain backwards compatibility with `name is None` for pyOpenSSL.
  84. if name is not None:
  85. setattr(module, name, dv)
  86. return dv
  87. def cached_property(func: Callable) -> property:
  88. cached_name = f"_cached_{func}"
  89. sentinel = object()
  90. def inner(instance: object):
  91. cache = getattr(instance, cached_name, sentinel)
  92. if cache is not sentinel:
  93. return cache
  94. result = func(instance)
  95. setattr(instance, cached_name, result)
  96. return result
  97. return property(inner)
  98. # Python 3.10 changed representation of enums. We use well-defined object
  99. # representation and string representation from Python 3.9.
  100. class Enum(enum.Enum):
  101. def __repr__(self) -> str:
  102. return f"<{self.__class__.__name__}.{self._name_}: {self._value_!r}>"
  103. def __str__(self) -> str:
  104. return f"{self.__class__.__name__}.{self._name_}"