Metadata-Version: 2.4
Name: ansi-x12
Version: 0.1.2
Summary: A small, framework-independent structural parser for ANSI X12 interchange documents.
Keywords: ansi-x12,edi,parser,x12
Author: Fifoa Labs
Author-email: Fifoa Labs <labs@fifoa.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
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: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Project-URL: Homepage, https://github.com/fifoa-labs/x12
Project-URL: Repository, https://github.com/fifoa-labs/x12
Project-URL: Issues, https://github.com/fifoa-labs/x12/issues
Description-Content-Type: text/markdown

# x12

A small, framework-independent Python library for lossless structural
parsing of ANSI X12 Electronic Data Interchange documents.

`x12` discovers interchange separators, tokenizes raw messages,
validates envelope structure, and produces immutable inspection
summaries without interpreting transaction-specific business meaning.

## Status

`ansi-x12` is actively maintained and publicly available on PyPI.

- **PyPI:** https://pypi.org/project/ansi-x12/
- **Source:** https://github.com/fifoa-labs/x12

Install the latest release:

```bash
pip install ansi-x12
```

Import it in Python:

``` python
import x12
```

The current release provides the structural layer of ANSI X12:

-   separator discovery
-   lossless tokenization
-   immutable segment models
-   ISA/GS/ST envelope parsing
-   control-number validation
-   declared-count validation
-   structural inspection

The package currently has:

-   no runtime dependencies
-   456 passing tests
-   100% statement coverage
-   100% branch coverage
-   full inline type annotations

## Installation

Install the latest release from PyPI:

``` bash
pip install ansi-x12
```

The distribution name is `ansi-x12`.

Import it in Python:

``` python
import x12
```

For local development:

``` bash
git clone https://github.com/fifoa-labs/x12.git
cd x12
uv sync --dev
```

## Quick Start

``` python
from pathlib import Path

from x12 import (
    inspect_x12_interchange,
    parse_x12_interchange,
    tokenize_x12,
)

payload = Path("message.x12").read_bytes()

document = tokenize_x12(payload)
interchange = parse_x12_interchange(document)
inspection = inspect_x12_interchange(interchange)
```

The resulting objects are immutable and preserve the original
byte-oriented structure of the interchange.

## Core Design

The package is intentionally layered:

``` text
Raw bytes
    │
    ▼
Separator discovery
    │
    ▼
Tokenizer
    │
    ▼
Immutable segment document
    │
    ▼
Envelope parser
    │
    ▼
Validated interchange
    │
    ▼
Structural inspector
```

Each layer has one responsibility and can be used independently.

## Design Goals

-   ANSI X12 generic
-   transaction-set agnostic
-   trading-partner agnostic
-   framework independent
-   lossless
-   deterministic
-   immutable
-   byte-oriented
-   fully typed
-   easy to audit
-   thoroughly tested

## What `x12` Does

`x12` handles structural concerns common to ANSI X12 interchanges:

-   Reads separator characters from the fixed-width ISA segment
-   Supports custom element, repetition, component, and segment
    separators
-   Preserves empty positional elements
-   Preserves element values as raw bytes
-   Preserves source segment order
-   Builds immutable document and envelope models
-   Parses ISA/IEA interchange envelopes
-   Parses GS/GE functional groups
-   Parses ST/SE transaction sets
-   Validates envelope boundary ordering
-   Validates matching control numbers
-   Validates declared group, transaction, and segment counts
-   Produces structural inventories and segment-frequency summaries

## What `x12` Does Not Do

`x12` intentionally does not interpret business semantics.

It does not:

-   interpret specific transaction sets
-   map application fields
-   understand industry workflows
-   persist data
-   perform database operations
-   depend on Django, Flask, FastAPI, or another framework
-   validate implementation-guide-specific business rules
-   convert transaction content into domain models

For example, `x12` can identify and validate a transaction set whose
ST01 value is `322`, but it does not interpret the meaning of its `Q5`,
`N7`, `R4`, or other business segments.

Higher-level packages should build transaction-specific behavior on top
of the structural models provided here.

## Package Layout

``` text
x12/
├── src/
│   └── x12/
│       ├── __init__.py
│       ├── envelopes.py
│       ├── exceptions.py
│       ├── inspection.py
│       ├── inspector.py
│       ├── parser.py
│       ├── py.typed
│       ├── segments.py
│       ├── separators.py
│       └── tokenizer.py
├── tests/
│   ├── fixtures/
│   │   └── sample_message
│   ├── test_envelopes.py
│   ├── test_exceptions.py
│   ├── test_init.py
│   ├── test_inspection.py
│   ├── test_inspector.py
│   ├── test_parser.py
│   ├── test_sample_message.py
│   ├── test_segments.py
│   ├── test_separators.py
│   └── test_tokenizer.py
├── LICENSE
├── Makefile
├── README.md
├── pyproject.toml
└── uv.lock
```

