Metadata-Version: 2.4
Name: hexconv
Version: 0.2.3
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: pytest; 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
```

## Quick tour

Use direct helpers when you want one obvious conversion:

```python
import hexconv as hx

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

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

hx.from_bytes_array([0xde, 0xad, 0xbe, 0xef]).to_hex()
# 'deadbeef'

hx.from_bytes_array([0xde, 0xad, 0xbe, 0xef]).hex
# 'deadbeef'

hx.from_text("data").to_hex_array(prefix=True)
# ['0x64', '0x61', '0x74', '0x61']

hx.from_int(0xdeadbeef).to_bytes()
# b'\xde\xad\xbe\xef'

hx.from_int_array([0x1234, 0x5678], width=2).to_hex_array(width=2)
# ['1234', '5678']
```

Use `from_auto` when you want a quick best-effort parse:

```python
hx.from_auto("0xdeadbeef").bytes
# b'\xde\xad\xbe\xef'

hx.from_auto(b"\xde\xad").hex
# 'dead'

hx.from_auto([0xde, 0xad, 0xbe, 0xef]).to(hx.Hex(prefix=True))
# '0xdeadbeef'
```

## Composable interface

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

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

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

hx.Int(width=4, endian="little")(0x12345678).hex
# '78563412'
```

Formats compose with `>>`:

```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'
```

Transforms sit between formats when data needs shaping:

```python
words = hx.Hex() >> hx.Chunk(2) >> hx.IntArray(width=2)
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']
```

## Configurable one-shot conversions

Use `convert` when you want an explicit source and target in one expression:

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

hx.convert("data", hx.Text(), hx.Hex(prefix=True))
# '0x64617461'

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

hx.convert("-_8", hx.Base64(urlsafe=True, padding=False), hx.Hex())
# 'fbff'
```

Hex strings may include common separators, so you do not need to manually strip
spaces, colons, dashes, or `0x` prefixes before converting.

## Common conversion patterns

Text, bytes, and hex:

```python
hx.Text()("hello").bytes
# b'hello'

hx.Hex()("68656c6c6f").text
# 'hello'

hx.BytesArray()([0x68, 0x69]).to(hx.Text())
# 'hi'
```

Integer packing:

```python
hx.Int(width=2)(0x1234).hex
# '1234'

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

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

Base encodings:

```python
hx.Text()("data").to(hx.Base64())
# 'ZGF0YQ=='

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

hx.Base64(urlsafe=True, padding=False)("-_8").hex
# 'fbff'
```

Escaped strings, hexdumps, structs, and bits:

```python
hx.Escaped()(r"\xde\xad").hex
# 'dead'

dump = hx.Hex()("deadbeef").to(hx.Hexdump(width=2))
hx.Hexdump()(dump).hex
# 'deadbeef'

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

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

hx.Bits()([1, 0, 1]).hex
# '05'
```

## 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("0b101", explain=True)

result.value.hex
# '05'
```

## 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:

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

## 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(width=2)
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", from_=hx.Text, to=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)`
