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 absolute_import, division, print_function 6 7import abc 8import binascii 9import inspect 10import sys 11import warnings 12 13 14# We use a UserWarning subclass, instead of DeprecationWarning, because CPython 15# decided deprecation warnings should be invisble by default. 16class CryptographyDeprecationWarning(UserWarning): 17 pass 18 19 20# Several APIs were deprecated with no specific end-of-life date because of the 21# ubiquity of their use. They should not be removed until we agree on when that 22# cycle ends. 23PersistentlyDeprecated2017 = CryptographyDeprecationWarning 24PersistentlyDeprecated2019 = CryptographyDeprecationWarning 25 26 27def _check_bytes(name, value): 28 if not isinstance(value, bytes): 29 raise TypeError("{} must be bytes".format(name)) 30 31 32def _check_byteslike(name, value): 33 try: 34 memoryview(value) 35 except TypeError: 36 raise TypeError("{} must be bytes-like".format(name)) 37 38 39def read_only_property(name): 40 return property(lambda self: getattr(self, name)) 41 42 43def register_interface(iface): 44 def register_decorator(klass): 45 verify_interface(iface, klass) 46 iface.register(klass) 47 return klass 48 49 return register_decorator 50 51 52def register_interface_if(predicate, iface): 53 def register_decorator(klass): 54 if predicate: 55 verify_interface(iface, klass) 56 iface.register(klass) 57 return klass 58 59 return register_decorator 60 61 62if hasattr(int, "from_bytes"): 63 int_from_bytes = int.from_bytes 64else: 65 66 def int_from_bytes(data, byteorder, signed=False): 67 assert byteorder == "big" 68 assert not signed 69 70 return int(binascii.hexlify(data), 16) 71 72 73if hasattr(int, "to_bytes"): 74 75 def int_to_bytes(integer, length=None): 76 return integer.to_bytes( 77 length or (integer.bit_length() + 7) // 8 or 1, "big" 78 ) 79 80 81else: 82 83 def int_to_bytes(integer, length=None): 84 hex_string = "%x" % integer 85 if length is None: 86 n = len(hex_string) 87 else: 88 n = length * 2 89 return binascii.unhexlify(hex_string.zfill(n + (n & 1))) 90 91 92class InterfaceNotImplemented(Exception): 93 pass 94 95 96if hasattr(inspect, "signature"): 97 signature = inspect.signature 98else: 99 signature = inspect.getargspec 100 101 102def verify_interface(iface, klass): 103 for method in iface.__abstractmethods__: 104 if not hasattr(klass, method): 105 raise InterfaceNotImplemented( 106 "{} is missing a {!r} method".format(klass, method) 107 ) 108 if isinstance(getattr(iface, method), abc.abstractproperty): 109 # Can't properly verify these yet. 110 continue 111 sig = signature(getattr(iface, method)) 112 actual = signature(getattr(klass, method)) 113 if sig != actual: 114 raise InterfaceNotImplemented( 115 "{}.{}'s signature differs from the expected. Expected: " 116 "{!r}. Received: {!r}".format(klass, method, sig, actual) 117 ) 118 119 120class _DeprecatedValue(object): 121 def __init__(self, value, message, warning_class): 122 self.value = value 123 self.message = message 124 self.warning_class = warning_class 125 126 127class _ModuleWithDeprecations(object): 128 def __init__(self, module): 129 self.__dict__["_module"] = module 130 131 def __getattr__(self, attr): 132 obj = getattr(self._module, attr) 133 if isinstance(obj, _DeprecatedValue): 134 warnings.warn(obj.message, obj.warning_class, stacklevel=2) 135 obj = obj.value 136 return obj 137 138 def __setattr__(self, attr, value): 139 setattr(self._module, attr, value) 140 141 def __delattr__(self, attr): 142 obj = getattr(self._module, attr) 143 if isinstance(obj, _DeprecatedValue): 144 warnings.warn(obj.message, obj.warning_class, stacklevel=2) 145 146 delattr(self._module, attr) 147 148 def __dir__(self): 149 return ["_module"] + dir(self._module) 150 151 152def deprecated(value, module_name, message, warning_class): 153 module = sys.modules[module_name] 154 if not isinstance(module, _ModuleWithDeprecations): 155 sys.modules[module_name] = _ModuleWithDeprecations(module) 156 return _DeprecatedValue(value, message, warning_class) 157 158 159def cached_property(func): 160 cached_name = "_cached_{}".format(func) 161 sentinel = object() 162 163 def inner(instance): 164 cache = getattr(instance, cached_name, sentinel) 165 if cache is not sentinel: 166 return cache 167 result = func(instance) 168 setattr(instance, cached_name, result) 169 return result 170 171 return property(inner) 172