hexconv 0.2.2__tar.gz → 0.2.4__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/src/hexconv.egg-info → hexconv-0.2.4}/PKG-INFO +106 -21
- {hexconv-0.2.2 → hexconv-0.2.4}/README.md +105 -20
- {hexconv-0.2.2 → hexconv-0.2.4}/pyproject.toml +1 -1
- {hexconv-0.2.2 → hexconv-0.2.4}/src/hexconv/_core.py +22 -5
- {hexconv-0.2.2 → hexconv-0.2.4/src/hexconv.egg-info}/PKG-INFO +106 -21
- {hexconv-0.2.2 → hexconv-0.2.4}/tests/test_hexconv.py +10 -0
- {hexconv-0.2.2 → hexconv-0.2.4}/LICENSE +0 -0
- {hexconv-0.2.2 → hexconv-0.2.4}/MANIFEST.in +0 -0
- {hexconv-0.2.2 → hexconv-0.2.4}/setup.cfg +0 -0
- {hexconv-0.2.2 → hexconv-0.2.4}/src/hexconv/__init__.py +0 -0
- {hexconv-0.2.2 → hexconv-0.2.4}/src/hexconv/py.typed +0 -0
- {hexconv-0.2.2 → hexconv-0.2.4}/src/hexconv.egg-info/SOURCES.txt +0 -0
- {hexconv-0.2.2 → hexconv-0.2.4}/src/hexconv.egg-info/dependency_links.txt +0 -0
- {hexconv-0.2.2 → hexconv-0.2.4}/src/hexconv.egg-info/requires.txt +0 -0
- {hexconv-0.2.2 → hexconv-0.2.4}/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.4
|
|
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
|
|
@@ -31,38 +31,77 @@ Dynamic: license-file
|
|
|
31
31
|
|
|
32
32
|
`hexconv` is a small, dependency-free Python toolkit for converting between
|
|
33
33
|
bytes, hex, integers, arrays, binary strings, base encodings, escaped strings,
|
|
34
|
-
hexdumps, and text.
|
|
34
|
+
hexdumps, structs, bits, and text.
|
|
35
35
|
|
|
36
|
-
It is
|
|
36
|
+
It is built around ergonomic one-liners and composable format objects.
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install hexconv
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Fast compose
|
|
45
|
+
|
|
46
|
+
The intended interface is small pieces snapped together:
|
|
47
|
+
`source format >> optional transforms >> target format`.
|
|
37
48
|
|
|
38
49
|
```python
|
|
39
50
|
import hexconv as hx
|
|
40
51
|
|
|
41
|
-
|
|
52
|
+
to_bytes = hx.Hex() >> hx.Bytes()
|
|
53
|
+
to_bytes("de ad be ef")
|
|
54
|
+
# b'\xde\xad\xbe\xef'
|
|
42
55
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
spaced_hex = hx.Bytes() >> hx.Hex(sep=" ")
|
|
57
|
+
spaced_hex(b"\xde\xad\xbe\xef")
|
|
58
|
+
# 'de ad be ef'
|
|
46
59
|
|
|
47
|
-
|
|
60
|
+
words = hx.Hex(sep=" ") >> hx.Chunk(2) >> hx.IntArray(width=2)
|
|
61
|
+
words("12 34 56 78")
|
|
62
|
+
# [4660, 22136]
|
|
48
63
|
|
|
49
|
-
|
|
50
|
-
|
|
64
|
+
text_to_urlsafe = hx.Text() >> hx.Base64(urlsafe=True, padding=False)
|
|
65
|
+
text_to_urlsafe("data")
|
|
66
|
+
# 'ZGF0YQ'
|
|
51
67
|
```
|
|
52
68
|
|
|
53
|
-
|
|
69
|
+
Hex input accepts common separators, so spaced or copied hex usually does not
|
|
70
|
+
need manual cleanup before conversion. `Hex(sep=" ")` is also how you emit
|
|
71
|
+
space-separated hex.
|
|
54
72
|
|
|
55
|
-
|
|
73
|
+
## Quick tour
|
|
74
|
+
|
|
75
|
+
Use direct helpers when you want one obvious conversion across many
|
|
76
|
+
representations:
|
|
56
77
|
|
|
57
78
|
```python
|
|
58
|
-
hx.
|
|
79
|
+
hx.from_hex("de ad be ef").bytes
|
|
59
80
|
# b'\xde\xad\xbe\xef'
|
|
60
81
|
|
|
61
|
-
hx.
|
|
62
|
-
# '
|
|
82
|
+
hx.from_text("data").to_hex_array(prefix=True)
|
|
83
|
+
# ['0x64', '0x61', '0x74', '0x61']
|
|
63
84
|
|
|
64
|
-
hx.
|
|
65
|
-
# '
|
|
85
|
+
hx.from_int(0xdeadbeef).bytes
|
|
86
|
+
# b'\xde\xad\xbe\xef'
|
|
87
|
+
|
|
88
|
+
hx.from_int_array([0x1234, 0x5678], width=2).to_hex_array(width=2)
|
|
89
|
+
# ['1234', '5678']
|
|
90
|
+
|
|
91
|
+
hx.from_binary("01100100 01100001 01110100 01100001").text
|
|
92
|
+
# 'data'
|
|
93
|
+
|
|
94
|
+
hx.from_base64("ZGF0YQ==").text
|
|
95
|
+
# 'data'
|
|
96
|
+
|
|
97
|
+
hx.from_escaped(r"\xde\xad").hex
|
|
98
|
+
# 'dead'
|
|
99
|
+
|
|
100
|
+
hx.from_struct((0x1234, 0x5678), fmt=">HH").hex
|
|
101
|
+
# '12345678'
|
|
102
|
+
|
|
103
|
+
hx.from_bits("101", pad=True).hex
|
|
104
|
+
# '05'
|
|
66
105
|
```
|
|
67
106
|
|
|
68
107
|
Use `from_auto` when you want a quick best-effort parse:
|
|
@@ -78,7 +117,22 @@ hx.from_auto([0xde, 0xad, 0xbe, 0xef]).to(hx.Hex(prefix=True))
|
|
|
78
117
|
# '0xdeadbeef'
|
|
79
118
|
```
|
|
80
119
|
|
|
81
|
-
|
|
120
|
+
## Composable interface
|
|
121
|
+
|
|
122
|
+
`hexconv` is built around small format objects. A format can parse directly:
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
hx.Hex()("de ad be ef").bytes
|
|
126
|
+
# b'\xde\xad\xbe\xef'
|
|
127
|
+
|
|
128
|
+
hx.Text()("data").hex
|
|
129
|
+
# '64617461'
|
|
130
|
+
|
|
131
|
+
hx.Int(width=4, endian="little")(0x12345678).hex
|
|
132
|
+
# '78563412'
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Formats compose with `>>`, so reusable converters are normal Python objects:
|
|
82
136
|
|
|
83
137
|
```python
|
|
84
138
|
to_text = hx.HexArray() >> hx.Text()
|
|
@@ -90,7 +144,7 @@ to_hex("data")
|
|
|
90
144
|
# '0x64617461'
|
|
91
145
|
```
|
|
92
146
|
|
|
93
|
-
|
|
147
|
+
Transforms sit between formats when data needs shaping:
|
|
94
148
|
|
|
95
149
|
```python
|
|
96
150
|
words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray(width=2)
|
|
@@ -106,7 +160,7 @@ pad_hex("dead")
|
|
|
106
160
|
# '0000dead'
|
|
107
161
|
```
|
|
108
162
|
|
|
109
|
-
Use the builder form when
|
|
163
|
+
Use the builder form when named steps read better:
|
|
110
164
|
|
|
111
165
|
```python
|
|
112
166
|
conv = (
|
|
@@ -120,7 +174,31 @@ conv("12345678")
|
|
|
120
174
|
# ['0x1234', '0x5678']
|
|
121
175
|
```
|
|
122
176
|
|
|
123
|
-
##
|
|
177
|
+
## Configurable one-shot conversions
|
|
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
|
|
124
202
|
|
|
125
203
|
Text, bytes, and hex:
|
|
126
204
|
|
|
@@ -232,6 +310,13 @@ result.value.hex
|
|
|
232
310
|
# '05'
|
|
233
311
|
```
|
|
234
312
|
|
|
313
|
+
## Design priorities
|
|
314
|
+
|
|
315
|
+
- Easy: obvious conversions stay one-liners.
|
|
316
|
+
- Composable: reusable converters are just `format >> transform >> format`.
|
|
317
|
+
- Flexible: configure endianness, width, padding, grouping, text encoding, and base encodings where needed.
|
|
318
|
+
- Fast and lightweight: no runtime dependencies, bytes-native internals, and direct use of Python's optimized `bytes`, `int`, `base64`, and `struct` primitives.
|
|
319
|
+
|
|
235
320
|
## Format objects
|
|
236
321
|
|
|
237
322
|
Format objects are both parsers and pipeline endpoints.
|
|
@@ -2,38 +2,77 @@
|
|
|
2
2
|
|
|
3
3
|
`hexconv` is a small, dependency-free Python toolkit for converting between
|
|
4
4
|
bytes, hex, integers, arrays, binary strings, base encodings, escaped strings,
|
|
5
|
-
hexdumps, and text.
|
|
5
|
+
hexdumps, structs, bits, and text.
|
|
6
6
|
|
|
7
|
-
It is
|
|
7
|
+
It is built around ergonomic one-liners and composable format objects.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install hexconv
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Fast compose
|
|
16
|
+
|
|
17
|
+
The intended interface is small pieces snapped together:
|
|
18
|
+
`source format >> optional transforms >> target format`.
|
|
8
19
|
|
|
9
20
|
```python
|
|
10
21
|
import hexconv as hx
|
|
11
22
|
|
|
12
|
-
|
|
23
|
+
to_bytes = hx.Hex() >> hx.Bytes()
|
|
24
|
+
to_bytes("de ad be ef")
|
|
25
|
+
# b'\xde\xad\xbe\xef'
|
|
13
26
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
27
|
+
spaced_hex = hx.Bytes() >> hx.Hex(sep=" ")
|
|
28
|
+
spaced_hex(b"\xde\xad\xbe\xef")
|
|
29
|
+
# 'de ad be ef'
|
|
17
30
|
|
|
18
|
-
|
|
31
|
+
words = hx.Hex(sep=" ") >> hx.Chunk(2) >> hx.IntArray(width=2)
|
|
32
|
+
words("12 34 56 78")
|
|
33
|
+
# [4660, 22136]
|
|
19
34
|
|
|
20
|
-
|
|
21
|
-
|
|
35
|
+
text_to_urlsafe = hx.Text() >> hx.Base64(urlsafe=True, padding=False)
|
|
36
|
+
text_to_urlsafe("data")
|
|
37
|
+
# 'ZGF0YQ'
|
|
22
38
|
```
|
|
23
39
|
|
|
24
|
-
|
|
40
|
+
Hex input accepts common separators, so spaced or copied hex usually does not
|
|
41
|
+
need manual cleanup before conversion. `Hex(sep=" ")` is also how you emit
|
|
42
|
+
space-separated hex.
|
|
25
43
|
|
|
26
|
-
|
|
44
|
+
## Quick tour
|
|
45
|
+
|
|
46
|
+
Use direct helpers when you want one obvious conversion across many
|
|
47
|
+
representations:
|
|
27
48
|
|
|
28
49
|
```python
|
|
29
|
-
hx.
|
|
50
|
+
hx.from_hex("de ad be ef").bytes
|
|
30
51
|
# b'\xde\xad\xbe\xef'
|
|
31
52
|
|
|
32
|
-
hx.
|
|
33
|
-
# '
|
|
53
|
+
hx.from_text("data").to_hex_array(prefix=True)
|
|
54
|
+
# ['0x64', '0x61', '0x74', '0x61']
|
|
34
55
|
|
|
35
|
-
hx.
|
|
36
|
-
# '
|
|
56
|
+
hx.from_int(0xdeadbeef).bytes
|
|
57
|
+
# b'\xde\xad\xbe\xef'
|
|
58
|
+
|
|
59
|
+
hx.from_int_array([0x1234, 0x5678], width=2).to_hex_array(width=2)
|
|
60
|
+
# ['1234', '5678']
|
|
61
|
+
|
|
62
|
+
hx.from_binary("01100100 01100001 01110100 01100001").text
|
|
63
|
+
# 'data'
|
|
64
|
+
|
|
65
|
+
hx.from_base64("ZGF0YQ==").text
|
|
66
|
+
# 'data'
|
|
67
|
+
|
|
68
|
+
hx.from_escaped(r"\xde\xad").hex
|
|
69
|
+
# 'dead'
|
|
70
|
+
|
|
71
|
+
hx.from_struct((0x1234, 0x5678), fmt=">HH").hex
|
|
72
|
+
# '12345678'
|
|
73
|
+
|
|
74
|
+
hx.from_bits("101", pad=True).hex
|
|
75
|
+
# '05'
|
|
37
76
|
```
|
|
38
77
|
|
|
39
78
|
Use `from_auto` when you want a quick best-effort parse:
|
|
@@ -49,7 +88,22 @@ hx.from_auto([0xde, 0xad, 0xbe, 0xef]).to(hx.Hex(prefix=True))
|
|
|
49
88
|
# '0xdeadbeef'
|
|
50
89
|
```
|
|
51
90
|
|
|
52
|
-
|
|
91
|
+
## Composable interface
|
|
92
|
+
|
|
93
|
+
`hexconv` is built around small format objects. A format can parse directly:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
hx.Hex()("de ad be ef").bytes
|
|
97
|
+
# b'\xde\xad\xbe\xef'
|
|
98
|
+
|
|
99
|
+
hx.Text()("data").hex
|
|
100
|
+
# '64617461'
|
|
101
|
+
|
|
102
|
+
hx.Int(width=4, endian="little")(0x12345678).hex
|
|
103
|
+
# '78563412'
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Formats compose with `>>`, so reusable converters are normal Python objects:
|
|
53
107
|
|
|
54
108
|
```python
|
|
55
109
|
to_text = hx.HexArray() >> hx.Text()
|
|
@@ -61,7 +115,7 @@ to_hex("data")
|
|
|
61
115
|
# '0x64617461'
|
|
62
116
|
```
|
|
63
117
|
|
|
64
|
-
|
|
118
|
+
Transforms sit between formats when data needs shaping:
|
|
65
119
|
|
|
66
120
|
```python
|
|
67
121
|
words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray(width=2)
|
|
@@ -77,7 +131,7 @@ pad_hex("dead")
|
|
|
77
131
|
# '0000dead'
|
|
78
132
|
```
|
|
79
133
|
|
|
80
|
-
Use the builder form when
|
|
134
|
+
Use the builder form when named steps read better:
|
|
81
135
|
|
|
82
136
|
```python
|
|
83
137
|
conv = (
|
|
@@ -91,7 +145,31 @@ conv("12345678")
|
|
|
91
145
|
# ['0x1234', '0x5678']
|
|
92
146
|
```
|
|
93
147
|
|
|
94
|
-
##
|
|
148
|
+
## Configurable one-shot conversions
|
|
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
|
|
95
173
|
|
|
96
174
|
Text, bytes, and hex:
|
|
97
175
|
|
|
@@ -203,6 +281,13 @@ result.value.hex
|
|
|
203
281
|
# '05'
|
|
204
282
|
```
|
|
205
283
|
|
|
284
|
+
## Design priorities
|
|
285
|
+
|
|
286
|
+
- Easy: obvious conversions stay one-liners.
|
|
287
|
+
- Composable: reusable converters are just `format >> transform >> format`.
|
|
288
|
+
- Flexible: configure endianness, width, padding, grouping, text encoding, and base encodings where needed.
|
|
289
|
+
- Fast and lightweight: no runtime dependencies, bytes-native internals, and direct use of Python's optimized `bytes`, `int`, `base64`, and `struct` primitives.
|
|
290
|
+
|
|
206
291
|
## Format objects
|
|
207
292
|
|
|
208
293
|
Format objects are both parsers and pipeline endpoints.
|
|
@@ -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.4"
|
|
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"
|
|
@@ -999,17 +999,34 @@ class Converter:
|
|
|
999
999
|
|
|
1000
1000
|
def convert(
|
|
1001
1001
|
value: Any,
|
|
1002
|
+
from_format: FormatLike | None = None,
|
|
1003
|
+
to_format: FormatLike | None = None,
|
|
1002
1004
|
*,
|
|
1003
|
-
from_: FormatLike =
|
|
1004
|
-
to: FormatLike =
|
|
1005
|
+
from_: FormatLike | None = None,
|
|
1006
|
+
to: FormatLike | None = None,
|
|
1005
1007
|
input_options: dict[str, Any] | None = None,
|
|
1006
1008
|
output_options: dict[str, Any] | None = None,
|
|
1007
1009
|
) -> Any:
|
|
1008
|
-
"""One-shot conversion between explicit formats.
|
|
1010
|
+
"""One-shot conversion between explicit formats.
|
|
1011
|
+
|
|
1012
|
+
Preferred:
|
|
1013
|
+
convert(value, Hex(), Bytes())
|
|
1014
|
+
|
|
1015
|
+
Backward-compatible:
|
|
1016
|
+
convert(value, from_=Hex(), to=Bytes())
|
|
1017
|
+
"""
|
|
1018
|
+
|
|
1019
|
+
if from_format is not None and from_ is not None:
|
|
1020
|
+
raise HexConvError("use either positional input format or from_, not both")
|
|
1021
|
+
if to_format is not None and to is not None:
|
|
1022
|
+
raise HexConvError("use either positional output format or to, not both")
|
|
1023
|
+
|
|
1024
|
+
input_format = from_format if from_format is not None else from_
|
|
1025
|
+
output_format = to_format if to_format is not None else to
|
|
1009
1026
|
|
|
1010
1027
|
return Converter(
|
|
1011
|
-
|
|
1012
|
-
|
|
1028
|
+
Auto if input_format is None else input_format,
|
|
1029
|
+
Bytes if output_format is None else output_format,
|
|
1013
1030
|
input_options=input_options,
|
|
1014
1031
|
output_options=output_options,
|
|
1015
1032
|
)(value)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hexconv
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
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
|
|
@@ -31,38 +31,77 @@ Dynamic: license-file
|
|
|
31
31
|
|
|
32
32
|
`hexconv` is a small, dependency-free Python toolkit for converting between
|
|
33
33
|
bytes, hex, integers, arrays, binary strings, base encodings, escaped strings,
|
|
34
|
-
hexdumps, and text.
|
|
34
|
+
hexdumps, structs, bits, and text.
|
|
35
35
|
|
|
36
|
-
It is
|
|
36
|
+
It is built around ergonomic one-liners and composable format objects.
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install hexconv
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Fast compose
|
|
45
|
+
|
|
46
|
+
The intended interface is small pieces snapped together:
|
|
47
|
+
`source format >> optional transforms >> target format`.
|
|
37
48
|
|
|
38
49
|
```python
|
|
39
50
|
import hexconv as hx
|
|
40
51
|
|
|
41
|
-
|
|
52
|
+
to_bytes = hx.Hex() >> hx.Bytes()
|
|
53
|
+
to_bytes("de ad be ef")
|
|
54
|
+
# b'\xde\xad\xbe\xef'
|
|
42
55
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
spaced_hex = hx.Bytes() >> hx.Hex(sep=" ")
|
|
57
|
+
spaced_hex(b"\xde\xad\xbe\xef")
|
|
58
|
+
# 'de ad be ef'
|
|
46
59
|
|
|
47
|
-
|
|
60
|
+
words = hx.Hex(sep=" ") >> hx.Chunk(2) >> hx.IntArray(width=2)
|
|
61
|
+
words("12 34 56 78")
|
|
62
|
+
# [4660, 22136]
|
|
48
63
|
|
|
49
|
-
|
|
50
|
-
|
|
64
|
+
text_to_urlsafe = hx.Text() >> hx.Base64(urlsafe=True, padding=False)
|
|
65
|
+
text_to_urlsafe("data")
|
|
66
|
+
# 'ZGF0YQ'
|
|
51
67
|
```
|
|
52
68
|
|
|
53
|
-
|
|
69
|
+
Hex input accepts common separators, so spaced or copied hex usually does not
|
|
70
|
+
need manual cleanup before conversion. `Hex(sep=" ")` is also how you emit
|
|
71
|
+
space-separated hex.
|
|
54
72
|
|
|
55
|
-
|
|
73
|
+
## Quick tour
|
|
74
|
+
|
|
75
|
+
Use direct helpers when you want one obvious conversion across many
|
|
76
|
+
representations:
|
|
56
77
|
|
|
57
78
|
```python
|
|
58
|
-
hx.
|
|
79
|
+
hx.from_hex("de ad be ef").bytes
|
|
59
80
|
# b'\xde\xad\xbe\xef'
|
|
60
81
|
|
|
61
|
-
hx.
|
|
62
|
-
# '
|
|
82
|
+
hx.from_text("data").to_hex_array(prefix=True)
|
|
83
|
+
# ['0x64', '0x61', '0x74', '0x61']
|
|
63
84
|
|
|
64
|
-
hx.
|
|
65
|
-
# '
|
|
85
|
+
hx.from_int(0xdeadbeef).bytes
|
|
86
|
+
# b'\xde\xad\xbe\xef'
|
|
87
|
+
|
|
88
|
+
hx.from_int_array([0x1234, 0x5678], width=2).to_hex_array(width=2)
|
|
89
|
+
# ['1234', '5678']
|
|
90
|
+
|
|
91
|
+
hx.from_binary("01100100 01100001 01110100 01100001").text
|
|
92
|
+
# 'data'
|
|
93
|
+
|
|
94
|
+
hx.from_base64("ZGF0YQ==").text
|
|
95
|
+
# 'data'
|
|
96
|
+
|
|
97
|
+
hx.from_escaped(r"\xde\xad").hex
|
|
98
|
+
# 'dead'
|
|
99
|
+
|
|
100
|
+
hx.from_struct((0x1234, 0x5678), fmt=">HH").hex
|
|
101
|
+
# '12345678'
|
|
102
|
+
|
|
103
|
+
hx.from_bits("101", pad=True).hex
|
|
104
|
+
# '05'
|
|
66
105
|
```
|
|
67
106
|
|
|
68
107
|
Use `from_auto` when you want a quick best-effort parse:
|
|
@@ -78,7 +117,22 @@ hx.from_auto([0xde, 0xad, 0xbe, 0xef]).to(hx.Hex(prefix=True))
|
|
|
78
117
|
# '0xdeadbeef'
|
|
79
118
|
```
|
|
80
119
|
|
|
81
|
-
|
|
120
|
+
## Composable interface
|
|
121
|
+
|
|
122
|
+
`hexconv` is built around small format objects. A format can parse directly:
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
hx.Hex()("de ad be ef").bytes
|
|
126
|
+
# b'\xde\xad\xbe\xef'
|
|
127
|
+
|
|
128
|
+
hx.Text()("data").hex
|
|
129
|
+
# '64617461'
|
|
130
|
+
|
|
131
|
+
hx.Int(width=4, endian="little")(0x12345678).hex
|
|
132
|
+
# '78563412'
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Formats compose with `>>`, so reusable converters are normal Python objects:
|
|
82
136
|
|
|
83
137
|
```python
|
|
84
138
|
to_text = hx.HexArray() >> hx.Text()
|
|
@@ -90,7 +144,7 @@ to_hex("data")
|
|
|
90
144
|
# '0x64617461'
|
|
91
145
|
```
|
|
92
146
|
|
|
93
|
-
|
|
147
|
+
Transforms sit between formats when data needs shaping:
|
|
94
148
|
|
|
95
149
|
```python
|
|
96
150
|
words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray(width=2)
|
|
@@ -106,7 +160,7 @@ pad_hex("dead")
|
|
|
106
160
|
# '0000dead'
|
|
107
161
|
```
|
|
108
162
|
|
|
109
|
-
Use the builder form when
|
|
163
|
+
Use the builder form when named steps read better:
|
|
110
164
|
|
|
111
165
|
```python
|
|
112
166
|
conv = (
|
|
@@ -120,7 +174,31 @@ conv("12345678")
|
|
|
120
174
|
# ['0x1234', '0x5678']
|
|
121
175
|
```
|
|
122
176
|
|
|
123
|
-
##
|
|
177
|
+
## Configurable one-shot conversions
|
|
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
|
|
124
202
|
|
|
125
203
|
Text, bytes, and hex:
|
|
126
204
|
|
|
@@ -232,6 +310,13 @@ result.value.hex
|
|
|
232
310
|
# '05'
|
|
233
311
|
```
|
|
234
312
|
|
|
313
|
+
## Design priorities
|
|
314
|
+
|
|
315
|
+
- Easy: obvious conversions stay one-liners.
|
|
316
|
+
- Composable: reusable converters are just `format >> transform >> format`.
|
|
317
|
+
- Flexible: configure endianness, width, padding, grouping, text encoding, and base encodings where needed.
|
|
318
|
+
- Fast and lightweight: no runtime dependencies, bytes-native internals, and direct use of Python's optimized `bytes`, `int`, `base64`, and `struct` primitives.
|
|
319
|
+
|
|
235
320
|
## Format objects
|
|
236
321
|
|
|
237
322
|
Format objects are both parsers and pipeline endpoints.
|
|
@@ -45,6 +45,16 @@ 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(sep=" "), hx.Bytes()) == b"\xde\xad\xbe\xef"
|
|
49
|
+
assert hx.convert(b"\xde\xad\xbe\xef", hx.Bytes(), hx.Hex(sep=" ")) == "de ad be ef"
|
|
50
|
+
assert hx.convert("data", hx.Text(), hx.Hex(prefix=True)) == "0x64617461"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_convert_rejects_mixed_positional_and_keyword_formats():
|
|
54
|
+
with pytest.raises(hx.HexConvError, match="from_"):
|
|
55
|
+
hx.convert("data", hx.Text(), from_=hx.Text(), to=hx.Hex())
|
|
56
|
+
with pytest.raises(hx.HexConvError, match="to"):
|
|
57
|
+
hx.convert("data", hx.Text(), hx.Hex(), to=hx.Hex())
|
|
48
58
|
|
|
49
59
|
|
|
50
60
|
def test_converter_options():
|
|
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
|