Passed
Push — main ( af1065...15d22f )
by Douglas
04:30
created

pocketutils.tools.io_tools.IoTools.hash_digest()   B

Complexity

Conditions 5

Size

Total Lines 21
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 19
nop 6
dl 0
loc 21
rs 8.9833
c 0
b 0
f 0
1
import abc
0 ignored issues
show
introduced by
Missing module docstring
Loading history...
2
import base64
3
import binascii
4
import hashlib
5
from typing import Literal, SupportsBytes
0 ignored issues
show
Bug introduced by
The name Literal does not seem to exist in module typing.
Loading history...
6
7
try:
8
    import base2048
9
except ImportError:
10
    base2048 = None
11
12
13
Encoding = (
14
    Literal["2048"]  # for fun
15
    | Literal["base85"]
16
    | Literal["base64"]
17
    | Literal["base64url"]
18
    | Literal["base64url-tilde"]
19
    | Literal["base32"]
20
    | Literal["base32-tilde"]
21
    | Literal["base32hex"]
22
    | Literal["base32hex-tilde"]
23
    | Literal["base32-bech32"]
24
    | Literal["base32-bech32-tilde"]
25
    | Literal["base32hex-bech32"]
26
    | Literal["base32hex-bech32-tilde"]
27
    | Literal["base16"]
28
    | Literal["hex"]
29
)
30
31
HashAlgorithm = (
32
    Literal["shake_256"]
33
    | Literal["shake_128"]
34
    | Literal["sha3_512"]
35
    | Literal["sha3_384"]
36
    | Literal["sha3_256"]
37
    | Literal["sha3_224"]
38
    | Literal["sha2_512"]
39
    | Literal["sha2_256"]
40
    | Literal["sha2_224"]
41
    | Literal["sha1"]
42
    | Literal["md5"]
43
    | Literal["crc32"]
44
)
45
46
47
class Enc(metaclass=abc.ABCMeta):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
48
    @property
49
    def name(self) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
50
        return self.__class__.__name__.lower()
51
52
    def encode(self, d: bytes) -> str:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "d" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
introduced by
Missing function or method docstring
Loading history...
53
        raise NotImplementedError()
54
55
    def decode(self, d: str) -> bytes:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "d" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
introduced by
Missing function or method docstring
Loading history...
56
        raise NotImplementedError()
57
58
59
class Base16(Enc):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
60
    def encode(self, d: bytes) -> str:
61
        return base64.b16encode(d).decode(encoding="ascii")
62
63
    def decode(self, d: str) -> bytes:
64
        return base64.b16decode(d)
65
66
67
class Base32(Enc):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
68
    def encode(self, d: bytes) -> str:
69
        return base64.b32encode(d).decode(encoding="ascii")
70
71
    def decode(self, d: str) -> bytes:
72
        return base64.b32decode(d)
73
74
75
class Base64(Enc):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
76
    def encode(self, d: bytes) -> str:
77
        return base64.standard_b64encode(d).decode(encoding="ascii")
78
79
    def decode(self, d: str) -> bytes:
80
        return base64.standard_b64decode(d)
81
82
83
class Base64Url(Enc):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
84
    def encode(self, d: bytes) -> str:
85
        return base64.urlsafe_b64encode(d).decode(encoding="ascii")
86
87
    def decode(self, d: str) -> bytes:
88
        return base64.urlsafe_b64decode(d)
89
90
91
class Base32Hex(Enc):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
92
    def encode(self, d: bytes) -> str:
93
        return base64.b32hexencode(d).decode(encoding="ascii")
0 ignored issues
show
Bug introduced by
The Module base64 does not seem to have a member named b32hexencode.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
94
95
    def decode(self, d: str) -> bytes:
96
        return base64.b32hexdecode(d)
0 ignored issues
show
Bug introduced by
The Module base64 does not seem to have a member named b32hexdecode.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
97
98
99
class Base32HexTilde(Enc):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
100
    def encode(self, d: bytes) -> str:
101
        return base64.b32hexencode(d).decode(encoding="ascii").replace("=", "~")
0 ignored issues
show
Bug introduced by
The Module base64 does not seem to have a member named b32hexencode.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
102
103
    def decode(self, d: str) -> bytes:
104
        return base64.b32hexdecode(d.replace("=", "~"))
0 ignored issues
show
Bug introduced by
The Module base64 does not seem to have a member named b32hexdecode.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
105
106
107
class Base2048(Enc):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
108
    def encode(self, d: bytes) -> str:
109
        return base2048.encode(d).decode(encoding="ascii")
110
111
    def decode(self, d: str) -> bytes:
112
        return base2048.decode(d)
113
114
115
class _Alphabet32Enc(Enc):
116
    _base32_alphabet = ""
117
    _to_alphabet = ""
118
119
    def encode(self, d: bytes) -> str:
120
        encoded = base64.b32encode(d).decode(encoding="ascii")
