mrrc 0.9.1

A Rust library for reading, writing, and manipulating MARC bibliographic records in ISO 2709 binary format
Documentation
[build-system]
requires = ["maturin>=1.0,<2.0"]
build-backend = "maturin"

[project]
name = "mrrc"
# The version comes from [workspace.package] in the root Cargo.toml:
# maturin reads src-python/Cargo.toml (manifest-path below), which
# inherits the workspace version.
dynamic = ["version"]
description = "MRRC: A fast MARC library written in Rust with Python bindings"
readme = "README.md"
# PEP 639 license expression; the license classifier is omitted per PEP 639.
license = "MIT"
authors = [
    {name = "Daniel Chudnov"}
]
requires-python = ">=3.10"
keywords = ["marc", "bibliographic", "data", "rust"]
classifiers = [
    "Development Status :: 3 - Alpha",
    "Intended Audience :: Developers",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
    "Programming Language :: Python :: 3.14",
    "Topic :: Software Development :: Libraries",
    "Typing :: Typed",
]

[project.urls]
Homepage = "https://dchud.github.io/mrrc/"
Documentation = "https://dchud.github.io/mrrc/"
Repository = "https://github.com/dchud/mrrc"
Changelog = "https://github.com/dchud/mrrc/blob/main/CHANGELOG.md"
Issues = "https://github.com/dchud/mrrc/issues"

[project.optional-dependencies]
# Runtime test dependencies — used by the wheel-test CI workflows via
# `mrrc[test]` so the dep list has a single source of truth and any future
# test dep added here is automatically picked up by CI.
test = [
    "pytest>=7.0",
    "pytest-benchmark>=4.0",
    "syrupy>=4.0",
    # tomllib is stdlib on 3.11+; tomli is the upstream backport for 3.10.
    # The error-coverage harness reads tests/error_coverage.toml at import
    # time and falls back to tomli when running under Python 3.10.
    "tomli>=2.0; python_version < '3.11'",
]
# Developer tooling (linters, type checkers) on top of the test deps.
# Self-references `mrrc[test]` so the test deps stay listed in exactly one
# place above; pip and uv both resolve self-referential extras correctly.
dev = [
    "mrrc[test]",
    "maturin>=1.0,<2.0",
    "mypy>=1.0",
    "pyright>=1.1",
    "ruff>=0.1",
]
benchmark = [
    "pytest>=7.0",
    "pytest-benchmark>=4.0",
    "tomli>=2.0; python_version < '3.11'",
]
docs = [
    "mkdocs-material>=9.0",
    "mkdocstrings[python]>=0.24",
    "pymdown-extensions>=10.0",
]
# pymarc is the oracle the parity tests compare against. Managed as an
# extra so the version is locked in uv.lock and Dependabot bumps it on
# the normal cadence: a pymarc release that changes behavior fails the
# oracle on the bump PR, where the divergence gets adjudicated (adopt
# the new behavior or document the deliberate divergence) instead of
# being discovered by users.
oracle = [
    "pymarc>=5.3",
]

[tool.maturin]
python-packages = ["mrrc"]
module-name = "mrrc._mrrc"
manifest-path = "src-python/Cargo.toml"
# Keep repo-operational files (issue tracker, CI config, docs site,
# scratch space) out of the sdist; they are not needed to build or test
# the package from source.
exclude = [
    { path = ".beads/**", format = "sdist" },
    { path = ".github/**", format = "sdist" },
    { path = ".githooks/**", format = "sdist" },
    { path = "docs/**", format = "sdist" },
    { path = "site/**", format = "sdist" },
    { path = "tmp/**", format = "sdist" },
]

[tool.ruff]
target-version = "py310"
# PEP 8's 79-column limit for Python. `ruff format` reflows code to fit;
# CHANGELOG.md keeps its own separate 100-column Markdown lint.
line-length = 79

[tool.ruff.lint]
# Defaults cover only E4/E7/E9 + F; select the broader set so the
# linter actually exercises the code. Both lint.yml and check.sh run
# bare `ruff check`, which picks this configuration up automatically.
select = [
    "E",   # pycodestyle errors
    "W",   # pycodestyle warnings
    "F",   # pyflakes
    "I",   # isort import ordering
    "UP",  # pyupgrade
    "B",   # flake8-bugbear
    "SIM", # flake8-simplify
    "RUF", # ruff-specific rules
]
# Line length is owned by the formatter, not the linter. `ruff format`
# reflows code to the 79-column limit; the only lines it leaves long are
# docstrings, comments, and atomic string literals it won't break.
# Enforcing E501 on top would just nag about those with no autofix, so the
# formatter is the single source of line-length truth.
ignore = ["E501"]
# Characters that look like ASCII but are legitimate content in a
# bibliographic library: en dash and multiplication sign in prose, and
# the ALA-LC romanization modifier letter (ʻ) in MARC test data.
allowed-confusables = ["", "×", "ʻ"]

[tool.pytest.ini_options]
testpaths = ["tests/python"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
markers = [
    "benchmark: mark test as a benchmark (deselect with '-m \"not benchmark\"')",
    "slow: mark test as slow running (deselect with '-m \"not slow\"')",
]
# Note: benchmark addopts moved to explicit --benchmark-* flags when running benchmarks
# Default runs skip benchmarks for fast feedback

[tool.mypy]
python_version = "3.10"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = false
strict = false

[tool.pyright]
pythonVersion = "3.10"
typeCheckingMode = "basic"