hexconv 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hexconv-0.2.2/PKG-INFO +349 -0
- hexconv-0.2.2/README.md +320 -0
- {hexconv-0.2.0 → hexconv-0.2.2}/pyproject.toml +1 -1
- hexconv-0.2.2/src/hexconv.egg-info/PKG-INFO +349 -0
- hexconv-0.2.0/PKG-INFO +0 -287
- hexconv-0.2.0/README.md +0 -258
- hexconv-0.2.0/src/hexconv.egg-info/PKG-INFO +0 -287
- {hexconv-0.2.0 → hexconv-0.2.2}/LICENSE +0 -0
- {hexconv-0.2.0 → hexconv-0.2.2}/MANIFEST.in +0 -0
- {hexconv-0.2.0 → hexconv-0.2.2}/setup.cfg +0 -0
- {hexconv-0.2.0 → hexconv-0.2.2}/src/hexconv/__init__.py +0 -0
- {hexconv-0.2.0 → hexconv-0.2.2}/src/hexconv/_core.py +0 -0
- {hexconv-0.2.0 → hexconv-0.2.2}/src/hexconv/py.typed +0 -0
- {hexconv-0.2.0 → hexconv-0.2.2}/src/hexconv.egg-info/SOURCES.txt +0 -0
- {hexconv-0.2.0 → hexconv-0.2.2}/src/hexconv.egg-info/dependency_links.txt +0 -0
- {hexconv-0.2.0 → hexconv-0.2.2}/src/hexconv.egg-info/requires.txt +0 -0
- {hexconv-0.2.0 → hexconv-0.2.2}/src/hexconv.egg-info/top_level.txt +0 -0
- {hexconv-0.2.0 → hexconv-0.2.2}/tests/test_hexconv.py +0 -0
hexconv-0.2.2/PKG-INFO
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hexconv
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Pythonic conversion toolkit for bytes, hex, integers, arrays, binary strings, base64, and text.
|
|
5
|
+
Author: hexconv contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: hex,bytes,binary,base64,conversion,encoding,toolkit
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Topic :: Utilities
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: build; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest; extra == "dev"
|
|
27
|
+
Requires-Dist: twine; extra == "dev"
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# hexconv
|
|
31
|
+
|
|
32
|
+
`hexconv` is a small, dependency-free Python toolkit for converting between
|
|
33
|
+
bytes, hex, integers, arrays, binary strings, base encodings, escaped strings,
|
|
34
|
+
hexdumps, and text.
|
|
35
|
+
|
|
36
|
+
It is designed around a simple idea: build the conversion you mean, then call it.
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
import hexconv as hx
|
|
40
|
+
|
|
41
|
+
decode = hx.HexArray() >> hx.Text()
|
|
42
|
+
|
|
43
|
+
decode([0x48, 0x65, 0x6C, 0x6C, 0x6F])
|
|
44
|
+
# 'Hello'
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install hexconv
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## The intended interface
|
|
54
|
+
|
|
55
|
+
Use format objects for direct parsing:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
hx.Hex()("dead beef").bytes
|
|
59
|
+
# b'\xde\xad\xbe\xef'
|
|
60
|
+
|
|
61
|
+
hx.Text()("data").hex
|
|
62
|
+
# '64617461'
|
|
63
|
+
|
|
64
|
+
hx.Int(width=4, endian="little")(0x12345678).hex
|
|
65
|
+
# '78563412'
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Use `from_auto` when you want a quick best-effort parse:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
hx.from_auto("0xdeadbeef").bytes
|
|
72
|
+
# b'\xde\xad\xbe\xef'
|
|
73
|
+
|
|
74
|
+
hx.from_auto(b"\xde\xad").hex
|
|
75
|
+
# 'dead'
|
|
76
|
+
|
|
77
|
+
hx.from_auto([0xde, 0xad, 0xbe, 0xef]).to(hx.Hex(prefix=True))
|
|
78
|
+
# '0xdeadbeef'
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Use `>>` to compose reusable converters:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
to_text = hx.HexArray() >> hx.Text()
|
|
85
|
+
to_text([0x48, 0x65, 0x6C, 0x6C, 0x6F])
|
|
86
|
+
# 'Hello'
|
|
87
|
+
|
|
88
|
+
to_hex = hx.Text() >> hx.Hex(prefix=True)
|
|
89
|
+
to_hex("data")
|
|
90
|
+
# '0x64617461'
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Use transforms between formats when the data needs shaping:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray(width=2)
|
|
97
|
+
words("12345678")
|
|
98
|
+
# [4660, 22136]
|
|
99
|
+
|
|
100
|
+
reverse_hex = hx.Hex() >> hx.Reverse() >> hx.Hex()
|
|
101
|
+
reverse_hex("deadbeef")
|
|
102
|
+
# 'efbeadde'
|
|
103
|
+
|
|
104
|
+
pad_hex = hx.Hex() >> hx.Pad.left(width=4) >> hx.Hex()
|
|
105
|
+
pad_hex("dead")
|
|
106
|
+
# '0000dead'
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Use the builder form when readability matters more than compactness:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
conv = (
|
|
113
|
+
hx.pipeline()
|
|
114
|
+
.from_(hx.Hex())
|
|
115
|
+
.chunk(2)
|
|
116
|
+
.to(hx.HexArray(prefix=True))
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
conv("12345678")
|
|
120
|
+
# ['0x1234', '0x5678']
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Common conversions
|
|
124
|
+
|
|
125
|
+
Text, bytes, and hex:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
hx.Text()("hello").bytes
|
|
129
|
+
# b'hello'
|
|
130
|
+
|
|
131
|
+
hx.Hex()("68656c6c6f").text
|
|
132
|
+
# 'hello'
|
|
133
|
+
|
|
134
|
+
hx.BytesArray()([0x68, 0x69]).to(hx.Text())
|
|
135
|
+
# 'hi'
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Integer packing:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
hx.Int(width=2)(0x1234).hex
|
|
142
|
+
# '1234'
|
|
143
|
+
|
|
144
|
+
hx.Int(width=2, endian="little")(0x1234).hex
|
|
145
|
+
# '3412'
|
|
146
|
+
|
|
147
|
+
(hx.Hex() >> hx.Chunk(2) >> hx.IntArray(width=2))("12345678")
|
|
148
|
+
# [4660, 22136]
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Base encodings:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
hx.Text()("data").to(hx.Base64())
|
|
155
|
+
# 'ZGF0YQ=='
|
|
156
|
+
|
|
157
|
+
hx.Text()("data").to(hx.Base32(padding=False))
|
|
158
|
+
# 'MRQXIYI'
|
|
159
|
+
|
|
160
|
+
hx.Base64(urlsafe=True, padding=False)("-_8").hex
|
|
161
|
+
# 'fbff'
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Escaped strings, hexdumps, structs, and bits:
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
hx.Escaped()(r"\xde\xad").hex
|
|
168
|
+
# 'dead'
|
|
169
|
+
|
|
170
|
+
dump = hx.Hex()("deadbeef").to(hx.Hexdump(width=2))
|
|
171
|
+
hx.Hexdump()(dump).hex
|
|
172
|
+
# 'deadbeef'
|
|
173
|
+
|
|
174
|
+
hx.Struct(">HH")((0x1234, 0x5678)).hex
|
|
175
|
+
# '12345678'
|
|
176
|
+
|
|
177
|
+
hx.Hex()("12345678").to(hx.Struct(">HH"))
|
|
178
|
+
# (4660, 22136)
|
|
179
|
+
|
|
180
|
+
hx.Bits()([1, 0, 1]).hex
|
|
181
|
+
# '05'
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Why explicit formats?
|
|
185
|
+
|
|
186
|
+
Some values are genuinely ambiguous:
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
"face"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
That can mean raw text:
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
hx.Text()("face").hex
|
|
196
|
+
# '66616365'
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
or hex bytes:
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
hx.Hex()("face").bytes
|
|
203
|
+
# b'\xfa\xce'
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
`hexconv` keeps the recommended path explicit so conversions are predictable.
|
|
207
|
+
If you want convenience heuristics, use `from_auto`:
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
hx.from_auto("0xdead").hex
|
|
211
|
+
# 'dead'
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
If you want to see what `from_auto` would infer, use `infer`:
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
result = hx.infer("0xdead")
|
|
218
|
+
|
|
219
|
+
result.value.hex
|
|
220
|
+
# 'dead'
|
|
221
|
+
|
|
222
|
+
result.reason
|
|
223
|
+
# '0x-prefixed hex string'
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`from_auto(..., explain=True)` returns the same explanation object:
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
result = hx.from_auto("0b101", explain=True)
|
|
230
|
+
|
|
231
|
+
result.value.hex
|
|
232
|
+
# '05'
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Format objects
|
|
236
|
+
|
|
237
|
+
Format objects are both parsers and pipeline endpoints.
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
value = hx.Hex()("deadbeef")
|
|
241
|
+
value.to(hx.Int())
|
|
242
|
+
# 3735928559
|
|
243
|
+
|
|
244
|
+
conv = hx.Hex() >> hx.Int()
|
|
245
|
+
conv("deadbeef")
|
|
246
|
+
# 3735928559
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Available format specs:
|
|
250
|
+
|
|
251
|
+
- `Bytes()`
|
|
252
|
+
- `BytesArray()`
|
|
253
|
+
- `BytesString()`
|
|
254
|
+
- `Hex()`
|
|
255
|
+
- `HexArray()`
|
|
256
|
+
- `HexInt()` / `LargeHexNumber()`
|
|
257
|
+
- `Int()`
|
|
258
|
+
- `IntArray()`
|
|
259
|
+
- `Text()`
|
|
260
|
+
- `Binary()`
|
|
261
|
+
- `Base64()`
|
|
262
|
+
- `Base32()`
|
|
263
|
+
- `Base85()`
|
|
264
|
+
- `Ascii85()`
|
|
265
|
+
- `Escaped()`
|
|
266
|
+
- `Hexdump()`
|
|
267
|
+
- `Struct(fmt)`
|
|
268
|
+
- `Bits()`
|
|
269
|
+
|
|
270
|
+
## Transforms
|
|
271
|
+
|
|
272
|
+
Transforms are byte-to-byte steps that sit between input and output formats.
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
hx.Hex() >> hx.Chunk(2) >> hx.HexArray()
|
|
276
|
+
hx.Hex() >> hx.Group(2) >> hx.IntArray(width=2)
|
|
277
|
+
hx.Hex() >> hx.Pad.left(width=8) >> hx.Hex()
|
|
278
|
+
hx.Hex() >> hx.Pad.right(block_size=8, byte=0xFF) >> hx.Hex()
|
|
279
|
+
hx.Hex() >> hx.Reverse() >> hx.Hex()
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Current transforms:
|
|
283
|
+
|
|
284
|
+
- `Chunk(width, strict=True)` — set downstream grouping width.
|
|
285
|
+
- `Group(width, strict=True)` — alias for `Chunk`.
|
|
286
|
+
- `Pad.left(width=..., block_size=..., byte=0)` — left-pad bytes.
|
|
287
|
+
- `Pad.right(width=..., block_size=..., byte=0)` — right-pad bytes.
|
|
288
|
+
- `Reverse()` — reverse byte order.
|
|
289
|
+
|
|
290
|
+
## Classic helper API
|
|
291
|
+
|
|
292
|
+
The original helper style is still supported:
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
hx.from_hex("dead beef").bytes
|
|
296
|
+
# b'\xde\xad\xbe\xef'
|
|
297
|
+
|
|
298
|
+
hx.from_text("data").hex
|
|
299
|
+
# '64617461'
|
|
300
|
+
|
|
301
|
+
hx.convert("data", from_=hx.Text, to=hx.HexString)
|
|
302
|
+
# '64617461'
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Source helpers:
|
|
306
|
+
|
|
307
|
+
- `from_bytes(value)`
|
|
308
|
+
- `from_bytes_array(values)`
|
|
309
|
+
- `from_bytes_string(value)`
|
|
310
|
+
- `from_hex(value)`
|
|
311
|
+
- `from_hex_array(values)`
|
|
312
|
+
- `from_hex_int(value)`
|
|
313
|
+
- `from_int(value)`
|
|
314
|
+
- `from_int_array(values, width=...)`
|
|
315
|
+
- `from_text(value)`
|
|
316
|
+
- `from_binary(value)`
|
|
317
|
+
- `from_base64(value)`
|
|
318
|
+
- `from_base32(value)`
|
|
319
|
+
- `from_base85(value)`
|
|
320
|
+
- `from_ascii85(value)`
|
|
321
|
+
- `from_escaped(value)`
|
|
322
|
+
- `from_hexdump(value)`
|
|
323
|
+
- `from_struct(value, fmt=...)`
|
|
324
|
+
- `from_bits(value)`
|
|
325
|
+
- `from_auto(value)`
|
|
326
|
+
- `infer(value)`
|
|
327
|
+
|
|
328
|
+
Common `Value` outputs:
|
|
329
|
+
|
|
330
|
+
- `to_bytes()`
|
|
331
|
+
- `to_bytearray()`
|
|
332
|
+
- `to_bytes_array()`
|
|
333
|
+
- `to_bytes_string()`
|
|
334
|
+
- `to_hex(sep="", prefix=False, uppercase=False)`
|
|
335
|
+
- `to_hex_array(width=1, prefix=False, uppercase=False)`
|
|
336
|
+
- `to_hex_numbers(width=1)`
|
|
337
|
+
- `to_int(endian="big", signed=False)`
|
|
338
|
+
- `to_int_array(width=1, endian="big", signed=False)`
|
|
339
|
+
- `to_text(encoding="ascii", errors="strict")`
|
|
340
|
+
- `to_binary(sep="")`
|
|
341
|
+
- `to_base64()`
|
|
342
|
+
- `to_base32()`
|
|
343
|
+
- `to_base85()`
|
|
344
|
+
- `to_ascii85()`
|
|
345
|
+
- `to_escaped()`
|
|
346
|
+
- `to_hexdump()`
|
|
347
|
+
- `to_struct(fmt)`
|
|
348
|
+
- `to_bits()`
|
|
349
|
+
- `to(format_spec)`
|
hexconv-0.2.2/README.md
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# hexconv
|
|
2
|
+
|
|
3
|
+
`hexconv` is a small, dependency-free Python toolkit for converting between
|
|
4
|
+
bytes, hex, integers, arrays, binary strings, base encodings, escaped strings,
|
|
5
|
+
hexdumps, and text.
|
|
6
|
+
|
|
7
|
+
It is designed around a simple idea: build the conversion you mean, then call it.
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
import hexconv as hx
|
|
11
|
+
|
|
12
|
+
decode = hx.HexArray() >> hx.Text()
|
|
13
|
+
|
|
14
|
+
decode([0x48, 0x65, 0x6C, 0x6C, 0x6F])
|
|
15
|
+
# 'Hello'
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install hexconv
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## The intended interface
|
|
25
|
+
|
|
26
|
+
Use format objects for direct parsing:
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
hx.Hex()("dead beef").bytes
|
|
30
|
+
# b'\xde\xad\xbe\xef'
|
|
31
|
+
|
|
32
|
+
hx.Text()("data").hex
|
|
33
|
+
# '64617461'
|
|
34
|
+
|
|
35
|
+
hx.Int(width=4, endian="little")(0x12345678).hex
|
|
36
|
+
# '78563412'
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Use `from_auto` when you want a quick best-effort parse:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
hx.from_auto("0xdeadbeef").bytes
|
|
43
|
+
# b'\xde\xad\xbe\xef'
|
|
44
|
+
|
|
45
|
+
hx.from_auto(b"\xde\xad").hex
|
|
46
|
+
# 'dead'
|
|
47
|
+
|
|
48
|
+
hx.from_auto([0xde, 0xad, 0xbe, 0xef]).to(hx.Hex(prefix=True))
|
|
49
|
+
# '0xdeadbeef'
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Use `>>` to compose reusable converters:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
to_text = hx.HexArray() >> hx.Text()
|
|
56
|
+
to_text([0x48, 0x65, 0x6C, 0x6C, 0x6F])
|
|
57
|
+
# 'Hello'
|
|
58
|
+
|
|
59
|
+
to_hex = hx.Text() >> hx.Hex(prefix=True)
|
|
60
|
+
to_hex("data")
|
|
61
|
+
# '0x64617461'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Use transforms between formats when the data needs shaping:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray(width=2)
|
|
68
|
+
words("12345678")
|
|
69
|
+
# [4660, 22136]
|
|
70
|
+
|
|
71
|
+
reverse_hex = hx.Hex() >> hx.Reverse() >> hx.Hex()
|
|
72
|
+
reverse_hex("deadbeef")
|
|
73
|
+
# 'efbeadde'
|
|
74
|
+
|
|
75
|
+
pad_hex = hx.Hex() >> hx.Pad.left(width=4) >> hx.Hex()
|
|
76
|
+
pad_hex("dead")
|
|
77
|
+
# '0000dead'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Use the builder form when readability matters more than compactness:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
conv = (
|
|
84
|
+
hx.pipeline()
|
|
85
|
+
.from_(hx.Hex())
|
|
86
|
+
.chunk(2)
|
|
87
|
+
.to(hx.HexArray(prefix=True))
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
conv("12345678")
|
|
91
|
+
# ['0x1234', '0x5678']
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Common conversions
|
|
95
|
+
|
|
96
|
+
Text, bytes, and hex:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
hx.Text()("hello").bytes
|
|
100
|
+
# b'hello'
|
|
101
|
+
|
|
102
|
+
hx.Hex()("68656c6c6f").text
|
|
103
|
+
# 'hello'
|
|
104
|
+
|
|
105
|
+
hx.BytesArray()([0x68, 0x69]).to(hx.Text())
|
|
106
|
+
# 'hi'
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Integer packing:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
hx.Int(width=2)(0x1234).hex
|
|
113
|
+
# '1234'
|
|
114
|
+
|
|
115
|
+
hx.Int(width=2, endian="little")(0x1234).hex
|
|
116
|
+
# '3412'
|
|
117
|
+
|
|
118
|
+
(hx.Hex() >> hx.Chunk(2) >> hx.IntArray(width=2))("12345678")
|
|
119
|
+
# [4660, 22136]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Base encodings:
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
hx.Text()("data").to(hx.Base64())
|
|
126
|
+
# 'ZGF0YQ=='
|
|
127
|
+
|
|
128
|
+
hx.Text()("data").to(hx.Base32(padding=False))
|
|
129
|
+
# 'MRQXIYI'
|
|
130
|
+
|
|
131
|
+
hx.Base64(urlsafe=True, padding=False)("-_8").hex
|
|
132
|
+
# 'fbff'
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Escaped strings, hexdumps, structs, and bits:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
hx.Escaped()(r"\xde\xad").hex
|
|
139
|
+
# 'dead'
|
|
140
|
+
|
|
141
|
+
dump = hx.Hex()("deadbeef").to(hx.Hexdump(width=2))
|
|
142
|
+
hx.Hexdump()(dump).hex
|
|
143
|
+
# 'deadbeef'
|
|
144
|
+
|
|
145
|
+
hx.Struct(">HH")((0x1234, 0x5678)).hex
|
|
146
|
+
# '12345678'
|
|
147
|
+
|
|
148
|
+
hx.Hex()("12345678").to(hx.Struct(">HH"))
|
|
149
|
+
# (4660, 22136)
|
|
150
|
+
|
|
151
|
+
hx.Bits()([1, 0, 1]).hex
|
|
152
|
+
# '05'
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Why explicit formats?
|
|
156
|
+
|
|
157
|
+
Some values are genuinely ambiguous:
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
"face"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
That can mean raw text:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
hx.Text()("face").hex
|
|
167
|
+
# '66616365'
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
or hex bytes:
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
hx.Hex()("face").bytes
|
|
174
|
+
# b'\xfa\xce'
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
`hexconv` keeps the recommended path explicit so conversions are predictable.
|
|
178
|
+
If you want convenience heuristics, use `from_auto`:
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
hx.from_auto("0xdead").hex
|
|
182
|
+
# 'dead'
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
If you want to see what `from_auto` would infer, use `infer`:
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
result = hx.infer("0xdead")
|
|
189
|
+
|
|
190
|
+
result.value.hex
|
|
191
|
+
# 'dead'
|
|
192
|
+
|
|
193
|
+
result.reason
|
|
194
|
+
# '0x-prefixed hex string'
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`from_auto(..., explain=True)` returns the same explanation object:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
result = hx.from_auto("0b101", explain=True)
|
|
201
|
+
|
|
202
|
+
result.value.hex
|
|
203
|
+
# '05'
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Format objects
|
|
207
|
+
|
|
208
|
+
Format objects are both parsers and pipeline endpoints.
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
value = hx.Hex()("deadbeef")
|
|
212
|
+
value.to(hx.Int())
|
|
213
|
+
# 3735928559
|
|
214
|
+
|
|
215
|
+
conv = hx.Hex() >> hx.Int()
|
|
216
|
+
conv("deadbeef")
|
|
217
|
+
# 3735928559
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Available format specs:
|
|
221
|
+
|
|
222
|
+
- `Bytes()`
|
|
223
|
+
- `BytesArray()`
|
|
224
|
+
- `BytesString()`
|
|
225
|
+
- `Hex()`
|
|
226
|
+
- `HexArray()`
|
|
227
|
+
- `HexInt()` / `LargeHexNumber()`
|
|
228
|
+
- `Int()`
|
|
229
|
+
- `IntArray()`
|
|
230
|
+
- `Text()`
|
|
231
|
+
- `Binary()`
|
|
232
|
+
- `Base64()`
|
|
233
|
+
- `Base32()`
|
|
234
|
+
- `Base85()`
|
|
235
|
+
- `Ascii85()`
|
|
236
|
+
- `Escaped()`
|
|
237
|
+
- `Hexdump()`
|
|
238
|
+
- `Struct(fmt)`
|
|
239
|
+
- `Bits()`
|
|
240
|
+
|
|
241
|
+
## Transforms
|
|
242
|
+
|
|
243
|
+
Transforms are byte-to-byte steps that sit between input and output formats.
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
hx.Hex() >> hx.Chunk(2) >> hx.HexArray()
|
|
247
|
+
hx.Hex() >> hx.Group(2) >> hx.IntArray(width=2)
|
|
248
|
+
hx.Hex() >> hx.Pad.left(width=8) >> hx.Hex()
|
|
249
|
+
hx.Hex() >> hx.Pad.right(block_size=8, byte=0xFF) >> hx.Hex()
|
|
250
|
+
hx.Hex() >> hx.Reverse() >> hx.Hex()
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Current transforms:
|
|
254
|
+
|
|
255
|
+
- `Chunk(width, strict=True)` — set downstream grouping width.
|
|
256
|
+
- `Group(width, strict=True)` — alias for `Chunk`.
|
|
257
|
+
- `Pad.left(width=..., block_size=..., byte=0)` — left-pad bytes.
|
|
258
|
+
- `Pad.right(width=..., block_size=..., byte=0)` — right-pad bytes.
|
|
259
|
+
- `Reverse()` — reverse byte order.
|
|
260
|
+
|
|
261
|
+
## Classic helper API
|
|
262
|
+
|
|
263
|
+
The original helper style is still supported:
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
hx.from_hex("dead beef").bytes
|
|
267
|
+
# b'\xde\xad\xbe\xef'
|
|
268
|
+
|
|
269
|
+
hx.from_text("data").hex
|
|
270
|
+
# '64617461'
|
|
271
|
+
|
|
272
|
+
hx.convert("data", from_=hx.Text, to=hx.HexString)
|
|
273
|
+
# '64617461'
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Source helpers:
|
|
277
|
+
|
|
278
|
+
- `from_bytes(value)`
|
|
279
|
+
- `from_bytes_array(values)`
|
|
280
|
+
- `from_bytes_string(value)`
|
|
281
|
+
- `from_hex(value)`
|
|
282
|
+
- `from_hex_array(values)`
|
|
283
|
+
- `from_hex_int(value)`
|
|
284
|
+
- `from_int(value)`
|
|
285
|
+
- `from_int_array(values, width=...)`
|
|
286
|
+
- `from_text(value)`
|
|
287
|
+
- `from_binary(value)`
|
|
288
|
+
- `from_base64(value)`
|
|
289
|
+
- `from_base32(value)`
|
|
290
|
+
- `from_base85(value)`
|
|
291
|
+
- `from_ascii85(value)`
|
|
292
|
+
- `from_escaped(value)`
|
|
293
|
+
- `from_hexdump(value)`
|
|
294
|
+
- `from_struct(value, fmt=...)`
|
|
295
|
+
- `from_bits(value)`
|
|
296
|
+
- `from_auto(value)`
|
|
297
|
+
- `infer(value)`
|
|
298
|
+
|
|
299
|
+
Common `Value` outputs:
|
|
300
|
+
|
|
301
|
+
- `to_bytes()`
|
|
302
|
+
- `to_bytearray()`
|
|
303
|
+
- `to_bytes_array()`
|
|
304
|
+
- `to_bytes_string()`
|
|
305
|
+
- `to_hex(sep="", prefix=False, uppercase=False)`
|
|
306
|
+
- `to_hex_array(width=1, prefix=False, uppercase=False)`
|
|
307
|
+
- `to_hex_numbers(width=1)`
|
|
308
|
+
- `to_int(endian="big", signed=False)`
|
|
309
|
+
- `to_int_array(width=1, endian="big", signed=False)`
|
|
310
|
+
- `to_text(encoding="ascii", errors="strict")`
|
|
311
|
+
- `to_binary(sep="")`
|
|
312
|
+
- `to_base64()`
|
|
313
|
+
- `to_base32()`
|
|
314
|
+
- `to_base85()`
|
|
315
|
+
- `to_ascii85()`
|
|
316
|
+
- `to_escaped()`
|
|
317
|
+
- `to_hexdump()`
|
|
318
|
+
- `to_struct(fmt)`
|
|
319
|
+
- `to_bits()`
|
|
320
|
+
- `to(format_spec)`
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hexconv"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Pythonic conversion toolkit for bytes, hex, integers, arrays, binary strings, base64, and text."
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
requires-python = ">=3.10"
|