Metadata-Version: 2.4
Name: edn
Version: 0.0.1.dev1
Summary: Pure python edn library
Author: Tom Brand
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Maintainer: Tom Brand
Requires-Python: >=3.12
Project-URL: Homepage, https://github.com/tom025/pyedn
Project-URL: Repository, https://github.com/tom025/pyedn
Project-URL: Issues, https://github.com/tom025/pyedn/issues
Description-Content-Type: text/markdown

# pyedn

A zero-dependency [EDN](https://github.com/edn-format/edn) (Extensible Data
Notation) library for Python. It reads EDN text into Python objects
(`loads`) and writes Python objects back to EDN text (`dumps`).

## Motivation

EDN is the data format used by Clojure and a number of tools in that
ecosystem (e.g. Datomic, Babashka, `deps.edn`). Python code that needs to
talk to those tools currently reaches for
[`edn-format`](https://github.com/swaroopch/edn_format), but that library
depends on `ply` for parsing — a project that is no longer maintained and
now gets flagged by dependency and vulnerability scanners.

pyedn exists to be a drop-in-spirit replacement: same job, no dependency
baggage.

## Design decisions

**Zero runtime dependencies.** Pure standard-library Python.
This is the entire reason the project exists, so it's treated as
non-negotiable rather than an aspiration — no runtime dependency is ever
added, including a parser generator. Dev/test tooling (e.g. `pytest`) is
fine, since it doesn't ship with the library.

**Hand-written reader, no parser generator.** The reader is a tokenizer
plus a recursive-descent parser, written by hand instead of generated from
a grammar. EDN is specified as a *reader*, and tagged elements (`#inst`,
`#uuid`, and arbitrary user tags) need custom dispatch logic that fits
hand-written code more naturally than a static grammar would.

**One type model for both directions.** `loads` and `dumps` share the same
classes and the same tag registry, rather than maintaining separate
read-side and write-side representations that could drift apart.

**Round-trip integrity.** `loads(dumps(x)) == x` is expected to hold for
every supported type. A broken round-trip is treated as a bug, not an
edge case.

## Type mapping

| EDN | Python |
|---|---|
| `nil` | `None` |
| `true` / `false` | `True` / `False` |
| integers | `int` |
| floats | `float` |
| `N`-suffixed integers | `int` (bigint) |
| exact decimals (`M`) / ratios | open decision (`Decimal` / `Fraction`) |
| strings | `str` |
| characters | `Char` type |
| keywords | `Keyword` type |
| symbols | `Symbol` type |
| lists `()` / vectors `[]` | open decision (kept distinct, or collapsed) |
| maps `{}` | `dict` (EDN allows any value as a key, unlike JSON) |
| sets `#{}` | `set` / `frozenset` |
| tagged elements `#tag value` | dispatched via the tag registry (`#inst`, `#uuid` built in) |

Items marked "open decision" are deliberately undecided — each will be
settled by a concrete test case when the library reaches that
requirement, rather than guessed at up front.

## Status

Early / pre-implementation. See `CLAUDE.md` for the working conventions
this project follows (test-driven, one small step at a time).

## Testing

```
pytest
```

Tests live in `tests/`.
