Add rules-based tax-unit construction engine + PyPI release machinery#3
Merged
Conversation
abb33e4 to
046881e
Compare
Extract the rules-based tax-unit / filing-status construction engine from policyengine-us-data into microunit (roadmap item 2). The engine is copied verbatim (no logic changes) and made source-agnostic and self-contained. This integrates additively with the existing unit-assignment scaffold. New modules: - src/microunit/tax_unit_construction.py: core engine. Public entry construct_tax_units(person, year, mode) with "policyengine" (default) and "census_documented" modes; HEAD/SPOUSE/DEPENDENT role constants. The only change vs. the source is the internal import (now microunit.rule_helpers); zero non-import edits to the logic. - src/microunit/rule_helpers.py: dependency/filing rule helpers (renamed from tax_unit_rule_helpers). The optional policyengine_us import shim is dropped; the qualifying-relative gross income limit now loads from packaged data, so the engine no longer depends on policyengine-us. - src/microunit/data/dependent_gross_income_limit.yaml: vendored IRC 151(d) exemption-amount values (through 2026), loaded via importlib.resources. Integration: - __init__.py: additively export construct_tax_units, the role constants, modes, CPSRelationshipCode, and the rule helpers (existing API unchanged). - units/tax.py: add construct_tax_partition(), a UnitPartition adapter over construct_tax_units, fulfilling the prior "port rules-based tax-unit construction here" TODO. assign_tax_partition still preserves native IDs. - units/__init__.py: export construct_tax_partition. - pyproject.toml: add numpy and pyyaml deps; ship the YAML as wheel/sdist data. - uv.lock: regenerated for the new direct dependencies. - README.md: document the engine, the two modes, the input contract, and the ACS-column-mapping boundary. Tests (60 passing total): test_tax_unit_construction.py ports the full CPS suite to the microunit namespace; test_tax_partition_adapter.py covers the new adapter; test_import.py checks the public API and packaged-data resolution. ACS boundary: acs_to_cps_columns.py (ACS-PUMS-specific RELSHIPP/RELP and spouse/parent inference) is intentionally NOT included. microunit takes already-normalized CPS-like person frames; ACS column mapping and the ACS-specific tests remain in policyengine-us-data. Extracted from PolicyEngine/policyengine-us-data@f7458313. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
046881e to
d3eccbb
Compare
Productionize microunit so it publishes to PyPI via PolicyEngine's standard
micro* release flow (matches microcalibrate/microimpute):
- .github/workflows/pr.yaml: ruff (lint + format) + pytest (3.11/3.13/3.14)
+ build, on PRs
- .github/workflows/versioning.yaml: on merge to main, bump the version from
changelog.d fragments (towncrier) and publish to PyPI via secrets.PYPI
- .github/workflows/changelog_entry.yaml: require a changelog fragment on PRs
- .github/workflows/main.yml: tests on push to main
- .github/{bump_version.py,fetch_version.py,publish-git-tag.sh}
- Makefile (install/test/check-format/format/build/changelog)
- towncrier config + CHANGELOG.md + changelog.d/ with the initial fragment
- LICENSE (MIT)
Base version set to 0.0.0 so the first auto-bump publishes 0.1.0. Existing
core.py/diagnostics.py are ruff-format-normalized for the new format gate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
Author
Added: release machinery to productionize for PyPIPushed
Base version set to Verified locally end-to-end: bump On merge → PyPI publish, the consumers (policyengine-us-data#1157, microplex-us#114) switch from the |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the rules-based tax-unit / filing-status construction engine extracted from
policyengine-us-data(source:upstream/main@f7458313), fulfilling roadmap item 2 ("Move reusable tax-unit construction out of policyengine-us-data / policyengine-us"). This is a careful copy-and-repackage — no logic changes to the engine — integrated additively with the existing unit-assignment scaffold (UnitPartition,assign_*_partition, diagnostics, registry are all untouched).microunitbecomes a publishable dependency for the CPS/ACS pipelines inpolicyengine-us-dataand formicroplex-us.New modules
src/microunit/tax_unit_construction.py— the core engine. Public entryconstruct_tax_units(person, year, mode)with modes"policyengine"(default) and"census_documented", plusHEAD/SPOUSE/DEPENDENTrole constants. The only change vs. the source is the internal import (nowmicrounit.rule_helpers); there are zero non-import edits to the logic.src/microunit/rule_helpers.py— dependency/filing rule helpers (renamed fromtax_unit_rule_helpers.py). The optionalpolicyengine_usimport shim is dropped; the qualifying-relative gross income limit now loads from packaged data, so the engine no longer depends onpolicyengine-us.src/microunit/data/dependent_gross_income_limit.yaml— vendored IRC 151(d) personal/dependent exemption-amount values (through 2026; used by the IRC 152(d)(1)(B) gross income test), loaded viaimportlib.resources.Integration (additive)
__init__.py— additively exportsconstruct_tax_units, the role constants, the modes,CPSRelationshipCode, and the rule helpers. The pre-existing public API is unchanged.units/tax.py— addsconstruct_tax_partition(), aUnitPartitionadapter overconstruct_tax_units, fulfilling the prior "Full rules-based tax-unit construction should be ported here" TODO.assign_tax_partitionstill preserves native source IDs.units/__init__.py— exportsconstruct_tax_partition.pyproject.toml— addsnumpyandpyyamldeps; ships the YAML as wheel/sdist package data.uv.lockregenerated for the new direct deps.README.md— documents the engine, the two modes, the input column contract, and the ACS-mapping boundary; marks roadmap item 2 done.Public API
construct_tax_units(person, year, mode="policyengine")returns(person_assignments, tax_unit):person_assignmentscarriesTAX_ID,tax_unit_role_input(bytes),is_related_to_head_or_spouse;tax_unitcarriesTAX_IDandfiling_status_input(bytes).ACS boundary decision
acs_to_cps_columns.py(~500 lines of ACS-PUMS-specificRELSHIPP/RELPtranslation and heuristic spouse/parent inference) is intentionally not included.microunitoperates on already-normalized CPS-like person frames; ACS column mapping — and the ACS-specific tests that exercise it and the ACS dataset writer — stay inpolicyengine-us-data, whose thin ACS wrapper can keep mapping columns and then callmicrounit.construct_tax_units. Documented in the README.Verification
Verified from a fresh clone of this branch and from an out-of-tree installed wheel:
uv run pytest→ 43 passed (existing scaffold suite + the full ported CPS construction suite + new adapter/import tests). Reproducible from the frozenuv.lock.uv run ruff check→ clean (whole repo);uv run ruff format --check→ clean on all added/modified files.import microunit/construct_tax_units(...)/construct_tax_partition(...)end-to-end and that the packaged YAML resolves viaimportlib.resources(dependent_gross_income_limit(2024) == 5050,(2026) == 5300).Pre-PyPI consumer install spec
🤖 Generated with Claude Code