121
        alphabet = dict(zip(self._base32_alphabet, self._to_alphabet))
122
        return "".join([alphabet[v] for v in encoded])
123
124
    def decode(self, d: str) -> bytes:
125
        alphabet = dict(zip(self._to_alphabet, self._base32_alphabet))
126
        d = "".join([alphabet[v] for v in d])
127
        return base64.b32hexdecode(d)
0 ignored issues
show
Bug introduced by
The Module base64 does not seem to have a member named b32hexdecode.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
128
129
130
class Base32Bech(_Alphabet32Enc):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
131
    _base32_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="
132
    _to_alphabet = "qpzry9x8gf2tvdw0s3jn54khce6mua7l="
133
134
    @property
135
    def name(self) -> str:
136
        return "base32-bech"
137
138
139
class Base32BechTilde(_Alphabet32Enc):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
140
    _base32_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="
141
    _to_alphabet = "qpzry9x8gf2tvdw0s3jn54khce6mua7l~"
142
143
    @property
144
    def name(self) -> str:
145
        return "base32-bech-tilde"
146
147
148
class Base32HexBech(_Alphabet32Enc):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
149
    _base32_alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUV="
150
    _to_alphabet = "023456789acdefghjklmnpqrstuvwxyz="
151
152
    @property
153
    def name(self) -> str:
154
        return "base32hex-bech"
155
156
157
class Base32HexBechTilde(_Alphabet32Enc):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
158
    _base32_alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUV="
159
    _to_alphabet = "023456789acdefghjklmnpqrstuvwxyz~"
160
161
    @property
162
    def name(self) -> str:
163
        return "base32hex-bech-tilde"
164
165
166
class Base64UrlTilde(Enc):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
167
    @property
168
    def name(self) -> str:
169
        return "base64url-tilde"
170
171
    def encode(self, d: bytes) -> str:
172
        return base64.urlsafe_b64encode(d).decode(encoding="ascii").replace("=", "~")
173
174
    def decode(self, d: str) -> bytes:
175
        return base64.urlsafe_b64decode(d.replace("~", "="))
176
177
178
class Base85(Enc):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
179
    def encode(self, d: bytes) -> str:
180
        return base64.b85encode(d).decode(encoding="ascii")
181
182
    def decode(self, d: str) -> bytes:
183
        return base64.b85decode(d)
184
185
186
ENCODINGS = {
187
    e.name: e
188
    for e in [
189
        Base16,
190
        Base32,
191
        Base32Hex,
192
        Base32HexTilde,
193
        Base32Bech,
194
        Base32HexBech,
195
        Base32BechTilde,
196
        Base32HexBechTilde,
197
        Base64,
198
        Base64Url,
199
        Base64UrlTilde,
200
        Base85,
201
        Base2048,
202
    ]
203
}
204
205
206
class IoTools:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
207
    @classmethod
208
    def hash_digest(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
209
        cls,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
210
        data: SupportsBytes | str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
211
        algorithm: HashAlgorithm,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
212
        *,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
213
        digest_length: int | None = None,
0 ignored issues
show
Coding Style introduced by
No space allowed around keyword argument assignment
Loading history...
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
214
        **kwargs,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
215
    ) -> bytes:
216
        kwargs = dict(usedforsecurity=False) | kwargs
217
        x = data.encode("utf-8") if isinstance(data, str) else bytes(data)
0 ignored issues
show
Coding Style Naming introduced by
Variable name "x" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
218
        if algorithm == "crc32":
0 ignored issues
show
unused-code introduced by
Unnecessary "elif" after "return"
Loading history...
219
            return bytes(binascii.crc32(x))
220
        elif algorithm.startswith("shake_"):
221
            m = getattr(hashlib, algorithm)(**kwargs)
0 ignored issues
show
Coding Style Naming introduced by
Variable name "m" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
222
            m.update(x)
223
            return m.digest(128 if digest_length is None else digest_length)
224
        else:
225
            m = hashlib.new(algorithm, **kwargs)
0 ignored issues
show
Coding Style Naming introduced by
Variable name "m" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
226
            m.update(x)
227
            return m.digest()
228
229
    @classmethod
230
    def encode(cls, d: bytes, to: Encoding = "base64"):
0 ignored issues
show
Coding Style Naming introduced by
Argument name "d" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
Argument name "to" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
introduced by
Missing function or method docstring
Loading history...
231
        if to not in ENCODINGS:
232
            raise ValueError(f"Unknown encoding {to}")
233
        return ENCODINGS[to].encode(d)
234
235
    @classmethod
236
    def decode(cls, d: bytes, to: Encoding = "base64"):
0 ignored issues
show
Coding Style Naming introduced by
Argument name "d" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
Argument name "to" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
introduced by
Missing function or method docstring
Loading history...
237
        if to not in ENCODINGS:
238
            raise ValueError(f"Unknown encoding {to}")
239
        return ENCODINGS[to].decode(d)
240
241
242
__all__ = ["IoTools"]
243