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 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)`
@@ -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.0"
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"