Help testing PEP 660 support in setuptools

Hello all,

We have been working for some time with the editable installs according to PEP 660 in setuptools, and I think we are close to the point we can start thinking about a release.

If anyone is interested in “beta”-testing and giving feedback, it would be very appreciated! (My idea is to use the same procedure we used for PEP 621: invite the community to try it out » release it as “beta” » eventually move it to “stable”).

Docs can be found temporarily at Development Mode (a.k.a. “Editable Installs”) - setuptools 63.0.0b1.post20220703 documentation.

Information about the design decisions can be found in pypa/setuptools#3265.


How can you help testing?

  1. In your pyproject.toml you can try replacing requires = ["setuptools", ...] with something similar to the following:
     [build-system]
     requires = [
         "setuptools @ git+https://github.com/pypa/setuptools@feature/pep660",
         ...
    ]
    
  2. Try to do an editable install in a new virtual environment, for example:
    $ python -m venv .venv
    $ .venv/bin/python -m pip install -U pip
    $ .venv/bin/python -m pip install --editable .
    
  3. Try playing with your project to see if the editable installation works.
    This kind of editable installation will allow new files/renamed files to be picked up without the need of re-installing (however spurious files may end up recognised as part of the package, it is part of the trade-off…)
  4. Uninstall your project and re-install it using the strict-mode:
    $ .venv/bin/python -m pip uninstall <your-project-name>
    $ .venv/bin/python -m pip install --editable . --config-settings editable-mode=strict
    
  5. Try playing with your project again to see if everything works.
    This kind of editable installation will try to emulate as close as possible the behaviour of the regular installation. On the other hand, if you add new files or remove existing ones, your might need a fresh editable re-installation.
    Please note that the “strict” mode will create some files under the build directory. If removed, your installation may not work.
  6. Please feel free to share your feedback in this thread :heart: (we also accept PRs with fixes, improvements, docs, etc :smile:)

