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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hexconv
3
- Version: 0.2.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 designed around a simple idea: build the conversion you mean, then call it.
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
- decode = hx.HexArray() >> hx.Text()
52
+ to_bytes = hx.Hex() >> hx.Bytes()
53
+ to_bytes("de ad be ef")
54
+ # b'\xde\xad\xbe\xef'
42
55
 
43
- decode([0x48, 0x65, 0x6C, 0x6C, 0x6F])
44
- # 'Hello'
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
- ## Install
60
+ words = hx.Hex(sep=" ") >> hx.Chunk(2) >> hx.IntArray(width=2)
61
+ words("12 34 56 78")
62
+ # [4660, 22136]
48
63
 
49
- ```bash
50
- pip install hexconv
64
+ text_to_urlsafe = hx.Text() >> hx.Base64(urlsafe=True, padding=False)
65
+ text_to_urlsafe("data")
66
+ # 'ZGF0YQ'
51
67
  ```
52
68
 
53
- ## The intended interface
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
- Use format objects for direct parsing:
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.Hex()("dead beef").bytes
79
+ hx.from_hex("de ad be ef").bytes
59
80
  # b'\xde\xad\xbe\xef'
60
81
 
61
- hx.Text()("data").hex
62
- # '64617461'
82
+ hx.from_text("data").to_hex_array(prefix=True)
83
+ # ['0x64', '0x61', '0x74', '0x61']
63
84
 
64
- hx.Int(width=4, endian="little")(0x12345678).hex
65
- # '78563412'
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
- Use `>>` to compose reusable converters:
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
- Use transforms between formats when the data needs shaping:
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 readability matters more than compactness:
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
- ## Common conversions
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 designed around a simple idea: build the conversion you mean, then call it.
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
- decode = hx.HexArray() >> hx.Text()
23
+ to_bytes = hx.Hex() >> hx.Bytes()
24
+ to_bytes("de ad be ef")
25
+ # b'\xde\xad\xbe\xef'
13
26
 
14
- decode([0x48, 0x65, 0x6C, 0x6C, 0x6F])
15
- # 'Hello'
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
- ## Install
31
+ words = hx.Hex(sep=" ") >> hx.Chunk(2) >> hx.IntArray(width=2)
32
+ words("12 34 56 78")
33
+ # [4660, 22136]
19
34
 
20
- ```bash
21
- pip install hexconv
35
+ text_to_urlsafe = hx.Text() >> hx.Base64(urlsafe=True, padding=False)
36
+ text_to_urlsafe("data")
37
+ # 'ZGF0YQ'
22
38
  ```
23
39
 
24
- ## The intended interface
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
- Use format objects for direct parsing:
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.Hex()("dead beef").bytes
50
+ hx.from_hex("de ad be ef").bytes
30
51
  # b'\xde\xad\xbe\xef'
31
52
 
32
- hx.Text()("data").hex
33
- # '64617461'
53
+ hx.from_text("data").to_hex_array(prefix=True)
54
+ # ['0x64', '0x61', '0x74', '0x61']
34
55
 
35
- hx.Int(width=4, endian="little")(0x12345678).hex
36
- # '78563412'
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
- Use `>>` to compose reusable converters:
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
- Use transforms between formats when the data needs shaping:
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 readability matters more than compactness:
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
- ## Common conversions
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.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 = 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.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 designed around a simple idea: build the conversion you mean, then call it.
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
- decode = hx.HexArray() >> hx.Text()
52
+ to_bytes = hx.Hex() >> hx.Bytes()
53
+ to_bytes("de ad be ef")
54
+ # b'\xde\xad\xbe\xef'
42
55
 
43
- decode([0x48, 0x65, 0x6C, 0x6C, 0x6F])
44
- # 'Hello'
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
- ## Install
60
+ words = hx.Hex(sep=" ") >> hx.Chunk(2) >> hx.IntArray(width=2)
61
+ words("12 34 56 78")
62
+ # [4660, 22136]
48
63
 
49
- ```bash
50
- pip install hexconv
64
+ text_to_urlsafe = hx.Text() >> hx.Base64(urlsafe=True, padding=False)
65
+ text_to_urlsafe("data")
66
+ # 'ZGF0YQ'
51
67
  ```
52
68
 
53
- ## The intended interface
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
- Use format objects for direct parsing:
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.Hex()("dead beef").bytes
79
+ hx.from_hex("de ad be ef").bytes
59
80
  # b'\xde\xad\xbe\xef'
60
81
 
61
- hx.Text()("data").hex
62
- # '64617461'
82
+ hx.from_text("data").to_hex_array(prefix=True)
83
+ # ['0x64', '0x61', '0x74', '0x61']
63
84
 
64
- hx.Int(width=4, endian="little")(0x12345678).hex
65
- # '78563412'
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
- Use `>>` to compose reusable converters:
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
- Use transforms between formats when the data needs shaping:
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 readability matters more than compactness:
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
- ## Common conversions
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