• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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