hexconv 0.2.4__tar.gz → 0.2.5__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.4/src/hexconv.egg-info → hexconv-0.2.5}/PKG-INFO +92 -125
- {hexconv-0.2.4 → hexconv-0.2.5}/README.md +91 -124
- {hexconv-0.2.4 → hexconv-0.2.5}/pyproject.toml +1 -1
- {hexconv-0.2.4 → hexconv-0.2.5/src/hexconv.egg-info}/PKG-INFO +92 -125
- {hexconv-0.2.4 → hexconv-0.2.5}/tests/test_hexconv.py +51 -3
- {hexconv-0.2.4 → hexconv-0.2.5}/LICENSE +0 -0
- {hexconv-0.2.4 → hexconv-0.2.5}/MANIFEST.in +0 -0
- {hexconv-0.2.4 → hexconv-0.2.5}/setup.cfg +0 -0
- {hexconv-0.2.4 → hexconv-0.2.5}/src/hexconv/__init__.py +0 -0
- {hexconv-0.2.4 → hexconv-0.2.5}/src/hexconv/_core.py +0 -0
- {hexconv-0.2.4 → hexconv-0.2.5}/src/hexconv/py.typed +0 -0
- {hexconv-0.2.4 → hexconv-0.2.5}/src/hexconv.egg-info/SOURCES.txt +0 -0
- {hexconv-0.2.4 → hexconv-0.2.5}/src/hexconv.egg-info/dependency_links.txt +0 -0
- {hexconv-0.2.4 → hexconv-0.2.5}/src/hexconv.egg-info/requires.txt +0 -0
- {hexconv-0.2.4 → hexconv-0.2.5}/src/hexconv.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hexconv
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: Pythonic conversion toolkit for bytes, hex, integers, arrays, binary strings, base64, and text.
|
|
5
5
|
Author: hexconv contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -41,83 +41,77 @@ It is built around ergonomic one-liners and composable format objects.
|
|
|
41
41
|
pip install hexconv
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
##
|
|
44
|
+
## Simple usage
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
Most conversions are one-liners. Use `from_auto` when you want a quick
|
|
47
|
+
best-effort parse, or pick an explicit helper when the input is ambiguous.
|
|
48
48
|
|
|
49
49
|
```python
|
|
50
50
|
import hexconv as hx
|
|
51
51
|
|
|
52
|
+
hx.from_auto("0x64617461").text
|
|
53
|
+
# 'data'
|
|
54
|
+
|
|
55
|
+
hx.from_bytes_array([0xde, 0xad, 0xbe, 0xef]).int
|
|
56
|
+
# 3735928559
|
|
57
|
+
|
|
58
|
+
hx.from_text("data").hex
|
|
59
|
+
# '64617461'
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Fast compose
|
|
63
|
+
|
|
64
|
+
Create your own converters by snapping together
|
|
65
|
+
`source format >> optional transforms >> target format`.
|
|
66
|
+
|
|
67
|
+
```python
|
|
52
68
|
to_bytes = hx.Hex() >> hx.Bytes()
|
|
53
|
-
to_bytes("
|
|
54
|
-
# b'
|
|
69
|
+
to_bytes("68 69")
|
|
70
|
+
# b'hi'
|
|
55
71
|
|
|
56
72
|
spaced_hex = hx.Bytes() >> hx.Hex(sep=" ")
|
|
57
|
-
spaced_hex(b"
|
|
58
|
-
# '
|
|
73
|
+
spaced_hex(b"data")
|
|
74
|
+
# '64 61 74 61'
|
|
59
75
|
|
|
60
|
-
words = hx.Hex(
|
|
76
|
+
words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray()
|
|
61
77
|
words("12 34 56 78")
|
|
62
78
|
# [4660, 22136]
|
|
63
79
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
# '
|
|
80
|
+
pack_to_hex = hx.Struct(">HH") >> hx.Hex()
|
|
81
|
+
pack_to_hex((0x1234, 0x5678))
|
|
82
|
+
# '12345678'
|
|
67
83
|
```
|
|
68
84
|
|
|
69
|
-
|
|
70
|
-
need manual cleanup before conversion. `Hex(sep=" ")` is also how you emit
|
|
71
|
-
space-separated hex.
|
|
85
|
+
## Configure when needed
|
|
72
86
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
Use direct helpers when you want one obvious conversion across many
|
|
76
|
-
representations:
|
|
87
|
+
Formats work bare by default. Add arguments only for the conversion that needs
|
|
88
|
+
them:
|
|
77
89
|
|
|
78
90
|
```python
|
|
79
|
-
hx.
|
|
91
|
+
hx.convert("de ad be ef", hx.Hex(), hx.Bytes())
|
|
80
92
|
# b'\xde\xad\xbe\xef'
|
|
81
93
|
|
|
82
|
-
hx.
|
|
83
|
-
#
|
|
84
|
-
|
|
85
|
-
hx.from_int(0xdeadbeef).bytes
|
|
86
|
-
# b'\xde\xad\xbe\xef'
|
|
94
|
+
hx.convert(b"\xde\xad\xbe\xef", hx.Bytes(), hx.Hex(sep=" ", prefix=True, uppercase=True))
|
|
95
|
+
# '0xDE AD BE EF'
|
|
87
96
|
|
|
88
|
-
hx.
|
|
89
|
-
#
|
|
97
|
+
hx.convert("π", hx.Text(encoding="utf-8"), hx.Hex())
|
|
98
|
+
# 'cf80'
|
|
90
99
|
|
|
91
|
-
hx.
|
|
92
|
-
# '
|
|
100
|
+
hx.from_int(0x12345678, endian="little").hex
|
|
101
|
+
# '78563412'
|
|
93
102
|
|
|
94
|
-
hx.
|
|
103
|
+
hx.convert("ZGF0YQ", hx.Base64(padding=False), hx.Text())
|
|
95
104
|
# 'data'
|
|
96
105
|
|
|
97
|
-
hx.
|
|
98
|
-
# '
|
|
99
|
-
|
|
100
|
-
hx.from_struct((0x1234, 0x5678), fmt=">HH").hex
|
|
101
|
-
# '12345678'
|
|
102
|
-
|
|
103
|
-
hx.from_bits("101", pad=True).hex
|
|
104
|
-
# '05'
|
|
106
|
+
hx.from_binary("00010110 10010110", bit_order="lsb").text
|
|
107
|
+
# 'hi'
|
|
105
108
|
```
|
|
106
109
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
hx.from_auto("0xdeadbeef").bytes
|
|
111
|
-
# b'\xde\xad\xbe\xef'
|
|
112
|
-
|
|
113
|
-
hx.from_auto(b"\xde\xad").hex
|
|
114
|
-
# 'dead'
|
|
115
|
-
|
|
116
|
-
hx.from_auto([0xde, 0xad, 0xbe, 0xef]).to(hx.Hex(prefix=True))
|
|
117
|
-
# '0xdeadbeef'
|
|
118
|
-
```
|
|
110
|
+
Hex strings may include common separators, so you do not need to manually strip
|
|
111
|
+
spaces, colons, dashes, or `0x` prefixes before converting. `Hex(sep=" ")` is
|
|
112
|
+
how you emit space-separated hex.
|
|
119
113
|
|
|
120
|
-
##
|
|
114
|
+
## Full pipeline guide
|
|
121
115
|
|
|
122
116
|
`hexconv` is built around small format objects. A format can parse directly:
|
|
123
117
|
|
|
@@ -128,8 +122,15 @@ hx.Hex()("de ad be ef").bytes
|
|
|
128
122
|
hx.Text()("data").hex
|
|
129
123
|
# '64617461'
|
|
130
124
|
|
|
131
|
-
hx.Int(
|
|
125
|
+
hx.Int(endian="little")(0x12345678).hex
|
|
132
126
|
# '78563412'
|
|
127
|
+
|
|
128
|
+
pack_words = hx.Struct(">HH")
|
|
129
|
+
pack_words((0x1234, 0x5678)).hex
|
|
130
|
+
# '12345678'
|
|
131
|
+
|
|
132
|
+
hx.Hex()("12345678").to(pack_words)
|
|
133
|
+
# (4660, 22136)
|
|
133
134
|
```
|
|
134
135
|
|
|
135
136
|
Formats compose with `>>`, so reusable converters are normal Python objects:
|
|
@@ -142,12 +143,20 @@ to_text([0x48, 0x65, 0x6C, 0x6C, 0x6F])
|
|
|
142
143
|
to_hex = hx.Text() >> hx.Hex(prefix=True)
|
|
143
144
|
to_hex("data")
|
|
144
145
|
# '0x64617461'
|
|
146
|
+
|
|
147
|
+
pack_to_hex = hx.Struct(">HH") >> hx.Hex()
|
|
148
|
+
pack_to_hex((0x1234, 0x5678))
|
|
149
|
+
# '12345678'
|
|
150
|
+
|
|
151
|
+
unpack_from_hex = hx.Hex() >> hx.Struct(">HH")
|
|
152
|
+
unpack_from_hex("12345678")
|
|
153
|
+
# (4660, 22136)
|
|
145
154
|
```
|
|
146
155
|
|
|
147
156
|
Transforms sit between formats when data needs shaping:
|
|
148
157
|
|
|
149
158
|
```python
|
|
150
|
-
words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray(
|
|
159
|
+
words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray()
|
|
151
160
|
words("12345678")
|
|
152
161
|
# [4660, 22136]
|
|
153
162
|
|
|
@@ -174,33 +183,7 @@ conv("12345678")
|
|
|
174
183
|
# ['0x1234', '0x5678']
|
|
175
184
|
```
|
|
176
185
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
Use `convert` when you want an explicit source and target in one expression:
|
|
180
|
-
|
|
181
|
-
```python
|
|
182
|
-
hx.convert("de ad be ef", hx.Hex(sep=" "), hx.Bytes())
|
|
183
|
-
# b'\xde\xad\xbe\xef'
|
|
184
|
-
|
|
185
|
-
hx.convert(b"\xde\xad\xbe\xef", hx.Bytes(), hx.Hex(sep=" "))
|
|
186
|
-
# 'de ad be ef'
|
|
187
|
-
|
|
188
|
-
hx.convert("data", hx.Text(), hx.Hex(prefix=True))
|
|
189
|
-
# '0x64617461'
|
|
190
|
-
|
|
191
|
-
hx.convert("π", hx.Text(encoding="utf-8"), hx.Hex())
|
|
192
|
-
# 'cf80'
|
|
193
|
-
|
|
194
|
-
hx.convert("-_8", hx.Base64(urlsafe=True, padding=False), hx.Hex())
|
|
195
|
-
# 'fbff'
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
Hex strings may include common separators, so you do not need to manually strip
|
|
199
|
-
spaces, colons, dashes, or `0x` prefixes before converting.
|
|
200
|
-
|
|
201
|
-
## Common conversion patterns
|
|
202
|
-
|
|
203
|
-
Text, bytes, and hex:
|
|
186
|
+
More conversion patterns:
|
|
204
187
|
|
|
205
188
|
```python
|
|
206
189
|
hx.Text()("hello").bytes
|
|
@@ -209,54 +192,24 @@ hx.Text()("hello").bytes
|
|
|
209
192
|
hx.Hex()("68656c6c6f").text
|
|
210
193
|
# 'hello'
|
|
211
194
|
|
|
212
|
-
hx.BytesArray()([
|
|
213
|
-
# '
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
Integer packing:
|
|
195
|
+
hx.BytesArray()([0x4F, 0x4B]).to(hx.Text())
|
|
196
|
+
# 'OK'
|
|
217
197
|
|
|
218
|
-
|
|
219
|
-
hx.Int(width=2)(0x1234).hex
|
|
220
|
-
# '1234'
|
|
221
|
-
|
|
222
|
-
hx.Int(width=2, endian="little")(0x1234).hex
|
|
198
|
+
hx.Int(endian="little")(0x1234).hex
|
|
223
199
|
# '3412'
|
|
224
200
|
|
|
225
|
-
(hx.Hex() >> hx.Chunk(2) >> hx.IntArray(width=2))("12345678")
|
|
226
|
-
# [4660, 22136]
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
Base encodings:
|
|
230
|
-
|
|
231
|
-
```python
|
|
232
|
-
hx.Text()("data").to(hx.Base64())
|
|
233
|
-
# 'ZGF0YQ=='
|
|
234
|
-
|
|
235
201
|
hx.Text()("data").to(hx.Base32(padding=False))
|
|
236
202
|
# 'MRQXIYI'
|
|
237
203
|
|
|
238
|
-
hx.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
Escaped strings, hexdumps, structs, and bits:
|
|
243
|
-
|
|
244
|
-
```python
|
|
245
|
-
hx.Escaped()(r"\xde\xad").hex
|
|
246
|
-
# 'dead'
|
|
247
|
-
|
|
248
|
-
dump = hx.Hex()("deadbeef").to(hx.Hexdump(width=2))
|
|
249
|
-
hx.Hexdump()(dump).hex
|
|
250
|
-
# 'deadbeef'
|
|
251
|
-
|
|
252
|
-
hx.Struct(">HH")((0x1234, 0x5678)).hex
|
|
253
|
-
# '12345678'
|
|
204
|
+
dump = hx.Text()("data").to(hx.Hexdump(width=2))
|
|
205
|
+
hx.Hexdump()(dump).text
|
|
206
|
+
# 'data'
|
|
254
207
|
|
|
255
208
|
hx.Hex()("12345678").to(hx.Struct(">HH"))
|
|
256
209
|
# (4660, 22136)
|
|
257
210
|
|
|
258
|
-
hx.Bits()(
|
|
259
|
-
# '
|
|
211
|
+
hx.Bits()("0100111101001011").text
|
|
212
|
+
# 'OK'
|
|
260
213
|
```
|
|
261
214
|
|
|
262
215
|
## Why explicit formats?
|
|
@@ -304,10 +257,10 @@ result.reason
|
|
|
304
257
|
`from_auto(..., explain=True)` returns the same explanation object:
|
|
305
258
|
|
|
306
259
|
```python
|
|
307
|
-
result = hx.from_auto("
|
|
260
|
+
result = hx.from_auto("0b0100111101001011", explain=True)
|
|
308
261
|
|
|
309
|
-
result.value.
|
|
310
|
-
# '
|
|
262
|
+
result.value.text
|
|
263
|
+
# 'OK'
|
|
311
264
|
```
|
|
312
265
|
|
|
313
266
|
## Design priorities
|
|
@@ -331,7 +284,7 @@ conv("deadbeef")
|
|
|
331
284
|
# 3735928559
|
|
332
285
|
```
|
|
333
286
|
|
|
334
|
-
Available format specs:
|
|
287
|
+
Available format specs all work in their simplest bare form:
|
|
335
288
|
|
|
336
289
|
- `Bytes()`
|
|
337
290
|
- `BytesArray()`
|
|
@@ -339,8 +292,8 @@ Available format specs:
|
|
|
339
292
|
- `Hex()`
|
|
340
293
|
- `HexArray()`
|
|
341
294
|
- `HexInt()` / `LargeHexNumber()`
|
|
342
|
-
- `Int()`
|
|
343
|
-
- `IntArray()`
|
|
295
|
+
- `Int()` / `DecimalInt()`
|
|
296
|
+
- `IntArray()` / `DecimalIntArray()`
|
|
344
297
|
- `Text()`
|
|
345
298
|
- `Binary()`
|
|
346
299
|
- `Base64()`
|
|
@@ -351,6 +304,20 @@ Available format specs:
|
|
|
351
304
|
- `Hexdump()`
|
|
352
305
|
- `Struct(fmt)`
|
|
353
306
|
- `Bits()`
|
|
307
|
+
- `Auto()`
|
|
308
|
+
|
|
309
|
+
Add options only when you need control over formatting or parsing:
|
|
310
|
+
|
|
311
|
+
- Display: `Hex(sep=" ", prefix=True, uppercase=True)`, `HexArray(width=2)`.
|
|
312
|
+
- Integer packing: `Int(width=4)`, `Int(endian="little")`, `IntArray(width=2)`.
|
|
313
|
+
- Text: `Text(encoding="utf-8", errors="replace")`.
|
|
314
|
+
- Base encodings: `Base64(urlsafe=True, padding=False)`, `Base32(padding=False)`.
|
|
315
|
+
- Binary/bits: `Binary(sep=" ", prefix=True, bit_order="lsb")`, `Bits(pad_side="right")`.
|
|
316
|
+
- Dumps/escapes/structs: `Hexdump(width=8)`, `Escaped(uppercase=True)`, `Struct(">HH")`.
|
|
317
|
+
|
|
318
|
+
Some options only matter in one direction. For example, `Hex(sep=" ")`
|
|
319
|
+
controls hex output formatting, while hex input already accepts common
|
|
320
|
+
separators such as spaces, colons, dashes, underscores, and `0x` prefixes.
|
|
354
321
|
|
|
355
322
|
## Transforms
|
|
356
323
|
|
|
@@ -358,7 +325,7 @@ Transforms are byte-to-byte steps that sit between input and output formats.
|
|
|
358
325
|
|
|
359
326
|
```python
|
|
360
327
|
hx.Hex() >> hx.Chunk(2) >> hx.HexArray()
|
|
361
|
-
hx.Hex() >> hx.Group(2) >> hx.IntArray(
|
|
328
|
+
hx.Hex() >> hx.Group(2) >> hx.IntArray()
|
|
362
329
|
hx.Hex() >> hx.Pad.left(width=8) >> hx.Hex()
|
|
363
330
|
hx.Hex() >> hx.Pad.right(block_size=8, byte=0xFF) >> hx.Hex()
|
|
364
331
|
hx.Hex() >> hx.Reverse() >> hx.Hex()
|
|
@@ -12,83 +12,77 @@ It is built around ergonomic one-liners and composable format objects.
|
|
|
12
12
|
pip install hexconv
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Simple usage
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
Most conversions are one-liners. Use `from_auto` when you want a quick
|
|
18
|
+
best-effort parse, or pick an explicit helper when the input is ambiguous.
|
|
19
19
|
|
|
20
20
|
```python
|
|
21
21
|
import hexconv as hx
|
|
22
22
|
|
|
23
|
+
hx.from_auto("0x64617461").text
|
|
24
|
+
# 'data'
|
|
25
|
+
|
|
26
|
+
hx.from_bytes_array([0xde, 0xad, 0xbe, 0xef]).int
|
|
27
|
+
# 3735928559
|
|
28
|
+
|
|
29
|
+
hx.from_text("data").hex
|
|
30
|
+
# '64617461'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Fast compose
|
|
34
|
+
|
|
35
|
+
Create your own converters by snapping together
|
|
36
|
+
`source format >> optional transforms >> target format`.
|
|
37
|
+
|
|
38
|
+
```python
|
|
23
39
|
to_bytes = hx.Hex() >> hx.Bytes()
|
|
24
|
-
to_bytes("
|
|
25
|
-
# b'
|
|
40
|
+
to_bytes("68 69")
|
|
41
|
+
# b'hi'
|
|
26
42
|
|
|
27
43
|
spaced_hex = hx.Bytes() >> hx.Hex(sep=" ")
|
|
28
|
-
spaced_hex(b"
|
|
29
|
-
# '
|
|
44
|
+
spaced_hex(b"data")
|
|
45
|
+
# '64 61 74 61'
|
|
30
46
|
|
|
31
|
-
words = hx.Hex(
|
|
47
|
+
words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray()
|
|
32
48
|
words("12 34 56 78")
|
|
33
49
|
# [4660, 22136]
|
|
34
50
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
# '
|
|
51
|
+
pack_to_hex = hx.Struct(">HH") >> hx.Hex()
|
|
52
|
+
pack_to_hex((0x1234, 0x5678))
|
|
53
|
+
# '12345678'
|
|
38
54
|
```
|
|
39
55
|
|
|
40
|
-
|
|
41
|
-
need manual cleanup before conversion. `Hex(sep=" ")` is also how you emit
|
|
42
|
-
space-separated hex.
|
|
56
|
+
## Configure when needed
|
|
43
57
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
Use direct helpers when you want one obvious conversion across many
|
|
47
|
-
representations:
|
|
58
|
+
Formats work bare by default. Add arguments only for the conversion that needs
|
|
59
|
+
them:
|
|
48
60
|
|
|
49
61
|
```python
|
|
50
|
-
hx.
|
|
62
|
+
hx.convert("de ad be ef", hx.Hex(), hx.Bytes())
|
|
51
63
|
# b'\xde\xad\xbe\xef'
|
|
52
64
|
|
|
53
|
-
hx.
|
|
54
|
-
#
|
|
55
|
-
|
|
56
|
-
hx.from_int(0xdeadbeef).bytes
|
|
57
|
-
# b'\xde\xad\xbe\xef'
|
|
65
|
+
hx.convert(b"\xde\xad\xbe\xef", hx.Bytes(), hx.Hex(sep=" ", prefix=True, uppercase=True))
|
|
66
|
+
# '0xDE AD BE EF'
|
|
58
67
|
|
|
59
|
-
hx.
|
|
60
|
-
#
|
|
68
|
+
hx.convert("π", hx.Text(encoding="utf-8"), hx.Hex())
|
|
69
|
+
# 'cf80'
|
|
61
70
|
|
|
62
|
-
hx.
|
|
63
|
-
# '
|
|
71
|
+
hx.from_int(0x12345678, endian="little").hex
|
|
72
|
+
# '78563412'
|
|
64
73
|
|
|
65
|
-
hx.
|
|
74
|
+
hx.convert("ZGF0YQ", hx.Base64(padding=False), hx.Text())
|
|
66
75
|
# 'data'
|
|
67
76
|
|
|
68
|
-
hx.
|
|
69
|
-
# '
|
|
70
|
-
|
|
71
|
-
hx.from_struct((0x1234, 0x5678), fmt=">HH").hex
|
|
72
|
-
# '12345678'
|
|
73
|
-
|
|
74
|
-
hx.from_bits("101", pad=True).hex
|
|
75
|
-
# '05'
|
|
77
|
+
hx.from_binary("00010110 10010110", bit_order="lsb").text
|
|
78
|
+
# 'hi'
|
|
76
79
|
```
|
|
77
80
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
hx.from_auto("0xdeadbeef").bytes
|
|
82
|
-
# b'\xde\xad\xbe\xef'
|
|
83
|
-
|
|
84
|
-
hx.from_auto(b"\xde\xad").hex
|
|
85
|
-
# 'dead'
|
|
86
|
-
|
|
87
|
-
hx.from_auto([0xde, 0xad, 0xbe, 0xef]).to(hx.Hex(prefix=True))
|
|
88
|
-
# '0xdeadbeef'
|
|
89
|
-
```
|
|
81
|
+
Hex strings may include common separators, so you do not need to manually strip
|
|
82
|
+
spaces, colons, dashes, or `0x` prefixes before converting. `Hex(sep=" ")` is
|
|
83
|
+
how you emit space-separated hex.
|
|
90
84
|
|
|
91
|
-
##
|
|
85
|
+
## Full pipeline guide
|
|
92
86
|
|
|
93
87
|
`hexconv` is built around small format objects. A format can parse directly:
|
|
94
88
|
|
|
@@ -99,8 +93,15 @@ hx.Hex()("de ad be ef").bytes
|
|
|
99
93
|
hx.Text()("data").hex
|
|
100
94
|
# '64617461'
|
|
101
95
|
|
|
102
|
-
hx.Int(
|
|
96
|
+
hx.Int(endian="little")(0x12345678).hex
|
|
103
97
|
# '78563412'
|
|
98
|
+
|
|
99
|
+
pack_words = hx.Struct(">HH")
|
|
100
|
+
pack_words((0x1234, 0x5678)).hex
|
|
101
|
+
# '12345678'
|
|
102
|
+
|
|
103
|
+
hx.Hex()("12345678").to(pack_words)
|
|
104
|
+
# (4660, 22136)
|
|
104
105
|
```
|
|
105
106
|
|
|
106
107
|
Formats compose with `>>`, so reusable converters are normal Python objects:
|
|
@@ -113,12 +114,20 @@ to_text([0x48, 0x65, 0x6C, 0x6C, 0x6F])
|
|
|
113
114
|
to_hex = hx.Text() >> hx.Hex(prefix=True)
|
|
114
115
|
to_hex("data")
|
|
115
116
|
# '0x64617461'
|
|
117
|
+
|
|
118
|
+
pack_to_hex = hx.Struct(">HH") >> hx.Hex()
|
|
119
|
+
pack_to_hex((0x1234, 0x5678))
|
|
120
|
+
# '12345678'
|
|
121
|
+
|
|
122
|
+
unpack_from_hex = hx.Hex() >> hx.Struct(">HH")
|
|
123
|
+
unpack_from_hex("12345678")
|
|
124
|
+
# (4660, 22136)
|
|
116
125
|
```
|
|
117
126
|
|
|
118
127
|
Transforms sit between formats when data needs shaping:
|
|
119
128
|
|
|
120
129
|
```python
|
|
121
|
-
words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray(
|
|
130
|
+
words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray()
|
|
122
131
|
words("12345678")
|
|
123
132
|
# [4660, 22136]
|
|
124
133
|
|
|
@@ -145,33 +154,7 @@ conv("12345678")
|
|
|
145
154
|
# ['0x1234', '0x5678']
|
|
146
155
|
```
|
|
147
156
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
Use `convert` when you want an explicit source and target in one expression:
|
|
151
|
-
|
|
152
|
-
```python
|
|
153
|
-
hx.convert("de ad be ef", hx.Hex(sep=" "), hx.Bytes())
|
|
154
|
-
# b'\xde\xad\xbe\xef'
|
|
155
|
-
|
|
156
|
-
hx.convert(b"\xde\xad\xbe\xef", hx.Bytes(), hx.Hex(sep=" "))
|
|
157
|
-
# 'de ad be ef'
|
|
158
|
-
|
|
159
|
-
hx.convert("data", hx.Text(), hx.Hex(prefix=True))
|
|
160
|
-
# '0x64617461'
|
|
161
|
-
|
|
162
|
-
hx.convert("π", hx.Text(encoding="utf-8"), hx.Hex())
|
|
163
|
-
# 'cf80'
|
|
164
|
-
|
|
165
|
-
hx.convert("-_8", hx.Base64(urlsafe=True, padding=False), hx.Hex())
|
|
166
|
-
# 'fbff'
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
Hex strings may include common separators, so you do not need to manually strip
|
|
170
|
-
spaces, colons, dashes, or `0x` prefixes before converting.
|
|
171
|
-
|
|
172
|
-
## Common conversion patterns
|
|
173
|
-
|
|
174
|
-
Text, bytes, and hex:
|
|
157
|
+
More conversion patterns:
|
|
175
158
|
|
|
176
159
|
```python
|
|
177
160
|
hx.Text()("hello").bytes
|
|
@@ -180,54 +163,24 @@ hx.Text()("hello").bytes
|
|
|
180
163
|
hx.Hex()("68656c6c6f").text
|
|
181
164
|
# 'hello'
|
|
182
165
|
|
|
183
|
-
hx.BytesArray()([
|
|
184
|
-
# '
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
Integer packing:
|
|
166
|
+
hx.BytesArray()([0x4F, 0x4B]).to(hx.Text())
|
|
167
|
+
# 'OK'
|
|
188
168
|
|
|
189
|
-
|
|
190
|
-
hx.Int(width=2)(0x1234).hex
|
|
191
|
-
# '1234'
|
|
192
|
-
|
|
193
|
-
hx.Int(width=2, endian="little")(0x1234).hex
|
|
169
|
+
hx.Int(endian="little")(0x1234).hex
|
|
194
170
|
# '3412'
|
|
195
171
|
|
|
196
|
-
(hx.Hex() >> hx.Chunk(2) >> hx.IntArray(width=2))("12345678")
|
|
197
|
-
# [4660, 22136]
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
Base encodings:
|
|
201
|
-
|
|
202
|
-
```python
|
|
203
|
-
hx.Text()("data").to(hx.Base64())
|
|
204
|
-
# 'ZGF0YQ=='
|
|
205
|
-
|
|
206
172
|
hx.Text()("data").to(hx.Base32(padding=False))
|
|
207
173
|
# 'MRQXIYI'
|
|
208
174
|
|
|
209
|
-
hx.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
Escaped strings, hexdumps, structs, and bits:
|
|
214
|
-
|
|
215
|
-
```python
|
|
216
|
-
hx.Escaped()(r"\xde\xad").hex
|
|
217
|
-
# 'dead'
|
|
218
|
-
|
|
219
|
-
dump = hx.Hex()("deadbeef").to(hx.Hexdump(width=2))
|
|
220
|
-
hx.Hexdump()(dump).hex
|
|
221
|
-
# 'deadbeef'
|
|
222
|
-
|
|
223
|
-
hx.Struct(">HH")((0x1234, 0x5678)).hex
|
|
224
|
-
# '12345678'
|
|
175
|
+
dump = hx.Text()("data").to(hx.Hexdump(width=2))
|
|
176
|
+
hx.Hexdump()(dump).text
|
|
177
|
+
# 'data'
|
|
225
178
|
|
|
226
179
|
hx.Hex()("12345678").to(hx.Struct(">HH"))
|
|
227
180
|
# (4660, 22136)
|
|
228
181
|
|
|
229
|
-
hx.Bits()(
|
|
230
|
-
# '
|
|
182
|
+
hx.Bits()("0100111101001011").text
|
|
183
|
+
# 'OK'
|
|
231
184
|
```
|
|
232
185
|
|
|
233
186
|
## Why explicit formats?
|
|
@@ -275,10 +228,10 @@ result.reason
|
|
|
275
228
|
`from_auto(..., explain=True)` returns the same explanation object:
|
|
276
229
|
|
|
277
230
|
```python
|
|
278
|
-
result = hx.from_auto("
|
|
231
|
+
result = hx.from_auto("0b0100111101001011", explain=True)
|
|
279
232
|
|
|
280
|
-
result.value.
|
|
281
|
-
# '
|
|
233
|
+
result.value.text
|
|
234
|
+
# 'OK'
|
|
282
235
|
```
|
|
283
236
|
|
|
284
237
|
## Design priorities
|
|
@@ -302,7 +255,7 @@ conv("deadbeef")
|
|
|
302
255
|
# 3735928559
|
|
303
256
|
```
|
|
304
257
|
|
|
305
|
-
Available format specs:
|
|
258
|
+
Available format specs all work in their simplest bare form:
|
|
306
259
|
|
|
307
260
|
- `Bytes()`
|
|
308
261
|
- `BytesArray()`
|
|
@@ -310,8 +263,8 @@ Available format specs:
|
|
|
310
263
|
- `Hex()`
|
|
311
264
|
- `HexArray()`
|
|
312
265
|
- `HexInt()` / `LargeHexNumber()`
|
|
313
|
-
- `Int()`
|
|
314
|
-
- `IntArray()`
|
|
266
|
+
- `Int()` / `DecimalInt()`
|
|
267
|
+
- `IntArray()` / `DecimalIntArray()`
|
|
315
268
|
- `Text()`
|
|
316
269
|
- `Binary()`
|
|
317
270
|
- `Base64()`
|
|
@@ -322,6 +275,20 @@ Available format specs:
|
|
|
322
275
|
- `Hexdump()`
|
|
323
276
|
- `Struct(fmt)`
|
|
324
277
|
- `Bits()`
|
|
278
|
+
- `Auto()`
|
|
279
|
+
|
|
280
|
+
Add options only when you need control over formatting or parsing:
|
|
281
|
+
|
|
282
|
+
- Display: `Hex(sep=" ", prefix=True, uppercase=True)`, `HexArray(width=2)`.
|
|
283
|
+
- Integer packing: `Int(width=4)`, `Int(endian="little")`, `IntArray(width=2)`.
|
|
284
|
+
- Text: `Text(encoding="utf-8", errors="replace")`.
|
|
285
|
+
- Base encodings: `Base64(urlsafe=True, padding=False)`, `Base32(padding=False)`.
|
|
286
|
+
- Binary/bits: `Binary(sep=" ", prefix=True, bit_order="lsb")`, `Bits(pad_side="right")`.
|
|
287
|
+
- Dumps/escapes/structs: `Hexdump(width=8)`, `Escaped(uppercase=True)`, `Struct(">HH")`.
|
|
288
|
+
|
|
289
|
+
Some options only matter in one direction. For example, `Hex(sep=" ")`
|
|
290
|
+
controls hex output formatting, while hex input already accepts common
|
|
291
|
+
separators such as spaces, colons, dashes, underscores, and `0x` prefixes.
|
|
325
292
|
|
|
326
293
|
## Transforms
|
|
327
294
|
|
|
@@ -329,7 +296,7 @@ Transforms are byte-to-byte steps that sit between input and output formats.
|
|
|
329
296
|
|
|
330
297
|
```python
|
|
331
298
|
hx.Hex() >> hx.Chunk(2) >> hx.HexArray()
|
|
332
|
-
hx.Hex() >> hx.Group(2) >> hx.IntArray(
|
|
299
|
+
hx.Hex() >> hx.Group(2) >> hx.IntArray()
|
|
333
300
|
hx.Hex() >> hx.Pad.left(width=8) >> hx.Hex()
|
|
334
301
|
hx.Hex() >> hx.Pad.right(block_size=8, byte=0xFF) >> hx.Hex()
|
|
335
302
|
hx.Hex() >> hx.Reverse() >> hx.Hex()
|
|
@@ -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.5"
|
|
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"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hexconv
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: Pythonic conversion toolkit for bytes, hex, integers, arrays, binary strings, base64, and text.
|
|
5
5
|
Author: hexconv contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -41,83 +41,77 @@ It is built around ergonomic one-liners and composable format objects.
|
|
|
41
41
|
pip install hexconv
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
##
|
|
44
|
+
## Simple usage
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
Most conversions are one-liners. Use `from_auto` when you want a quick
|
|
47
|
+
best-effort parse, or pick an explicit helper when the input is ambiguous.
|
|
48
48
|
|
|
49
49
|
```python
|
|
50
50
|
import hexconv as hx
|
|
51
51
|
|
|
52
|
+
hx.from_auto("0x64617461").text
|
|
53
|
+
# 'data'
|
|
54
|
+
|
|
55
|
+
hx.from_bytes_array([0xde, 0xad, 0xbe, 0xef]).int
|
|
56
|
+
# 3735928559
|
|
57
|
+
|
|
58
|
+
hx.from_text("data").hex
|
|
59
|
+
# '64617461'
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Fast compose
|
|
63
|
+
|
|
64
|
+
Create your own converters by snapping together
|
|
65
|
+
`source format >> optional transforms >> target format`.
|
|
66
|
+
|
|
67
|
+
```python
|
|
52
68
|
to_bytes = hx.Hex() >> hx.Bytes()
|
|
53
|
-
to_bytes("
|
|
54
|
-
# b'
|
|
69
|
+
to_bytes("68 69")
|
|
70
|
+
# b'hi'
|
|
55
71
|
|
|
56
72
|
spaced_hex = hx.Bytes() >> hx.Hex(sep=" ")
|
|
57
|
-
spaced_hex(b"
|
|
58
|
-
# '
|
|
73
|
+
spaced_hex(b"data")
|
|
74
|
+
# '64 61 74 61'
|
|
59
75
|
|
|
60
|
-
words = hx.Hex(
|
|
76
|
+
words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray()
|
|
61
77
|
words("12 34 56 78")
|
|
62
78
|
# [4660, 22136]
|
|
63
79
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
# '
|
|
80
|
+
pack_to_hex = hx.Struct(">HH") >> hx.Hex()
|
|
81
|
+
pack_to_hex((0x1234, 0x5678))
|
|
82
|
+
# '12345678'
|
|
67
83
|
```
|
|
68
84
|
|
|
69
|
-
|
|
70
|
-
need manual cleanup before conversion. `Hex(sep=" ")` is also how you emit
|
|
71
|
-
space-separated hex.
|
|
85
|
+
## Configure when needed
|
|
72
86
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
Use direct helpers when you want one obvious conversion across many
|
|
76
|
-
representations:
|
|
87
|
+
Formats work bare by default. Add arguments only for the conversion that needs
|
|
88
|
+
them:
|
|
77
89
|
|
|
78
90
|
```python
|
|
79
|
-
hx.
|
|
91
|
+
hx.convert("de ad be ef", hx.Hex(), hx.Bytes())
|
|
80
92
|
# b'\xde\xad\xbe\xef'
|
|
81
93
|
|
|
82
|
-
hx.
|
|
83
|
-
#
|
|
84
|
-
|
|
85
|
-
hx.from_int(0xdeadbeef).bytes
|
|
86
|
-
# b'\xde\xad\xbe\xef'
|
|
94
|
+
hx.convert(b"\xde\xad\xbe\xef", hx.Bytes(), hx.Hex(sep=" ", prefix=True, uppercase=True))
|
|
95
|
+
# '0xDE AD BE EF'
|
|
87
96
|
|
|
88
|
-
hx.
|
|
89
|
-
#
|
|
97
|
+
hx.convert("π", hx.Text(encoding="utf-8"), hx.Hex())
|
|
98
|
+
# 'cf80'
|
|
90
99
|
|
|
91
|
-
hx.
|
|
92
|
-
# '
|
|
100
|
+
hx.from_int(0x12345678, endian="little").hex
|
|
101
|
+
# '78563412'
|
|
93
102
|
|
|
94
|
-
hx.
|
|
103
|
+
hx.convert("ZGF0YQ", hx.Base64(padding=False), hx.Text())
|
|
95
104
|
# 'data'
|
|
96
105
|
|
|
97
|
-
hx.
|
|
98
|
-
# '
|
|
99
|
-
|
|
100
|
-
hx.from_struct((0x1234, 0x5678), fmt=">HH").hex
|
|
101
|
-
# '12345678'
|
|
102
|
-
|
|
103
|
-
hx.from_bits("101", pad=True).hex
|
|
104
|
-
# '05'
|
|
106
|
+
hx.from_binary("00010110 10010110", bit_order="lsb").text
|
|
107
|
+
# 'hi'
|
|
105
108
|
```
|
|
106
109
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
hx.from_auto("0xdeadbeef").bytes
|
|
111
|
-
# b'\xde\xad\xbe\xef'
|
|
112
|
-
|
|
113
|
-
hx.from_auto(b"\xde\xad").hex
|
|
114
|
-
# 'dead'
|
|
115
|
-
|
|
116
|
-
hx.from_auto([0xde, 0xad, 0xbe, 0xef]).to(hx.Hex(prefix=True))
|
|
117
|
-
# '0xdeadbeef'
|
|
118
|
-
```
|
|
110
|
+
Hex strings may include common separators, so you do not need to manually strip
|
|
111
|
+
spaces, colons, dashes, or `0x` prefixes before converting. `Hex(sep=" ")` is
|
|
112
|
+
how you emit space-separated hex.
|
|
119
113
|
|
|
120
|
-
##
|
|
114
|
+
## Full pipeline guide
|
|
121
115
|
|
|
122
116
|
`hexconv` is built around small format objects. A format can parse directly:
|
|
123
117
|
|
|
@@ -128,8 +122,15 @@ hx.Hex()("de ad be ef").bytes
|
|
|
128
122
|
hx.Text()("data").hex
|
|
129
123
|
# '64617461'
|
|
130
124
|
|
|
131
|
-
hx.Int(
|
|
125
|
+
hx.Int(endian="little")(0x12345678).hex
|
|
132
126
|
# '78563412'
|
|
127
|
+
|
|
128
|
+
pack_words = hx.Struct(">HH")
|
|
129
|
+
pack_words((0x1234, 0x5678)).hex
|
|
130
|
+
# '12345678'
|
|
131
|
+
|
|
132
|
+
hx.Hex()("12345678").to(pack_words)
|
|
133
|
+
# (4660, 22136)
|
|
133
134
|
```
|
|
134
135
|
|
|
135
136
|
Formats compose with `>>`, so reusable converters are normal Python objects:
|
|
@@ -142,12 +143,20 @@ to_text([0x48, 0x65, 0x6C, 0x6C, 0x6F])
|
|
|
142
143
|
to_hex = hx.Text() >> hx.Hex(prefix=True)
|
|
143
144
|
to_hex("data")
|
|
144
145
|
# '0x64617461'
|
|
146
|
+
|
|
147
|
+
pack_to_hex = hx.Struct(">HH") >> hx.Hex()
|
|
148
|
+
pack_to_hex((0x1234, 0x5678))
|
|
149
|
+
# '12345678'
|
|
150
|
+
|
|
151
|
+
unpack_from_hex = hx.Hex() >> hx.Struct(">HH")
|
|
152
|
+
unpack_from_hex("12345678")
|
|
153
|
+
# (4660, 22136)
|
|
145
154
|
```
|
|
146
155
|
|
|
147
156
|
Transforms sit between formats when data needs shaping:
|
|
148
157
|
|
|
149
158
|
```python
|
|
150
|
-
words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray(
|
|
159
|
+
words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray()
|
|
151
160
|
words("12345678")
|
|
152
161
|
# [4660, 22136]
|
|
153
162
|
|
|
@@ -174,33 +183,7 @@ conv("12345678")
|
|
|
174
183
|
# ['0x1234', '0x5678']
|
|
175
184
|
```
|
|
176
185
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
Use `convert` when you want an explicit source and target in one expression:
|
|
180
|
-
|
|
181
|
-
```python
|
|
182
|
-
hx.convert("de ad be ef", hx.Hex(sep=" "), hx.Bytes())
|
|
183
|
-
# b'\xde\xad\xbe\xef'
|
|
184
|
-
|
|
185
|
-
hx.convert(b"\xde\xad\xbe\xef", hx.Bytes(), hx.Hex(sep=" "))
|
|
186
|
-
# 'de ad be ef'
|
|
187
|
-
|
|
188
|
-
hx.convert("data", hx.Text(), hx.Hex(prefix=True))
|
|
189
|
-
# '0x64617461'
|
|
190
|
-
|
|
191
|
-
hx.convert("π", hx.Text(encoding="utf-8"), hx.Hex())
|
|
192
|
-
# 'cf80'
|
|
193
|
-
|
|
194
|
-
hx.convert("-_8", hx.Base64(urlsafe=True, padding=False), hx.Hex())
|
|
195
|
-
# 'fbff'
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
Hex strings may include common separators, so you do not need to manually strip
|
|
199
|
-
spaces, colons, dashes, or `0x` prefixes before converting.
|
|
200
|
-
|
|
201
|
-
## Common conversion patterns
|
|
202
|
-
|
|
203
|
-
Text, bytes, and hex:
|
|
186
|
+
More conversion patterns:
|
|
204
187
|
|
|
205
188
|
```python
|
|
206
189
|
hx.Text()("hello").bytes
|
|
@@ -209,54 +192,24 @@ hx.Text()("hello").bytes
|
|
|
209
192
|
hx.Hex()("68656c6c6f").text
|
|
210
193
|
# 'hello'
|
|
211
194
|
|
|
212
|
-
hx.BytesArray()([
|
|
213
|
-
# '
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
Integer packing:
|
|
195
|
+
hx.BytesArray()([0x4F, 0x4B]).to(hx.Text())
|
|
196
|
+
# 'OK'
|
|
217
197
|
|
|
218
|
-
|
|
219
|
-
hx.Int(width=2)(0x1234).hex
|
|
220
|
-
# '1234'
|
|
221
|
-
|
|
222
|
-
hx.Int(width=2, endian="little")(0x1234).hex
|
|
198
|
+
hx.Int(endian="little")(0x1234).hex
|
|
223
199
|
# '3412'
|
|
224
200
|
|
|
225
|
-
(hx.Hex() >> hx.Chunk(2) >> hx.IntArray(width=2))("12345678")
|
|
226
|
-
# [4660, 22136]
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
Base encodings:
|
|
230
|
-
|
|
231
|
-
```python
|
|
232
|
-
hx.Text()("data").to(hx.Base64())
|
|
233
|
-
# 'ZGF0YQ=='
|
|
234
|
-
|
|
235
201
|
hx.Text()("data").to(hx.Base32(padding=False))
|
|
236
202
|
# 'MRQXIYI'
|
|
237
203
|
|
|
238
|
-
hx.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
Escaped strings, hexdumps, structs, and bits:
|
|
243
|
-
|
|
244
|
-
```python
|
|
245
|
-
hx.Escaped()(r"\xde\xad").hex
|
|
246
|
-
# 'dead'
|
|
247
|
-
|
|
248
|
-
dump = hx.Hex()("deadbeef").to(hx.Hexdump(width=2))
|
|
249
|
-
hx.Hexdump()(dump).hex
|
|
250
|
-
# 'deadbeef'
|
|
251
|
-
|
|
252
|
-
hx.Struct(">HH")((0x1234, 0x5678)).hex
|
|
253
|
-
# '12345678'
|
|
204
|
+
dump = hx.Text()("data").to(hx.Hexdump(width=2))
|
|
205
|
+
hx.Hexdump()(dump).text
|
|
206
|
+
# 'data'
|
|
254
207
|
|
|
255
208
|
hx.Hex()("12345678").to(hx.Struct(">HH"))
|
|
256
209
|
# (4660, 22136)
|
|
257
210
|
|
|
258
|
-
hx.Bits()(
|
|
259
|
-
# '
|
|
211
|
+
hx.Bits()("0100111101001011").text
|
|
212
|
+
# 'OK'
|
|
260
213
|
```
|
|
261
214
|
|
|
262
215
|
## Why explicit formats?
|
|
@@ -304,10 +257,10 @@ result.reason
|
|
|
304
257
|
`from_auto(..., explain=True)` returns the same explanation object:
|
|
305
258
|
|
|
306
259
|
```python
|
|
307
|
-
result = hx.from_auto("
|
|
260
|
+
result = hx.from_auto("0b0100111101001011", explain=True)
|
|
308
261
|
|
|
309
|
-
result.value.
|
|
310
|
-
# '
|
|
262
|
+
result.value.text
|
|
263
|
+
# 'OK'
|
|
311
264
|
```
|
|
312
265
|
|
|
313
266
|
## Design priorities
|
|
@@ -331,7 +284,7 @@ conv("deadbeef")
|
|
|
331
284
|
# 3735928559
|
|
332
285
|
```
|
|
333
286
|
|
|
334
|
-
Available format specs:
|
|
287
|
+
Available format specs all work in their simplest bare form:
|
|
335
288
|
|
|
336
289
|
- `Bytes()`
|
|
337
290
|
- `BytesArray()`
|
|
@@ -339,8 +292,8 @@ Available format specs:
|
|
|
339
292
|
- `Hex()`
|
|
340
293
|
- `HexArray()`
|
|
341
294
|
- `HexInt()` / `LargeHexNumber()`
|
|
342
|
-
- `Int()`
|
|
343
|
-
- `IntArray()`
|
|
295
|
+
- `Int()` / `DecimalInt()`
|
|
296
|
+
- `IntArray()` / `DecimalIntArray()`
|
|
344
297
|
- `Text()`
|
|
345
298
|
- `Binary()`
|
|
346
299
|
- `Base64()`
|
|
@@ -351,6 +304,20 @@ Available format specs:
|
|
|
351
304
|
- `Hexdump()`
|
|
352
305
|
- `Struct(fmt)`
|
|
353
306
|
- `Bits()`
|
|
307
|
+
- `Auto()`
|
|
308
|
+
|
|
309
|
+
Add options only when you need control over formatting or parsing:
|
|
310
|
+
|
|
311
|
+
- Display: `Hex(sep=" ", prefix=True, uppercase=True)`, `HexArray(width=2)`.
|
|
312
|
+
- Integer packing: `Int(width=4)`, `Int(endian="little")`, `IntArray(width=2)`.
|
|
313
|
+
- Text: `Text(encoding="utf-8", errors="replace")`.
|
|
314
|
+
- Base encodings: `Base64(urlsafe=True, padding=False)`, `Base32(padding=False)`.
|
|
315
|
+
- Binary/bits: `Binary(sep=" ", prefix=True, bit_order="lsb")`, `Bits(pad_side="right")`.
|
|
316
|
+
- Dumps/escapes/structs: `Hexdump(width=8)`, `Escaped(uppercase=True)`, `Struct(">HH")`.
|
|
317
|
+
|
|
318
|
+
Some options only matter in one direction. For example, `Hex(sep=" ")`
|
|
319
|
+
controls hex output formatting, while hex input already accepts common
|
|
320
|
+
separators such as spaces, colons, dashes, underscores, and `0x` prefixes.
|
|
354
321
|
|
|
355
322
|
## Transforms
|
|
356
323
|
|
|
@@ -358,7 +325,7 @@ Transforms are byte-to-byte steps that sit between input and output formats.
|
|
|
358
325
|
|
|
359
326
|
```python
|
|
360
327
|
hx.Hex() >> hx.Chunk(2) >> hx.HexArray()
|
|
361
|
-
hx.Hex() >> hx.Group(2) >> hx.IntArray(
|
|
328
|
+
hx.Hex() >> hx.Group(2) >> hx.IntArray()
|
|
362
329
|
hx.Hex() >> hx.Pad.left(width=8) >> hx.Hex()
|
|
363
330
|
hx.Hex() >> hx.Pad.right(block_size=8, byte=0xFF) >> hx.Hex()
|
|
364
331
|
hx.Hex() >> hx.Reverse() >> hx.Hex()
|
|
@@ -45,11 +45,56 @@ def test_converter_class_matches_proposed_usage():
|
|
|
45
45
|
|
|
46
46
|
def test_convert_one_shot():
|
|
47
47
|
assert hx.convert("data", from_=hx.Text, to=hx.HexString) == "64617461"
|
|
48
|
-
assert hx.convert("de ad be ef", hx.Hex(
|
|
48
|
+
assert hx.convert("de ad be ef", hx.Hex(), hx.Bytes()) == b"\xde\xad\xbe\xef"
|
|
49
49
|
assert hx.convert(b"\xde\xad\xbe\xef", hx.Bytes(), hx.Hex(sep=" ")) == "de ad be ef"
|
|
50
50
|
assert hx.convert("data", hx.Text(), hx.Hex(prefix=True)) == "0x64617461"
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
def test_documented_format_options_quick_tour():
|
|
54
|
+
assert hx.from_hex("de ad:be-ef").bytes == b"\xde\xad\xbe\xef"
|
|
55
|
+
assert hx.from_bytes(b"\xde\xad\xbe\xef").to(hx.Hex(sep=":", uppercase=True)) == "DE:AD:BE:EF"
|
|
56
|
+
assert hx.from_bytes_array([0xDE, 0xAD, 0xBE, 0xEF]).to(
|
|
57
|
+
hx.HexArray(width=2, prefix=True, uppercase=True)
|
|
58
|
+
) == ["0xDEAD", "0xBEEF"]
|
|
59
|
+
assert hx.from_text("π", encoding="utf-8").to(hx.Hex(prefix=True)) == "0xcf80"
|
|
60
|
+
assert hx.from_int(0x12345678, endian="little").hex == "78563412"
|
|
61
|
+
assert hx.from_int_array([0x1234, 0x5678], width=2).to(hx.HexArray(width=2)) == [
|
|
62
|
+
"1234",
|
|
63
|
+
"5678",
|
|
64
|
+
]
|
|
65
|
+
assert hx.from_binary("01100100 01100001 01110100 01100001").text == "data"
|
|
66
|
+
assert hx.from_base64("-_8", urlsafe=True, padding=False).hex == "fbff"
|
|
67
|
+
assert hx.from_base32("MRQXIYI", padding=False).text == "data"
|
|
68
|
+
assert hx.from_escaped(r"\xDE\xAD").hex == "dead"
|
|
69
|
+
assert hx.from_struct((0x1234, 0x5678), fmt=">HH").hex == "12345678"
|
|
70
|
+
assert hx.from_bits("101", pad_side="right").hex == "a0"
|
|
71
|
+
|
|
72
|
+
assert hx.convert("de ad be ef", hx.Hex(), hx.Bytes()) == b"\xde\xad\xbe\xef"
|
|
73
|
+
assert (
|
|
74
|
+
hx.convert(
|
|
75
|
+
b"\xde\xad\xbe\xef",
|
|
76
|
+
hx.Bytes(),
|
|
77
|
+
hx.Hex(sep=" ", prefix=True, uppercase=True),
|
|
78
|
+
)
|
|
79
|
+
== "0xDE AD BE EF"
|
|
80
|
+
)
|
|
81
|
+
assert hx.convert(["0xDEAD", "0xBEEF"], hx.HexArray(width=2), hx.IntArray(width=2)) == [
|
|
82
|
+
57005,
|
|
83
|
+
48879,
|
|
84
|
+
]
|
|
85
|
+
assert hx.convert("78563412", hx.Hex(), hx.Int(endian="little")) == 305419896
|
|
86
|
+
assert hx.convert("0b10100000", hx.Binary(bit_order="lsb"), hx.Hex()) == "05"
|
|
87
|
+
assert hx.from_text("data").to(hx.Base64(urlsafe=True, padding=False)) == "ZGF0YQ"
|
|
88
|
+
assert hx.from_text("data").to(hx.Base85()) == "WMOn+"
|
|
89
|
+
assert hx.from_text("data").to(hx.Ascii85(adobe=True)) == "<~A79Rg~>"
|
|
90
|
+
assert hx.from_hex("deadbeef").to(hx.Hexdump(width=2, offset=False, ascii=False)) == "de ad\nbe ef"
|
|
91
|
+
assert hx.from_hex("dead").to(hx.Escaped(uppercase=True)) == r"\xDE\xAD"
|
|
92
|
+
assert hx.from_hex("0506").to(hx.Binary(sep=" ", prefix=True, bit_order="lsb")) == (
|
|
93
|
+
"0b10100000 0b01100000"
|
|
94
|
+
)
|
|
95
|
+
assert hx.from_hex("05").to(hx.Bits(bit_order="lsb")) == [1, 0, 1, 0, 0, 0, 0, 0]
|
|
96
|
+
|
|
97
|
+
|
|
53
98
|
def test_convert_rejects_mixed_positional_and_keyword_formats():
|
|
54
99
|
with pytest.raises(hx.HexConvError, match="from_"):
|
|
55
100
|
hx.convert("data", hx.Text(), from_=hx.Text(), to=hx.Hex())
|
|
@@ -115,10 +160,10 @@ def test_constructable_hex_and_text_specs():
|
|
|
115
160
|
|
|
116
161
|
|
|
117
162
|
def test_pipeline_builder_and_shift_composition():
|
|
118
|
-
conv = hx.pipeline().from_(hx.Hex(
|
|
163
|
+
conv = hx.pipeline().from_(hx.Hex()).chunk(2).to(hx.IntArray())
|
|
119
164
|
assert conv("12 34 56 78") == [0x1234, 0x5678]
|
|
120
165
|
|
|
121
|
-
shift_conv = hx.Hex(
|
|
166
|
+
shift_conv = hx.Hex() >> hx.Chunk(2) >> hx.HexArray()
|
|
122
167
|
assert shift_conv("12 34 56 78") == ["1234", "5678"]
|
|
123
168
|
|
|
124
169
|
|
|
@@ -161,6 +206,9 @@ def test_struct_format_pack_and_unpack():
|
|
|
161
206
|
assert packed.hex == "12345678"
|
|
162
207
|
assert packed.to(hx.Struct(">HH")) == (0x1234, 0x5678)
|
|
163
208
|
assert hx.convert((0x1234, 0x5678), from_=hx.Struct(">HH"), to=hx.Hex()) == "12345678"
|
|
209
|
+
assert hx.Struct(">HH")((0x1234, 0x5678)).hex == "12345678"
|
|
210
|
+
assert (hx.Struct(">HH") >> hx.Hex())((0x1234, 0x5678)) == "12345678"
|
|
211
|
+
assert (hx.Hex() >> hx.Struct(">HH"))("12345678") == (0x1234, 0x5678)
|
|
164
212
|
|
|
165
213
|
|
|
166
214
|
def test_bit_array_and_lsb_bit_order():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|