Skip to content

chore: release 0.7.1 - #196

Merged
shivamlalakiya merged 1 commit into
mainfrom
release/0.7.1
Sep 8, 2026
Merged

chore: release 0.7.1#196
shivamlalakiya merged 1 commit into
mainfrom
release/0.7.1

Conversation

@shivamlalakiya

Copy link
Copy Markdown
Contributor

Cuts 0.7.1. Steps 1 to 5 of the RELEASING.md checklist; the tag and the release go on after this merges, and the Publish release click stays with the maintainer.

Why now, and why this is not just housekeeping

Notebooks 02 and 03 import philanthropy.datasets.make_donor_panel, which does not exist in the published 0.7.0 wheel. Verified in a clean venv from a neutral cwd, because the repository root shadows site-packages and an earlier check run from the root reported the opposite:

$ ./v070/bin/python -c "import philanthropy.datasets as d; print(sorted(d.__all__))"
['fetch_kdd98_donors', 'generate_synthetic_donor_data', 'load_ciob_fundraising']

Both notebooks therefore fall into a try/except ImportError that pip-installs from git+...@main. Two consequences, and the second is the one a JOSS reviewer sees:

  1. The fallback cannot work in-process. The failed from philanthropy.datasets import make_donor_panel leaves the old module cached in sys.modules, so the re-import after a successful install raises the same error. It only works where philanthropy is absent entirely, which is fresh Colab. Anyone who followed the README's pip install philanthropy and then opened the notebook locally gets a hard failure.
  2. The "zero install, try it now" Colab badge runs an unreleased git snapshot rather than the archived release the paper points at.

Releasing is the root-cause fix. The notebook install cells are a separate PR that merges only once 0.7.1 is actually on PyPI, because until then an unconditional pip install "philanthropy[viz]>=0.7.1" would break fresh Colab too.

The version number, which was not obvious

plan.md says 0.7.1. The source tree on main already said 0.8.0: _share_of_wallet.py documented the output rename as "Until 0.8.0 the column was named sow_score" and warned "deprecated since 0.8.0", and docs/reference/index.md had a "Live on main (0.8.0)" heading. The two were written independently and never checked against each other.

The unreleased block is minor-shaped by semver: new public API (GiftIntervalCalibrator, metrics.interval_score, metrics.interval_report, datasets.make_donor_panel) plus a renamed output column. None of it is in 0.7.0.

Shipping it as 0.7.1 is a deliberate, documented departure from this project's own versioning policy, taken because the alternative is worse. Cutting 0.8.0 makes three standing promises come due today: removing WealthScreeningImputerKNN(group_col_idx=...), removing philanthropy.utils.make_donor_dataset, and flipping the FiscalYearGroupedSplitter drop_repeat_donors default to True. That last one changes the default behaviour of the splitter this package's central argument rests on, six weeks before the merge freeze. Those stay queued for a real 0.8.0.

The mitigation is that the rename is name-only: ShareOfWalletScorer.transform column 0 holds the same values in the same position, only get_feature_names_out() spells it differently, and get_legacy_feature_names_out() returns the old spelling under a DeprecationWarning until 0.9.0. Code reading the column positionally needs nothing.

What changed here

  • pyproject.toml: 0.7.0 to 0.7.1. Nothing else carries the runtime version; philanthropy.__version__ reads installed metadata.
  • CHANGELOG.md: ## [Unreleased] becomes ## [0.7.1] - 2026-09-08, with a fresh empty ## [Unreleased] above it. publish.yml rejects a heading still reading - TBD, so the date is load-bearing rather than cosmetic.
  • CHANGELOG.md heading cleanup, which is RELEASING step 1 doing its job. .gitattributes sets merge=union on this file so concurrent PRs stop conflicting, and the cost is that git will never flag a problem in it again. The union driver had left the unreleased block with four ### Added headings, two ### Fixed, two ### Changed and two ### Deprecated. Collapsed to one of each in Keep a Changelog order. No bullet text was edited and none was dropped.
  • One bullet moved section: the refactor: extract validation block and remove dead complex-data guard in transformers #194 refactor was filed under ### Fixed, and a deduplication with no behaviour change belongs under ### Changed. Union merges are line-based, not section-aware, which is exactly the failure mode RELEASING step 1 says to eyeball for.
  • CITATION.cff: version and date-released. The file's own comment says version tracks the newest published release, and the tag will point at this commit, so the archived citation has to name 0.7.1 rather than the release before it.
  • _share_of_wallet.py and docs/reference/index.md: the four strings that described the rename as arriving in 0.8.0 now say 0.7.1, including the live DeprecationWarning text users actually read. Every future promise still says 0.8.0 and is untouched: group_col_idx, utils.make_donor_dataset, and the drop_repeat_donors default. tests/test_deprecations.py stores removal versions only and test_sow_score_still_works_and_warns matches on "removed in 0.9.0", so no test needed changing.
  • docs/reference/index.md also stops telling readers that pip install philanthropy gives them 0.7.0.

