Repository navigation
Expand file tree
/
Copy pathpyproject.toml
More file actions
213 lines (202 loc) · 9.99 KB
/
Copy pathpyproject.toml
File metadata and controls
213 lines (202 loc) · 9.99 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
[build-system]
requires = ["setuptools>=70", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "aggregate_api"
# Written once, as the literal `__version__` in src/aggregate_api/__init__.py,
# and read from there by [tool.setuptools.dynamic]. A bump edits that one line
# and touches no environment.
dynamic = ["version"]
description = "aggregate Loss Lab (aLL): FastAPI service and single-page web app for the aggregate actuarial library."
readme = "README.md"
authors = [
{name = "Stephen J. Mildenhall", email = "steve@convexrisk.com"},
]
maintainers = [
{name = "Stephen J. Mildenhall", email = "steve@convexrisk.com"},
]
keywords = [
"actuarial",
"insurance",
"reinsurance",
"risk",
"aggregate loss",
"compound distribution",
"FFT",
"pricing",
"DecL",
]
classifiers = [
# Beta rather than Production/Stable at 1.0.0, deliberately. The package is
# installable and supported; the `/v1` surface still follows what the
# bundled web app needs, so it is not a frozen interface. The library can
# claim Production/Stable because its API promise is in force; this cannot
# yet.
"Development Status :: 4 - Beta",
"Intended Audience :: Financial and Insurance Industry",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Office/Business :: Financial",
"Topic :: Scientific/Engineering :: Mathematics",
"Framework :: FastAPI",
]
dependencies = [
# The library this service wraps. This line states what the code needs and
# nothing more: where `aggregate` comes from on the author's machines is the
# [tool.uv.sources] table below, and a wheel installer gets it from PyPI.
#
# The floor is 1.61.0 (1.42.0 of this package), the library the suite runs
# against: 1.59.0 for the unmatched bracket parse error the build route
# serves, 1.56.0 for the reinsured verdict the strip grades, 1.42.0 and
# 1.43.0 for named and arithmetic stable laws. It moves only when the API
# starts using something newer, and at a release the library goes to PyPI
# first. A bare `aggregate` would let a resolver reach 0.30.1, the newest of
# the old line, which has none of these modules and fails at import rather
# than at install.
#
# No ceiling. It was caution about two private names, `parser._PARSER` and
# `parser_errors._TERMINAL_LABELS`; tests/test_private_names.py guards them
# instead, so a major that moves either fails a test rather than an install.
#
# The `elt` extra brings eltq, for secondary uncertainty in an event loss
# table: an ELT6 `elt` source without `no2u` imports it.
"aggregate[elt]>=1.61.0",
# Static tables (the author's own package). The api asks it for a semantic
# table document (the IR), never for markup: the browser owns geometry. The
# same install also ships the walker that renders that IR, which is what
# makes version skew between the two impossible. See dev/plan-gt2-ir.md.
#
# **An ordinary registry dependency since a53.** 6.0.0 has shipped to PyPI,
# so the editable source this used to carry is gone and a fresh clone works
# with nothing checked out beside it.
#
# The floor is load bearing and must not be relaxed. PyPI also holds 5.3,
# the previous generation of the same package under the same name, and the
# two cannot coexist in one environment: 5.3 has no `build`,
# `canonical_json` or `IR_VERSION`, so resolving to it does not fail at
# install, it fails at import. Without `>=6` a resolver working around some
# other constraint could quietly pick it.
#
# Raised to 7.0.0 at 1.31.0: 7.0 removed `TableSpec.math`, which nothing
# here or in the library uses. Raised to 8.1.0 at 1.32.0, the floor for
# every consumer and the one `aggregate` 1.11 itself requires; tables
# rendered through it differ from 7.0 in places (its CHANGELOG marks each
# such release "Output changes").
"greater-tables>=8.1.0",
"fastapi>=0.115",
"uvicorn[standard]>=0.30",
"pydantic>=2.7",
"pydantic-settings>=2.4",
# Frame routes hand orjson the frame's numpy array directly. Serializing a
# 65,536 by 43 density frame took 2.2 s through the per-cell coercion,
# pydantic and json.dumps; orjson does the same bytes in under 0.1 s. See
# dev/done/plan-fast-frames.md, [orjson-frames].
"orjson>=3.10",
]
license = "BSD-3-Clause"
# Was >=3.11 through a31. `greater-tables` declares >=3.13 and that floor is
# the binding one. Nothing in its source needs 3.13, so if 3.11 or 3.12 ever
# matters here again, relax it there rather than pinning an old release here.
requires-python = ">=3.13"
[project.urls]
Homepage = "https://github.com/mynl/aggregate_api"
"Source Code" = "https://github.com/mynl/aggregate_api"
Documentation = "https://github.com/mynl/aggregate_api#readme"
Changelog = "https://github.com/mynl/aggregate_api/blob/main/CHANGELOG.md"
Issues = "https://github.com/mynl/aggregate_api/issues"
[project.optional-dependencies]
dev = [
"pytest>=7",
# FastAPI's TestClient is a thin wrapper over httpx.
"httpx>=0.27",
"ruff>=0.6",
# So the suite covers the `.parquet` frame route (the `parquet` extra).
"pyarrow>=15",
]
# The `.parquet` frame route, `GET /v1/objects/{id}/frame/{which}.parquet`,
# for R, Python and Power Query readers. An extra because the pyarrow wheel is
# about 28 MB and a lean install serving the SPA never needs it; without it
# the route answers 501 naming this extra and `/v1/meta` leaves `parquet` out
# of `frame_formats`. The floor is where `use_byte_stream_split` started
# taking a column list.
parquet = ["pyarrow>=15"]
# The resource block of GET /v1/status. An extra rather than a runtime
# dependency so the lean install is untouched and a deployment opts in with
# `uv sync --extra status`; `resources.py` carries a stdlib fallback and says
# which source it used, so the page works either way.
#
# Worth declaring even though psutil is usually already present: it arrives
# transitively through ipython, which aggregate pulls, so the import succeeds
# by accident rather than by intent and is one unrelated dependency change away
# from not. That is what an explicit extra is for.
status = ["psutil>=5.9"]
[project.scripts]
aggregate-api = "aggregate_api.__main__:main"
# ---------------------------------------------------------------------------
# `aggregate` installs editable from the library's worktree, wherever this
# project is synced: on DOOB `V:\worktrees\aggregate_REFACTOR`, on the VPS
# `~/worktrees/aggregate_REFACTOR`, a symlink to its clone. Relative, never a
# drive letter. The editable install includes the library session's
# uncommitted changes, and `uv sync` keeps it rather than replacing it.
# uv.lock records the source with no version.
#
# Published wheels never contain this table, so `pip install` from PyPI or from
# a GitHub release's wheel resolves `aggregate` from PyPI against the floor
# above. `uv sync --no-sources` does the same from this tree.
#
# What it gives up: uv honors the table when this project is installed as a git
# dependency and resolves the path inside the git repository, so
#
# uv pip install git+https://github.com/mynl/aggregate_api
#
# fails, as it did before 1.0.0, which is why the table was removed then. That
# route installed an api whose `/` returns 404, because the built web page is
# not in git, so nothing that worked is lost; the README points at the wheel.
#
# `greater-tables` stays a registry dependency. To co-develop it for a session,
# `uv pip install -e V:/dev/greatest-tables` (the folder keeps the working name
# it carried for a day), which lasts until the next `uv sync`.
# ---------------------------------------------------------------------------
[tool.uv.sources]
aggregate = { path = "../../worktrees/aggregate_REFACTOR", editable = true }
# uv caches a local project's metadata keyed on pyproject.toml alone. With the
# version dynamic it must also watch the file the version lives in, or a lock or
# an install can carry the previous number.
[tool.uv]
cache-keys = [{ file = "pyproject.toml" }, { file = "src/aggregate_api/__init__.py" }]
[tool.setuptools]
include-package-data = true
package-dir = {"" = "src"}
packages = ["aggregate_api", "aggregate_api.routes"]
# Read statically: setuptools parses the literal rather than importing the
# package, which would pull in aggregate and FastAPI at build time.
[tool.setuptools.dynamic]
version = { attr = "aggregate_api.__version__" }
[tool.setuptools.package-data]
# The built SPA bundle (index.html, assets/, and the icons, webmanifest and
# service worker Vite copies from web/public/) is dropped here by the web build
# and is gitignored whole. These globs read the filesystem, not git, so a wheel
# carries whatever the last build wrote; release-check.ps1 refuses to run
# without one.
#
# **`static/assets/*` is a separate entry and must stay one.** A package_data
# glob matches files in one directory and does not recurse, so `static/*` alone
# shipped `index.html` and left every script and stylesheet it loads behind.
# The installed app then served a 200 for `/` and a 404 for all thirteen
# assets: the shell painted and nothing ran. `static/**/*` would also work, and
# is not used here because it would sweep in `static/dev/`, which holds
# development instruments that have no business in a release.
#
# `status_page.html` is the operator's page, served by routes/status.py straight
# out of the package. Deliberately not under `static/`: the web build wipes that
# directory on every run, so a copy there would be deleted by the next deploy,
# and a status page whose delivery depends on the pipeline it reports on cannot
# report on that pipeline failing.
#
# `static/use/*` is the /use page (1.31.0), a directory of its own for the same
# no-recursion reason as `static/assets/*`: without it the wheel serves a 404 at
# `/use/` while every asset the page loads is present.
aggregate_api = ["static/*", "static/assets/*", "static/use/*", "status_page.html"]
[tool.ruff]
line-length = 100