Metadata-Version: 2.4
Name: hexconv
Version: 0.3.1
Summary: Pythonic conversion toolkit for bytes, hex, integers, arrays, binary strings, base64, and text.
Author: hexconv contributors
License-Expression: MIT
Keywords: hex,bytes,binary,base64,conversion,encoding,toolkit
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Utilities
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: build; extra == "dev"
Requires-Dist: hypothesis; extra == "dev"
Requires-Dist: mypy; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# hexconv

`hexconv` is a small, dependency-free Python toolkit for converting between
bytes, hex, integers, arrays, binary strings, base encodings, escaped strings,
hexdumps, structs, bits, and text.

It is built around ergonomic one-liners and composable format objects.

## Install

```bash
pip install hexconv
```

## Simple usage

Most conversions are one-liners. Use `from_auto` when you want a quick
best-effort parse, or pick an explicit helper when the input is ambiguous.

```python
import hexconv as hx

hx.from_auto("0x64617461").text
# 'data'

hx.from_bytes_array([0xde, 0xad, 0xbe, 0xef]).int
# 3735928559

hx.from_text("data").hex
# '64617461'
```

The bare `.hex`, `.text`, and `.bytes` properties are shortcuts for zero-argument
`to_*` methods. Call the method form when you need options:

```python
value = hx.from_hex("deadbeef")

value.to_hex()
# 'deadbeef'  (same as value.hex)

value.to_hex(sep=" ", uppercase=True)
# 'DE AD BE EF'

value.to_bytes()
# b'\xde\xad\xbe\xef'

hx.from_hex("68656c6c6f").to_text()
# 'hello'
```

## Fast compose

Create your own converters by snapping together
`source format >> optional transforms >> target format`.

```python
to_bytes = hx.Hex() >> hx.Bytes()
to_bytes("68 69")
# b'hi'

spaced_hex = hx.Bytes() >> hx.Hex(sep=" ")
spaced_hex(b"data")
# '64 61 74 61'

words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray()
words("12 34 56 78")
# [4660, 22136]

pack_to_hex = hx.Struct(">HH") >> hx.Hex()
pack_to_hex((0x1234, 0x5678))
# '12345678'
```

## Configure when needed

Formats work bare by default. Add arguments only for the conversion that needs
them:

```python
hx.convert("de ad be ef", hx.Hex(), hx.Bytes())
# b'\xde\xad\xbe\xef'

hx.convert(b"\xde\xad\xbe\xef", hx.Bytes(), hx.Hex(sep=" ", prefix=True, uppercase=True))
# '0xDE AD BE EF'

hx.convert("π", hx.Text(encoding="utf-8"), hx.Hex())
# 'cf80'

hx.from_int(0x12345678, endian="little").hex
# '78563412'

hx.convert("ZGF0YQ", hx.Base64(padding=False), hx.Text())
# 'data'

hx.from_binary("00010110 10010110", bit_order="lsb").text
# 'hi'
```

Hex strings may include common separators, so you do not need to manually strip
spaces, colons, dashes, or `0x` prefixes before converting. `Hex(sep=" ")` is
how you emit space-separated hex.

## Full pipeline guide

`hexconv` is built around small format objects. A format can parse directly:

```python
hx.Hex()("de ad be ef").bytes
# b'\xde\xad\xbe\xef'

hx.Text()("data").hex
# '64617461'

hx.Int(endian="little")(0x12345678).hex
# '78563412'

pack_words = hx.Struct(">HH")
pack_words((0x1234, 0x5678)).hex
# '12345678'

hx.Hex()("12345678").to(pack_words)
# (4660, 22136)
```

Formats compose with `>>`, so reusable converters are normal Python objects:

```python
to_text = hx.HexArray() >> hx.Text()
to_text([0x48, 0x65, 0x6C, 0x6C, 0x6F])
# 'Hello'

to_hex = hx.Text() >> hx.Hex(prefix=True)
to_hex("data")
# '0x64617461'

pack_to_hex = hx.Struct(">HH") >> hx.Hex()
pack_to_hex((0x1234, 0x5678))
# '12345678'

unpack_from_hex = hx.Hex() >> hx.Struct(">HH")
unpack_from_hex("12345678")
# (4660, 22136)
```

Transforms sit between formats when data needs shaping:

```python
words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray()
words("12345678")
# [4660, 22136]

reverse_hex = hx.Hex() >> hx.Reverse() >> hx.Hex()
reverse_hex("deadbeef")
# 'efbeadde'

pad_hex = hx.Hex() >> hx.Pad.left(width=4) >> hx.Hex()
pad_hex("dead")
# '0000dead'
```

Use the builder form when named steps read better:

```python
conv = (
    hx.pipeline()
    .from_(hx.Hex())
    .chunk(2)
    .to(hx.HexArray(prefix=True))
)

conv("12345678")
# ['0x1234', '0x5678']
```

More conversion patterns:

```python
hx.BytesArray()([0x4F, 0x4B]).to(hx.Text())
# 'OK'

hx.Int(endian="little")(0x1234).hex
# '3412'

hx.Text()("data").to(hx.Base32(padding=False))
# 'MRQXIYI'

dump = hx.Text()("data").to(hx.Hexdump(width=2))
hx.Hexdump()(dump).text
# 'data'

hx.Hex()("12345678").to(hx.Struct(">HH"))
# (4660, 22136)

hx.Bits()("0100111101001011").text
# 'OK'
```

## Why explicit formats?

Some values are genuinely ambiguous:

```python
"face"
```

That can mean raw text:

```python
hx.Text()("face").hex
# '66616365'
```

or hex bytes:

```python
hx.Hex()("face").bytes
# b'\xfa\xce'
```

`hexconv` keeps the recommended path explicit so conversions are predictable.
If you want convenience heuristics, use `from_auto`:

