hexconv 0.2.1__tar.gz → 0.2.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hexconv
3
- Version: 0.2.1
3
+ Version: 0.2.3
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,18 +31,9 @@ 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 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([0x3C, 0x4D, 0x41, 0x47])
44
- # '<MAG'
45
- ```
36
+ It is built around ergonomic one-liners and composable format objects.
46
37
 
47
38
  ## Install
48
39
 
@@ -50,19 +41,33 @@ decode([0x3C, 0x4D, 0x41, 0x47])
50
41
  pip install hexconv
51
42
  ```
52
43
 
53
- ## The intended interface
44
+ ## Quick tour
54
45
 
55
- Use format objects for direct parsing:
46
+ Use direct helpers when you want one obvious conversion:
56
47
 
57
48
  ```python
58
- hx.Hex()("dead beef").bytes
49
+ import hexconv as hx
50
+
51
+ hx.from_hex("dead beef").to_bytes()
59
52
  # b'\xde\xad\xbe\xef'
60
53
 
61
- hx.Text()("data").hex
62
- # '64617461'
54
+ hx.from_hex("dead beef").bytes
55
+ # b'\xde\xad\xbe\xef'
63
56
 
64
- hx.Int(width=4, endian="little")(0x12345678).hex
65
- # '78563412'
57
+ hx.from_bytes_array([0xde, 0xad, 0xbe, 0xef]).to_hex()
58
+ # 'deadbeef'
59
+
60
+ hx.from_bytes_array([0xde, 0xad, 0xbe, 0xef]).hex
61
+ # 'deadbeef'
62
+
63
+ hx.from_text("data").to_hex_array(prefix=True)
64
+ # ['0x64', '0x61', '0x74', '0x61']
65
+
66
+ hx.from_int(0xdeadbeef).to_bytes()
67
+ # b'\xde\xad\xbe\xef'
68
+
69
+ hx.from_int_array([0x1234, 0x5678], width=2).to_hex_array(width=2)
70
+ # ['1234', '5678']
66
71
  ```
67
72
 
68
73
  Use `from_auto` when you want a quick best-effort parse:
@@ -78,19 +83,34 @@ hx.from_auto([0xde, 0xad, 0xbe, 0xef]).to(hx.Hex(prefix=True))
78
83
  # '0xdeadbeef'
79
84
  ```
80
85
 
81
- Use `>>` to compose reusable converters:
86
+ ## Composable interface
87
+
88
+ `hexconv` is built around small format objects. A format can parse directly:
89
+
90
+ ```python
91
+ hx.Hex()("dead beef").bytes
92
+ # b'\xde\xad\xbe\xef'
93
+
94
+ hx.Text()("data").hex
95
+ # '64617461'
96
+
97
+ hx.Int(width=4, endian="little")(0x12345678).hex
98
+ # '78563412'
99
+ ```
100
+
101
+ Formats compose with `>>`:
82
102
 
83
103
  ```python
84
104
  to_text = hx.HexArray() >> hx.Text()
85
- to_text([0x3C, 0x4D, 0x41, 0x47])
86
- # '<MAG'
105
+ to_text([0x48, 0x65, 0x6C, 0x6C, 0x6F])
106
+ # 'Hello'
87
107
 
88
108
  to_hex = hx.Text() >> hx.Hex(prefix=True)
89
109
  to_hex("data")
90
110
  # '0x64617461'
91
111
  ```
92
112
 
93
- Use transforms between formats when the data needs shaping:
113
+ Transforms sit between formats when data needs shaping:
94
114
 
95
115
  ```python
96
116
  words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray(width=2)
@@ -106,7 +126,7 @@ pad_hex("dead")
106
126
  # '0000dead'
107
127
  ```
108
128
 
109
- Use the builder form when readability matters more than compactness:
129
+ Use the builder form when named steps read better:
110
130
 
111
131
  ```python