## Module Responsibilities

### `separators.py`

Discovers control characters from the fixed-width ISA header.

``` python
from x12 import derive_x12_separators

separators = derive_x12_separators(payload)

print(separators.element)
print(separators.repetition)
print(separators.component)
print(separators.segment)
```

The returned `X12Separators` object contains:

-   `element`
-   `repetition`
-   `component`
-   `segment`

For interchange version `00402` and later, ISA11 is exposed as the
repetition separator. Earlier versions return `None` for `repetition`.

### `tokenizer.py`

Converts raw X12 bytes into an immutable `X12Document`.

``` python
from x12 import tokenize_x12

document = tokenize_x12(payload)
```

The tokenizer:

-   derives separators from ISA
-   splits the payload into segments
-   preserves empty elements
-   preserves raw element bytes
-   preserves segment order
-   assigns contiguous zero-based segment indexes
-   ignores permitted formatting whitespace between segments
-   rejects malformed segment identifiers
-   rejects incomplete documents

It performs no transaction-specific interpretation.

### `segments.py`

Defines the core tokenized models:

-   `X12Segment`
-   `X12Document`

A segment exposes its elements using one-based X12 positions:

``` python
segment = document.find_segments("ST")[0]

assert segment.element(1) == b"999"
assert segment.element(2) == b"0001"
assert segment.element(3) is None
```

Empty and missing elements are distinct:

``` python
assert segment.element(1) == b""
assert segment.element(20) is None
```

`X12Document` supports direct iteration and length:

``` python
for segment in document:
    print(segment.index, segment.tag)

print(len(document))
```

### `envelopes.py`

Defines immutable envelope models:

-   `X12TransactionSet`
-   `X12FunctionalGroup`
-   `X12Interchange`

These objects organize the flat token stream into the standard X12
envelope hierarchy:

``` text
ISA
└── GS
    └── ST
        ├── transaction body
        └── SE
    └── GE
└── IEA
```

They expose convenience properties for common envelope values,
including:

-   transaction-set code
-   envelope control numbers
-   implementation version
-   declared counts
-   actual counts
-   complete ordered segment collections

### `parser.py`

Converts an `X12Document` into a validated `X12Interchange`.

``` python
from x12 import parse_x12_interchange

interchange = parse_x12_interchange(document)
```

Validation includes:

-   ISA as the first segment
-   IEA as the final segment
-   GS/GE functional-group boundaries
-   ST/SE transaction-set boundaries
-   exact envelope element counts
-   required envelope elements
-   matching ST02 and SE02
-   matching GS06 and GE02
-   matching ISA13 and IEA02
-   SE01 transaction segment count
-   GE01 transaction-set count
-   IEA01 functional-group count
-   invalid nested envelope segments

The parser validates envelope structure only.

### `inspection.py`

Defines immutable inspection result models:

-   `X12SegmentFrequency`
-   `X12TransactionInspection`
-   `X12FunctionalGroupInspection`
-   `X12InspectionResult`

These models provide a stable representation of structural metadata and
document inventories.

### `inspector.py`

Builds an `X12InspectionResult` from a validated interchange.

``` python
from x12 import inspect_x12_interchange

inspection = inspect_x12_interchange(interchange)
```

Inspection data includes:

-   interchange version
-   interchange control number
-   usage indicator
-   separators
-   functional groups
-   transaction-set codes
-   transaction counts
-   segment counts
-   ordered segment tags
-   unique segment tags
-   repeating segment tags
-   segment frequencies

Example:

``` python
print(inspection.transaction_set_codes)
print(inspection.total_segment_count)
print(inspection.unique_segment_tags)
print(inspection.repeating_segment_tags)
```

## Public API

The main package-level imports are:

``` python
from x12 import (
    X12Document,
    X12EnvelopeError,
    X12Error,
    X12FunctionalGroup,
    X12FunctionalGroupInspection,
    X12InspectionResult,
    X12Interchange,
    X12Segment,
    X12SegmentError,
    X12SegmentFrequency,
    X12SeparatorError,
    X12Separators,
    X12TokenizerError,
    X12TransactionInspection,
    X12TransactionSet,
    derive_x12_separators,
    inspect_x12_interchange,
    parse_x12_interchange,
    tokenize_x12,
)
```

