Metadata-Version: 2.4
Name: charded
Version: 1.0.3
Summary: charded
Author-email: Alex Kalaverin <alex@kalaver.in>
Project-URL: Homepage, https://kalaver.in
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Requires-Python: <3.14,>=3.12
Description-Content-Type: text/markdown
Requires-Dist: charset-normalizer>=3.4.7
Requires-Dist: kain<2,>=1.1.4
Requires-Dist: python-magic>=0.4.27

---
title: charded
description: Type-safe, generic string/bytes container with charset detection
---

[ref: #charded]

# charded

`charded` is a small Python library for working with `str` and `bytes` as a
single, type-safe container. It detects charsets automatically, converts
lazily between text and bytes, and proxies the familiar `str` API so the
wrapper feels invisible.

It gives you four public entry points:

- `Str` — a generic `str | bytes` container.
- `to_text` — convert `str` or `bytes` to text.
- `to_bytes` — convert `str` or `bytes` to bytes.
- `to_ascii` — decode bytes to text with a default ASCII charset.

[ref: #installation]

## Installation

Install with `uv`:

```bash
uv add charded
```

Or with any PEP 517-compatible tool:

```bash
pip install charded
```

[ref: #str]

## Wrapping text and bytes with `Str`

`Str[T]` is parameterized by `T`, which is either `str` or `bytes`. Pass any
`str` or `bytes` object and read it back through `str()`, `bytes()`,
`.string`, or `.bytes`.

```python
from charded import Str

s = Str(b"hello")
assert str(s) == "hello"
assert bytes(s) == b"hello"
assert s.string == "hello"
assert s.bytes == b"hello"

t = Str("héllo")
assert str(t) == "héllo"
assert bytes(t) == b"h\xc3\xa9llo"
```

[ref: #charset-detection]

## Charset detection

`Str` detects the content charset using `charset-normalizer`, internal
heuristics, and an adaptive binary-offset scan. Access `.charset` to see the
result.

```python
from charded import Str

assert Str(b"hello").charset == "ascii"
assert Str(b"hello\x00world").charset == "bin"
```

The detection order is:

1. Explicit `charset=` argument if provided.
2. Result from `charset-normalizer`.
3. Internal heuristic scan.
4. Fallback to ASCII for bytes or a compatible charset for text.

[ref: #mime-detection]

## MIME type detection

`Str.mime` uses `python-magic` to sniff the content type and description.
It returns `None` when detection fails.

```python
from charded import Str

mime = Str(b'{"a": 1}').mime
assert mime is not None
assert mime["type"] == "application/json"
```

[ref: #string-api]

## Using `Str` as a string

`Str` proxies common `str` operations, so you can slice, iterate, compare,
and call most `str` methods directly.

```python
from charded import Str

s = Str(b"hello world")

assert len(s) == 11
assert s[0:5] == "hello"
assert s.startswith("hello")
assert s.split() == ["hello", "world"]
assert s.upper() == "HELLO WORLD"
assert "world" in s
```

[ref: #tokenize]

## Tokenizing with `Str.tokenize`

`Str.tokenize` splits the string using a regular expression. Capturing groups
are not allowed; use non-capturing groups `(?:...)` instead.

```python
from charded import Str

s = Str("foo-bar_baz, qux")
assert s.tokenize(r"[a-z]+") == ("foo", "bar", "baz", "qux")
```

[ref: #utility-functions]

## Utility functions

`to_text`, `to_bytes`, and `to_ascii` perform quick conversions without
creating a persistent `Str` instance.

```python
from charded import to_ascii, to_bytes, to_text

assert to_text(b"hello") == "hello"
assert to_bytes("hello") == b"hello"
assert to_ascii(b"hello") == "hello"
```

[ref: #type-safety]

## Type safety and aliases

`Str` is a true generic. Use `Str[bytes]` or `Str[str]` to constrain input
and let type checkers catch mismatches at compile time.

```python
from charded import Str


def process(data: Str[bytes]) -> str:
    return data.string.upper()


assert process(Str(b"hello")) == "HELLO"
```

Convenience aliases are available in `charded.string`:

```python
from charded.string import StrBytes, StrText

raw: StrBytes = Str(b"hello")
text: StrText = Str(raw.string)

assert text.string == "hello"
```

[ref: #full-example]

## Full example

```python
from charded import Str, to_bytes, to_text

raw = b"h\xc3\xa9llo"

s = Str(raw)
assert str(s) == "héllo"
assert bytes(s) == raw
assert s.charset is not None
assert s.startswith("hél")

# Quick conversions without wrapping
assert to_text(raw) == "héllo"
assert to_bytes("héllo") == raw
```
