setuptools: pip 21.3+ in-tree-builds with setuptools, do not work with read-only source trees

Description

We are using pip as part of our build pipeline within a project containing a mix of languages. In order to keep everything clean and reproducible, we mount the source code read-only in a docker container and run the build steps there.

Even it’s a mix of languages and tools, this approach used to work fine until now, when pip as the only of all the tools involved suddenly tried to touch the source tree during build.

I have read though the discussion in https://github.com/pypa/pip/issues/7555 that led to this change, but I feel like the point that there might be good reasons not to touch the source directory didn’t get enough attention:

  • You want to keep source code and build artifacts separate
  • You don’t want to clutter your scm
  • Files created within the docker container are usually owned by root. So in our setup, our source directory would be cluttered with files owned by root after the container exits
  • You want to make sure that no build script accidentally modifies the source code at build time. This is a very important point especially in large projects where it’s hard to find side-effects in build scripts

Expected behavior

All other major packaging tools I know offer an option to chose a build directory that is separate from the source dir. I would expect that pip works in a similar way.

pip version

21.3

Python version

3.10

OS

ubuntu

How to Reproduce

Create a python package, mount read-only into a docker container and try to let pip install it, e.g:

mkdir readonly
echo "from setuptools import setup\nsetup(name='hello', version='1.0.0')" > readonly/setup.py
docker run -ti --rm -v `pwd`/readonly:/src:ro python:3.10 /bin/bash -c "pip install --upgrade pip; pip install /src"
rm -rf readonly

Output

Building wheels for collected packages: hello
  Building wheel for hello (setup.py) ... error
  ERROR: Command errored out with exit status 1:
   command: /usr/local/bin/python -u -c 'import io, os, sys, setuptools, tokenize; sys.argv[0] = '"'"'/src/setup.py'"'"'; __file__='"'"'/src/setup.py'"'"';f = getattr(tokenize, '"'"'open'"'"', open)(__file__) if os.path.exists(__file__) else io.StringIO('"'"'from setuptools import setup; setup()'"'"');code = f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' bdist_wheel -d /tmp/pip-wheel-gx0vxrjn
       cwd: /src/
  Complete output (8 lines):
  running bdist_wheel
  running build
  installing to build/bdist.linux-x86_64/wheel
  running install
  running install_egg_info
  running egg_info
  creating hello.egg-info
  error: could not create 'hello.egg-info': Read-only file system
  ----------------------------------------
  ERROR: Failed building wheel for hello
  Running setup.py clean for hello
Failed to build hello

Code of Conduct

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 3
  • Comments: 21 (12 by maintainers)

Most upvoted comments

https://pip.pypa.io/en/stable/topics/local-project-installs/#build-artifacts

Changed in version 22.1: The --use-deprecated=out-of-tree-build option has been removed.

Welp, the use-deprecated option is now deprecated. 😱

Is there any remaining workaround for the increasingly common use-case of wanting to pip install off a read-only mounted directory (like a network mounted FS or docker volume)? Currently I’ve just pinned the older pip and crossed my fingers that a solution is forthcoming 🤞.

It’s a little inaccurate to describe it as a “workaround flag”. In reality, pip needed to change the build process to in-tree builds to handle a number of internal issues and blockages that were caused by the previous “copy and build” approach. The idea that installers should “trust the backend” to do the build has been around since the original introduction of PEP 517, 5 years ago. We kept out-of-tree builds for a long time, and then when we finally did remove them, we followed our normal deprecation process.

It’s a shame that in all that time, setuptools didn’t address the in-tree build issue, but they have a lot of legacy issues to address, and (as for most open source projects) very limited maintainer resource. So it’s not surprising that they had other priorities. But I don’t think that we can hold back development on pip indefinitely because of that, unfortunately.

Does anyone know if the required change to do out-of-source builds is practical to implement in setuptools?

Practical? Yes, I’m sure it is. Easy? Quite probably not. My understanding is that nothing is easy in setuptools, because people can do arbitrarily complex stuff in setup.py. I imagine migrating to a much more declarative UI (which is what setuptools has been working on for some time) is the best way to make out-of-source builds safe (in the majority of cases at least). Of course, if I’m right, then setuptools could end up allowing out-of-tree builds only if the project opts in, which means that you’d still need to get the project to update their build…

Sorry I can’t offer a different short term solution, but copying the source code to a read-write directory is the short term solution, for what it’s worth.

@pradyunsg Well, I understand that this is hard to understand from a perspective where pip is tool used in your daily work. But when it’s part of a much larger project, that message becomes a small line in a logfile of a build pipeline and will probably be never read as long as the pipeline is running fine. In that specific case, the pip call was even wrapped by another tool that suppressed its output. Perhaps writing the warning to stderr would have made a difference, but ultimately only a pipeline failure is a clear signal that something needs to be done. And it’s kind of the job of a nightly build to detect such things. Actually I think there are many more people out there whose pipelines were also broken by that change, they just haven’t tested the new pip version yet.

Don’t get me wrong: I’m not complaining about the way this was executed. I personally think it was done excellently (first a warning with a flag for testing, remove it with a flag for backwards compability before remove it completely). I’m just questioning that removing the feature to have out-of-tree builds (or even deprecating it) is a good idea as long as there is no replacement.

Thank you all for your patient explanations of the setuptools and PEP 517 situation, but I think that even if setuptools were to offer such a feature in the future, I as a user cannot be expected to know whether a particular package that I want to install with pip uses setuptools or something else and if that thing supports out-of-tree builds or not. Only pip could help me there by offering me a pip install --but-please-dont-let-anybody-touch-my-source-tree-no-matter-the-cost to mitigate the limitations of setuptools and other possible backends.

@dhirschfeld I’m not sure how to escalate this further.

I’m pretty sure that this is affecting many people using docker volume mounts in their CI pipelines, since the sane default is to use a read-only source volume from a build reproducibility and caching standpoint. I’m just hoping someone can help.

I think this needs to be transferred to the setuptools repo.