Most applications only need:

``` python
from x12 import (
    inspect_x12_interchange,
    parse_x12_interchange,
    tokenize_x12,
)
```

## Exception Hierarchy

``` text
X12Error
├── X12EnvelopeError
│   └── X12SeparatorError
└── X12TokenizerError
    └── X12SegmentError
```

Example:

``` python
from x12 import X12Error

try:
    document = tokenize_x12(payload)
    interchange = parse_x12_interchange(document)
except X12Error as exc:
    print(f"Invalid X12 document: {exc}")
```

Use the specialized exception types when callers need to distinguish
between separator, tokenizer, segment, and envelope failures.

## Byte-Oriented API

The public parsing API accepts `bytes`, not text strings.

``` python
payload = path.read_bytes()
document = tokenize_x12(payload)
```

This is intentional.

X12 separators are single-byte structural values, and fixed-width ISA
offsets are defined at the byte level. Keeping the parser byte-oriented
avoids accidental decoding, normalization, or whitespace changes before
structural parsing is complete.

Applications may decode individual elements later using the encoding
appropriate for their trading partner or implementation guide.

## Immutability

All core models are frozen dataclasses with slots.

This includes:

-   separators
-   segments
-   documents
-   transaction sets
-   functional groups
-   interchanges
-   inspection results

Immutability makes parsed results:

-   deterministic
-   hashable
-   safe to share
-   difficult to modify accidentally
-   easier to reason about during validation and testing

## Type Information

The package ships with a `py.typed` marker and inline type annotations.

Type checkers can use the installed package directly:

``` python
from x12 import X12Interchange, parse_x12_interchange
```

The project is checked with mypy in strict mode.

## Development

This project uses:

-   uv
-   pytest
-   pytest-cov
-   pytest-xdist
-   Ruff
-   mypy
-   build
-   Twine

Install development dependencies:

``` bash
make sync
```

Run the test suite:

``` bash
make test
```

Run tests in parallel:

``` bash
make test-fast
```

Run coverage:

``` bash
make coverage
```

Run linting:

``` bash
make lint
```

Check formatting:

``` bash
make format-check
```

Apply formatting and safe fixes:

``` bash
make format
```

Run type checking:

``` bash
make typecheck
```

Run the complete local validation suite:

``` bash
make check
```

Run the complete release validation:

``` bash
make release-check
```

## Testing

The test suite covers:

-   separator extraction
-   custom separators
-   legacy and modern ISA versions
-   malformed fixed-width ISA segments
-   lossless tokenization
-   empty positional elements
-   inter-segment formatting whitespace
-   invalid segment identifiers
-   immutable model invariants
-   envelope ordering
-   missing envelope boundaries
-   nested envelope failures
-   envelope control-number matching
-   declared-count validation
-   inspection summaries
-   segment-frequency ordering
-   generic complete-message fixture parsing
-   package public API exports
-   wheel-safe type metadata

Current release quality:

``` text
456 tests passed
597 statements covered
158 branches covered
100% statement coverage
100% branch coverage
```

The test fixture is synthetic and generic. It contains no production
customer, carrier, location, phone, shipment, or equipment information.

## Building the Package

Build both the source distribution and wheel:

``` bash
make build
```

Validate distribution metadata:

``` bash
make check-dist
```

Inspect the wheel contents:

``` bash
make wheel-contents
```

Install the wheel into a temporary clean environment:

``` bash
make install-wheel
```

Releases are published automatically through GitHub Actions using PyPI
Trusted Publishing.

The built wheel should include:

``` text
x12/__init__.py
x12/envelopes.py
x12/exceptions.py
x12/inspection.py
x12/inspector.py
x12/parser.py
x12/py.typed
x12/segments.py
x12/separators.py
x12/tokenizer.py
```

It should not include tests, development caches, coverage data, or
application-specific code.

## Extension Guidelines

Future additions may include:

-   serialization
-   message builders
-   diagnostic formatting
-   pretty printing
-   streaming tokenization
-   implementation-guide extension interfaces

Any addition to the core package should remain:

-   generic
-   structural
-   deterministic
-   framework independent
-   transaction-set agnostic

Transaction-specific parsers should live in separate packages or
higher-level application layers.

## Guiding Principle

If a feature requires knowing what a segment means, it does not belong
in the core `x12` package.

The core library understands structure.

Higher-level integrations understand meaning.

## License

MIT

------------------------------------------------------------------------

Built and maintained by **Fifoa Labs**.
