Repository navigation
Expand file tree
/
Copy pathpyproject.toml
More file actions
170 lines (153 loc) · 6.86 KB
/
Copy pathpyproject.toml
File metadata and controls
170 lines (153 loc) · 6.86 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
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "views-frames"
version = "2.0.0"
description = "The VIEWS platform data-contract layer: immutable array+identifier frames (numpy only, root of the dependency DAG)."
authors = [
{ name = "Simon Polichinel von der Maase", email = "simmaa@prio.org" },
]
readme = "README.md"
requires-python = ">=3.10"
license = "MIT"
license-files = ["LICENSE"]
# License is declared via the SPDX `license` expression above (PEP 639); the deprecated
# `License ::` classifier is intentionally omitted to avoid the expression/classifier clash.
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Science/Research",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Scientific/Engineering",
"Typing :: Typed",
]
dependencies = [
"numpy>=1.26,<3",
]
[project.urls]
Homepage = "https://github.com/views-platform/views-frames"
Repository = "https://github.com/views-platform/views-frames"
Changelog = "https://github.com/views-platform/views-frames/blob/main/CHANGELOG.md"
# Optional serialization extra — pyarrow is allowed ONLY behind io/ (README §3.1).
[project.optional-dependencies]
arrow = ["pyarrow>=14,<20"]
# Optional docs/notebooks extra — plotting + jupyter for the showcase notebooks
# (notebooks/, an un-gated dev artifact outside src/). NEVER runtime dependencies;
# self-references [arrow] so `uv sync --extra docs` also enables the parquet round-trip
# demo in notebooks/01_frames.ipynb. matplotlib/scipy are notebook-only (the core is
# numpy-only); these are imported by notebooks, never by the package.
docs = [
"views-frames[arrow]",
"matplotlib>=3.8,<4",
"scipy>=1.11,<2",
"jupyterlab>=4,<5",
# nbmake drives the non-blocking notebook-execution CI check (issue #151): it just
# runs the notebooks end-to-end to catch frozen-API drift. A test tool, but it rides
# in [docs] so the single `uv sync --extra docs` provisions everything the notebooks
# job needs (kernel + plotting + the checker).
"nbmake>=1.5,<2",
]
[dependency-groups]
dev = [
"pytest>=8.0",
"pytest-cov>=5",
"ruff>=0.8",
"mypy>=1.13",
# pyarrow is the consumer-facing [arrow] extra; installed in dev so CI
# exercises io/arrow. It must never be imported outside src/views_frames/io/.
"pyarrow>=14,<20",
# Enforces [tool.importlinter] below. All three packages are cycle-free today;
# this keeps them that way rather than discovering otherwise in six months.
"import-linter>=2.0",
# tomllib is stdlib from 3.11; the 3.10 floor needs the backport, or
# tests/test_packaging.py collects nothing on the leg the floor job exists for
# (register C-80).
"tomli>=2; python_version<'3.11'",
]
# Both contracts record the dependency direction the code already has — they are not a
# new architecture. `|` between siblings means *independent*: they may not import each
# other, which is what actually prevents a cycle. (`:` would permit it.)
#
# Contract 1 duplicates tests/test_import_enforcement.py, which is stricter (it also bans
# foreign views_* packages, pandas et al., and pyarrow outside io/). The duplication is
# deliberate: the same contract is landing across the platform's cycle-free repos, so it
# is written the same way here. Do not delete the test believing this replaces it.
[tool.importlinter]
root_packages = ["views_frames", "views_frames_summarize", "views_frames_reconcile"]
[[tool.importlinter.contracts]]
name = "The core does not depend on its consumers"
type = "layers"
layers = [
"views_frames_summarize | views_frames_reconcile",
"views_frames",
]
# `conformance` is in the top layer because that is its role — it validates frames from
# outside. It imports nothing internal today (it duck-types against `Any`), so it would
# satisfy any layer; putting it at the bottom would break this contract the first time
# someone added a frame import for a type hint.
#
# NOTE: `io` sits *below* the frames because that is what the code does — `io/npz` and
# `io/arrow` are flat array codecs importing only `_typing`, and the frames call them.
# `Persistable` puts `save`/`load` on the frame, so the frame is what reaches the
# serializer. ADR-002 described the inverse until its 2026-08-17 amendment (register
# C-82); it now matches this contract.
[[tool.importlinter.contracts]]
name = "The leaf's internal layering runs one way"
type = "layers"
layers = [
"views_frames.feature_frame | views_frames.prediction_frame | views_frames.target_frame | views_frames.conformance",
"views_frames.protocols",
"views_frames.index",
"views_frames._validation | views_frames.io",
"views_frames._typing | views_frames.metadata | views_frames.spatial_level",
]
[tool.hatch.build.targets.wheel]
packages = ["src/views_frames", "src/views_frames_summarize", "src/views_frames_reconcile"]
[tool.hatch.build.targets.sdist]
include = ["/src"]
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-ra -q"
# Coverage: the leaf is critical infrastructure (ADR-005) — CI enforces 100%
# line coverage (`uv run pytest --cov --cov-fail-under=100`). Genuinely
# unreachable defensive lines must carry an explicit `# pragma: no cover`.
[tool.coverage.run]
source = ["src"]
# Branch coverage on: the `--cov-fail-under=100` gate enforces line AND branch
# coverage, so a conditional with an untested outcome fails CI (register C-36).
branch = true
[tool.coverage.report]
show_missing = true
[tool.ruff]
line-length = 88
target-version = "py310"
# research/ is the volatile estimator lab and notebooks/ holds the un-gated showcase
# notebooks + their helper (notebooks/_synthetic.py) — both sit outside the frozen,
# gated src/ and are not held to the leaf's lint/format bar.
extend-exclude = ["research", "notebooks"]
[tool.ruff.lint]
select = ["E", "F", "I", "N", "UP", "B", "A", "C4", "SIM"]
[tool.ruff.lint.per-file-ignores]
# The falsification stubs assert against the README via long, literal regexes
# whose lines cannot wrap without changing their semantics.
"tests/test_falsification_*.py" = ["E501"]
# The reconciliation parity tests are faithfully relocated from views-postprocessing
# (Epic 11 / ADR-023); their dense numpy/dict-comprehension assertions are kept verbatim
# rather than re-wrapped.
"tests/test_reconciliation_*.py" = ["E501"]
# The dev/ops scripts (oracle regeneration; the reconciliation parity verifier) are not
# CI-run and carry dense numpy one-liners + report f-strings; not held to the wrap bar.
"scripts/*.py" = ["E501"]
[tool.mypy]
python_version = "3.10"
strict = true
warn_return_any = true
warn_unused_configs = true
[[tool.mypy.overrides]]
module = ["pyarrow", "pyarrow.*"]
ignore_missing_imports = true