Repository navigation
Expand file tree
/
Copy pathpyproject.toml
More file actions
341 lines (318 loc) · 13.8 KB
/
Copy pathpyproject.toml
File metadata and controls
341 lines (318 loc) · 13.8 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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
[project]
name = "sklearn_wrap"
dynamic = ["version"]
description = "A Package for wrapping Python classes into Scikit-Learn estimators"
readme = "README.md"
maintainers = [
{ name = "Guillaume Tauzin", email = "gtauzin@stateful-y.io" }
]
authors = [
{ name = "Guillaume Tauzin", email = "gtauzin@stateful-y.io" }
]
license = { text = "Apache-2.0" }
requires-python = ">=3.11"
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
]
dependencies = [
"scikit-learn>=1.6.0",
"joblib>=1.2.0",
]
[project.optional-dependencies]
config = [
"pydantic>=2.0",
"pyyaml>=6.0",
]
[dependency-groups]
dev = [
{include-group = "tests"},
{include-group = "lint"},
{include-group = "docs"},
{include-group = "fix"},
{include-group = "examples"},
]
tests = [
"pytest>=8.3",
"pytest-cov>=7.0",
"pytest-mock>=3.14",
"pytest-xdist>=3.8",
"covdefaults>=2.3",
"hypothesis>=6.100",
]
lint = [
"interrogate>=1.7.0",
"ruff>=0.8.0",
"rumdl>=0.1.33",
"ty>=0.0.12",
]
docs = [
# Griffe is imported directly by docs_build/_api_pages.py to discover the package's
# public surface -- the same analysis mkdocstrings renders the API pages from,
# so the two cannot disagree about what is public. Declaring it records a
# dependency that is real: relying on it arriving transitively is how a future
# mkdocstrings release breaks every generated project at once.
#
# It is NOT free, contrary to what this comment claimed until a fan-out
# measured it. Griffe 2.x is split across three distributions and
# mkdocstrings[python] pulls only `griffelib`, which is what actually provides
# the `griffe` module. Declaring `griffe` therefore adds two packages the docs
# build does not use -- the `griffe` metapackage and `griffecli` (~15 KB total).
# Kept anyway, and the reason is a file collision, not taste. `griffe` 1.x
# OWNS the `griffe/` module directory; `griffelib` 2.x provides that same
# path. Naming `griffelib` in a project whose mkdocstrings still resolves to
# the 1.x line would install two distributions owning one import path.
# Depending on `griffe` cannot do that: it resolves either to 1.x, which owns
# the module, or to the 2.x metapackage, which defers to griffelib. Exactly
# one distribution owns `griffe/` in both cases, and ~15 KB is a cheap price
# for that.
#
# Deliberately NOT pinned tighter than mkdocstrings pins it (`>=1.13`): two
# floors on one package is a resolver conflict waiting for a release.
"griffe>=1.0",
"mkdocs>=1.6.0",
"mkdocs-autorefs>=1.0",
"mkdocs-material>=9.7",
"mkdocstrings[python]>=0.26.0",
"pygments>=2.21,<2.22",
# The documentation engine. Zensical is the maintained successor to Material
# for MkDocs (which reaches a hard EOL on 2026-11-05); it reads this project's
# mkdocs.yml natively and forwards the API reference to the real mkdocstrings
# package. `mkdocs`/`mkdocs-material` above are kept so CI can still run a
# MkDocs build as a comparison during the engine transition.
"zensical>=0.0.51",
# Floor is a security floor: 10.x carries a path traversal in the b64 extension
# (GHSA-9xwg-3r6f-jcx2, fixed in 11.0.0) and 11.0.0 carries a ReDoS in four inline
# processors (GHSA-gm37-52c6-37mw, fixed in 11.0.1). Zensical works with either
# major, so nothing here argues for staying on 10.
#
# This floor used to read >=10.21.3 with a comment forbidding >=11, because the
# examples group's notebook runtime capped it at <11 and a project with examples
# could not resolve otherwise. That is what let a known-vulnerable version sit in
# five repositories: the cap was real, the CVEs landed behind it, and nothing
# rechecked the cap afterwards. That runtime has since relaxed its cap, and the
# examples group floors at the first release that did -- the two must move together.
"pymdown-extensions>=11.0.1",
# Also a direct import of docs_build/_api_pages.py, which reads mkdocstrings'
# `preload_modules` out of mkdocs.yml so that list has one definition rather
# than two. Same argument as griffe above: it arrives via mkdocs anyway, but
# relying on a transitive dependency is how a future release breaks this.
"pyyaml>=6.0",
# Imported by docs_build/serve.py to watch src/ and regenerate the API pages
# during a live preview, so a new class appears without a restart. mkdocs
# already pulls watchdog transitively (its livereload uses it), so declaring
# it adds no install today -- but the preview watcher must keep working when
# the engine changes to one that does not bundle it, and an explicit
# declaration is what survives that. Same reasoning as griffe/pyyaml above.
"watchdog>=4.0",
]
fix = [
# prek runs the hooks. Pinned here, never invoked via `uvx`: prek substitutes its
# own Rust implementations for the `repo: builtin` hooks, so an unpinned prek would
# mean unpinned hook code -- exactly what the config comment below rules out.
"prek>=0.4.10",
]
examples = [
# Floor is set by the docs group, not by anything marimo does. marimo caps
# pymdown-extensions, and every release before 0.23.16 capped it at <11 -- below
# the version that fixes two published advisories. Lowering this floor puts those
# CVEs back in the lockfile of every project that generates examples.
"marimo>=0.23.16",
"numpy>=2.3.0", # required by plotly
"pandas>=2.2.0", # required by plotly
"plotly>=6.5.0",
"xgboost>=2.0.0",
]
[project.urls]
Homepage = "https://github.com/stateful-y/sklearn-wrap"
Documentation = "https://sklearn-wrap.readthedocs.io"
Repository = "https://github.com/stateful-y/sklearn-wrap"
"Bug Tracker" = "https://github.com/stateful-y/sklearn-wrap/issues"
[build-system]
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/sklearn_wrap"]
# Puts the repo root AND src/ on the EDITABLE install's sys.path (dev mode only,
# nothing changes in the wheel). The repo root is the loading gate for
# `docs_build/`-local markdown extensions: mkdocs and its successor load
# `markdown_extensions` by module name at config time, before any hook runs, when
# `docs_build/` is not otherwise importable -- with this, `docs_build._markers`
# loads on a plain `mkdocs build` with no PYTHONPATH.
#
# `src` is NOT optional here: naming `dev-mode-dirs` at all REPLACES hatchling's
# default editable path (which was `src/`), so listing only `["."]` silently stops
# exposing the package itself -- `import sklearn_wrap` then fails, which a
# notebook cell (`from sklearn_wrap... import ...`) and any runtime import
# hit at build time, while a `--strict` docs build stays green because mkdocstrings
# reads the source via `paths: [src]`, not by importing. Both dirs, always.
dev-mode-dirs = [".", "src"]
[tool.hatch.version]
source = "vcs"
fallback-version = "0.0.0"
[tool.hatch.build.hooks.vcs]
version-file = "src/sklearn_wrap/_version.py"
[tool.ruff]
line-length = 120
target-version = "py311"
src = ["src", "tests"]
show-fixes = true
# Every tool that writes throwaway output writes it under `.artifacts/` rather
# than dropping a directory at the repo root. See the same key in the pytest and
# coverage sections, `envdir` in the noxfile, and `site_dir` in mkdocs.yml.
cache-dir = ".artifacts/ruff"
exclude = ["src/sklearn_wrap/_version.py", "examples/"]
[tool.ruff.format]
preview = true
docstring-code-format = true
docstring-code-line-length = 100
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"UP", # pyupgrade
"ARG", # flake8-unused-arguments
"SIM", # flake8-simplify
"PL", # Pylint
"S", # flake8-bandit (security)
"T10", # flake8-debugger
"T201", # Print statement
]
ignore = [
"E203", # space before : (needed for how black formats slicing)
"E501", # line too long (handled by formatter)
"B008", # do not perform function calls in argument defaults
"PLC0415", # import not at top-level (lazy imports are intentional, e.g. docs hooks)
"PLR0913", # too many args in func def
"PLR0915", # too many statements
"PLR0912", # too many branches
"PLR2004", # magic value used in comparison
]
[tool.ruff.lint.per-file-ignores]
"tests/**/*" = ["ARG", "S101", "S104", "S105", "S106", "S107", "S108", "S110", "S112", "S301", "S311", "S603", "S607", "T201", "PLR2004"] # Insecure-looking patterns that are legitimate in tests: asserts; bind-all-interfaces; hardcoded passwords/temp paths; try/except pass/continue; pickle; non-crypto random; subprocess; prints; magic values
# The template's documentation build tooling lives in docs_build/. Every script
# there prints build progress and may hold per-build caches.
#
# Deliberately `docs_build/*.py`, NOT `docs_build/_*.py`: the narrower glob would
# silently drop coverage for any un-underscored script, which is the failure
# shape this fleet specialises in. The pattern matches what the directory is for.
"docs_build/*.py" = ["T201", "PLW0603", "S506", "S603", "S112"] # Build tooling: prints, per-build cache globals, mkdocs.yml full-loader, subprocess to run notebooks, and skip-on-error loops over controlled input
# `docs/*.py` is kept too, for a project's OWN scripts under docs/ (yohou has
# docs/precache_datasets.py) -- the template ships none there any more, but a
# project script prints progress the same way and should not lose its exemption.
"docs/*.py" = ["T201", "PLW0603", "S506", "S603", "S112"]
[tool.ruff.lint.isort]
known-first-party = ["sklearn_wrap"]
[tool.interrogate]
ignore-init-method = true
ignore-init-module = true
ignore-magic = false
ignore-semiprivate = false
ignore-private = false
ignore-property-decorators = false
ignore-module = false
ignore-nested-functions = false
ignore-nested-classes = true
ignore-setters = false
fail-under = 75
exclude = ["setup.py", "docs", "docs_build", "build", "tests", "*_version.py"]
ignore-regex = ["^get$", "^mock_.*", ".*BaseClass.*"]
verbose = 0
quiet = false
whitelist-regex = []
color = true
omit-covered-files = false
[tool.pytest.ini_options]
testpaths = ["tests"]
cache_dir = ".artifacts/pytest"
addopts = [
"--strict-markers",
"--strict-config",
"--color=yes",
"--cov=sklearn_wrap",
"--cov-report=term-missing:skip-covered",
"--cov-report=html",
"--cov-report=xml",
]
markers = [
"integration: marks tests as integration tests that run subprocesses (deselect with '-m \"not integration\"')",
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
"example: marks tests for example notebooks (deselect with '-m \"not example\"')",
]
[tool.coverage.paths]
source = ["src/sklearn_wrap"]
[tool.coverage.run]
source = ["sklearn_wrap"]
# `parallel = true` writes `<data_file>.<host>.<pid>.<random>` siblings, so this
# keeps the whole family under `.artifacts/` rather than scattering `.coverage.*`
# across the repo root.
data_file = ".artifacts/.coverage"
branch = true
parallel = true
sigterm = true
concurrency = ["multiprocessing"]
plugins = ["covdefaults"]
omit = [
"*/tests/*",
"*/__pycache__/*",
"*/_version.py",
]
[tool.coverage.report]
precision = 2
show_missing = true
skip_covered = false
fail_under = 100
include_namespace_packages = true
[tool.coverage.html]
directory = ".artifacts/htmlcov"
[tool.coverage.xml]
# The ONLY place the coverage XML path is written. `addopts` carries a bare
# `--cov-report=xml`, which defers to this key; an explicit `--cov-report=xml:PATH`
# on a command line would override it instead, which is how the CI upload path and
# the produced filename silently drifted apart before. Nothing else names this file
# except the Codecov upload step, and a test asserts the two agree.
output = ".artifacts/coverage.xml"
[tool.rumdl]
flavor = "mkdocs"
line-length = 120
disable = [
"MD012", # Multiple consecutive blank lines (common in generated content)
"MD013", # Line length (handled by formatter, many long URLs/tables)
"MD022", # Heading blank lines (varies by document style)
"MD031", # No blank line before fenced code block
"MD032", # List should be preceded by blank line
"MD033", # Inline HTML (needed for README logo, MkDocs grid cards)
"MD034", # URL/email without angle brackets
"MD045", # Image missing alt text (logos use empty alt intentionally)
"MD046", # Code block style (indented blocks required inside MkDocs tabs)
"MD051", # Link fragments (cross-doc references resolved at MkDocs build time)
]
exclude = ["CHANGELOG.md", ".artifacts", ".venv", "openspec"]
[tool.rumdl.per-file-ignores]
".claude/skills/**" = ["MD007"] # Nested lists in skill files use variable indent
".github/PULL_REQUEST_TEMPLATE.md" = ["MD041"] # PR template starts with checklist
".github/skills/**" = ["MD007"] # Nested lists in skill files use variable indent
"README.md" = ["MD041"] # README starts with logo, not heading
# The page is a bare `--8<--` include; the included CHANGELOG.md supplies the
# H1. Adding one here would render two H1s and, because Material's ToC takes
# first.children when the first heading is level 1, an empty ToC on the one
# page that most needs one.
"docs/pages/reference/changelog.md" = ["MD041"]
"docs/index.md" = ["MD041", "MD026"] # + welcome H1 intentionally ends with "!"
"CONTRIBUTING.md" = ["MD057"] # Relative link to docs/ is valid at project level
[tool.commitizen]
name = "cz_conventional_commits"
version_scheme = "semver"
version_provider = "scm"
update_changelog_on_bump = false
major_version_zero = true
tag_format = "v$version"