Limitations

  • The strict installation needs an auxiliary directory (under the build folder) to work properly.
  • In accordance with PEP 660, the editable term is used to refer only to Python modules inside the package directories.
  • Non-Python files, external (data) files, executable script files, binary extensions, headers and metadata may be exposed as a snapshot of the version they were at the moment of the installation.
  • Adding new dependencies, entry-points or changing your project’s metadata require a fresh “editable” re-installation.
  • Console scripts and GUI scripts MUST be specified via entry-points to work properly.
  • Strict editable installs require the file system to support either symbolic or hard links.
  • Editable installations may not work with namespaces created with pkgutil or pkg_resouces. Please use PEP 420-style implicit namespaces.
  • Support for PEP 420-style implicit namespace packages with flat-layout will be kept under experimental status until we (as a community) agree in a proper way of handling namespace packages via import hooks (see discussion in python/cpython#92054).
    If you experience problems, you can try converting your package structure to the src-layout.
    (It should be fine to use the strict-mode though).
  • The editable installation may not work if you are overwriting setuptools commands (e.g. build/build_py/build_ext), using a plugin that does that, or relying on “tricks” using setup.py to customize the build.

If any plugin writer would like to discuss how to improve inter-operability, please let me know. I have started working on some preliminary API with that in mind.

9 Likes

Just being curious, should this work also without git? URL pointing to the zip of the head of the branch:

 [build-system]
 requires = [
     "setuptools @ https://github.com/pypa/setuptools/archive/refs/heads/feature/pep660.zip",
     ...
]

I just tried it out, and to my surprise it works… (I though a direct file URL would need to be a sdist or wheel).

Pip will install from a zipped source tree. That may not be the case for other (probably hypothetical :wink:) installers - PEP 440 states “the exact URLs and targets supported will be tool dependent”, so you can’t even be sure that sdists or wheels will work, if you want to be pedantic…

2 Likes

Thanks for your work! I just tried this out and it seems to be working well. I didn’t do any extensive testing though, just installed my package in both modes, made some minor change and checked if everything still works. And it does :slight_smile:

One minor thing, even though I’m not sure if setuptools can really do anything about this :sweat_smile:: It would be nice if the files under the build directory created in strict-mode would be removed when I uninstall my project. Ideally, even the build directory itself would be removed if it’s empty afterwards.

Thank you very much Stefan.

I don’t know how to handle this situation… The way PEP 660 was approved, the only way to get file links to work is by creating an auxiliary dir (this is suggested in the text of the PEP) (and in my opinion the link tree is the best approach to have a “strict” installation).

The problem with this alternative is that pip does not know about the auxiliary files and setuptools cannot know when the the package is uninstalled…

We can add some instructions to be displayed in the terminal to the user, but they will be hidden anyway unless the user calls pip with --verbose.

I assume you can’t just add it to RECORD or however the frontend is determining on its own which files need to be uninstalled?

I suppose this isn’t that big of a deal in practice, though, since it only affects dev machines and the build directory is created anyway when building any package. I’d be more concerned about people (including myself) accidentally deleting it (e.g. git clean) while the package is still installed vs. not deleting it when the package is uninstalled.

I haven’t thought about that… I can explore this option. (It does imply that I will have to re-implement the logic for the wheel file instead of reusing the wheel package).

There is a warning about that, but it is not shown by pip by default.

2 Likes

If the main/only benefit is just to tidy up the internals of the (already messy) build directory a bit after uninstalling editable packages for development, to me it doesn’t seem worthwhile to spend time and effort reinventing the wheel just for that, unless there’s something else I’m missing here.

Maybe there’s some way to show it at least on first run if strict mode is enabled? Alternatively, if you rely on users reading the docs to figure out how to opt in to strict mode to begin with, then you can just rely on a warning there (which I suggest you make more prominent, e.g. in a warning, etc. admonition, besides just being in the prose).

2 Likes

Good call. The strict mode is not active by default so we can assume that the users will know about it by reading the docs. I will add a more prominent warning there.

2 Likes

It is also an option to change the wheel spec to include symlinks. This is something that has been requested a few times, and there have been discussions about it, but so far nothing has reached a point where anyone has put forward a concrete proposal.

Obviously, that’s a bigger piece of work, and it will affect more of the ecosystem than the current setuptools approach, but it’s a possibility.

I’m pretty sure this won’t work. It certainly shouldn’t - imagine if absolute paths were allowed in RECORD, then someone could publish a wheel that included /etc/passwd in the RECORD file. If pip were to blindly copy that into the installed package and then respect it on uninstall, you could trash the user’s system…

I assumed it wasn’t, but I didn’t think about that implication. Not only that, but assuming pip had the right to delete it, you could also replace it during install, which would do more than just trash the user’s system—it could give you full control over it. Its basically data_files 2.0, if not worse, heh. So yeah…not the best idea.

Doesn’t pip regenerate the RECORD file on normal installs anyways?

While I brought this up, I actually agree - to me this seems like a really minor issue considering that strict mode is not the default, so most users won’t deal with this.

Another thing that might help is to give a concrete example in the docs when I would want to use strict mode. From what I understand, I could just use ‘lax/loose mode’ with different git branches instead (one with some new files, the other without)?

This varies from developer to developer, workflow from workflow. My personal view is the following:

If a project is in its early stages, developers probably want to use the “lax/loose” mode, because it allows all sorts of refactoring (rename/add/delete/split files, etc…).

The strict mode is more useful later, when the project shape is more stable and the developers don’t want to loose time with constantly (re-)installing the project for local tests (although I do recommend using a “regular/conventional” installation in the CI whenever possible). The strict mode does help to find problems (e.g. when the project is not properly configured and the sdist is missing some files by accident).

Of course, people might do just well sticking with one of the two modes.

I will try to think about some example to put in the docs, however I would not like to be prescriptive (because people may have different/strong opinions on this topic). If you (or anyone) have any suggestion, that would be very helpful.

Probably. That’s what I imagined would happen when I said “wouldn’t work”. I just didn’t have the time to check.

FWIW, I tested this on my project - which is very simple so doesn’t provide much new testing info I assume, but it does use setuptools-scm in the build - and it worked as far as I can tell.

Thanks so much for working on this! I’m looking forward to removing my blank setup.cfg :slightly_smiling_face:

2 Likes

I’ve also tested this in our private repo, which uses an in-repo PEP 517 wrapper around Setuptools, conditionally generates C (with Cython), and compiles extension modules. Our test suite passes

2 Likes

@abravalheri Thanks a lot for this work!

I have tested a project that uses pkgutil-style namespace packages and is not using src layout.

As mentioned in the limitations it does not work, due to the editable install using _TopLevelFinder.

It would work just fine if using the _StaticPth mechanism, though.

For various reasons it is unlikely that this project can migrate to PEP 420 namespace packages or src layout.

Would it make sense for setupools to support another editable_mode config setting than strict to force using the static pth method ? Or default to the static pth method which is the most compatible and let users opt in the top level finder mechanism via config setting ?

Hi @sbidoul, thank you very much for having a look on this.

Big part of the discussion in pypa/setuptools#2872 (and others here) was motivated by static .pth files being considered “broken”/“flawed” for flat-layout projects. The _TopLevelFinder is a compromise between the strict behaviour and the behaviour that people are used to for iterative development.

If we introduce a new option, we have to move very carefully to not re-ignite discussions in which we don’t have consensus.

Is this project that you are working with open source? (I could try to have a look and understand better if there is a different way)

Meanwhile, I added SETUPTOOLS_ENABLE_FEATURES=legacy-editable (env var) as a escape hatch to recover the python setup.py develop behaviour.