Not in this PR, on purpose

  • SECURITY.md already supports 0.7.x, so 0.7.1 needs no edit there.
  • .zenodo.json carries no version field; Zenodo reads it at deposit time.
  • The ### Removed in 0.7.0 section of the deprecations page stays as it is. Those removals did happen in 0.7.0.
  • ## [1.0.0] - TBD is left exactly where it sits. It is staged, not being cut, and the publish gate compares the tag against the pyproject.toml of the commit the tag points at, so its presence changes nothing.

Verification

Run locally against the editable install, after reinstalling so the metadata version matches the bumped pyproject.toml:

python -m pip install -e ".[dev]"
python -c "from importlib.metadata import version; print(version('philanthropy'))"   # 0.7.1
make ci
make riskcov
python -m build && python -m twine check --strict dist/*

Output pasted in a comment below.

Cut 0.7.1 so the notebooks stop depending on unreleased API. Notebooks 02
and 03 import datasets.make_donor_panel, which the published 0.7.0 wheel
does not contain, so both fall into a try/except that installs from
git+main and cannot recover in-process: the failed import leaves the stale
module in sys.modules, so the re-import after a successful install raises
the same error. The Colab badge therefore ran an unreleased snapshot
rather than the archived release the paper points at.

Version number: plan.md said 0.7.1, while the source tree already
described this content as 0.8.0 in three _share_of_wallet.py strings and
one deprecations heading. The block is minor-shaped, so 0.7.1 is a
deliberate departure from the project's own semver policy, taken because
cutting 0.8.0 would make three standing removal promises come due today,
including the FiscalYearGroupedSplitter drop_repeat_donors default flip.
Those stay queued. The rename shipping here is name-only: same values,
same position, old spelling still reachable under a DeprecationWarning
until 0.9.0.

Also collapses the duplicate CHANGELOG headings the union merge driver
left in the unreleased block (four Added, two each of Fixed, Changed and
Deprecated) and moves the #194 refactor bullet from Fixed to Changed.
RELEASING step 1 exists to catch exactly that; merge=union is line-based,
not section-aware, so git will never flag it.

Local gate: 1977 passed, 4 skipped, 97.99% total coverage against the 92%
floor, riskcov 98% against 93, python -m build plus twine check --strict
both PASSED on the 0.7.1 wheel and sdist.
@shivamlalakiya

Copy link
Copy Markdown
Contributor Author

Local gate, run against the editable install after reinstalling so the metadata version matches the bumped pyproject.toml. That reinstall matters: philanthropy.__version__ reads installed metadata rather than the source tree, so a stale editable install reports the old version and CI never catches the mismatch because CI installs fresh.

$ python -c "from importlib.metadata import version; print(version('philanthropy'))"
0.7.1

$ make ci
Required test coverage of 92.0% reached. Total coverage: 97.99%
========== 1977 passed, 4 skipped, 187 warnings in 112.92s (0:01:52) ===========
==> All CI checks passed locally.

$ make riskcov
TOTAL                                                 2039     31    512     32    98%
(floor 93)

$ python -m pytest tests/test_public_api_contract.py -q
83 passed

$ python -m build && python -m twine check --strict dist/*
Successfully built philanthropy-0.7.1.tar.gz and philanthropy-0.7.1-py3-none-any.whl
Checking dist/philanthropy-0.7.1-py3-none-any.whl: PASSED
Checking dist/philanthropy-0.7.1.tar.gz: PASSED

Exit code 0 on all of them.

The premise was checked against the published wheel rather than assumed, in a clean venv from a neutral cwd:

$ ./v070/bin/python -c "import philanthropy.datasets as d; print(sorted(d.__all__))"
['fetch_kdd98_donors', 'generate_synthetic_donor_data', 'load_ciob_fundraising']
GiftIntervalCalibrator False
interval_score False
legacy shim False

So none of the unreleased block is in 0.7.0, which is what makes this a real release rather than a version-string bump.

What this PR does not do: it does not tag, and it does not publish. After merge the tag goes on main (not on this branch tip, since squash merges mean the branch tip never lands on main), and the GitHub release is created as a draft. publish.yml fires on release: published, the PyPI upload is irreversible and cannot be re-uploaded under the same version, and RELEASING.md designs that click as the maintainer's gate. It stays there.

@shivamlalakiya
shivamlalakiya merged commit dfb4663 into main Sep 8, 2026
15 checks passed
@shivamlalakiya
shivamlalakiya deleted the release/0.7.1 branch September 8, 2026 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant