markinp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- markinp-0.1.0/.github/workflows/ci.yml +31 -0
- markinp-0.1.0/.github/workflows/release.yml +57 -0
- markinp-0.1.0/.gitignore +31 -0
- markinp-0.1.0/.pre-commit-hooks.yaml +7 -0
- markinp-0.1.0/CHANGELOG.md +25 -0
- markinp-0.1.0/CITATION.cff +23 -0
- markinp-0.1.0/CLAUDE.md +261 -0
- markinp-0.1.0/LICENSE +21 -0
- markinp-0.1.0/PKG-INFO +242 -0
- markinp-0.1.0/README.md +211 -0
- markinp-0.1.0/action.yml +50 -0
- markinp-0.1.0/conda-recipe/meta.yaml +57 -0
- markinp-0.1.0/docs/error-codes.md +71 -0
- markinp-0.1.0/markinp/__init__.py +47 -0
- markinp-0.1.0/markinp/build.py +314 -0
- markinp-0.1.0/markinp/cli.py +233 -0
- markinp-0.1.0/markinp/diagnostics.py +226 -0
- markinp-0.1.0/markinp/model.py +115 -0
- markinp-0.1.0/markinp/parse.py +257 -0
- markinp-0.1.0/markinp/py.typed +0 -0
- markinp-0.1.0/markinp/report.py +173 -0
- markinp-0.1.0/markinp/tokens.py +48 -0
- markinp-0.1.0/markinp/validate.py +191 -0
- markinp-0.1.0/markinp/write.py +40 -0
- markinp-0.1.0/plan.md +295 -0
- markinp-0.1.0/pyproject.toml +76 -0
- markinp-0.1.0/tests/__init__.py +0 -0
- markinp-0.1.0/tests/conftest.py +22 -0
- markinp-0.1.0/tests/fixtures/all_zero_history.inp +2 -0
- markinp-0.1.0/tests/fixtures/bom.inp +2 -0
- markinp-0.1.0/tests/fixtures/captures_long.csv +7 -0
- markinp-0.1.0/tests/fixtures/captures_wide.csv +3 -0
- markinp-0.1.0/tests/fixtures/content_after_semicolon.inp +2 -0
- markinp-0.1.0/tests/fixtures/covariate_count.inp +3 -0
- markinp-0.1.0/tests/fixtures/duplicates.inp +3 -0
- markinp-0.1.0/tests/fixtures/empty_file.inp +2 -0
- markinp-0.1.0/tests/fixtures/empty_group.inp +3 -0
- markinp-0.1.0/tests/fixtures/frequency_count.inp +3 -0
- markinp-0.1.0/tests/fixtures/illegal_history_char.inp +2 -0
- markinp-0.1.0/tests/fixtures/illegal_stratum.inp +3 -0
- markinp-0.1.0/tests/fixtures/missing_covariate.inp +3 -0
- markinp-0.1.0/tests/fixtures/missing_semicolon.inp +2 -0
- markinp-0.1.0/tests/fixtures/mixed_whitespace.inp +2 -0
- markinp-0.1.0/tests/fixtures/multistrata.inp +2 -0
- markinp-0.1.0/tests/fixtures/negative_frequency.inp +2 -0
- markinp-0.1.0/tests/fixtures/noninteger_frequency.inp +2 -0
- markinp-0.1.0/tests/fixtures/nonnumeric_frequency.inp +2 -0
- markinp-0.1.0/tests/fixtures/ragged_history.inp +3 -0
- markinp-0.1.0/tests/fixtures/unterminated_comment.inp +3 -0
- markinp-0.1.0/tests/fixtures/valid_comments.inp +4 -0
- markinp-0.1.0/tests/fixtures/valid_covariates.inp +3 -0
- markinp-0.1.0/tests/fixtures/valid_multi_group.inp +3 -0
- markinp-0.1.0/tests/fixtures/valid_single_group.inp +3 -0
- markinp-0.1.0/tests/test_build.py +96 -0
- markinp-0.1.0/tests/test_cli.py +120 -0
- markinp-0.1.0/tests/test_parse.py +77 -0
- markinp-0.1.0/tests/test_report.py +64 -0
- markinp-0.1.0/tests/test_validate.py +146 -0
- markinp-0.1.0/tests/test_write.py +46 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: ${{ matrix.os }} / Python ${{ matrix.python-version }}
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
16
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: ${{ matrix.python-version }}
|
|
22
|
+
- name: Install
|
|
23
|
+
run: python -m pip install -e ".[dev]"
|
|
24
|
+
- name: Lint
|
|
25
|
+
run: ruff check .
|
|
26
|
+
- name: Type check
|
|
27
|
+
run: mypy markinp
|
|
28
|
+
- name: Test
|
|
29
|
+
run: pytest
|
|
30
|
+
- name: Smoke-test the CLI
|
|
31
|
+
run: markinp --help
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publishes to PyPI via Trusted Publishing (OIDC) when a version tag is pushed
|
|
4
|
+
# or a GitHub Release is published. No API tokens are stored in the repo.
|
|
5
|
+
#
|
|
6
|
+
# One-time setup on PyPI: create the project's "pending publisher" pointing at
|
|
7
|
+
# owner: leonbzt
|
|
8
|
+
# repo: markinp
|
|
9
|
+
# workflow: release.yml
|
|
10
|
+
# environment: pypi
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
push:
|
|
14
|
+
tags: ["v*"]
|
|
15
|
+
release:
|
|
16
|
+
types: [published]
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
build:
|
|
23
|
+
name: Build distributions
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
- name: Build sdist and wheel
|
|
31
|
+
run: |
|
|
32
|
+
python -m pip install --upgrade build
|
|
33
|
+
python -m build
|
|
34
|
+
- name: Check metadata
|
|
35
|
+
run: |
|
|
36
|
+
python -m pip install --upgrade twine
|
|
37
|
+
twine check dist/*
|
|
38
|
+
- uses: actions/upload-artifact@v4
|
|
39
|
+
with:
|
|
40
|
+
name: dist
|
|
41
|
+
path: dist/
|
|
42
|
+
|
|
43
|
+
publish:
|
|
44
|
+
name: Publish to PyPI
|
|
45
|
+
needs: build
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
environment:
|
|
48
|
+
name: pypi
|
|
49
|
+
url: https://pypi.org/p/markinp
|
|
50
|
+
permissions:
|
|
51
|
+
id-token: write # required for Trusted Publishing
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/download-artifact@v4
|
|
54
|
+
with:
|
|
55
|
+
name: dist
|
|
56
|
+
path: dist/
|
|
57
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
markinp-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
*.egg
|
|
9
|
+
|
|
10
|
+
# Virtual environments
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
|
|
15
|
+
# Tooling caches
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.ruff_cache/
|
|
18
|
+
.mypy_cache/
|
|
19
|
+
.hypothesis/
|
|
20
|
+
.coverage
|
|
21
|
+
htmlcov/
|
|
22
|
+
|
|
23
|
+
# Editors / OS
|
|
24
|
+
.vscode/
|
|
25
|
+
.idea/
|
|
26
|
+
*.swp
|
|
27
|
+
.DS_Store
|
|
28
|
+
|
|
29
|
+
# Third-party MARK example data used for local corpus testing only.
|
|
30
|
+
# Kept out of the repo for licensing reasons; see README "Testing".
|
|
31
|
+
sample_data/
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/), and this project adheres to
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] — 2026-07-02
|
|
10
|
+
|
|
11
|
+
Initial release.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- Core library: `parse`, `validate`, `inspect`, `build`, `write`.
|
|
15
|
+
- CLI (`markinp`) with `validate`, `inspect`, and `build` commands;
|
|
16
|
+
`validate` accepts multiple files and exits non-zero if any fail.
|
|
17
|
+
- Human-readable and `--json` (schema version 1) output for every command.
|
|
18
|
+
- Diagnostic taxonomy MK001–MK020 and MK900 (partial-support detection),
|
|
19
|
+
documented in `docs/error-codes.md`.
|
|
20
|
+
- Deterministic `.inp` writer and `parse -> write -> parse` round-trip stability.
|
|
21
|
+
- Distribution: PyPI Trusted Publishing workflow, a reusable GitHub Action,
|
|
22
|
+
a pre-commit hook, and a Bioconda recipe.
|
|
23
|
+
|
|
24
|
+
[Unreleased]: https://github.com/leonbzt/markinp/compare/v0.1.0...HEAD
|
|
25
|
+
[0.1.0]: https://github.com/leonbzt/markinp/releases/tag/v0.1.0
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use markinp, please cite it as below."
|
|
3
|
+
title: "markinp: read, validate, and build Program MARK encounter-history (.inp) files"
|
|
4
|
+
type: software
|
|
5
|
+
authors:
|
|
6
|
+
- family-names: Botzenhardt
|
|
7
|
+
given-names: Leon
|
|
8
|
+
email: leon.botzenhardt@gmail.com
|
|
9
|
+
version: 0.1.0
|
|
10
|
+
license: MIT
|
|
11
|
+
repository-code: "https://github.com/leonbzt/markinp"
|
|
12
|
+
keywords:
|
|
13
|
+
- capture-recapture
|
|
14
|
+
- mark-recapture
|
|
15
|
+
- Program MARK
|
|
16
|
+
- encounter history
|
|
17
|
+
- ecology
|
|
18
|
+
abstract: >-
|
|
19
|
+
markinp is an independent, unofficial command-line utility and Python library
|
|
20
|
+
for reading, validating, and building Program MARK encounter-history (.inp)
|
|
21
|
+
files. It performs file I/O and validation only and does not fit models or
|
|
22
|
+
compute statistics. It is not affiliated with or endorsed by the authors of
|
|
23
|
+
Program MARK or RMark.
|
markinp-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Guidance for Claude Code (and any AI assistant) working in this repository.
|
|
4
|
+
Read this file **and** `plan.md` before making changes. `plan.md` holds the
|
|
5
|
+
scope, milestones, and the full error taxonomy; this file holds the rules,
|
|
6
|
+
the domain primer, and the commands.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. What this project is
|
|
11
|
+
|
|
12
|
+
`markio` is a small, well-tested command-line utility and Python library for
|
|
13
|
+
**reading, validating, and building Program MARK encounter-history (`.inp`)
|
|
14
|
+
files**. It removes the most common friction in the capture-recapture
|
|
15
|
+
workflow: hand-building `.inp` files in a text editor or Excel, and discovering
|
|
16
|
+
formatting mistakes only when MARK rejects the file with an unhelpful message.
|
|
17
|
+
|
|
18
|
+
It does **three** things and nothing more:
|
|
19
|
+
|
|
20
|
+
1. **validate** an existing `.inp` file and report precise, actionable errors.
|
|
21
|
+
2. **inspect** an `.inp` file and summarize its structure.
|
|
22
|
+
3. **build** a valid `.inp` file from a tidy capture table (CSV).
|
|
23
|
+
|
|
24
|
+
Target users: wildlife ecologists, conservation biologists, and fisheries
|
|
25
|
+
scientists who use Program MARK (directly or via RMark). Most are not
|
|
26
|
+
programmers. **UX and error-message quality are the product**, not an add-on.
|
|
27
|
+
|
|
28
|
+
> **Disclaimer to preserve in the README:** `markio` is an independent,
|
|
29
|
+
> unofficial utility. It is not affiliated with, endorsed by, or maintained by
|
|
30
|
+
> the authors of Program MARK or RMark. "MARK" is referenced only to describe
|
|
31
|
+
> the file format it interoperates with.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 2. Golden rules (non-negotiable)
|
|
36
|
+
|
|
37
|
+
1. **Never reimplement MARK's statistics.** This tool does file I/O and
|
|
38
|
+
validation only. No model fitting, no likelihoods, no estimation. Ever.
|
|
39
|
+
2. **Library-first.** Every capability lives in an importable function with a
|
|
40
|
+
clean signature and type hints. The CLI is a thin wrapper over the library
|
|
41
|
+
so that RMark users and pipelines can call the same code from Python.
|
|
42
|
+
3. **Errors are the product.** Every problem is reported as a structured
|
|
43
|
+
*diagnostic* with a stable code, a severity, the line number, a plain-English
|
|
44
|
+
message, and an actionable hint. Never raise a raw traceback at the user.
|
|
45
|
+
4. **Never silently change a user's data.** Report a diagnostic; only modify
|
|
46
|
+
data when the user explicitly asks (e.g. `--fix`, `--collapse`). Silent
|
|
47
|
+
"helpful" reinterpretation is forbidden — it is the exact failure mode we
|
|
48
|
+
are replacing.
|
|
49
|
+
5. **Deterministic output.** Given the same input, output bytes are identical.
|
|
50
|
+
Sort predictably. This keeps diffs clean and makes CI usable.
|
|
51
|
+
6. **Two output modes, always.** Human-readable by default; `--json` for
|
|
52
|
+
machines. Exit non-zero when errors are found so the tool works in CI.
|
|
53
|
+
7. **Keep dependencies minimal.** It must install cleanly on Linux, macOS, and
|
|
54
|
+
Windows and be packageable for Bioconda. Prefer the standard library. Do not
|
|
55
|
+
add a heavy dependency without recording the reason in `plan.md`.
|
|
56
|
+
8. **Respect the spec exactly.** When unsure about the `.inp` format, consult
|
|
57
|
+
Chapter 2 of the MARK "Gentle Introduction" book (the field's canonical
|
|
58
|
+
reference) and encode the rule as a test rather than guessing.
|
|
59
|
+
9. **No GUI.** Command line and library only.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 3. MARK `.inp` format primer (authoritative for this repo)
|
|
64
|
+
|
|
65
|
+
An `.inp` file is a plain-text file. Each record is one line, terminated by a
|
|
66
|
+
semicolon. Fields are separated by whitespace (spaces or tabs).
|
|
67
|
+
|
|
68
|
+
**Grammar of one record:**
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
[/* comment */] HISTORY FREQ_1 [FREQ_2 ... FREQ_g] [COV_1 ... COV_c] ; [/* comment */]
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- **HISTORY** — the encounter history, one character per sampling occasion.
|
|
75
|
+
For the standard format: `1` = detected/captured, `0` = not.
|
|
76
|
+
- **FREQ_1 … FREQ_g** — one integer frequency **per group** `g`. The frequency
|
|
77
|
+
is the count of individuals sharing this history in that group. A record for
|
|
78
|
+
a single individual uses `1` in its group's column and `0` in the others.
|
|
79
|
+
Frequencies may be **negative** to denote losses on capture (removals).
|
|
80
|
+
- **COV_1 … COV_c** — optional numeric individual covariates, `c` of them.
|
|
81
|
+
**Covariates cannot have missing values.**
|
|
82
|
+
- **`;`** — terminates the record. **The single most common user error is a
|
|
83
|
+
missing semicolon.**
|
|
84
|
+
- **Comments** are delimited by `/* ... */` and may appear anywhere; they are
|
|
85
|
+
frequently used at the start of a line to label the individual.
|
|
86
|
+
|
|
87
|
+
**Worked example** — 2 groups (e.g. Male/Female) and 1 covariate (weight):
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
/* ind 001 */ 1001 1 0 10;
|
|
91
|
+
/* ind 002 */ 1101 0 2 5;
|
|
92
|
+
0101 3 1 6;
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Record 1: history `1001`, group-1 freq `1`, group-2 freq `0`, covariate `10`.
|
|
96
|
+
Record 2: history `1101`, group-1 freq `0`, group-2 freq `2`, covariate `5`.
|
|
97
|
+
Record 3: history `0101`, group-1 freq `3`, group-2 freq `1`, covariate `6`.
|
|
98
|
+
|
|
99
|
+
**Cross-record invariants:**
|
|
100
|
+
|
|
101
|
+
- Every HISTORY must have the **same length** (= number of occasions).
|
|
102
|
+
- Every record must have the **same number of frequency columns** (= number of
|
|
103
|
+
groups) and the **same number of covariate columns**.
|
|
104
|
+
- The data type, number of occasions, groups, covariates, and strata are set by
|
|
105
|
+
the user when the MARK project is created; they are usually **not** stored in
|
|
106
|
+
the file itself. `markio` therefore *infers* them and lets the user assert
|
|
107
|
+
expected values with flags for stricter checking.
|
|
108
|
+
|
|
109
|
+
**Data types (scope note):**
|
|
110
|
+
|
|
111
|
+
- v0 fully supports the **standard 0/1 encounter-history format** used by live
|
|
112
|
+
recapture (CJS / Jolly-Seber) and closed-captures models. This is the same
|
|
113
|
+
subset RMark's `convert.inp` handles.
|
|
114
|
+
- **Known-fate** and **dead-recovery (Brownie)** use paired `LDLD…` columns;
|
|
115
|
+
**multistrata** uses letters for states. v0 should *detect* these and validate
|
|
116
|
+
what it safely can (structural checks), but full support is a later milestone.
|
|
117
|
+
Do not pretend to fully validate a format we have not implemented — say so in
|
|
118
|
+
a diagnostic.
|
|
119
|
+
|
|
120
|
+
**Encoding & line endings:** MARK is a Windows-origin tool and input often comes
|
|
121
|
+
from Excel, so files are commonly UTF-8 or Latin-1 with CRLF line endings, and
|
|
122
|
+
may carry a BOM. Read robustly; normalize internally; flag odd encodings.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 4. Architecture
|
|
127
|
+
|
|
128
|
+
Keep the library and the CLI strictly separated.
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
markio/
|
|
132
|
+
__init__.py # version, public API re-exports
|
|
133
|
+
model.py # dataclasses: Dataset, EncounterHistory, DataType, Diagnostic, Severity
|
|
134
|
+
parse.py # .inp text -> Dataset (the reader/decoder); tracks line numbers
|
|
135
|
+
validate.py # Dataset (+ optional asserted params) -> list[Diagnostic]
|
|
136
|
+
build.py # tidy CSV (long/wide) -> Dataset (the builder)
|
|
137
|
+
write.py # Dataset -> .inp text (the encoder), deterministic
|
|
138
|
+
report.py # render list[Diagnostic] as human text or JSON
|
|
139
|
+
cli.py # Typer app; thin wrappers that call the functions above
|
|
140
|
+
tests/
|
|
141
|
+
fixtures/ # small valid and invalid .inp files, one concern each
|
|
142
|
+
...
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Rule:** `cli.py` contains no domain logic. It parses arguments, calls library
|
|
146
|
+
functions, and hands results to `report.py`. Anything the CLI can do must be
|
|
147
|
+
doable in three lines of Python via the library.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 5. Tech stack & commands
|
|
152
|
+
|
|
153
|
+
- **Language:** Python 3.10+ (use modern typing: `list[str]`, `X | None`).
|
|
154
|
+
- **CLI:** [Typer](https://typer.tiangolo.com/) (auto-generates `--help`).
|
|
155
|
+
- **Core parsing/building:** standard library only (`csv`, `io`, `re`).
|
|
156
|
+
Do not require pandas in the core. (A pandas convenience layer at the CLI
|
|
157
|
+
edge is allowed only if justified in `plan.md`.)
|
|
158
|
+
- **Tests:** pytest. Optional `hypothesis` for round-trip property tests.
|
|
159
|
+
- **Lint + format:** [Ruff](https://docs.astral.sh/ruff/) (does both).
|
|
160
|
+
- **Type check:** mypy (strict) or pyright.
|
|
161
|
+
- **Build backend:** hatchling, via `pyproject.toml`.
|
|
162
|
+
|
|
163
|
+
Standard commands (assume a virtualenv; keep these working):
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
pip install -e ".[dev]" # install package + dev extras
|
|
167
|
+
ruff format . # format
|
|
168
|
+
ruff check . --fix # lint
|
|
169
|
+
mypy markio # type check
|
|
170
|
+
pytest -q # run tests
|
|
171
|
+
markio --help # smoke-test the CLI
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
CI must run `ruff check`, `mypy`, and `pytest` on a matrix of
|
|
175
|
+
{Linux, macOS, Windows} × {3.10, 3.11, 3.12, 3.13}. Windows is not optional —
|
|
176
|
+
our users are on Windows and our files carry CRLF.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## 6. Coding conventions
|
|
181
|
+
|
|
182
|
+
- Type-hint every function. No untyped public functions.
|
|
183
|
+
- Prefer small pure functions. Parsing returns data; it does not print.
|
|
184
|
+
- No bare `except:`. Catch specific exceptions and convert to diagnostics.
|
|
185
|
+
- User-facing strings are plain, specific, and kind. A good message names the
|
|
186
|
+
line, says what is wrong, and says what to do. Example:
|
|
187
|
+
`line 12: record has 3 frequency columns but the file uses 2 groups — remove one value or check for a stray space before the semicolon`.
|
|
188
|
+
- Docstrings on every public function: one-line summary, args, returns, and a
|
|
189
|
+
tiny example where it helps.
|
|
190
|
+
- Keep functions under ~40 lines where reasonable; if a function grows a
|
|
191
|
+
branch per diagnostic, that is a signal to move the rule into `validate.py`.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 7. The diagnostic contract
|
|
196
|
+
|
|
197
|
+
All problems flow through one type (see `model.py`):
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
@dataclass(frozen=True)
|
|
201
|
+
class Diagnostic:
|
|
202
|
+
code: str # stable, e.g. "MK001"
|
|
203
|
+
severity: Severity # ERROR | WARNING | INFO
|
|
204
|
+
message: str # what is wrong, in plain English
|
|
205
|
+
hint: str # what to do about it
|
|
206
|
+
line: int | None # 1-based source line, when known
|
|
207
|
+
col: int | None = None
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
- Codes are stable and documented in `plan.md`. Never renumber a released code.
|
|
211
|
+
- `validate()` returns `list[Diagnostic]`; it never exits or prints.
|
|
212
|
+
- `report.py` decides presentation. Exit codes: `0` if no ERROR-severity
|
|
213
|
+
diagnostics, `1` if any ERROR. `--strict` promotes WARNING to ERROR.
|
|
214
|
+
- `--json` emits a versioned object: `{"schema_version": 1, "diagnostics": [...], "summary": {...}}`.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 8. Testing rules
|
|
219
|
+
|
|
220
|
+
- **Every diagnostic code has at least one fixture** that triggers it and a
|
|
221
|
+
test asserting the exact code appears (and clean files do not trigger it).
|
|
222
|
+
- **Round-trip tests:** `parse -> write -> parse` must be stable; and
|
|
223
|
+
`build -> validate` on generated files must produce zero errors.
|
|
224
|
+
- Fixtures live in `tests/fixtures/`, are tiny, and target one concern each.
|
|
225
|
+
Name them by intent, e.g. `missing_semicolon.inp`, `ragged_history.inp`.
|
|
226
|
+
- Prefer golden-file / snapshot tests for CLI output so UX regressions are
|
|
227
|
+
caught.
|
|
228
|
+
- A change to parsing or a format rule is not complete without a test.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## 9. What NOT to do
|
|
233
|
+
|
|
234
|
+
- Do not add model fitting, estimation, or anything statistical.
|
|
235
|
+
- Do not "auto-correct" a file unless the user passed an explicit fix flag.
|
|
236
|
+
- Do not invent format rules — verify against Chapter 2 of the MARK book.
|
|
237
|
+
- Do not break determinism (no unsorted sets in output, no timestamps in files).
|
|
238
|
+
- Do not add a dependency for convenience; justify every one in `plan.md`.
|
|
239
|
+
- Do not claim full validation of known-fate / dead-recovery / multistrata
|
|
240
|
+
until those milestones land; emit an honest "partial support" diagnostic.
|
|
241
|
+
- Do not build a GUI or a web app.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## 10. Distribution (keep these paths open from day one)
|
|
246
|
+
|
|
247
|
+
- Publish to **PyPI** via GitHub Actions Trusted Publishing.
|
|
248
|
+
- Then submit a **Bioconda** recipe (our users install via conda).
|
|
249
|
+
- Ship a **GitHub Action** and a **pre-commit hook** so `markio validate` can
|
|
250
|
+
guard other people's pipelines.
|
|
251
|
+
- Maintain `CITATION.cff` and semantic versioning.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## 11. How to work in this repo
|
|
256
|
+
|
|
257
|
+
1. Read `plan.md`. Pick the current milestone; do not skip ahead.
|
|
258
|
+
2. Make the smallest change that advances one task. Keep tests green.
|
|
259
|
+
3. For any new format behavior: add a fixture, add a test, then implement.
|
|
260
|
+
4. Update `plan.md` checkboxes and the error-taxonomy table when codes change.
|
|
261
|
+
5. Never leave the CLI able to do something the library cannot.
|
markinp-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Leon Botzenhardt
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|