diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index c44894a4..00000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1,14 +0,0 @@ -## Updating the code - -Please make sure that introduced changes are consistent with the testing api and add additional tests if relevant. - -## Updating the versioning - -Add a towncrier fragment under `changelog.d/` with the format -`..md`, where `` is one of `added`, -`changed`, `fixed`, `removed`, or `breaking`. - -Do not edit `CHANGELOG.md` directly in feature PRs. After the PR is merged, -the versioning workflow runs `make changelog`, deletes the consumed fragments, -updates `CHANGELOG.md`, bumps `pyproject.toml`, and commits the result as -`Update package version`. diff --git a/.github/has-functional-changes.sh b/.github/has-functional-changes.sh deleted file mode 100755 index 4c8a1d03..00000000 --- a/.github/has-functional-changes.sh +++ /dev/null @@ -1,12 +0,0 @@ -#! /usr/bin/env bash - -IGNORE_DIFF_ON="README.md CONTRIBUTING.md Makefile docs/* .gitignore LICENSE* .github/* data/*" - -last_tagged_commit=`git describe --tags --abbrev=0 --first-parent` # --first-parent ensures we don't follow tags not published in master through an unlikely intermediary merge commit - -if git diff-index --name-only --exit-code $last_tagged_commit -- . `echo " $IGNORE_DIFF_ON" | sed 's/ / :(exclude)/g'` # Check if any file that has not be listed in IGNORE_DIFF_ON has changed since the last tag was published. -then - echo "No functional changes detected." - exit 1 -else echo "The functional files above were changed." -fi \ No newline at end of file diff --git a/.github/is-version-number-acceptable.sh b/.github/is-version-number-acceptable.sh deleted file mode 100755 index 9a912adf..00000000 --- a/.github/is-version-number-acceptable.sh +++ /dev/null @@ -1,33 +0,0 @@ -#! /usr/bin/env bash - -if [[ ${GITHUB_REF#refs/heads/} == master ]] -then - echo "No need for a version check on master." - exit 0 -fi - -if ! $(dirname "$BASH_SOURCE")/has-functional-changes.sh -then - echo "No need for a version update." - exit 0 -fi - -current_version=`python .github/fetch_version.py` - -if git rev-parse --verify --quiet $current_version -then - echo "Version $current_version already exists in commit:" - git --no-pager log -1 $current_version - echo - echo "Update the version number in setup.py before merging this branch into master." - echo "Look at the CONTRIBUTING.md file to learn how the version number should be updated." - exit 1 -fi - -if ! $(dirname "$BASH_SOURCE")/has-functional-changes.sh | grep --quiet CHANGELOG.md -then - echo "CHANGELOG.md has not been modified, while functional changes were made." - echo "Explain what you changed before merging this branch into master." - echo "Look at the CONTRIBUTING.md file to learn how to write the changelog." - exit 2 -fi \ No newline at end of file diff --git a/README.md b/README.md index feb9b613..4124b1cd 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Microimpute +> PolicyEngine has archived microimpute. [Microcosm](https://github.com/PolicyEngine/microcosm) builds PolicyEngine's microdata and fits its imputation models with the `microcosm-fit` package. [PyPI](https://pypi.org/project/microimpute/) keeps the package's releases, so `pip install microimpute` still works. The [open issues](https://github.com/PolicyEngine/microimpute/issues) include correctness bugs that no release fixes, such as [the default QRF's miscalibrated quantiles](https://github.com/PolicyEngine/microimpute/issues/205). + Microimpute is a Python package for imputing variables from one survey dataset onto another. It wraps five imputation methods behind a common interface so you can benchmark them on your data and pick the one that works best, rather than defaulting to a single approach. ## Methods @@ -39,10 +41,6 @@ For image export (PNG/JPG): pip install microimpute[images] ``` -## Contributing - -Pull requests are welcome. If you find a bug or have a feature idea, open an issue or submit a PR. - ## License PolicyEngine releases the code in this repository under the [MIT License](https://github.com/PolicyEngine/microimpute/blob/main/LICENSE), and its original text and figures under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) with attribution to PolicyEngine. Third-party data and materials keep their own terms. The optional `matching` extra installs rpy2, which its authors license under GPL-2.0-or-later. The paper notebook `paper/imputing-from-scf-to-cps.ipynb` imports policyengine-us and policyengine-core, which carry AGPL-3.0. diff --git a/changelog.d/retire-microimpute.changed.md b/changelog.d/retire-microimpute.changed.md new file mode 100644 index 00000000..cbdfe9ab --- /dev/null +++ b/changelog.d/retire-microimpute.changed.md @@ -0,0 +1 @@ +Point readers to Microcosm, which builds PolicyEngine's microdata, and mark the package inactive. diff --git a/changelog.d/version-from-metadata.fixed.md b/changelog.d/version-from-metadata.fixed.md new file mode 100644 index 00000000..0eac62c2 --- /dev/null +++ b/changelog.d/version-from-metadata.fixed.md @@ -0,0 +1 @@ +Read `microimpute.__version__` from the installed package's metadata, so it matches the installed release instead of always reporting 1.1.2. diff --git a/docs/index.md b/docs/index.md index 8634c9d0..b1e5d41a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,16 +1,16 @@ # Microimpute +> PolicyEngine has archived microimpute. [Microcosm](https://github.com/PolicyEngine/microcosm) builds PolicyEngine's microdata and fits its imputation models with the `microcosm-fit` package. [PyPI](https://pypi.org/project/microimpute/) keeps the package's releases, so `pip install microimpute` still works. The [open issues](https://github.com/PolicyEngine/microimpute/issues) include correctness bugs that no release fixes, such as [the default QRF's miscalibrated quantiles](https://github.com/PolicyEngine/microimpute/issues/205). + Microimpute is a Python package for imputing variables from one survey dataset onto another. It wraps five imputation methods behind a common interface so you can benchmark them on your data and pick the one that works best: impute one or multiple variables with any of the available methods, and compare their performance using quantile loss and log loss. -The package currently supports: +The package supports: - Hot Deck Matching - Ordinary Least Squares (OLS) Linear Regression - Quantile Regression Forests (QRF) - Quantile Regression - Mixture Density Networks (MDN) -This is a work in progress and may evolve over time with new methods and features. - ## Microimputation dashboard To visualize imputation and benchmarking results, install Node.js, run `make dashboard` from the root of the [microimpute repository](https://github.com/PolicyEngine/microimpute), and open http://localhost:3000. diff --git a/microimpute/__init__.py b/microimpute/__init__.py index 5acfe3e2..7b8e81df 100644 --- a/microimpute/__init__.py +++ b/microimpute/__init__.py @@ -19,7 +19,14 @@ - Visualization: performance and comparison plots """ -__version__ = "1.1.2" +from importlib.metadata import PackageNotFoundError, version as _version + +try: + __version__ = _version("microimpute") +except PackageNotFoundError: + # Running from a source tree with no install. Keep it PEP 440 parseable so + # a consumer calling packaging.version.parse on it does not raise. + __version__ = "0.0.0+unknown" # Import automated imputation from microimpute.comparisons.autoimpute import AutoImputeResult, autoimpute diff --git a/pyproject.toml b/pyproject.toml index 35f898a9..7b3d3c6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ authors = [ { name = "Nikhil Woodruff", email = "nikhil.woodruff@outlook.com" } ] classifiers = [ + "Development Status :: 7 - Inactive", "Intended Audience :: Science/Research", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.12", diff --git a/tests/test_version.py b/tests/test_version.py new file mode 100644 index 00000000..9b195393 --- /dev/null +++ b/tests/test_version.py @@ -0,0 +1,63 @@ +"""The reported version must match the installed distribution. + +`__init__.py` used to hard-code `__version__` as 1.1.2, which fell behind the +package's own version (3.1.1 by September 2026). These tests catch that drift. +""" + +import subprocess +import sys +import textwrap +from importlib.metadata import version +from pathlib import Path + +from packaging.version import Version + +import microimpute + +REPO_ROOT = Path(__file__).resolve().parents[1] + + +def test_version_matches_installed_distribution(): + assert microimpute.__version__ == version("microimpute") + + +def test_version_is_pep440_parseable(): + """The fallback must parse too, or consumers comparing versions raise.""" + Version(microimpute.__version__) + Version("0.0.0+unknown") + + +def test_fallback_used_when_distribution_is_absent(): + """Without a distribution to read, the package reports 0.0.0+unknown.""" + # A fresh interpreter makes the metadata lookup for microimpute fail and + # then imports the package from this source tree, so the import runs the + # fallback branch in microimpute/__init__.py itself. + code = textwrap.dedent( + """ + import importlib.metadata as metadata + import sys + + real_version = metadata.version + + + def missing(name): + if name == "microimpute": + raise metadata.PackageNotFoundError(name) + return real_version(name) + + + metadata.version = missing + sys.path.insert(0, sys.argv[1]) + import microimpute + + print(microimpute.__version__) + """ + ) + result = subprocess.run( + [sys.executable, "-c", code, str(REPO_ROOT)], + capture_output=True, + text=True, + timeout=300, + ) + assert result.returncode == 0, result.stderr + assert result.stdout.strip().splitlines()[-1] == "0.0.0+unknown"