Skip to content

move_wheel_files fails on setuptools dependencies #4264

Closed

Description

@jaraco

Reported in pypa/setuptools#951, it appears that when upgrading or force-reinstalling setuptools dependencies, pip calls into pkg_resources to get package metadata, but since the package is currently uninstalled, the search for its metadata fails.

I haven't looked into the pip code, but I wanted to ask this project first: Can someone provide a brief explanation for what pip is doing here and if there's an adjustment that pip or pkg_resources could make to avoid the failure?

Activity

  1. dstufft commented on Feb 11, 2017

    @dstufft
    Member

    Um, well move_wheel_file is use to install a wheel. I think that inside of it, it calls distutils_scheme() which creates a Distribution object to figure out where distutils would have installed the files so that we can install them to the same location (taking into account ~/.pydistutils.cfg files and such).

  2. jaraco commented on Feb 13, 2017

    @jaraco
    MemberAuthor

    Hmm. The Distribution object that's created is a distutils.dist.Distribution, which wouldn't be calling into setuptools unless that object had been monkeypatched by an import of setuptools. Searching through the pip code, it does seem that setuptools is imported at the invocation of InstallRequirement.setup_py.

    That explains why the issue only occurs when a pure sdist package is first installed (such as junit-xml). It's the installation of that package that triggers the import of setuptools in the pip runtime, which triggers the monkeypatch, which triggers the enumeration of distutils.setup_keywords entry points, which forces the dependencies of setuptools to be required, but since six isn't currently installed things fall apart. I suspect this issue could also be triggered by the upgrade or downgrade of any setuptools dependency from a source dist.

    My first instinct is to see if we can adapt pip not to import setuptools and thus not monkeypatch the Distribution object. I'm unsure if the monkeypatching is necessary.

    Indeed, when I apply this patch, the failure goes away:

    diff --git a/pip/req/req_install.py b/pip/req/req_install.py
    index 1a98f377..f4716c57 100644
    --- a/pip/req/req_install.py
    +++ b/pip/req/req_install.py
    @@ -383,17 +383,10 @@ class InstallRequirement(object):
         @property
         def setup_py(self):
             assert self.source_dir, "No source dir for %s" % self
    -        try:
    -            import setuptools  # noqa
    -        except ImportError:
    -            if get_installed_version('setuptools') is None:
    -                add_msg = "Please install setuptools."
    -            else:
    -                add_msg = traceback.format_exc()
    -            # Setuptools is not available
    -            raise InstallationError(
    -                "Could not import setuptools which is required to "
    -                "install from a source distribution.\n%s" % add_msg
    +        if get_installed_version('setuptools') is None:
    +            raise InstallationError("Setuptools is required to install "
    +                "from a source distribution, but none was found. Please "
    +                "install setuptools."
                 )
     
             setup_py = os.path.join(self.setup_py_dir, 'setup.py')
    

    I can imagine this change would cause failures in environments where setuptools is importable but not installed as pip sees it. To retain the existing behavior, one could import setuptools in a subprocess to check for the existence of an importable setuptools... or bypass a full import and call into find_module instead to see if it can be found without executing it.

    I'd also be open to the possibility of removing the implicit monkeypatching of distutils, such that all setup scripts would would have to be updated to reference the setuptools objects directly or call the monkeypatching explicitly. There's a lot of unknowns and possibly intractable challenges in that scenario.

    What do you think about the patch above? Do late versions of pip need to detect and pass on an importable but not installed setuptools? Do you have any other ideas?

  3. jaraco commented on Feb 13, 2017

    @jaraco
    MemberAuthor

    I'd also be open to the possibility of removing the implicit monkeypatching of distutils

    Another less aggressive option would be for setuptools to expose a routine to unmonkeypatch distutils, and pip could call that after the import. Another option would be for pip to import the Distribution object before it has a chance to be monkeypatched and always use that.

  4. dstufft commented on Feb 13, 2017

    @dstufft
    Member

    Do late versions of pip need to detect and pass on an importable but not installed setuptools?

    I don't think there is any reason they need to work if setuptools isn't "installed", at least not off the top of my head.

  5. added a commit that references this issue on Feb 14, 2017
    8dbf77f
  6. added a commit that references this issue on Feb 21, 2017
    479e47b
  7. bsundsrud commented on Mar 3, 2017

    @bsundsrud

    Is there an ETA on releasing a fix for this? This is heavily impacting our workflow.

  8. underyx commented on Mar 20, 2017

    @underyx
    Contributor

    cryptography depends on setuptools so if we run pip-compile, it pins the appdirs version.

    If you then try to use tox to run anything, it will

    1. Get the latest setuptools and appdirs with broken uninstallation
    2. Try to install our requirements which have a non-latest appdirs version pinned, triggering an uninstall and breaking everything

    Rather annoying. Any chance of a release soon?

  9. added a commit that references this issue on Jun 21, 2017
  10. added
    auto-lockedOutdated issues that have been locked by automation
    on Jun 3, 2019
  11. locked as resolved and limited conversation to collaborators on Jun 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    auto-lockedOutdated issues that have been locked by automation

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions