utils.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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. DeprecatedIn47 = 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. Buffer = typing.Union[bytes, bytearray, memoryview]
  29. def _check_bytes(name: str, value: bytes) -> None:
  30. if not isinstance(value, bytes):
  31. raise TypeError(f"{name} must be bytes")
  32. def _check_byteslike(name: str, value: Buffer) -> None:
  33. try:
  34. memoryview(value)
  35. except TypeError:
  36. raise TypeError(f"{name} must be bytes-like")
  37. def int_to_bytes(integer: int, length: int | None = None) -> bytes:
  38. if length == 0:
  39. raise ValueError("length argument can't be 0")
  40. return integer.to_bytes(
  41. length or (integer.bit_length() + 7) // 8 or 1, "big"
  42. )
  43. class InterfaceNotImplemented(Exception):
  44. pass
  45. class _DeprecatedValue:
  46. def __init__(self, value: object, message: str, warning_class):
  47. self.value = value
  48. self.message = message
  49. self.warning_class = warning_class
  50. class _ModuleWithDeprecations(types.ModuleType):
  51. def __init__(self, module: types.ModuleType):
  52. super().__init__(module.__name__)
  53. self.__dict__["_module"] = module
  54. def __getattr__(self, name: str) -> typing.Any:
  55. obj = getattr(self._module, name)
  56. if isinstance(obj, _DeprecatedValue):
  57. warnings.warn(obj.message, obj.warning_class, stacklevel=2)
  58. obj = obj.value
  59. return obj
  60. def __setattr__(self, attr: str, value: object) -> None:
  61. setattr(self._module, attr, value)
  62. def __delattr__(self, attr: str) -> None:
  63. obj = getattr(self._module, attr)
  64. if isinstance(obj, _DeprecatedValue):
  65. warnings.warn(obj.message, obj.warning_class, stacklevel=2)
  66. delattr(self._module, attr)
  67. def __dir__(self) -> Sequence[str]:
  68. return ["_module", *dir(self._module)]
  69. def deprecated(
  70. value: object,
  71. module_name: str,
  72. message: str,
  73. warning_class: type[Warning],
  74. name: str | None = None,
  75. ) -> _DeprecatedValue:
  76. module = sys.modules[module_name]
  77. if not isinstance(module, _ModuleWithDeprecations):
  78. sys.modules[module_name] = module = _ModuleWithDeprecations(module)
  79. dv = _DeprecatedValue(value, message, warning_class)
  80. # Maintain backwards compatibility with `name is None` for pyOpenSSL.
  81. if name is not None:
  82. setattr(module, name, dv)
  83. return dv
  84. def cached_property(func: Callable) -> property:
  85. cached_name = f"_cached_{func}"
  86. sentinel = object()
  87. def inner(instance: object):
  88. cache = getattr(instance, cached_name, sentinel)
  89. if cache is not sentinel:
  90. return cache
  91. result = func(instance)
  92. setattr(instance, cached_name, result)
  93. return result
  94. return property(inner)
  95. # Python 3.10 changed representation of enums. We use well-defined object
  96. # representation and string representation from Python 3.9.
  97. class Enum(enum.Enum):
  98. def __repr__(self) -> str:
  99. return f"<{self.__class__.__name__}.{self._name_}: {self._value_!r}>"
  100. def __str__(self) -> str:
  101. return f"{self.__class__.__name__}.{self._name_}"