Metadata-Version: 2.4
Name: meridian-euid
Version: 0.4.5
Summary: Non-normative reference implementation of Meridian Enterprise Unique Identifiers (EUIDs).
Author: Meridian contributors
License-Expression: MIT
Project-URL: Homepage, https://github.com/lsmc-bio/meridian-euid
Project-URL: Repository, https://github.com/lsmc-bio/meridian-euid
Project-URL: Documentation, https://github.com/lsmc-bio/meridian-euid/blob/main/reference-implementations/python/README.md
Project-URL: Issues, https://github.com/lsmc-bio/meridian-euid/issues
Keywords: euid,identifier,checksum,mod32,crockford-base32,barcode,label
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: mypy>=1.10; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"
Dynamic: license-file

# meridian-euid (Python)

[![Python CI](https://github.com/lsmc-bio/meridian-euid/actions/workflows/python-ci.yml/badge.svg)](https://github.com/lsmc-bio/meridian-euid/actions/workflows/python-ci.yml)
[![GitHub Release](https://img.shields.io/github/v/release/lsmc-bio/meridian-euid?style=flat-square&label=release)](https://github.com/lsmc-bio/meridian-euid/releases/latest)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE)

Python reference implementation of the [Meridian EUID specification](../../SPEC.md).

The v0.4.0 line is strict and canonical:

- one syntax: `DOMAIN-PREFIX-BODYCHECKSUM`
- one validation mode
- no sandbox aliases
- no absent-domain form
- no tolerant normalization

## Requirements

- Python 3.10+

## Installation

### From source (development)

```bash
git clone https://github.com/lsmc-bio/meridian-euid.git
cd meridian-euid/reference-implementations/python

pip install -e .
pip install -e ".[dev]"
```

## CLI Usage

The `meridian-euid` command provides four subcommands.

### validate

Validate a canonical EUID.

```bash
meridian-euid validate A1-Z9-3V9
```

### encode

Encode an integer as a canonical EUID.

```bash
meridian-euid encode 123 Z9 --domain-code A1
```

| Argument/Option | Required | Description |
|-----------------|----------|-------------|
| `integer` | Yes | Integer in `1..9223372036854775807` |
| `prefix` | Yes | Prefix token (1-4 uppercase Crockford Base32 characters) |
| `--domain-code` | Yes | Domain token (1-4 uppercase Crockford Base32 characters) |

### parse

Parse a validated EUID into its components.

```bash
meridian-euid parse A1-Z9-3V9
```

### compute-check

Compute the check character for a payload.

```bash
# Payload is DOMAIN + PREFIX + BODY, with no hyphens
meridian-euid compute-check A1Z93V
```

## Programmatic API

### Canonical functions

```python
from meridian_euid import (
    BIGINT_MAX,
    compute_check_character,
    encode,
    is_valid,
    parse,
    validate,
)

assert validate("A1-Z9-3V9") == "A1-Z9-3V9"
assert is_valid("A1-Z9-3V9") is True
assert encode(123, "Z9", domain_code="A1") == "A1-Z9-3V9"
assert parse("A1-Z9-3V9") == {
    "domain_code": "A1",
    "prefix": "Z9",
    "body": "3V",
    "checksum": "9",
    "integer": 123,
}
assert compute_check_character("A1Z93V") == "9"
assert BIGINT_MAX == 9223372036854775807
```

### Governance helpers

```python
from meridian_euid import (
    assert_prefix_issuer_app_code,
    assert_registered_domain,
    load_domain_registry,
    load_prefix_ownership_registry,
    resolve_prefix_issuer_app_code,
    validate_issuer_app_code,
    validate_registries_consistent,
)

domains = load_domain_registry()
ownership = load_prefix_ownership_registry()

assert_registered_domain("A1", registry=domains)
assert validate_issuer_app_code("meridian.lims") == "meridian.lims"
assert resolve_prefix_issuer_app_code("A1", "Z9", registry=ownership) == "meridian.lims"
assert_prefix_issuer_app_code("A1", "Z9", "meridian.lims", registry=ownership)
validate_registries_consistent()
```

When called without `path=...`, the governance helpers load the bundled registry JSON files shipped inside the wheel. Pass `path=` to point at an alternate registry file.

`issuer_app_code` values are governance tokens, not part of the canonical EUID grammar. They MUST match:

`^[a-z0-9]+(?:[._-][a-z0-9]+)*$`

### Exceptions

```python
from meridian_euid import (
    EUIDChecksumError,
    EUIDDomainCodeError,
    EUIDError,
    EUIDFormatError,
    EUIDGovernanceError,
    EUIDIssuerAppCodeError,
    EUIDPrefixOwnershipError,
)
```

| Exception | Raised when |
|-----------|-------------|
| `EUIDFormatError` | Canonical syntax, prefix, BODY, or checksum-token validation fails |
| `EUIDChecksumError` | The checksum does not match |
| `EUIDDomainCodeError` | The domain token is syntactically invalid |
| `EUIDGovernanceError` | Registry-backed governance checks fail |
| `EUIDIssuerAppCodeError` | An `issuer_app_code` fails grammar validation |
| `EUIDPrefixOwnershipError` | A prefix is unclaimed or claimed by a different `issuer_app_code` |

## Running Tests

```bash
pytest --cov --cov-report=term-missing
ruff check meridian_euid tests
mypy meridian_euid --ignore-missing-imports
```

Tests use shared vectors from [test-vectors/v3.json](../../test-vectors/v3.json).

## Related Documentation

- [SPEC.md](../../SPEC.md) — Normative specification
- [domain_code_registry.json](../../domain_code_registry.json)
- [prefix_ownership_registry.json](../../prefix_ownership_registry.json)
- [checksum_elaboration.md](../../checksum_elaboration.md)