```python
hx.from_auto("0xdead").hex
# 'dead'
```

If you want to see what `from_auto` would infer, use `infer`:

```python
result = hx.infer("0xdead")

result.value.hex
# 'dead'

result.reason
# '0x-prefixed hex string'
```

`from_auto(..., explain=True)` returns the same explanation object:

```python
result = hx.from_auto("0b0100111101001011", explain=True)

result.value.text
# 'OK'
```

## Design priorities

- Easy: obvious conversions stay one-liners.
- Composable: reusable converters are just `format >> transform >> format`.
- Flexible: configure endianness, width, padding, grouping, text encoding, and base encodings where needed.
- Fast and lightweight: no runtime dependencies, bytes-native internals, and direct use of Python's optimized `bytes`, `int`, `base64`, and `struct` primitives.

## Format objects

Format objects are both parsers and pipeline endpoints.

```python
value = hx.Hex()("deadbeef")
value.to(hx.Int())
# 3735928559

conv = hx.Hex() >> hx.Int()
conv("deadbeef")
# 3735928559
```

Available format specs all work in their simplest bare form:

- `Bytes()`
- `BytesArray()`
- `BytesString()`
- `Hex()`
- `HexArray()`
- `HexInt()` / `LargeHexNumber()`
- `Int()` / `DecimalInt()`
- `IntArray()` / `DecimalIntArray()`
- `Text()`
- `Binary()`
- `Base64()`
- `Base32()`
- `Base85()`
- `Ascii85()`
- `Escaped()`
- `Hexdump()`
- `Struct(fmt)`
- `Bits()`
- `Auto()`

The short names are ergonomic aliases of the explicit long-form specs — pick
whichever reads better, they resolve to the same format:

| Short | Long form |
| --- | --- |
| `Hex` | `HexString` |
| `Binary` | `BinaryString` |
| `Base64` / `Base32` / `Base85` / `Ascii85` | `Base64String` / `Base32String` / `Base85String` / `Ascii85String` |
| `Escaped` | `EscapedString` |
| `Hexdump` | `HexDump` |
| `Struct` | `StructFormat` |
| `Bits` | `BitArray` |
| `DecimalInt` / `DecimalIntArray` | `Int` / `IntArray` |
| `LargeHexNumber` | `HexInt` |
| `ASCIIText` / `RawString` | `Text` |
| `Group` (transform) | `Chunk` |

Add options only when you need control over formatting or parsing:

- Display: `Hex(sep=" ", prefix=True, uppercase=True)`, `HexArray(width=2)`.
- Integer packing: `Int(width=4)`, `Int(endian="little")`, `IntArray(width=2)`.
- Text: `Text(encoding="utf-8", errors="replace")`.
- Base encodings: `Base64(urlsafe=True, padding=False)`, `Base32(padding=False)`.
- Binary/bits: `Binary(sep=" ", prefix=True, bit_order="lsb")`, `Bits(pad_side="right")`.
- Dumps/escapes/structs: `Hexdump(width=8)`, `Escaped(uppercase=True)`, `Struct(">HH")`.

Some options only matter in one direction. For example, `Hex(sep=" ")`
controls hex output formatting, while hex input already accepts common
separators such as spaces, colons, dashes, underscores, and `0x` prefixes.

## Transforms

Transforms are byte-to-byte steps that sit between input and output formats.

```python
hx.Hex() >> hx.Chunk(2) >> hx.HexArray()
hx.Hex() >> hx.Group(2) >> hx.IntArray()
hx.Hex() >> hx.Pad.left(width=8) >> hx.Hex()
hx.Hex() >> hx.Pad.right(block_size=8, byte=0xFF) >> hx.Hex()
hx.Hex() >> hx.Reverse() >> hx.Hex()
```

Current transforms:

- `Chunk(width, strict=True)` — set downstream grouping width.
- `Group(width, strict=True)` — alias for `Chunk`.
- `Pad.left(width=..., block_size=..., byte=0)` — left-pad bytes.
- `Pad.right(width=..., block_size=..., byte=0)` — right-pad bytes.
- `Reverse()` — reverse byte order.

## Classic helper API

The original helper style is still supported:

```python
hx.from_hex("dead beef").bytes
# b'\xde\xad\xbe\xef'

hx.from_text("data").hex
# '64617461'

hx.convert("data", hx.Text, hx.HexString)
# '64617461'
```

Source helpers:

- `from_bytes(value)`
- `from_bytes_array(values)`
- `from_bytes_string(value)`
- `from_hex(value)`
- `from_hex_array(values)`
- `from_hex_int(value)`
- `from_int(value)`
- `from_int_array(values, width=...)`
- `from_text(value)`
- `from_binary(value)`
- `from_base64(value)`
- `from_base32(value)`
- `from_base85(value)`
- `from_ascii85(value)`
- `from_escaped(value)`
- `from_hexdump(value)`
- `from_struct(value, fmt=...)`
- `from_bits(value)`
- `from_auto(value)`
- `infer(value)`

Common `Value` outputs:

- `to_bytes()`
- `to_bytearray()`
- `to_bytes_array()`
- `to_bytes_string()`
- `to_hex(sep="", prefix=False, uppercase=False)`
- `to_hex_array(width=1, prefix=False, uppercase=False)`
- `to_hex_numbers(width=1)`
- `to_int(endian="big", signed=False)`
- `to_int_array(width=1, endian="big", signed=False)`
- `to_text(encoding="ascii", errors="strict")`
- `to_binary(sep="")`
- `to_base64()`
- `to_base32()`
- `to_base85()`
- `to_ascii85()`
- `to_escaped()`
- `to_hexdump()`
- `to_struct(fmt)`
- `to_bits()`
- `to(format_spec)`