112
132
  conv = (
@@ -120,7 +140,28 @@ conv("12345678")
120
140
  # ['0x1234', '0x5678']
121
141
  ```
122
142
 
123
- ## Common conversions
143
+ ## Configurable one-shot conversions
144
+
145
+ Use `convert` when you want an explicit source and target in one expression:
146
+
147
+ ```python
148
+ hx.convert("de ad be ef", hx.Hex(sep=" "), hx.Bytes())
149
+ # b'\xde\xad\xbe\xef'
150
+
151
+ hx.convert("data", hx.Text(), hx.Hex(prefix=True))
152
+ # '0x64617461'
153
+
154
+ hx.convert("π", hx.Text(encoding="utf-8"), hx.Hex())
155
+ # 'cf80'
156
+
157
+ hx.convert("-_8", hx.Base64(urlsafe=True, padding=False), hx.Hex())
158
+ # 'fbff'
159
+ ```
160
+
161
+ Hex strings may include common separators, so you do not need to manually strip
162
+ spaces, colons, dashes, or `0x` prefixes before converting.
163
+
164
+ ## Common conversion patterns
124
165
 
125
166
  Text, bytes, and hex:
126
167
 
@@ -232,6 +273,13 @@ result.value.hex
232
273
  # '05'
233
274
  ```
234
275
 
276
+ ## Design priorities
277
+
278
+ - Easy: obvious conversions stay one-liners.
279
+ - Composable: reusable converters are just `format >> transform >> format`.
280
+ - Flexible: configure endianness, width, padding, grouping, text encoding, and base encodings where needed.
281
+ - Fast and lightweight: no runtime dependencies, bytes-native internals, and direct use of Python's optimized `bytes`, `int`, `base64`, and `struct` primitives.
282
+
235
283
  ## Format objects
236
284
 
237
285
  Format objects are both parsers and pipeline endpoints.
@@ -2,18 +2,9 @@
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 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([0x3C, 0x4D, 0x41, 0x47])
15
- # '<MAG'
16
- ```
7
+ It is built around ergonomic one-liners and composable format objects.
17
8
 
18
9
  ## Install
19
10
 
@@ -21,19 +12,33 @@ decode([0x3C, 0x4D, 0x41, 0x47])
21
12
  pip install hexconv
22
13
  ```
23
14
 
24
- ## The intended interface
15
+ ## Quick tour
25
16
 
26
- Use format objects for direct parsing:
17
+ Use direct helpers when you want one obvious conversion:
27
18
 
28
19
  ```python
29
- hx.Hex()("dead beef").bytes
20
+ import hexconv as hx
21
+
22
+ hx.from_hex("dead beef").to_bytes()
30
23
  # b'\xde\xad\xbe\xef'
31
24
 
32
- hx.Text()("data").hex
33
- # '64617461'
25
+ hx.from_hex("dead beef").bytes
26
+ # b'\xde\xad\xbe\xef'
34
27
 
35
- hx.Int(width=4, endian="little")(0x12345678).hex
36
- # '78563412'
28
+ hx.from_bytes_array([0xde, 0xad, 0xbe, 0xef]).to_hex()
29
+ # 'deadbeef'
30
+
31
+ hx.from_bytes_array([0xde, 0xad, 0xbe, 0xef]).hex
32
+ # 'deadbeef'
33
+
34
+ hx.from_text("data").to_hex_array(prefix=True)
35
+ # ['0x64', '0x61', '0x74', '0x61']
36
+
37
+ hx.from_int(0xdeadbeef).to_bytes()
38
+ # b'\xde\xad\xbe\xef'
39
+
40
+ hx.from_int_array([0x1234, 0x5678], width=2).to_hex_array(width=2)
41
+ # ['1234', '5678']
37
42
  ```
38
43
 
39
44
  Use `from_auto` when you want a quick best-effort parse:
@@ -49,19 +54,34 @@ hx.from_auto([0xde, 0xad, 0xbe, 0xef]).to(hx.Hex(prefix=True))
49
54
  # '0xdeadbeef'
50
55
  ```
51
56
 
52
- Use `>>` to compose reusable converters:
57
+ ## Composable interface
58
+
59
+ `hexconv` is built around small format objects. A format can parse directly:
60
+
61
+ ```python
62
+ hx.Hex()("dead beef").bytes
63
+ # b'\xde\xad\xbe\xef'
64
+
65
+ hx.Text()("data").hex
66
+ # '64617461'
67
+
68
+ hx.Int(width=4, endian="little")(0x12345678).hex
69
+ # '78563412'
70
+ ```
71
+
72
+ Formats compose with `>>`:
53
73
 
54
74
  ```python
55
75
  to_text = hx.HexArray() >> hx.Text()
56
- to_text([0x3C, 0x4D, 0x41, 0x47])
57
- # '<MAG'
76
+ to_text([0x48, 0x65, 0x6C, 0x6C, 0x6F])
77
+ # 'Hello'
58
78
 
59
79
  to_hex = hx.Text() >> hx.Hex(prefix=True)
60
80
  to_hex("data")
61
81
  # '0x64617461'
62
82
  ```
63
83
 
64
- Use transforms between formats when the data needs shaping:
84
+ Transforms sit between formats when data needs shaping:
65
85
 
66
86
  ```python
67
87
  words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray(width=2)
@@ -77,7 +97,7 @@ pad_hex("dead")
77
97
  # '0000dead'
78
98
  ```
79
99
 
80
- Use the builder form when readability matters more than compactness:
100
+ Use the builder form when named steps read better:
81
101
 
82
102
  ```python
83
103
  conv = (
@@ -91,7 +111,28 @@ conv("12345678")
91
111
  # ['0x1234', '0x5678']
92
112
  ```
93
113
 
94
- ## Common conversions
114
+ ## Configurable one-shot conversions
115
+
116
+ Use `convert` when you want an explicit source and target in one expression:
117
+
118
+ ```python
119
+ hx.convert("de ad be ef", hx.Hex(sep=" "), hx.Bytes())
120
+ # b'\xde\xad\xbe\xef'
121
+
122
+ hx.convert("data", hx.Text(), hx.Hex(prefix=True))
123
+ # '0x64617461'
124
+
125
+ hx.convert("π", hx.Text(encoding="utf-8"), hx.Hex())
126
+ # 'cf80'
127
+
128
+ hx.convert("-_8", hx.Base64(urlsafe=True, padding=False), hx.Hex())
129
+ # 'fbff'
130
+ ```
131
+
132
+ Hex strings may include common separators, so you do not need to manually strip
133
+ spaces, colons, dashes, or `0x` prefixes before converting.
134
+
135
+ ## Common conversion patterns
95
136
 
96
137
  Text, bytes, and hex:
97
138
 
@@ -203,6 +244,13 @@ result.value.hex
203
244
  # '05'
204
245
  ```
205
246
 
247
+ ## Design priorities
248
+
249
+ - Easy: obvious conversions stay one-liners.
250
+ - Composable: reusable converters are just `format >> transform >> format`.
251
+ - Flexible: configure endianness, width, padding, grouping, text encoding, and base encodings where needed.
252
+ - Fast and lightweight: no runtime dependencies, bytes-native internals, and direct use of Python's optimized `bytes`, `int`, `base64`, and `struct` primitives.
253
+
206
254
  ## Format objects
207
255
 
208
256
  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.1"
7
+ version = "0.2.3"
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 = Auto,
1004
- to: FormatLike = Bytes,
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
- from_,
1012
- to,
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.1
3
+ Version: 0.2.3
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,18 +31,9 @@ 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 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([0x3C, 0x4D, 0x41, 0x47])
44
- # '<MAG'
45
- ```
36
+ It is built around ergonomic one-liners and composable format objects.
46
37
 
47
38
  ## Install
48
39
 
@@ -50,19 +41,33 @@ decode([0x3C, 0x4D, 0x41, 0x47])
50
41
  pip install hexconv
51
42
  ```
52
43
 
53
- ## The intended interface
44
+ ## Quick tour
54
45
 
55
- Use format objects for direct parsing:
46
+ Use direct helpers when you want one obvious conversion:
56
47
 
57
48
  ```python
58
- hx.Hex()("dead beef").bytes
49
+ import hexconv as hx
50
+
51
+ hx.from_hex("dead beef").to_bytes()
59
52
  # b'\xde\xad\xbe\xef'
60
53
 
61
- hx.Text()("data").hex
62
- # '64617461'
54
+ hx.from_hex("dead beef").bytes
55
+ # b'\xde\xad\xbe\xef'
63
56
 
64
- hx.Int(width=4, endian="little")(0x12345678).hex
65
- # '78563412'
57
+ hx.from_bytes_array([0xde, 0xad, 0xbe, 0xef]).to_hex()
58
+ # 'deadbeef'
59
+
60
+ hx.from_bytes_array([0xde, 0xad, 0xbe, 0xef]).hex
61
+ # 'deadbeef'
62
+
63
+ hx.from_text("data").to_hex_array(prefix=True)
64
+ # ['0x64', '0x61', '0x74', '0x61']
65
+
66
+ hx.from_int(0xdeadbeef).to_bytes()
67
+ # b'\xde\xad\xbe\xef'
68
+
69
+ hx.from_int_array([0x1234, 0x5678], width=2).to_hex_array(width=2)
70
+ # ['1234', '5678']
66
71
  ```
67
72
 
68
73
  Use `from_auto` when you want a quick best-effort parse:
@@ -78,19 +83,34 @@ hx.from_auto([0xde, 0xad, 0xbe, 0xef]).to(hx.Hex(prefix=True))
78
83
  # '0xdeadbeef'
79
84
  ```
80
85
 
81
- Use `>>` to compose reusable converters:
86
+ ## Composable interface
87
+
88
+ `hexconv` is built around small format objects. A format can parse directly:
89
+
90
+ ```python
91
+ hx.Hex()("dead beef").bytes
92
+ # b'\xde\xad\xbe\xef'
93
+
94
+ hx.Text()("data").hex
95
+ # '64617461'
96
+
97
+ hx.Int(width=4, endian="little")(0x12345678).hex
98
+ # '78563412'
99
+ ```
100
+
101
+ Formats compose with `>>`:
82
102
 
83
103
  ```python
84
104
  to_text = hx.HexArray() >> hx.Text()
85
- to_text([0x3C, 0x4D, 0x41, 0x47])
86
- # '<MAG'
105
+ to_text([0x48, 0x65, 0x6C, 0x6C, 0x6F])
106
+ # 'Hello'
87
107
 
88
108
  to_hex = hx.Text() >> hx.Hex(prefix=True)
89
109
  to_hex("data")
90
110
  # '0x64617461'
91
111
  ```
92
112
 
93
- Use transforms between formats when the data needs shaping:
113
+ Transforms sit between formats when data needs shaping:
94
114
 
95
115
  ```python
96
116
  words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray(width=2)
@@ -106,7 +126,7 @@ pad_hex("dead")
106
126
  # '0000dead'
107
127
  ```
108
128
 
109
- Use the builder form when readability matters more than compactness:
129
+ Use the builder form when named steps read better:
110
130
 
111
131
  ```python
112
132
  conv = (
@@ -120,7 +140,28 @@ conv("12345678")
120
140
  # ['0x1234', '0x5678']
121
141
  ```
122
142
 
123
- ## Common conversions
143
+ ## Configurable one-shot conversions
144
+
145
+ Use `convert` when you want an explicit source and target in one expression:
146
+
147
+ ```python
148
+ hx.convert("de ad be ef", hx.Hex(sep=" "), hx.Bytes())
149
+ # b'\xde\xad\xbe\xef'
150
+
151
+ hx.convert("data", hx.Text(), hx.Hex(prefix=True))
152
+ # '0x64617461'
153
+
154
+ hx.convert("π", hx.Text(encoding="utf-8"), hx.Hex())
155
+ # 'cf80'
156
+
157
+ hx.convert("-_8", hx.Base64(urlsafe=True, padding=False), hx.Hex())
158
+ # 'fbff'
159
+ ```
160
+
161
+ Hex strings may include common separators, so you do not need to manually strip
162
+ spaces, colons, dashes, or `0x` prefixes before converting.
163
+
164
+ ## Common conversion patterns
124
165
 
125
166
  Text, bytes, and hex:
126
167
 
@@ -232,6 +273,13 @@ result.value.hex
232
273
  # '05'
233
274
  ```
234
275
 
276
+ ## Design priorities
277
+
278
+ - Easy: obvious conversions stay one-liners.
279
+ - Composable: reusable converters are just `format >> transform >> format`.
280
+ - Flexible: configure endianness, width, padding, grouping, text encoding, and base encodings where needed.
281
+ - Fast and lightweight: no runtime dependencies, bytes-native internals, and direct use of Python's optimized `bytes`, `int`, `base64`, and `struct` primitives.
282
+
235
283
  ## Format objects
236
284
 
237
285
  Format objects are both parsers and pipeline endpoints.
@@ -45,6 +45,15 @@ 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("data", hx.Text(), hx.Hex(prefix=True)) == "0x64617461"
50
+
51
+
52
+ def test_convert_rejects_mixed_positional_and_keyword_formats():
53
+ with pytest.raises(hx.HexConvError, match="from_"):
54
+ hx.convert("data", hx.Text(), from_=hx.Text(), to=hx.Hex())
55
+ with pytest.raises(hx.HexConvError, match="to"):
56
+ hx.convert("data", hx.Text(), hx.Hex(), to=hx.Hex())
48
57
 
49
58
 
50
59
  def test_converter_options():
File without changes
File without changes
File without changes
File without changes
File without changes