Repository navigation
Expand file tree
/
Copy pathpyproject.toml
More file actions
190 lines (173 loc) · 10 KB
/
Copy pathpyproject.toml
File metadata and controls
190 lines (173 loc) · 10 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "track2data"
# Sourced from track2data/_version.py -- the single source of truth for the
# version, which also feeds ProjectManifest.app_version (stamped into every
# exported dataset's provenance) and the GUI's About box. See
# [tool.hatch.version] below and tests/test_version_consistency.py.
dynamic = ["version"]
description = "Open-source desktop app that turns idtracker.ai output folders into analysis-ready behavioural datasets."
readme = "README.md"
license = { text = "MIT" }
authors = [{ name = "Martina Bellio", email = "m.bellio2199@gmail.com" }]
keywords = [
"animal-behaviour", "behavioural-phenotyping", "collective-behaviour",
"idtrackerai", "trajectory-analysis", "zebrafish", "ethology",
]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Scientific/Engineering :: Bio-Informatics",
"Typing :: Typed",
]
requires-python = ">=3.11"
dependencies = [
"numpy>=1.26",
"pandas>=2.0",
"pydantic>=2.0",
"click>=8.0",
"scipy>=1.11", # preprocess.pipeline hard-imports identity_switch
# (linear_sum_assignment) and smoothing (savgol_filter)
# unconditionally -- not actually optional at runtime
"h5py>=3.8", # trajectories.h5 is idtracker.ai's own default output
# format (trajectories_formats defaults to
# ['h5','npy','csv']) -- readers/idtrackerai/formats/h5.py.
# The reader degrades gracefully to npy/csv if h5py is
# ever made optional again; see reader.py::_load_payload.
]
[project.optional-dependencies]
video = ["av>=12.0"] # pyav for frame extraction
zones = ["shapely>=2.0"] # polygon geometry
xarray = ["xarray>=2023.1"] # interop/movement.py: a session as a movement-style Dataset
export = ["openpyxl>=3.1", "pyarrow>=15.0"]
docs = ["mkdocs-material>=9.5", "mkdocstrings[python]>=0.24"]
ui = ["PySide6>=6.6"] # desktop GUI (LGPL — safe for MIT distribution)
build = ["pyinstaller>=6.0"] # packaging/track2data.spec (issue #42) -- not needed for
# regular dev/test, only for producing standalone binaries
dev = [
"pytest>=8.0",
"pytest-cov>=5.0",
"pytest-qt>=4.0",
"hypothesis>=6.0",
"mypy>=1.10",
"ruff>=0.4",
"numpy>=1.26",
"pandas>=2.0",
"pydantic>=2.0",
"PySide6>=6.6",
"shapely>=2.0", # tests/test_zones/* import track2data.zones.geometry directly
"pyarrow>=15.0", # cache/store.py (parquet) and exporters/feather.py call into
# pandas methods that import pyarrow internally, at call time
"openpyxl>=3.1", # tests/test_exporters/test_excel.py imports it directly to
# verify workbook content; exporters/excel.py also needs it
# unconditionally to write .xlsx at all
"xarray>=2023.1", # tests/test_interop/* build a movement-style Dataset
"pyyaml>=6.0", # tests/test_issue_templates.py parses the GitHub issue forms
# in .github/ISSUE_TEMPLATE/. Test-only: nothing in the
# shipped package reads YAML.
"tables>=3.9", # tests/real_samples/oracles.py reads DeepLabCut's pandas-HDF5
# output through PyTables. Test-only ORACLE: the shipped reader
# must not depend on it (decided at design gate G-H5).
]
[project.scripts]
track2data = "track2data.cli:main"
track2data-gui = "app.main:main"
[project.urls]
Homepage = "https://github.com/mbel-tech/Track2Data"
Repository = "https://github.com/mbel-tech/Track2Data"
Issues = "https://github.com/mbel-tech/Track2Data/issues"
Changelog = "https://github.com/mbel-tech/Track2Data/blob/main/CHANGELOG.md"
[project.entry-points."track2data.readers"]
idtrackerai = "track2data.readers.idtrackerai.reader:IDTrackerAiReader"
idtrackerai_v5 = "track2data.readers.idtrackerai_v5:IDTrackerAiV5Reader"
# idtrackerai_v4 deliberately NOT registered as an entry point -- see DECISIONS.md D-012.
# The class is still hardcoded-registered as a built-in in readers/__init__.py (like the
# two above), it's just not advertised to entry-point introspection as a working reader.
[project.entry-points."track2data.metrics"]
# Intentionally empty -- built-in metrics register by direct import in
# metrics/__init__.py::_load_builtins(), not via entry points, so that a
# metric whose optional dependency (scipy, shapely) is missing degrades
# to "not registered" instead of breaking the registry, and so built-ins
# work from a source checkout with no installed distribution metadata.
# Third-party plug-ins DO use this group -- see TECHNICAL_SPEC.md §12.1.
[project.entry-points."track2data.exporters"]
# Intentionally empty for the same reason -- see the note above and
# exporters/__init__.py::_load_builtins().
[tool.hatch.version]
path = "track2data/_version.py"
[tool.hatch.build.targets.wheel]
packages = ["track2data", "app", "ui"]
# PEP 561 marker. Without it a downstream type checker ignores every
# annotation in this package, however complete they are -- which was the
# situation despite ruff's ANN rules requiring them throughout.
artifacts = ["track2data/py.typed"]
# ── Ruff ───────────────────────────────────────────────────────────────────────
[tool.ruff]
target-version = "py311"
line-length = 100
[tool.ruff.lint]
select = ["E", "F", "I", "N", "UP", "ANN", "B", "SIM", "RUF"]
ignore = [
"ANN401", # Any allowed in internal helpers
]
[tool.ruff.lint.per-file-ignores]
"tests/**" = ["ANN", "S", "RUF012"] # RUF012: ClassVar annotation not required in test ABC subclasses
"tests/real_samples/**" = [ # The supplied reader fixture-test suite, kept close to the
"E501", "SIM115", "B905", "E731", # original so it stays comparable with its source. Only
"N802", "N812", "RUF005", "RUF046", "SIM118", # style rules are relaxed; F, B and the
] # remaining E rules still gate.
"track2data/core/errors.py" = ["N801", "N818"] # ImportError_ uses trailing _ to avoid shadowing built-in
"track2data/core/progress.py" = ["N818"] # OperationCancelled is a control-flow signal, not an "error"
"scripts/extract_bboxes.py" = ["ANN"] # utility script; public-API type-annotation policy does not apply
".claude/skills/**" = ["ANN"] # agent tooling (GUI driver), not product surface -- same
# rationale as extract_bboxes.py above. Only ANN is relaxed,
# so real errors (F, E, B, SIM, ...) still gate normally.
"app/**" = ["ANN", "N815"] # N815: Qt signal names are camelCase by convention
"ui/**" = ["ANN", "N815", "N802"] # N802: Qt event-handler overrides (showEvent, etc.) are camelCase by convention
# ── Pytest ─────────────────────────────────────────────────────────────────────
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v --tb=short"
markers = [
"r_parity: marks tests that compare output against R pipeline reference",
"r_parity_local: marks tests requiring embargoed local R-pipeline fixtures (skip in CI)",
"slow: marks tests that are expected to be slow",
"corpus_local: marks tests requiring the embargoed 70-session real idtracker.ai corpus (skip in CI; skips automatically when the corpus directory is absent)",
"network: marks tests that call a third-party API (Crossref DOI resolution); excluded in CI, since an outage there is not a defect here. Run with: pytest -m network",
"quirk: format-quirk test; parses raw fixture files and needs no Track2Data (tests/real_samples)",
"contract: reader-contract test; runs against the registered readers (tests/real_samples)",
"real_sample: needs downloaded real tracker output (about 70 MB); opt in with -m quirk, -m contract or -m real_sample, or T2D_REAL_SAMPLES=1",
]
# ── mypy ───────────────────────────────────────────────────────────────────────
# Engine only. app/ and ui/ are excluded because PySide6's stubs would
# dominate the signal, and tests because their fixtures deliberately build
# partial objects. Ruff's ANN rules already require annotations everywhere;
# this is what checks they are *correct* -- nothing did before, and
# `# type: ignore` comments were accumulating unchecked.
[tool.mypy]
files = ["track2data"]
python_version = "3.11"
ignore_missing_imports = true
warn_unused_ignores = true
warn_redundant_casts = true
[tool.coverage.run]
# The whole shipped package, not just the engine. Measuring only
# track2data/ meant the reported number was widely read as whole-project
# coverage when tests/test_ui/ and tests/test_app/ ran against no floor at
# all -- see docs/TECHNICAL_SPEC.md §11 for the policy and the two
# thresholds CI actually enforces.
source = ["track2data", "app", "ui"]
omit = ["track2data/cli.py"]
[tool.coverage.report]
# The floor for a plain `pytest --cov` run. CI additionally holds the
# engine and the GUI to their own thresholds from the same coverage data
# (`coverage report --include=...`), because a well-covered GUI must not
# be able to mask an engine regression, and the two carry very different
# consequences when they break.
fail_under = 85