utils.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  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. import abc
  5. import enum
  6. import sys
  7. import types
  8. import typing
  9. import warnings
  10. # We use a UserWarning subclass, instead of DeprecationWarning, because CPython
  11. # decided deprecation warnings should be invisble by default.
  12. class CryptographyDeprecationWarning(UserWarning):
  13. pass
  14. # Several APIs were deprecated with no specific end-of-life date because of the
  15. # ubiquity of their use. They should not be removed until we agree on when that
  16. # cycle ends.
  17. DeprecatedIn36 = CryptographyDeprecationWarning
  18. DeprecatedIn37 = CryptographyDeprecationWarning
  19. DeprecatedIn39 = CryptographyDeprecationWarning
  20. def _check_bytes(name: str, value: bytes) -> None:
  21. if not isinstance(value, bytes):
  22. raise TypeError("{} must be bytes".format(name))
  23. def _check_byteslike(name: str, value: bytes) -> None:
  24. try:
  25. memoryview(value)
  26. except TypeError:
  27. raise TypeError("{} must be bytes-like".format(name))
  28. def int_to_bytes(integer: int, length: typing.Optional[int] = None) -> bytes:
  29. return integer.to_bytes(
  30. length or (integer.bit_length() + 7) // 8 or 1, "big"
  31. )
  32. class InterfaceNotImplemented(Exception):
  33. pass
  34. # DeprecatedIn39 -- Our only known consumer is aws-encryption-sdk, but we've
  35. # made this a no-op to avoid breaking old versions.
  36. def verify_interface(
  37. iface: abc.ABCMeta, klass: object, *, check_annotations: bool = False
  38. ):
  39. # Exists exclusively for `aws-encryption-sdk` which relies on it existing,
  40. # even though it was never a public API.
  41. pass
  42. class _DeprecatedValue:
  43. def __init__(self, value: object, message: str, warning_class):
  44. self.value = value
  45. self.message = message
  46. self.warning_class = warning_class
  47. class _ModuleWithDeprecations(types.ModuleType):
  48. def __init__(self, module: types.ModuleType):
  49. super().__init__(module.__name__)
  50. self.__dict__["_module"] = module
  51. def __getattr__(self, attr: str) -> object:
  52. obj = getattr(self._module, attr)
  53. if isinstance(obj, _DeprecatedValue):
  54. warnings.warn(obj.message, obj.warning_class, stacklevel=2)
  55. obj = obj.value
  56. return obj
  57. def __setattr__(self, attr: str, value: object) -> None:
  58. setattr(self._module, attr, value)
  59. def __delattr__(self, attr: str) -> None:
  60. obj = getattr(self._module, attr)
  61. if isinstance(obj, _DeprecatedValue):
  62. warnings.warn(obj.message, obj.warning_class, stacklevel=2)
  63. delattr(self._module, attr)
  64. def __dir__(self) -> typing.Sequence[str]:
  65. return ["_module"] + dir(self._module)
  66. def deprecated(
  67. value: object,
  68. module_name: str,
  69. message: str,
  70. warning_class: typing.Type[Warning],
  71. name: typing.Optional[str] = None,
  72. ) -> _DeprecatedValue:
  73. module = sys.modules[module_name]
  74. if not isinstance(module, _ModuleWithDeprecations):
  75. sys.modules[module_name] = module = _ModuleWithDeprecations(module)
  76. dv = _DeprecatedValue(value, message, warning_class)
  77. # Maintain backwards compatibility with `name is None` for pyOpenSSL.
  78. if name is not None:
  79. setattr(module, name, dv)
  80. return dv
  81. def cached_property(func: typing.Callable) -> property:
  82. cached_name = "_cached_{}".format(func)
  83. sentinel = object()
  84. def inner(instance: object):
  85. cache = getattr(instance, cached_name, sentinel)
  86. if cache is not sentinel:
  87. return cache
  88. result = func(instance)
  89. setattr(instance, cached_name, result)
  90. return result
  91. return property(inner)
  92. # Python 3.10 changed representation of enums. We use well-defined object
  93. # representation and string representation from Python 3.9.
  94. class Enum(enum.Enum):
  95. def __repr__(self) -> str:
  96. return f"<{self.__class__.__name__}.{self._name_}: {self._value_!r}>"
  97. def __str__(self) -> str:
  98. return f"{self.__class__.__name__}.{self._name_}"