Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/cryptography/utils.py: 70%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

76 statements  

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 

5from __future__ import annotations 

6 

7import enum 

8import sys 

9import types 

10import typing 

11import warnings 

12from collections.abc import Sequence 

13 

14 

15# We use a UserWarning subclass, instead of DeprecationWarning, because CPython 

16# decided deprecation warnings should be invisible by default. 

17class CryptographyDeprecationWarning(UserWarning): 

18 pass 

19 

20 

21# Several APIs were deprecated with no specific end-of-life date because of the 

22# ubiquity of their use. They should not be removed until we agree on when that 

23# cycle ends. 

24DeprecatedIn36 = CryptographyDeprecationWarning 

25DeprecatedIn40 = CryptographyDeprecationWarning 

26DeprecatedIn41 = CryptographyDeprecationWarning 

27DeprecatedIn42 = CryptographyDeprecationWarning 

28DeprecatedIn43 = CryptographyDeprecationWarning 

29DeprecatedIn47 = CryptographyDeprecationWarning 

30DeprecatedIn50 = CryptographyDeprecationWarning 

31 

32 

33# If you're wondering why we don't use `Buffer`, it's because `Buffer` would 

34# be more accurately named: Bufferable. It means something which has an 

35# `__buffer__`. Which means you can't actually treat the result as a buffer 

36# (and do things like take a `len()`). 

37Buffer = typing.Union[bytes, bytearray, memoryview] 

38 

39 

40def _check_bytes(name: str, value: bytes) -> None: 

41 if not isinstance(value, bytes): 

42 raise TypeError(f"{name} must be bytes") 

43 

44 

45def _check_byteslike(name: str, value: Buffer) -> None: 

46 if isinstance(value, (bytes, bytearray, memoryview)): 

47 return 

48 try: 

49 memoryview(value) 

50 except TypeError: 

51 raise TypeError(f"{name} must be bytes-like") 

52 

53 

54def int_to_bytes(integer: int, length: int | None = None) -> bytes: 

55 if length == 0: 

56 raise ValueError("length argument can't be 0") 

57 return integer.to_bytes( 

58 length or (integer.bit_length() + 7) // 8 or 1, "big" 

59 ) 

60 

61 

62class InterfaceNotImplemented(Exception): 

63 pass 

64 

65 

66class _DeprecatedValue: 

67 def __init__(self, value: object, message: str, warning_class): 

68 self.value = value 

69 self.message = message 

70 self.warning_class = warning_class 

71 

72 

73class _ModuleWithDeprecations(types.ModuleType): 

74 def __init__(self, module: types.ModuleType): 

75 super().__init__(module.__name__) 

76 self.__dict__["_module"] = module 

77 

78 def __getattr__(self, name: str) -> typing.Any: 

79 obj = getattr(self._module, name) 

80 if isinstance(obj, _DeprecatedValue): 

81 warnings.warn(obj.message, obj.warning_class, stacklevel=2) 

82 obj = obj.value 

83 else: 

84 # Cache non-deprecated attributes in our own `__dict__` so that 

85 # subsequent lookups are ordinary module attribute accesses and 

86 # don't pay for this `__getattr__` (which would otherwise defeat 

87 # CPython's LOAD_ATTR module caching for every attribute of the 

88 # module). `__setattr__` and `__delattr__` keep the cache 

89 # coherent. 

90 self.__dict__[name] = obj 

91 return obj 

92 

93 def __setattr__(self, attr: str, value: object) -> None: 

94 if isinstance(value, _DeprecatedValue): 

95 self.__dict__.pop(attr, None) 

96 else: 

97 self.__dict__[attr] = value 

98 setattr(self._module, attr, value) 

99 

100 def __delattr__(self, attr: str) -> None: 

101 obj = getattr(self._module, attr) 

102 if isinstance(obj, _DeprecatedValue): 

103 warnings.warn(obj.message, obj.warning_class, stacklevel=2) 

104 

105 self.__dict__.pop(attr, None) 

106 delattr(self._module, attr) 

107 

108 def __dir__(self) -> Sequence[str]: 

109 return ["_module", *dir(self._module)] 

110 

111 

112def deprecated( 

113 value: object, 

114 module_name: str, 

115 message: str, 

116 warning_class: type[Warning], 

117 name: str | None = None, 

118) -> _DeprecatedValue: 

119 module = sys.modules[module_name] 

120 if not isinstance(module, _ModuleWithDeprecations): 

121 sys.modules[module_name] = module = _ModuleWithDeprecations(module) 

122 dv = _DeprecatedValue(value, message, warning_class) 

123 # Maintain backwards compatibility with `name is None` for pyOpenSSL. 

124 if name is not None: 

125 setattr(module, name, dv) 

126 return dv 

127 

128 

129# Python 3.10 changed representation of enums. We use well-defined object 

130# representation and string representation from Python 3.9. 

131class Enum(enum.Enum): 

132 def __repr__(self) -> str: 

133 return f"<{self.__class__.__name__}.{self._name_}: {self._value_!r}>" 

134 

135 def __str__(self) -> str: 

136 return f"{self.__class__.__name__}.{self._name_}"