pythonsetuptoolssetup.pypython-packagingpython-install

setuptools post-install: get all packages installed to check versions


In Post-install script with Python setuptools, this answer shows how to make a post-install command.

I want to make a post-install command that checks for a version matchup between sub-packages from a mono-repo.

How can I get a list of packages being installed during a post-install command?


Current Attempt

from pkg_resources import working_set
from setuptools import setup
from setuptools.command.install import install


class PostInstallCommand(install):
    REPO_BASE_NAME = "foo"

    def run(self) -> None:
        install.run(self)

        one_subpkg_name = f"{self.REPO_BASE_NAME}-one"
        another_subpkg_name = f"{self.REPO_BASE_NAME}-another"
        test_subpkg_name = f"{self.REPO_BASE_NAME}-test"
        all_versions: list[str] = [
            working_set.by_key[one_subpkg_name].version,
            working_set.by_key[another_subpkg_name].version,
            working_set.by_key[test_subpkg_name].version,
        ]
        if len(set(all_versions)) != 1:
            raise NotImplementedError(
                f"test package {test_subpkg_name}'s installed versions "
                f"{all_versions} have a mismatch."
            )


setup(
    ...,
    cmdclass={"install": PostInstallCommand}
)

This solution using pkg_resources.working_set errors out, it seems working_set doesn't function at install-time:

        ...
        File "/private/var/folders/41/wlbjqvm94zn1_vbrg9fqff8m0000gn/T/pip-build-env-1iljhvso/overlay/lib/python3.10/site-packages/setuptools/dist.py", line 1217, in run_command
          super().run_command(command)
        File "/private/var/folders/41/wlbjqvm94zn1_vbrg9fqff8m0000gn/T/pip-build-env-1iljhvso/overlay/lib/python3.10/site-packages/setuptools/_distutils/dist.py", line 987, in run_command
          cmd_obj.run()
        File "<string>", line 39, in run
      KeyError: 'foo-one'

Further Information

Why can't we simply pin versions? Long story short we use setuptools_scm's get_version to dynamically synchronize versions housed in a requirements.txt, so we don't have access to the versions up front.


Solution

  • I'm not sure if going down this path is best practice, but if you really wanted to you could just run a pip freeze after installation, parse the outputs into an array of sets/dicts and do a Set.difference() across the array items.

    subpackage_deps = [
     {"foo=1.1.0", "bar=1.0.0"},
     {"foo=1.1.0", "baz=2.0.1"}
    ]
    
    subpackage_deps[0] - sub package_deps[1]
    

    I, however, don't see the purpose in doing this. Why not let setup tools do its job? If you are concerned about consistency between projects, why not write an automated test and enforce it using continuous integration to ensure parity of dependency versions between sub-projects (ie: by comparing dependency versions accross setup.py)?

    References

    https://docs.python.org/3/tutorial/datastructures.html#sets