The key point that I left out of the message with the backend-bootstrap-location
proposal in it, is that I started out intending to have that message be in support of Option 1 and suggest a possible syntax for that approach, but actually trying to spec out the details convinced me that @njs has a valid point regarding the relative ease of explaining Option 3.
The starting point for that exploration was the location of setuptools.build_meta
in the setuptools repo: setuptools/setuptools/build_meta.py at main · pypa/setuptools · GitHub
With my proposed Option 3 spelling above, it’s fairly easy to follow how the bootstrapping is supposed to work: the repo root directory ("."
) gets added to the front of sys.path, then the setuptools.build_meta
backend gets imported the same way it would for any other package that declared it as its build backend. This is also how @takluyver’s intreehooks
helper backend for bootstrapping flit
already works, so we have prior art for it being a viable bootstrapping option.
We also know from the open pip 19.0.x PRs that it isn’t that hard to add a sys.path[0]
injection feature to the pep517
support library (I wouldn’t do it the way I did in those PRs as a public API, but that’s just a matter of replacing an environment variable with a command line argument and a magic prefix with a normal function parameter)
But what happens if we try to specify the location of the build backend directly? How would it be spelled in pyproject.toml
? What are frontends actually supposed to do with that information? How would folks introspecting project metadata determine which build backend is actually being bootstrapped? (that one’s not a functional requirement, it would just be nice to have)
Suppose we denoted a bootstrapped backend this way:
[build-backend]
requires=[]
bootstrap-build-backend="./setuptools/build_meta.py"
Should a frontend run that file as a script? Should it run it with runpy.run_path()
? Should it import it as a module, using the appropriate version dependent incantation to do so?
What are the implications for how build_meta.py
is written? Will explicit relative imports work? Will setuptools.*
absolute imports work? Will __name__
be "__main__"
, or "build_meta"
or "setuptools.build_meta"
?
There are (somewhat) reasonable answers available to all of those questions, but I didn’t think any of them were as elegant as the idea of just switching to a slightly more constrained version of Option 3 such that you specified where in the tree the backend implementation could be found, specified build-backend
as normal, and then the backend itself executed in just the same way as it would for any other project.
The other thing I realised is that the “What if non-backends use the backend bootstrapping option to add an extra in-tree path to their hook execution environment?” concern with Option 3 applies just as much to Option 1, as either way you can make a custom in-tree backend that includes from my_real_build_backend import *
as one of its lines and runs arbitrary code (including sys.path
adjustments) before and after that, and that’s something that intreehooks
already allows with PEP 517 today.