slice-packer-torus 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Margaret Nanyonga
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,216 @@
1
+ Metadata-Version: 2.4
2
+ Name: slice-packer-torus
3
+ Version: 1.1.0
4
+ Summary: What a torus pod can actually still admit, what a chip failure costs, and who is allowed to fix it
5
+ Author: Margaret Nanyonga
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/dimaggi-ai/slice-packer-torus
8
+ Project-URL: Research, https://dimaggi-ai.github.io/research
9
+ Keywords: torus,k-ary n-cube,capacity planning,fragmentation,multi-tenancy,fault domains
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Intended Audience :: System Administrators
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Scientific/Engineering
15
+ Classifier: Topic :: System :: Distributed Computing
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest==8.3.4; extra == "dev"
21
+ Dynamic: license-file
22
+
23
+ # slice-packer-torus
24
+
25
+ **A pod's free-chip count is not its capacity, and a chip failure does not cost
26
+ one chip.**
27
+
28
+ A job on a torus-connected pod does not need chips. It needs a *rectangle* --- an
29
+ axis-aligned sub-grid, because that is the only shape whose diameter, bisection
30
+ and collective schedule anyone has computed. That one constraint turns four
31
+ ordinary operational questions into different questions than the ones capacity
32
+ reports answer:
33
+
34
+ | The question people ask | The question that decides the outcome |
35
+ | --- | --- |
36
+ | How many chips are free? | How large a job can still be placed? |
37
+ | What does a chip failure cost? | *Where* did the chip fail? |
38
+ | Can we recover automatically? | What does waiting for a human cost? |
39
+ | Can tenants be isolated? | Which requests make isolation impossible? |
40
+
41
+ Six geometric models, one measured failure history, a command line, and a
42
+ validation registry that prints what it declines to check before it prints
43
+ anything it does check.
44
+
45
+ **There is exactly one measurement in this repository** --- the Titan failure
46
+ history the hazard model reads. The geometry has no empirical anchor. See
47
+ [What this does not check](#what-this-does-not-check).
48
+
49
+ ## Seven findings
50
+
51
+ **1. A pod described as a torus hands most jobs a mesh.** A slice inherits a
52
+ closed ring in a dimension only if it spans that dimension *completely*. Over
53
+ 485 slices placed at random on a 16-ary 3-cube, not one received a
54
+ three-dimensional torus, and every slice lost at least one ring. The wraparound belongs to the pod; the job gets what is left.
55
+
56
+ **2. Free chips overstate capacity, and the gap widens as the pod fills.**
57
+
58
+ ```
59
+ occupancy free placeable overstated trials with a gap
60
+ 10% 3,217 2,953 8% 31/60
61
+ 40% 2,131 1,696 20% 51/60
62
+ 55% 1,547 1,090 30% 55/60
63
+ 85% 634 448 29% 41/60
64
+ ```
65
+
66
+ **3. One chip failure costs between 6% and 50% of a slice, depending on where
67
+ it was.** Getting back to a rectangle means pulling a face in past the failure.
68
+ A corner costs one plane; the centre costs half the slice.
69
+
70
+ ```
71
+ where coordinate chips lost share
72
+ corner (0, 0, 0) 256 6.2%
73
+ face (0, 8, 8) 256 6.2%
74
+ interior (4, 8, 8) 1,280 31.2%
75
+ centre (8, 8, 8) 2,048 50.0%
76
+ ```
77
+
78
+ The worst case is **exactly half the slice for every shape**, so packing cannot
79
+ buy it down. The best case is `chips / longest axis` --- which means the compact
80
+ shape that wins on diameter is the one with the *highest* typical failure cost.
81
+ At 4,096 chips in a 64-ary pod, the compact `16x16x16` loses 256 chips to its
82
+ cheapest failure and the flat `1x64x64` loses 64.
83
+
84
+ **4. The option that keeps your checkpoint is the one that runs out first.**
85
+ Shrinking in place is cheap in hardware and breaks the rank grid, so the
86
+ checkpoint no longer restores. Moving at the same shape keeps the checkpoint and
87
+ costs other tenants their chips. Of 120 simulated failures a shrink existed in
88
+ all 120 and a same-shape move in only 97.
89
+
90
+ **5. Isolation has a price, and one request it cannot fill at any price.**
91
+ Dedicating a rack per tenant admitted 40,000 chips where an open pod took
92
+ 75,392. And a job that wants a closed ring must span the axis, which means
93
+ touching every rack in it --- so "give me a torus" and "keep me off other
94
+ tenants' hardware" are contradictory, not merely competing.
95
+
96
+ **6. The approval queue becomes the outage exactly when the pod is busiest.**
97
+ Confining autonomous action to the failing job means anything that evicts a
98
+ neighbour needs a person. Share of failures that reach that boundary:
99
+
100
+ ```
101
+ occupancy 25%: 0/40 50%: 0/40 75%: 5/40 95%: 38/40
102
+ ```
103
+
104
+ `cost_of_waiting` prints what the L1 action gives up against the best action
105
+ available, and returns `None` --- never zero --- when there is no autonomous
106
+ action at all.
107
+
108
+ **7. On the one measured fleet, hazard-ranked *placement* pays and hazard-ranked
109
+ *eviction* does not.** New in 1.1: `hazard` reads the public Titan GPU lifetime
110
+ dataset --- 30,207 GPUs, 100,889 GPU-years (Ostrouchov et al., SC '20), fetched
111
+ and SHA-pinned by `make data`. Deaths per GPU-year climb 0.0009 → 0.0082 →
112
+ 0.119 → 0.134 across the old batch's first four years (no infant mortality ---
113
+ the first year is the safest), and order 0.034 → 0.071 → 0.115 by cage, bottom
114
+ of the cooling path to top. Rank held-out chips by cohort hazard learned on the
115
+ other half of the fleet and the top 30% hold 55% of the deaths (lift 1.84×).
116
+ But price a preemptive drain against an unplanned reconstitution and even the
117
+ worst cohort rides unless the drain costs under ~3% of the rebuild: the
118
+ break-even hazard at a 90-day window and a 4:1 cost ratio is 1.17/GPU-year,
119
+ nearly nine times anything the fleet ever measured. Titan's operators reached the same
120
+ verdict in production: they re-cut the job mix onto reliable nodes; they did
121
+ not evict ahead of failure. The rates are Titan's own and do not transfer
122
+ (ASSUMPTIONS A12); the shape --- cohort beats fleet-uniform --- is the claim.
123
+
124
+ ## Quickstart
125
+
126
+ ```bash
127
+ make venv
128
+ make smoke-test # tests, registry and examples, under a minute
129
+ ```
130
+
131
+ ```bash
132
+ slicepacker example # the reference scenario end to end
133
+ slicepacker shapes 4096 -k 64 -n 3 # legal shapes, and which objective picks which
134
+ slicepacker cordon --shape 16,16,16 # what one chip costs, best to worst
135
+ slicepacker pack examples/pod-fragmented.json
136
+ slicepacker isolate examples/tenants-contradictory.json
137
+ slicepacker reconstitute examples/failure-no-room.json pretrain-7 4,8,8
138
+ ```
139
+
140
+ Exit codes are part of the interface: `0` answered, `1` **refused**, `2` the
141
+ input could not be read. A refusal is a correct answer, and a scheduler that
142
+ treats it as an error papers over the conditions this tool exists to surface.
143
+
144
+ ## The models
145
+
146
+ | Module | What it decides |
147
+ | --- | --- |
148
+ | `torus` | k-ary n-cube diameter and bisection; whether a slice inherits a ring |
149
+ | `packing` | placement, fragmentation, the largest job still placeable |
150
+ | `cordon` | what it costs to get back to a rectangle after a chip dies |
151
+ | `embed` | shrink versus move, and the drain a move requires |
152
+ | `tenant` | blast-domain isolation, its price, and its contradictions |
153
+ | `reconstitute` | what a control plane may do alone, and what waiting costs |
154
+ | `hazard` | when a chip earns eviction before it fails, measured on the Titan fleet |
155
+
156
+ `docs/the-models.md` explains each in prose. `docs/integration.md` covers
157
+ wiring it to a scheduler.
158
+
159
+ ## What this does not check
160
+
161
+ The validation registry prints sixteen declined items above its results on
162
+ every run. The first two matter most:
163
+
164
+ > No measured **packing**. Every geometry number here is a model output:
165
+ > nothing has been compared against a real torus pod or a real scheduler. The
166
+ > one measured thing is the **failure history** the hazard module reads, and
167
+ > it anchors failure rates only --- not one packing figure.
168
+
169
+ > The calibrated points pin textbook **closed forms**, which are identities
170
+ > about an idealised k-ary n-cube. Agreeing with them shows this code implements
171
+ > the model correctly. It is not evidence that the model describes any machine.
172
+ > The Titan points are the only cells anchored to a measured fleet --- and they
173
+ > anchor the *failure process*, not one packing figure.
174
+
175
+ Also declined: link failures, routing, time, correlated failure, non-rectangular
176
+ allocation, the reshard cost model, the L0/L1 boundary itself, first-fit
177
+ placement, what a rack is, the cost of refusing seam-straddling slices, the
178
+ paper's filtered device-level counts (the summary file cannot express them),
179
+ any transfer of Titan's rates to another machine, and repeated-play eviction.
180
+
181
+ The mutation tests in `tests/test_mutations.py` delete machinery on purpose and
182
+ assert the exact set of registry points that turns red. Nineteen mutations, a
183
+ green unmutated control so no red set can be an artefact, and two tests that
184
+ apply a real change and assert the registry does **not** notice --- because on
185
+ the geometry side it genuinely cannot. One Titan mutation earned its point a
186
+ sharper assertion: dropping the exposure clamp preserved every ordering and
187
+ first went red only once the mid-life magnitude was pinned. Every asserted red set was measured. Predicting them first was
188
+ wrong nine times out of seventeen, and two of those surprises were points that
189
+ had been passing on machinery that was no longer there.
190
+
191
+ ## Reproducing
192
+
193
+ ```bash
194
+ make data # fetch + SHA-verify the Titan dataset (once; ~4 MB)
195
+ make test # 104 unit tests and 22 mutation tests (~5 min: each mutation reruns the registry)
196
+ make validate # 29 registry points and 16 declined items
197
+ make examples # 24 examples, each pinned to its exit code
198
+ make experiments # the three figures quoted above; each exits 1 if it stops holding
199
+ ```
200
+
201
+ ## Reading order
202
+
203
+ - `DECISIONS.md` --- sixteen choices, what each bought and cost. Six were
204
+ forced by defects found while building this, and say so.
205
+ - `ASSUMPTIONS.md` --- twelve things taken as given.
206
+ - `SOURCES.md` --- three, and what they are and are not.
207
+ - `STATUS.md` --- what works, what is missing, what would change the answers.
208
+
209
+ ## Series
210
+
211
+ Part of a program on the usable capacity of large accelerator fleets:
212
+ [dimaggi-ai.github.io/research](https://dimaggi-ai.github.io/research).
213
+
214
+ ## Licence
215
+
216
+ MIT. Copyright (c) 2026 Margaret Nanyonga.
@@ -0,0 +1,194 @@
1
+ # slice-packer-torus
2
+
3
+ **A pod's free-chip count is not its capacity, and a chip failure does not cost
4
+ one chip.**
5
+
6
+ A job on a torus-connected pod does not need chips. It needs a *rectangle* --- an
7
+ axis-aligned sub-grid, because that is the only shape whose diameter, bisection
8
+ and collective schedule anyone has computed. That one constraint turns four
9
+ ordinary operational questions into different questions than the ones capacity
10
+ reports answer:
11
+
12
+ | The question people ask | The question that decides the outcome |
13
+ | --- | --- |
14
+ | How many chips are free? | How large a job can still be placed? |
15
+ | What does a chip failure cost? | *Where* did the chip fail? |
16
+ | Can we recover automatically? | What does waiting for a human cost? |
17
+ | Can tenants be isolated? | Which requests make isolation impossible? |
18
+
19
+ Six geometric models, one measured failure history, a command line, and a
20
+ validation registry that prints what it declines to check before it prints
21
+ anything it does check.
22
+
23
+ **There is exactly one measurement in this repository** --- the Titan failure
24
+ history the hazard model reads. The geometry has no empirical anchor. See
25
+ [What this does not check](#what-this-does-not-check).
26
+
27
+ ## Seven findings
28
+
29
+ **1. A pod described as a torus hands most jobs a mesh.** A slice inherits a
30
+ closed ring in a dimension only if it spans that dimension *completely*. Over
31
+ 485 slices placed at random on a 16-ary 3-cube, not one received a
32
+ three-dimensional torus, and every slice lost at least one ring. The wraparound belongs to the pod; the job gets what is left.
33
+
34
+ **2. Free chips overstate capacity, and the gap widens as the pod fills.**
35
+
36
+ ```
37
+ occupancy free placeable overstated trials with a gap
38
+ 10% 3,217 2,953 8% 31/60
39
+ 40% 2,131 1,696 20% 51/60
40
+ 55% 1,547 1,090 30% 55/60
41
+ 85% 634 448 29% 41/60
42
+ ```
43
+
44
+ **3. One chip failure costs between 6% and 50% of a slice, depending on where
45
+ it was.** Getting back to a rectangle means pulling a face in past the failure.
46
+ A corner costs one plane; the centre costs half the slice.
47
+
48
+ ```
49
+ where coordinate chips lost share
50
+ corner (0, 0, 0) 256 6.2%
51
+ face (0, 8, 8) 256 6.2%
52
+ interior (4, 8, 8) 1,280 31.2%
53
+ centre (8, 8, 8) 2,048 50.0%
54
+ ```
55
+
56
+ The worst case is **exactly half the slice for every shape**, so packing cannot
57
+ buy it down. The best case is `chips / longest axis` --- which means the compact
58
+ shape that wins on diameter is the one with the *highest* typical failure cost.
59
+ At 4,096 chips in a 64-ary pod, the compact `16x16x16` loses 256 chips to its
60
+ cheapest failure and the flat `1x64x64` loses 64.
61
+
62
+ **4. The option that keeps your checkpoint is the one that runs out first.**
63
+ Shrinking in place is cheap in hardware and breaks the rank grid, so the
64
+ checkpoint no longer restores. Moving at the same shape keeps the checkpoint and
65
+ costs other tenants their chips. Of 120 simulated failures a shrink existed in
66
+ all 120 and a same-shape move in only 97.
67
+
68
+ **5. Isolation has a price, and one request it cannot fill at any price.**
69
+ Dedicating a rack per tenant admitted 40,000 chips where an open pod took
70
+ 75,392. And a job that wants a closed ring must span the axis, which means
71
+ touching every rack in it --- so "give me a torus" and "keep me off other
72
+ tenants' hardware" are contradictory, not merely competing.
73
+
74
+ **6. The approval queue becomes the outage exactly when the pod is busiest.**
75
+ Confining autonomous action to the failing job means anything that evicts a
76
+ neighbour needs a person. Share of failures that reach that boundary:
77
+
78
+ ```
79
+ occupancy 25%: 0/40 50%: 0/40 75%: 5/40 95%: 38/40
80
+ ```
81
+
82
+ `cost_of_waiting` prints what the L1 action gives up against the best action
83
+ available, and returns `None` --- never zero --- when there is no autonomous
84
+ action at all.
85
+
86
+ **7. On the one measured fleet, hazard-ranked *placement* pays and hazard-ranked
87
+ *eviction* does not.** New in 1.1: `hazard` reads the public Titan GPU lifetime
88
+ dataset --- 30,207 GPUs, 100,889 GPU-years (Ostrouchov et al., SC '20), fetched
89
+ and SHA-pinned by `make data`. Deaths per GPU-year climb 0.0009 → 0.0082 →
90
+ 0.119 → 0.134 across the old batch's first four years (no infant mortality ---
91
+ the first year is the safest), and order 0.034 → 0.071 → 0.115 by cage, bottom
92
+ of the cooling path to top. Rank held-out chips by cohort hazard learned on the
93
+ other half of the fleet and the top 30% hold 55% of the deaths (lift 1.84×).
94
+ But price a preemptive drain against an unplanned reconstitution and even the
95
+ worst cohort rides unless the drain costs under ~3% of the rebuild: the
96
+ break-even hazard at a 90-day window and a 4:1 cost ratio is 1.17/GPU-year,
97
+ nearly nine times anything the fleet ever measured. Titan's operators reached the same
98
+ verdict in production: they re-cut the job mix onto reliable nodes; they did
99
+ not evict ahead of failure. The rates are Titan's own and do not transfer
100
+ (ASSUMPTIONS A12); the shape --- cohort beats fleet-uniform --- is the claim.
101
+
102
+ ## Quickstart
103
+
104
+ ```bash
105
+ make venv
106
+ make smoke-test # tests, registry and examples, under a minute
107
+ ```
108
+
109
+ ```bash
110
+ slicepacker example # the reference scenario end to end
111
+ slicepacker shapes 4096 -k 64 -n 3 # legal shapes, and which objective picks which
112
+ slicepacker cordon --shape 16,16,16 # what one chip costs, best to worst
113
+ slicepacker pack examples/pod-fragmented.json
114
+ slicepacker isolate examples/tenants-contradictory.json
115
+ slicepacker reconstitute examples/failure-no-room.json pretrain-7 4,8,8
116
+ ```
117
+
118
+ Exit codes are part of the interface: `0` answered, `1` **refused**, `2` the
119
+ input could not be read. A refusal is a correct answer, and a scheduler that
120
+ treats it as an error papers over the conditions this tool exists to surface.
121
+
122
+ ## The models
123
+
124
+ | Module | What it decides |
125
+ | --- | --- |
126
+ | `torus` | k-ary n-cube diameter and bisection; whether a slice inherits a ring |
127
+ | `packing` | placement, fragmentation, the largest job still placeable |
128
+ | `cordon` | what it costs to get back to a rectangle after a chip dies |
129
+ | `embed` | shrink versus move, and the drain a move requires |
130
+ | `tenant` | blast-domain isolation, its price, and its contradictions |
131
+ | `reconstitute` | what a control plane may do alone, and what waiting costs |
132
+ | `hazard` | when a chip earns eviction before it fails, measured on the Titan fleet |
133
+
134
+ `docs/the-models.md` explains each in prose. `docs/integration.md` covers
135
+ wiring it to a scheduler.
136
+
137
+ ## What this does not check
138
+
139
+ The validation registry prints sixteen declined items above its results on
140
+ every run. The first two matter most:
141
+
142
+ > No measured **packing**. Every geometry number here is a model output:
143
+ > nothing has been compared against a real torus pod or a real scheduler. The
144
+ > one measured thing is the **failure history** the hazard module reads, and
145
+ > it anchors failure rates only --- not one packing figure.
146
+
147
+ > The calibrated points pin textbook **closed forms**, which are identities
148
+ > about an idealised k-ary n-cube. Agreeing with them shows this code implements
149
+ > the model correctly. It is not evidence that the model describes any machine.
150
+ > The Titan points are the only cells anchored to a measured fleet --- and they
151
+ > anchor the *failure process*, not one packing figure.
152
+
153
+ Also declined: link failures, routing, time, correlated failure, non-rectangular
154
+ allocation, the reshard cost model, the L0/L1 boundary itself, first-fit
155
+ placement, what a rack is, the cost of refusing seam-straddling slices, the
156
+ paper's filtered device-level counts (the summary file cannot express them),
157
+ any transfer of Titan's rates to another machine, and repeated-play eviction.
158
+
159
+ The mutation tests in `tests/test_mutations.py` delete machinery on purpose and
160
+ assert the exact set of registry points that turns red. Nineteen mutations, a
161
+ green unmutated control so no red set can be an artefact, and two tests that
162
+ apply a real change and assert the registry does **not** notice --- because on
163
+ the geometry side it genuinely cannot. One Titan mutation earned its point a
164
+ sharper assertion: dropping the exposure clamp preserved every ordering and
165
+ first went red only once the mid-life magnitude was pinned. Every asserted red set was measured. Predicting them first was
166
+ wrong nine times out of seventeen, and two of those surprises were points that
167
+ had been passing on machinery that was no longer there.
168
+
169
+ ## Reproducing
170
+
171
+ ```bash
172
+ make data # fetch + SHA-verify the Titan dataset (once; ~4 MB)
173
+ make test # 104 unit tests and 22 mutation tests (~5 min: each mutation reruns the registry)
174
+ make validate # 29 registry points and 16 declined items
175
+ make examples # 24 examples, each pinned to its exit code
176
+ make experiments # the three figures quoted above; each exits 1 if it stops holding
177
+ ```
178
+
179
+ ## Reading order
180
+
181
+ - `DECISIONS.md` --- sixteen choices, what each bought and cost. Six were
182
+ forced by defects found while building this, and say so.
183
+ - `ASSUMPTIONS.md` --- twelve things taken as given.
184
+ - `SOURCES.md` --- three, and what they are and are not.
185
+ - `STATUS.md` --- what works, what is missing, what would change the answers.
186
+
187
+ ## Series
188
+
189
+ Part of a program on the usable capacity of large accelerator fleets:
190
+ [dimaggi-ai.github.io/research](https://dimaggi-ai.github.io/research).
191
+
192
+ ## Licence
193
+
194
+ MIT. Copyright (c) 2026 Margaret Nanyonga.
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "slice-packer-torus"
7
+ version = "1.1.0"
8
+ description = "What a torus pod can actually still admit, what a chip failure costs, and who is allowed to fix it"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Margaret Nanyonga" }]
13
+ keywords = [
14
+ "torus",
15
+ "k-ary n-cube",
16
+ "capacity planning",
17
+ "fragmentation",
18
+ "multi-tenancy",
19
+ "fault domains",
20
+ ]
21
+ classifiers = [
22
+ "Intended Audience :: Science/Research",
23
+ "Intended Audience :: System Administrators",
24
+ "License :: OSI Approved :: MIT License",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Topic :: Scientific/Engineering",
27
+ "Topic :: System :: Distributed Computing",
28
+ ]
29
+ dependencies = []
30
+
31
+ [project.optional-dependencies]
32
+ dev = ["pytest==8.3.4"]
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/dimaggi-ai/slice-packer-torus"
36
+ Research = "https://dimaggi-ai.github.io/research"
37
+
38
+ [project.scripts]
39
+ slicepacker = "slicepacker.cli:main"
40
+
41
+ [tool.setuptools.packages.find]
42
+ where = ["src"]
43
+
44
+ [tool.pytest.ini_options]
45
+ testpaths = ["tests"]
46
+ pythonpath = ["src", "validation"]
47
+ addopts = "-q"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+