econeval 0.3.1__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.
- econeval-0.3.1/LICENSE +21 -0
- econeval-0.3.1/PKG-INFO +373 -0
- econeval-0.3.1/README.md +353 -0
- econeval-0.3.1/pyproject.toml +48 -0
- econeval-0.3.1/setup.cfg +4 -0
- econeval-0.3.1/src/econeval/__init__.py +85 -0
- econeval-0.3.1/src/econeval/__main__.py +10 -0
- econeval-0.3.1/src/econeval/cli.py +284 -0
- econeval-0.3.1/src/econeval/config.py +599 -0
- econeval-0.3.1/src/econeval/errors.py +12 -0
- econeval-0.3.1/src/econeval/interop.py +269 -0
- econeval-0.3.1/src/econeval/invariants.py +502 -0
- econeval-0.3.1/src/econeval/reporting.py +1083 -0
- econeval-0.3.1/src/econeval/scenarios.py +1515 -0
- econeval-0.3.1/src/econeval.egg-info/PKG-INFO +373 -0
- econeval-0.3.1/src/econeval.egg-info/SOURCES.txt +26 -0
- econeval-0.3.1/src/econeval.egg-info/dependency_links.txt +1 -0
- econeval-0.3.1/src/econeval.egg-info/entry_points.txt +2 -0
- econeval-0.3.1/src/econeval.egg-info/requires.txt +11 -0
- econeval-0.3.1/src/econeval.egg-info/top_level.txt +1 -0
- econeval-0.3.1/tests/test_advanced_checks.py +363 -0
- econeval-0.3.1/tests/test_cli.py +337 -0
- econeval-0.3.1/tests/test_config.py +66 -0
- econeval-0.3.1/tests/test_interop.py +66 -0
- econeval-0.3.1/tests/test_invariants.py +36 -0
- econeval-0.3.1/tests/test_reporting.py +563 -0
- econeval-0.3.1/tests/test_scenarios.py +270 -0
- econeval-0.3.1/tests/test_security.py +25 -0
econeval-0.3.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 EconEval Contributors
|
|
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.
|
econeval-0.3.1/PKG-INFO
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: econeval
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: CI/CD checks for economic, policy, and statistical models
|
|
5
|
+
Author: EconEval Contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: pydantic>=2.0
|
|
11
|
+
Requires-Dist: numexpr>=2.14
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: pre-commit>=3.7; extra == "dev"
|
|
14
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
16
|
+
Requires-Dist: ruff>=0.5.0; extra == "dev"
|
|
17
|
+
Provides-Extra: stats
|
|
18
|
+
Requires-Dist: statsmodels>=0.14; extra == "stats"
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# EconEval
|
|
22
|
+
|
|
23
|
+
<p align="left">
|
|
24
|
+
<img src="assets/logo.svg" alt="EconEval logo" width="120" />
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/econeval/)
|
|
28
|
+
[](https://github.com/Farukhsb/econeval/actions/workflows/ci.yml)
|
|
29
|
+
[](https://www.python.org/)
|
|
30
|
+
[](https://github.com/Farukhsb/econeval/releases/tag/v0.3.1)
|
|
31
|
+
|
|
32
|
+
EconEval is a small open-source framework for checking economic and policy models in CI.
|
|
33
|
+
|
|
34
|
+
It is built for the kind of code that can look fine at the syntax level and still be wrong in practice. A model can run, pass unit tests, and still break an economic rule, drift off course after a data change, or produce results that no longer make sense under stress. EconEval is meant to catch those problems early, before they reach a report, dashboard, or paper.
|
|
35
|
+
|
|
36
|
+
Latest release: [`v0.3.1`](https://github.com/Farukhsb/econeval/releases/tag/v0.3.1)
|
|
37
|
+
|
|
38
|
+
## What It Does
|
|
39
|
+
|
|
40
|
+
EconEval currently does three things:
|
|
41
|
+
|
|
42
|
+
- loads a model check config
|
|
43
|
+
- runs invariant tests against a Python object
|
|
44
|
+
- writes JSON, JUnit, Markdown, or HTML reports that CI can keep or fail on
|
|
45
|
+
|
|
46
|
+
The config layer is validated with Pydantic, and invariant evaluation can route to `numexpr` for vectorized math or a restricted logical evaluator for model-state checks.
|
|
47
|
+
|
|
48
|
+
That gives you a practical starting point for:
|
|
49
|
+
|
|
50
|
+
- checking that important economic rules still hold
|
|
51
|
+
- making model assumptions explicit in code
|
|
52
|
+
- failing pull requests when a change breaks a rule you care about
|
|
53
|
+
|
|
54
|
+
## Who Should Use This
|
|
55
|
+
|
|
56
|
+
EconEval is a fit for people who need model checks that are closer to policy and economics than generic unit tests.
|
|
57
|
+
|
|
58
|
+
It is especially useful when the people reviewing a model are not just software engineers, but also domain stakeholders who care about whether the model still makes sense economically.
|
|
59
|
+
|
|
60
|
+
- academic economists validating research code and replication projects
|
|
61
|
+
- policy analysts checking that a model still respects program rules and constraints
|
|
62
|
+
- quantitative consultants delivering models to clients with auditability requirements
|
|
63
|
+
- teams working on forecasting, scenario analysis, or simulation pipelines
|
|
64
|
+
- researchers who want a lightweight validation layer before a model is published or deployed
|
|
65
|
+
- applied data scientists building models that need economics-aware guardrails
|
|
66
|
+
- analysts who want CI checks they can explain in a report or appendix
|
|
67
|
+
|
|
68
|
+
## Why It Exists
|
|
69
|
+
|
|
70
|
+
Traditional software tests are useful, but they do not tell you whether a model still behaves like a valid model.
|
|
71
|
+
|
|
72
|
+
For example, a change might:
|
|
73
|
+
|
|
74
|
+
- flip the sign of an elasticity
|
|
75
|
+
- violate a market-clearing condition
|
|
76
|
+
- break a policy constraint
|
|
77
|
+
- quietly change the meaning of a downstream output
|
|
78
|
+
|
|
79
|
+
EconEval gives you a place to encode those rules and run them automatically.
|
|
80
|
+
|
|
81
|
+
## Current MVP
|
|
82
|
+
|
|
83
|
+
The first usable version of EconEval does three things well:
|
|
84
|
+
|
|
85
|
+
1. read a simple YAML config
|
|
86
|
+
2. evaluate invariant expressions against a model object
|
|
87
|
+
3. return a clear pass or fail result that GitHub Actions can use
|
|
88
|
+
|
|
89
|
+
That is enough to support a real workflow without pretending to solve every validation problem at once.
|
|
90
|
+
|
|
91
|
+
## Example Config
|
|
92
|
+
|
|
93
|
+
```yaml
|
|
94
|
+
project: demo-model
|
|
95
|
+
version: 1
|
|
96
|
+
|
|
97
|
+
invariants:
|
|
98
|
+
- name: elasticity_must_be_negative
|
|
99
|
+
expression: model.elasticity < 0
|
|
100
|
+
- name: supply_must_be_non_negative
|
|
101
|
+
expression: model.supply >= 0
|
|
102
|
+
|
|
103
|
+
stress_tests:
|
|
104
|
+
- name: stagflation_shock
|
|
105
|
+
dataset: data/stagflation.csv
|
|
106
|
+
metric: mape
|
|
107
|
+
threshold: 0.15
|
|
108
|
+
|
|
109
|
+
- name: macro_stagflation
|
|
110
|
+
kind: synthetic
|
|
111
|
+
metric: invariants
|
|
112
|
+
manipulations:
|
|
113
|
+
- variable: input_data.unemployment_rate
|
|
114
|
+
action: add
|
|
115
|
+
value: 0.04
|
|
116
|
+
- variable: input_data.energy_costs
|
|
117
|
+
action: multiply
|
|
118
|
+
value: 1.5
|
|
119
|
+
invariants:
|
|
120
|
+
- name: slowdown_flag
|
|
121
|
+
expression: model.predicted_gdp_growth < 0.01
|
|
122
|
+
|
|
123
|
+
fairness:
|
|
124
|
+
enabled: true
|
|
125
|
+
metrics:
|
|
126
|
+
- demographic_parity_difference
|
|
127
|
+
- disparate_impact_ratio
|
|
128
|
+
- gini
|
|
129
|
+
- atkinson
|
|
130
|
+
- equal_opportunity_difference
|
|
131
|
+
- equalized_odds_difference
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## How It Fits Together
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
model repo
|
|
138
|
+
-> econeval.yml
|
|
139
|
+
-> load config
|
|
140
|
+
-> run invariant checks
|
|
141
|
+
-> collect results
|
|
142
|
+
-> fail or pass CI
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Project Layout
|
|
146
|
+
|
|
147
|
+
```text
|
|
148
|
+
econeval/
|
|
149
|
+
.github/
|
|
150
|
+
workflows/
|
|
151
|
+
ci.yml
|
|
152
|
+
action.yml
|
|
153
|
+
examples/
|
|
154
|
+
basic_model/
|
|
155
|
+
model.py
|
|
156
|
+
econeval.yml
|
|
157
|
+
drift_model/
|
|
158
|
+
model.py
|
|
159
|
+
econeval.yml
|
|
160
|
+
fairness_model/
|
|
161
|
+
README.md
|
|
162
|
+
model.py
|
|
163
|
+
econeval.yml
|
|
164
|
+
broken_model/
|
|
165
|
+
model.py
|
|
166
|
+
econeval.yml
|
|
167
|
+
policy_model/
|
|
168
|
+
model.py
|
|
169
|
+
econeval.yml
|
|
170
|
+
advanced_model/
|
|
171
|
+
model.py
|
|
172
|
+
econeval.yml
|
|
173
|
+
src/
|
|
174
|
+
econeval/
|
|
175
|
+
__init__.py
|
|
176
|
+
cli.py
|
|
177
|
+
config.py
|
|
178
|
+
invariants.py
|
|
179
|
+
scenarios.py
|
|
180
|
+
reporting.py
|
|
181
|
+
tests/
|
|
182
|
+
test_config.py
|
|
183
|
+
test_invariants.py
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Install
|
|
187
|
+
|
|
188
|
+
For development from a checkout:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
git clone https://github.com/Farukhsb/econeval.git
|
|
192
|
+
cd econeval
|
|
193
|
+
pip install -e .[dev]
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
`pytest` and `ruff` are included in the `dev` extra. If you only want the CLI, install the package without the extra.
|
|
197
|
+
|
|
198
|
+
EconEval parses its YAML-like config format with its own loader, so you do not need `PyYAML` for the current release.
|
|
199
|
+
|
|
200
|
+
Once the package is published to PyPI, the normal install path will be:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
pip install econeval
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
If you want the published package state, start from the `v0.3.1` release tag or the GitHub release page.
|
|
207
|
+
|
|
208
|
+
## How To Use It
|
|
209
|
+
|
|
210
|
+
Create a config file that lists the checks you want to enforce, then point EconEval at a Python model class.
|
|
211
|
+
|
|
212
|
+
Command line example:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
econeval --config examples/basic_model/econeval.yml --model examples/basic_model/model.py --class DemoModel --report econeval-report.json
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
If you prefer module execution, `python -m econeval` works the same way.
|
|
219
|
+
|
|
220
|
+
To write a text report instead:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
econeval --config examples/advanced_model/econeval.yml --model examples/advanced_model/model.py --class AdvancedModel --report econeval-report.md --format markdown
|
|
224
|
+
econeval --config examples/advanced_model/econeval.yml --model examples/advanced_model/model.py --class AdvancedModel --report econeval-report.html --format html
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## GitHub Action
|
|
228
|
+
|
|
229
|
+
The repository also exposes a composite GitHub Action so workflows can run EconEval in one step.
|
|
230
|
+
|
|
231
|
+
```yaml
|
|
232
|
+
- uses: Farukhsb/econeval@v1
|
|
233
|
+
with:
|
|
234
|
+
config: examples/basic_model/econeval.yml
|
|
235
|
+
model: examples/basic_model/model.py
|
|
236
|
+
class: DemoModel
|
|
237
|
+
report: econeval-report.json
|
|
238
|
+
python-version: "3.11"
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
The action installs the package from the action source, sets up Python, and runs the CLI with the inputs you provide.
|
|
242
|
+
|
|
243
|
+
## Fairness Checks
|
|
244
|
+
|
|
245
|
+
Fairness checks expect a tabular dataset with:
|
|
246
|
+
|
|
247
|
+
- a group column, defaulting to `group`
|
|
248
|
+
- one or more feature columns that are passed to `predict(features)`
|
|
249
|
+
- an `actual` column when you use label-based metrics such as `equal_opportunity_difference` or `equalized_odds_difference`
|
|
250
|
+
|
|
251
|
+
The example in [examples/fairness_model/README.md](examples/fairness_model/README.md) shows the full data format.
|
|
252
|
+
|
|
253
|
+
The built-in thresholds are:
|
|
254
|
+
|
|
255
|
+
- `demographic_parity_difference`: pass at `<= 0.2`
|
|
256
|
+
- `disparate_impact_ratio`: pass at `>= 0.8`
|
|
257
|
+
- `equal_opportunity_difference`: pass at `<= 0.2`
|
|
258
|
+
- `equalized_odds_difference`: pass at `<= 0.2`
|
|
259
|
+
- `gini`: pass at `<= 0.3`
|
|
260
|
+
- `atkinson`: pass at `<= 0.2`
|
|
261
|
+
|
|
262
|
+
Fairness results also include a `severity` field:
|
|
263
|
+
|
|
264
|
+
- `pass` for checks within the threshold
|
|
265
|
+
- `warn` for borderline misses that should not fail CI
|
|
266
|
+
- `fail` for clear misses or execution errors
|
|
267
|
+
|
|
268
|
+
To run the full advanced example with synthetic shocks, drift checks, fairness metrics, and scan checks:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
econeval --config examples/advanced_model/econeval.yml --model examples/advanced_model/model.py --class AdvancedModel --report artifacts/advanced-report.md --format markdown
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
What the current runner expects:
|
|
275
|
+
|
|
276
|
+
- a model file that defines a class you can import by name
|
|
277
|
+
- a `predict(features)` method for stress tests, drift checks, and fairness checks
|
|
278
|
+
- CSV datasets with an `actual` column for stress tests
|
|
279
|
+
- CSV datasets with the feature or group columns required by the check
|
|
280
|
+
|
|
281
|
+
If your runtime looks different, use a thin adapter. EconEval now normalizes
|
|
282
|
+
common shapes like callable models, `solve()`-style solver wrappers, and
|
|
283
|
+
PyMC-style posterior predictive samplers so you can bridge external engines
|
|
284
|
+
without rewriting the check pipeline.
|
|
285
|
+
|
|
286
|
+
Example invariant rule:
|
|
287
|
+
|
|
288
|
+
```yaml
|
|
289
|
+
- name: elasticity_must_be_negative
|
|
290
|
+
expression: model.elasticity < 0
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
If the expression returns `False`, the invariant fails.
|
|
294
|
+
|
|
295
|
+
The JSON report includes the project name, a summary count, and the result of each invariant, economic check, stress test, drift check, economic drift check, and fairness check.
|
|
296
|
+
|
|
297
|
+
## Expression Engine
|
|
298
|
+
|
|
299
|
+
EconEval uses a restricted AST-based expression engine for invariants.
|
|
300
|
+
|
|
301
|
+
That keeps the syntax simple for users while avoiding raw `eval()`. It is still a security-sensitive surface, so the allowed syntax is intentionally narrow:
|
|
302
|
+
|
|
303
|
+
- comparisons, boolean logic, simple arithmetic, and attribute access on the model object
|
|
304
|
+
|
|
305
|
+
It explicitly rejects function calls, subscripts, comprehensions, lambdas, dictionaries, sets, and private attributes such as `__class__`.
|
|
306
|
+
|
|
307
|
+
If you need a broader or more standardized expression engine later, the most likely replacement options are `asteval` or `numexpr`, depending on whether you need general Python-like rules or numeric-only expressions.
|
|
308
|
+
|
|
309
|
+
## Examples
|
|
310
|
+
|
|
311
|
+
- `examples/basic_model` shows the happy path with invariants, stress tests, drift checks, and fairness checks.
|
|
312
|
+
- `examples/broken_model` shows a model and dataset that fail the checks.
|
|
313
|
+
- `examples/drift_model` focuses on drift validation, including trend drift over time.
|
|
314
|
+
- `examples/fairness_model` focuses on fairness checks and a simple stress test.
|
|
315
|
+
- `examples/policy_model` is a minimal policy-focused fairness example.
|
|
316
|
+
- `examples/advanced_model` shows accounting identities, monotonicity, convergence, grid sweeps, and synthetic shocks.
|
|
317
|
+
- `examples/advanced_model` also shows synthetic manipulations, economic drift checks, and GitHub-friendly report output.
|
|
318
|
+
- `examples/advanced_model` now includes a native scan check for monotonicity and elasticity-style responses.
|
|
319
|
+
- `examples/demo_notebook.ipynb` is a short walkthrough you can open in Jupyter or VS Code.
|
|
320
|
+
- The repository examples are intended to double as a lightweight demo workflow.
|
|
321
|
+
|
|
322
|
+
## Roadmap
|
|
323
|
+
|
|
324
|
+
Planned or likely next steps for the project:
|
|
325
|
+
|
|
326
|
+
- expand stress testing with parameter shocks and Monte Carlo runs
|
|
327
|
+
- deepen drift detection over time with rolling windows and alerting
|
|
328
|
+
- add fairness and equity checks for policy-relevant models
|
|
329
|
+
- add deeper interop with tools like `pandas`, `statsmodels`, `PyMC`, `GAMS`, and Julia
|
|
330
|
+
- fairness and drift checks already accept pandas-like row data through `to_dict(orient="records")`
|
|
331
|
+
- install `econeval[stats]` if you want the optional `statsmodels`-based drift helper
|
|
332
|
+
- use `--format dashboard` for a richer HTML overview with filtering and collapsible drill-downs
|
|
333
|
+
|
|
334
|
+
## Release Flow
|
|
335
|
+
|
|
336
|
+
Publishing a GitHub Release triggers the release workflow in [`.github/workflows/release.yml`](.github/workflows/release.yml).
|
|
337
|
+
|
|
338
|
+
That workflow:
|
|
339
|
+
|
|
340
|
+
- installs the package
|
|
341
|
+
- runs the test suite
|
|
342
|
+
- runs EconEval against the example model
|
|
343
|
+
- uploads a release report artifact
|
|
344
|
+
|
|
345
|
+
## Next Step
|
|
346
|
+
|
|
347
|
+
The next useful additions are:
|
|
348
|
+
|
|
349
|
+
- a richer report viewer
|
|
350
|
+
- more scenario types
|
|
351
|
+
|
|
352
|
+
## Release Checklist
|
|
353
|
+
|
|
354
|
+
When you are ready to publish a new version:
|
|
355
|
+
|
|
356
|
+
1. run the test suite locally
|
|
357
|
+
2. update the package version if needed
|
|
358
|
+
3. tag the release, for example `v0.3.1`
|
|
359
|
+
4. publish the GitHub Release so the release workflow runs
|
|
360
|
+
5. confirm the release artifact uploaded from Actions
|
|
361
|
+
6. confirm the wheel and sdist were published to PyPI
|
|
362
|
+
|
|
363
|
+
To publish to PyPI through GitHub Actions, enable PyPI trusted publishing for this
|
|
364
|
+
repository and then publish the GitHub Release. The release workflow will build the
|
|
365
|
+
distribution and upload it automatically.
|
|
366
|
+
|
|
367
|
+
## Changelog
|
|
368
|
+
|
|
369
|
+
See [CHANGELOG.md](CHANGELOG.md) for version-by-version changes.
|
|
370
|
+
|
|
371
|
+
## License
|
|
372
|
+
|
|
373
|
+
MIT
|