-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Install build requirements before calling setup.py #4799
Conversation
from pip._internal.vcs import vcs | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def make_abstract_dist(req): | ||
def make_abstract_dist(req_to_install): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer the req
spelling; it's shorter and more to the point. Plus, this is noise in the PR diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
tests/functional/test_wheel.py
Outdated
@@ -210,6 +210,7 @@ def test_wheel_package_with_latin1_setup(script, data, common_wheels): | |||
assert 'Successfully built SetupPyUTF8' in result.stdout | |||
|
|||
|
|||
@pytest.mark.xfail(reason="PEP 518 does not support links") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait... Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More specifically, this version does not support dependencies that don't have binary (wheel) files available. The reason why is explained in the initial post.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation potentially allows pip to spawn multiple sub-processes recursively to install build_dependencies. Solving that problem is too complex until we have the resolver, and the practical benefits will be realized with this implementation (think numpy, which depends on Cython, which has wheels available).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More specifically, this version does not support dependencies that don't have binary (wheel) files available.
OK... so this version isn't mergeable because it doesn't support scenarios pip currently supports? Am I understanding that right? Or are you saying "pip has a bug in situation X", in which case let's fix that bug first rather than proposing code which stops pip working at all in situation X. If necessary, submit an isolated PR that says "pip has a catastrophic bug in situation X - detect that situation and refuse to proceed". Then once that's in, this PR can simply ignore situation X, leaving it as a future extension.
This is another example where you're including too much in one PR. You need to make your PRs much more granular. And yes, that does mean that work on later stages has to wait till earlier stages are thrashed out and merged. But that's better than a huge, un-reviewable mess that never gets merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this version isn't mergeable because it doesn't support scenarios pip currently supports
let's fix that bug first rather than proposing code which stops pip working at all in situation X.
How can I say this? Situation X is rare. In fact, I know of no actual scenarios where situation X would actually occur. In addition, situation X is only supported because at the time, people did not consider the full implications of supporting it. Correctly supporting situation X requires either:
- An entirely new refactoring and/or rewrite of key areas (correct).
- Recursive process spawning (unmonitored and uncontrolled), which is what the current implementation does (this is horrible).
In contrast, there is another situation: situation Y. Situation Y, in contrast to situation X, is widely used and important.
This PR drops the recursive process spawning (very bad), removing support for situation X (which would actually maybe be used once) and adds support for situation Y (widely used).
But that's better than a huge, un-reviewable mess that never gets merged.
Now that would be implementing situation X correctly; something that even the original implementer avoided.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More specifically, I can rattle off a list of at least three major projects (scipy, scikit-learn, numpy) that would use situation Y and don't care about situation X and quite a few more minor projects. I can think of none that would need situation X.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies by the way for the abstract "X" and "Y". I didn't really follow the actual problem, so I simplified (or not...)
As I say, if X is rare, then a PR that identifies it and explicitly reports a "we don't handle this" error should be fairly uncontroversial. So submit that, get it accepted, and then this PR can ignore that case in safety. By merging the two steps into one PR, we end up muddling the issues. Also, a PR that says "drop support for X" would have to clearly explain what X was, and I'd be able to avoid all these clumsy circumlocutions :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So submit that, get it accepted, and then this PR can ignore that case in safety.
Will do. Communication is good.
OK, deep breath. I just re-read PEP 518, to clarify in my own mind what's going on. The only thing that PEP specifies is that if a project specifies X, Y, and Z as build dependencies in #4647 says that currently, we're running This PR talks about "PEP 518 problems", but it's not clear which problems it's fixing. Is it an attempt to fix #4647? You also mention recursion. Specifically I understand that to mean that if project X specifies Y as a build requirement, and project Y is only available as source with project Z as a dependency, then PEP 518 requires pip to:
I don't see how this relates to #4647, which demonstrates its issue with a single project having a build dependency which is available as a binary. So I'm going to assume that the two problems are independent (at least in principle - if the pip code to implement things is messy and difficult to disentangle, then fair enough but that's just the classic "simple matter of coding"...) I do see that handling recursive builds (and their build environments) is likely to be complex. I don't think we can unilaterally decide to omit that part of the behaviour just because it's hard, though. What I will say, is that it's unlikely the authors of PEP 518 had thought much about the recursion situation. I certainly don't remember it coming up in the distutils-sig discussion. And I'm reasonably comfortable with your assertion that situations like this are rare, whereas binary build dependencies are a lot more common. Is this worth limiting the PEP in some way, such as permitting tools to ignore build dependencies that themselves need building, I don't know. Possibly. Maybe @dstufft @njsmith @brettcannon and @ncoghlan as authors/BDFL-delegate of PEP 518 have a view. Maybe it needs to be discussed on distutils-sig. I wish you'd clearly stated the problem and asked for a discussion, rather than arbitrarily picking an approach and burying it without explanation in a long and complex PR that you'd been repeatedly asked to simplify. If nothing else, I'd have had some time this evening to do other things :-) But I do think it's good that this has surfaced now. We have a chance to make a reasoned decision, which is much better in the long run. |
The recursion issue did occur to me during the drafting process, but I
didn't think there was anything to say about it at the spec level.
Conceptually, there's an install_package operation which sometimes calls a
build_wheel_from_source operation, and build_wheel_from_source sometimes
has to create a temporary environment and then call install_package to
install things into that environment, which means that yeah, recursion can
happen, but it doesn't introduce any particular theoretical problems afaict
and there isn't really any other way it could work. (In particular, you
really do need to use a separate temporary environment for different
builds, because they might have conflicting requirements.)
I can well imagine that it causes some kind of practical problems inside
pip though. Sorry about that?
…On Oct 20, 2017 14:35, "xoviat" ***@***.***> wrote:
I don't think we can unilaterally decide to omit that part of the
behaviour just because it's hard, though.
I also agree with this part, but I need to fix one problem at a time. The
recursion discussion was never had, so something went in that wasn't well
considered. And the workload to fix the problem correctly is not trivial.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#4799 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAlOaE3a5gwL7IERuPolNYJlN1pC__3Sks5suRIhgaJpZM4QA0YC>
.
|
We purposefully didn't dive into details of building dependencies as that's a build tool decision on how to make that work; the PEP is just a way to specify dependencies like requirements files, not on how to actually execute any build or install dependencies. For me, if you have a build dependency that requires building, then you need to build that build dependency. |
Thanks @brettcannon @njsmith that pretty much seals it for me. It's a requirement of the PEP, so we have to implement it if we want to claim we support the PEP. Sorry @xoviat - looks like we need to support full recursion, even though it's a rare case. (It's a minor consolation, but loops in the build dependency graph would be a packaging error, and we'd be fine to fail the build if we get those). |
I'm OK with it. I'd rather we managed to be more user friendly than that, but I won't lose sleep over it, personally. One other corner case that comes to mind (again, possibly related to something you've mentioned elsewhere). If the user specifies |
I'd prefer a description of how such a problem would get triggered, and an explanation of why we deadlock (I assume you mean "deadlock" rather than "infinite loop" - but what locking do we do?) I think at this point we need less code, and more discussion and specifications. |
When I say deadlock, I mean deadlock. You need to reboot. |
Note: I'd consider The other possibility to consider is that build systems like Fedora's koji allow for the notion of a shared buildroot: once something is in the buildroot, future builds can just use it, they don't need to rebuild it each time. For |
Well, I can see one concurrent build per level of recursion. Surely recursion can't go that deep that it's a problem. I'f you've hit massive explosion of processes, that sounds like an implementation bug.
That sounds like an entirely reasonable approach. I'd certainly hope we wouldn't try to build any given build dependency multiple times.
I'm happy with that - if for no other reason than that with the "assume setuptools as a build dependency if nothing is explicitly specified" principle, it would be awfully easy to accidentally slip into endlessly building setuptools from source in order to build setuptools... I actually suspect it's entirely reasonable to simply say that If we wanted to get into "practicality beats purity" discussions, the key point here is that it's likely that there's a distinction to be had between "build systems" (in the PEP 517 sense - setuptools, flit, enscons, ...) and "build dependencies" (like "numpy because |
For the distro package use case, the option we would want is something along the lines of |
That approach would actually pair nicely with the implicit build dependency installation ignoring the
if they really wanted a "build completely from source" setup. |
Hmm, I'm not sure that's something that we've even considered. I don't know how that fits alongside the idea of isolated builds, actually (if you manually install the build deps are they then visible in the actual install?) I'd have to defer to someone who understands the build isolation stuff to comment her - I'm getting out of my depth... |
It may help to know how the RPM build process works, since it's a "generate-and-filter" model:
So the chroot will have both the build dependencies and all sorts of other intermediate artifacts (object files, helper apps, etc), but those get discarded by the filters that indicate which outputs should end up in which RPMs. Related to that, we likely wouldn't use Python level isolation for system package builds - we'd need the system site-packages to be available when building wheels, as we'd often be managing the build dependencies with RPM's BuildRequires, rather than the Python level settings (however, we'd still benefit from the Python level settings - we'd just use them to generate the right RPM level settings, rather than using them directly at build time) |
Sounds like setuptools should just have a |
I don't understand your point about the finder, no. And yes, see my point about "build tools" being different from other build dependencies... |
In a released sdist, setuptools does not require anything besides Python to install itself. |
I do.
The finder provides a list of versions of a package available; the current implementation just uses the latest version in that list. Whatever pip does as dependency resolution today, the build environments are setup without going through that entire section of the codebase. |
@ncoghlan How about this approach -- build wheels for the underlying dependencies from source and puts these wheels into a directory and use I'm just asking. :) |
@xoviat I feel it would be nice to keep #4802 for discussing what pip implements for PEP 518; how pip implements it should probably be discussed here.1 I'll respond here, to some implementation related points you made there:
What about command line arguments (of the top of my head at least these are significant
That's sort of a major-ish refactor -- something like this will be needed eventually but probably not in pip 10. (I have some ideas about some refactors of the codebase that should make this easier to do later but that's next year stuff) If we're going with just wheels, my understanding is that we won't need to manage build environments. We can build a new one for every wheel we're building and then trash it -- which is doable in 1I like to segment approach/implementation discussions into issues/PR for discussions with lots of parties so that those that are not familiar with the implementation details are not alienated or left speculating. :) |
Alright. Could you please explain what's wrong with this assumption? |
Is it because egg_info is called before I don't exactly remember the flow. |
@@ -91,17 +94,43 @@ def dist(self, finder): | |||
) | |||
return dist | |||
|
|||
def prep_for_dist(self): | |||
self.req.run_egg_info() | |||
self.req.assert_source_matches_version() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specifically here.
Okay... Now I get it. You've basically wrapped that call to be inside the BuildEnvironment context and moved BuildEnvironment into a longer existing scope -- correct? |
tests/data/src/pep518-3.0/setup.py
Outdated
@@ -1,6 +1,8 @@ | |||
#!/usr/bin/env python | |||
from setuptools import find_packages, setup | |||
|
|||
import simple # Test gh-4647 regression |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you make a separate test please?
A single test should only try to test one thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test was incorrect when it was initially written. There is no need to have a broken test and a correct test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah right. I think the comment can be changed to "ensure dependency is installed".
@pradyunsg Before I tackle the final points, is there anything else? |
src/pip/_internal/build_env.py
Outdated
|
||
|
||
class NoOpBuildEnvironment(BuildEnvironment): | ||
def __enter__(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: I should have overridden the initialization method here as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see anything that stands out to me on another quick glance.
news/4799.bugfix
Outdated
@@ -0,0 +1 @@ | |||
Fix situation where build requirements are installed before calling setup.py. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need a news entry since this bug never actually made it to a release.
Okay, so can we go ahead and merge this? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does what it says on the the title. LGTM.
I'd like someone else to review this but if no one has the time to, in the coming weeks, I'll merge. (sorry that's the timescale due to the limited dev time we have)
] | ||
args = [ | ||
sys.executable, '-m', 'pip', 'install', '--ignore-installed', | ||
'--prefix', prefix |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: trailing comma
cc @pradyunsg |
@xoviat why did you close this when @pradyunsg has promised to merge it if no-one else is available to review it? |
I missed the ping. Apologies.
…On Thu, 1 Mar 2018, 04:19 Paul Moore, ***@***.***> wrote:
@xoviat <https://github.com/xoviat> why did you close this when @pradyunsg
<https://github.com/pradyunsg> has promised to merge it if no-one else is
available to review it?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#4799 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ADH7SV6-kS6NPlrMrMDRvN11PVvu_Sqsks5tZdgKgaJpZM4QA0YC>
.
|
Still curious why it was closed tho. |
Sorry, I closed this because I have too many PRs open at once. If it's going to be merged soon, then I'll reopen it. |
I think this is good to go. I'll merge once the CI is happy. |
Yay! Thanks guys for getting this done - I know it's been hard work, but it's really appreciated 😄 🎉 |
Thanks all! This moves us closer to pip 10.0 I suppose? :) |
So does this mean that pip now officially, actually supports PEP 518? |
Yep! ^>^
It's currently limited to binary-only build dependencies. I expect once we have some feedback on it, we'll know how this limitation fairs. I guess by pip 11, this constraint would be lifted. |
To make sure I understand: the build dependencies have to have a wheel available? |
Yes.
…On Thu, 1 Mar 2018, 15:10 Nathaniel J. Smith, ***@***.***> wrote:
To make sure I understand: the build dependencies have to have a wheel
available?
—
You are receiving this because you modified the open/close state.
Reply to this email directly, view it on GitHub
<#4799 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ADH7SZE2KNNMpZi5Y3YW5XF_UM0xqVUIks5tZ8H6gaJpZM4QA0YC>
.
|
master
does not comply with the specification because it runsegg_info
before the build environment is set up. In addition, the PEP 518 system had a circular import, indicating that it needed to be moved to a separate file.Solution: move the
BuildEnvironment
into a new file calledbackend.py
and make it a property of a requirement so that it persists throughout the life ofsetup.py
from the first time it's called to when the build directory is cleaned up.Notably, the first implementation of PEP 518 also did not account for recursion (building source build requirements), which was completely untested (that implementation disabled
no-binary
but did not selectonly-binary
). I had a test failure where pip would basically rebuild the entire pypi, so I simply disabled non-binary requirements for now.Ideally, we would use a single process with an "build environment manager" that figures out circular build dependencies, but I decided to wait until we have a resolver (and PEP 517 is completed) before attempting that.
Closes gh-4647.