-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathpyproject.toml
More file actions
205 lines (184 loc) · 9.96 KB
/
Copy pathpyproject.toml
File metadata and controls
205 lines (184 loc) · 9.96 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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
[project]
name = "cvx-linalg"
# Derived from the git tag by hatch-vcs (see [tool.hatch.version]) rather than written
# here, so the tag is the single source of truth and a release is `git tag` alone -- no
# bump commit, and no way for a number in this file to disagree with what was tagged.
dynamic = ["version"]
description = "Linear algebra utilities for portfolio optimization"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"numpy>=2.0.0",
]
authors = [{name = "Thomas Schmelzer", email = "thomas.schmelzer@gmail.com"}]
license = "MIT"
license-files = ["LICENSE"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Financial and Insurance Industry",
"Intended Audience :: Science/Research",
"Topic :: Scientific/Engineering :: Mathematics",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
]
[project.optional-dependencies]
ewm = [
"polars>=1.40.0",
]
[project.urls]
Homepage = "https://github.com/Jebel-Quant/linalg"
Repository = "https://github.com/Jebel-Quant/linalg"
[build-system]
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"
# Supplies the `version` that [project] declares dynamic, from `git describe`. Two
# consequences worth knowing:
# * The build needs the tag to be visible. hatch-vcs does not fail without it -- it
# falls back to a dev version like 1.0.1.dev3+g<sha> -- so rhiza_release.yml checks
# the built distribution's filename against the tag, and its checkout uses
# fetch-depth: 0. An sdist carries the resolved number in PKG-INFO, so building from
# one needs no git.
# * Between tags the local version is the *next* patch as a dev release
# (guess-next-dev, the default), which is why nothing pins a fallback-version here:
# a wrong-looking version must surface as a release failure, not as a plausible
# constant.
[tool.hatch.version]
source = "vcs"
[tool.hatch.build.targets.wheel]
packages = ["src/cvx"]
[tool.hatch.build]
include = [
"LICENSE",
"README.md",
"src/cvx/linalg",
]
[dependency-groups]
# A group here earns its place only when something has to be present in the project
# *environment*. Every rhiza gate provisions its own tooling on the fly — `--with
# pytest --with pytest-cov ...` in `test`, `--with ty` / `--with mypy` in `typecheck`,
# `--with interrogate` in `docs-coverage`, `uvx pre-commit` (which runs the ruff hooks)
# in `fmt` — so a tool named by a `--with` flag must NOT be declared here; the
# declaration only duplicates the pin.
#
# What does qualify: a library the test suite or a notebook imports.
dev = [
"hypothesis>=6.150.0", # tests/linalg/test_property.py imports it directly (not via importorskip)
"pandas>=3.0.0", # test_ewm_cov.py::test_matches_pandas_ewm_cov cross-checks against pandas' ewm
"polars>=1.40.0", # src/cvx/linalg/covariance/ewm_cov.py and its tests
"marimo>=0.20.0", # book/marimo/notebooks/*.py; deptry scans MARIMO_FOLDER, so DEP001 without it
"pytest>=9.0.3", # injected by every pytest-backed target, but declared so a bare `uv run pytest tests/...` works
# pytest.ini sets a global `timeout = 60` AND `filterwarnings = error`, and
# `benchmark`/`hypothesis-test`/`stress` run pytest WITHOUT injecting this plugin —
# so its absence is not a warning here, it is a hard "Unknown config option" failure.
"pytest-timeout>=2.0",
]
test = [
"pytest>=9.0.3",
]
# There is no `lint` group: ruff runs through the pre-commit hooks, which `make fmt`
# executes via uvx, so nothing would resolve such a group. It used to be declared as a
# literal `lint = []` only because the synced .rhiza/tests/test_pyproject.py asserted its
# presence; that assertion is gone in rhiza after #1484.
[tool.rhiza-task]
# Settings for the task runner that replaced the synced make layer in rhiza v1.4.0. Read as
# layer 3 of rhiza-task's resolution order (defaults -> .rhiza/.env -> pyproject.toml ->
# RHIZA_* environment). Only entries that differ from the CLI's defaults belong here;
# `uvx rhiza-task print <setting>` shows what one resolves to.
#
# This table is the missing half of the v1.4.2 migration: `chore: drop the retired make
# layer` deleted the files that set the three values below, and every one of them is a
# non-default, so without this table the gates quietly relaxed.
# The retired python.mk had `TYPECHECKER ?= both`; the CLI defaults to `ty` alone, so the
# deletion dropped `mypy --strict` from the typecheck gate. `both` is also stricter here than
# it was under make: the shell `case` let mypy's exit status win, so a ty failure could pass,
# where the CLI raises on each run.
typechecker = "both"
# The retired quality.mk pinned `RHIZA_CHECKS_VERSION ?= 0.2.1` and installed
# `pytest-rhiza==$(RHIZA_CHECKS_VERSION)` from PyPI. The CLI's default names a git tag at
# v0.2.0, so the deletion *downgraded* the conformance checks -- the drift the pin exists to
# prevent. Pinned exactly rather than floored: a gate that moves under you is not a gate.
#
# 0.6.0 is required by this repo's dynamic [project].version, not merely available: it is
# the first release whose test_pyproject accepts a version derived from the VCS. Under
# 0.2.1 three of its assertions read `[project].version` as the empty string and fail --
# the required-field check, the semver shape, and the tag-agreement one -- because they
# predate `dynamic = ["version"]` being a thing a rhiza project could do. 0.6.0 skips each
# with a reason instead. jquantstats, which made this move first, pins the same version.
pytest-rhiza = "pytest-rhiza==0.6.0"
# The deleted `Makefile` set `MARIMO_FOLDER = book/marimo/notebooks`; the CLI defaults to
# `docs/notebooks`, which is not where this repo keeps its notebooks. rhiza_marimo.yml reads
# this setting via `rhiza-task print marimo_folder` to build its notebook matrix, and `book`
# exports from it, so the default would have produced an empty matrix and no exported
# notebooks -- both of which look like success.
marimo-folder = "book/marimo/notebooks"
# Not set, on purpose:
# coverage-fail-under -- the make layer never overrode python.mk's 90, and `test` passed
# --cov-fail-under on the command line, which outranks any config
# file, so 90 is what this repo has always enforced. The CLI default
# is also 90: unchanged, not lost. (The `fail-under = 100` below is
# [tool.interrogate]'s docstring gate, a different thing.)
# mkdocs-extra-packages -- the Makefile carried only `--with 'mkdocstrings[python]'`, which
# rhiza-task now makes the default.
# source-folder, ci-os-matrix, layers -- already the defaults.
[tool.uv]
# `default-groups` used to add a "typecheck" group here so `uv run ty check` resolved
# from the project environment. The make typecheck target injects `--with ty` /
# `--with mypy` itself, so the group is gone and the default (["dev"]) is right.
default-groups = ["dev"]
[tool.bumpversion]
# bump-my-version searches only .bumpversion.toml, .bumpversion.cfg, setup.cfg and
# pyproject.toml. With no table in one of those it does not fail — it falls back to
# `git describe` and reports the last reachable tag as the current version. That used
# to be the trap this comment warned about; with [project].version dynamic (hatch-vcs
# derives it from the tag) the tag *is* the version, so the fallback is now simply the
# right answer. No current_version here, and no file for a bump to rewrite: this table
# only supplies the commit/tag policy the release flow relies on.
allow_dirty = false
# /rhiza:release commits and tags itself so the changelog lands in the bump commit; a
# bare `bump-my-version bump` would otherwise add a second commit and duplicate tag.
commit = false
tag = false
# uv.lock used to need an entry here, because it recorded this project's own version
# and the uv-lock pre-commit hook fails on a stale lock. With the version dynamic, uv
# omits it from the lock's own package block entirely, so there is nothing left to
# rewrite -- and no way for the lock to fall behind a release.
[tool.coverage.run]
branch = true
[tool.interrogate]
fail-under = 100
ignore-init-method = false
ignore-module = false
verbose = 1
[tool.check_test_layout]
# Two deliberate deviations from Rhiza's 1:1 parity, both already reasoned out in
# the "Test layout" section of CLAUDE.md; this table makes that decision
# machine-readable so the checker reports it as intentional rather than as 53
# violations.
#
# 1. The test root drops the `cvx` namespace segment (tests/linalg/... rather
# than tests/cvx/linalg/...). There is a single top-level package, so the
# extra segment buys nothing.
# 2. Tests are free functions, not `Test<Class>` wrappers mirroring each source
# class, and three files cover behaviour spanning modules with no 1:1 source
# counterpart: properties (hypothesis), packaging/__version__, and the
# marimo notebook layout.
#
# Pointing the checker at the package root (--src src/cvx) resolves (1) but not
# (2): 16 of the remaining violations are missing `Test<Class>` wrappers, which
# the function-style convention deliberately does not have. Per-module coverage
# is guaranteed by the full line-and-branch coverage gate instead.
enforce = false
reason = "Tests mirror one file per source module under tests/linalg/, dropping the redundant cvx namespace segment, and are written as free functions rather than Test<Class> wrappers; three files (properties, packaging, marimo layout) deliberately span modules with no 1:1 counterpart. See the Test layout section of CLAUDE.md. Per-module reach is guaranteed by the full line-and-branch pytest coverage gate."
[tool.deptry]
known_first_party = ["cvx"]
[tool.deptry.package_module_name_map]
numpy = "numpy"
polars = "polars"
hypothesis = "hypothesis"
pandas = "pandas"
marimo = "marimo"
pytest = "pytest"
pytest-timeout = "pytest_timeout"
"cvx-linalg" = "cvx"