-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Add run command #7925
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
Add run command #7925
Conversation
…ection of purpose.
+1 in principle to this addition. IMO it solves an important bootstrapping problem, and offers a straightforward approach for simpler or more adhoc workflows, where setting up and managing a virtualenv is unnecessary overhead. |
You can use
There seems to be a problem with using
|
With this latest commit, the behavior is as prescribed:
|
I played with this a little bit and I think I have several general concerns with including this in pip:
|
I was thinking had about this last night, and I have come to the conclusion that I am strongly in favour of this change (in principle, at least - we may need to iterate a bit on the implementation). I'll explain my motivation first, and then provide some responses to @chrahunt's points below. Why do we need this feature?Writing a simple script in Python should be easy (I hope this can be taken as given). Certainly a lot of Python code is part of complex applications with full development processes, etc., but there's also a huge amount of code that is "just" simple automation scripts, adhoc utilities, or similar. I'd also assume that we can take as given that we want using packages from PyPI to be simple. For such simple scripts, there's a significant step change in complexity as soon as you depend on a 3rd party package. Instead of just running the script using Python, you need to decide whether to install the dependencies into your system Python, whether to use a virtualenv, etc. This is often a lot more complexity than a simple one-off1 script deserves. Personally, I have often written scripts that avoided 3rd party packages even when they would have made my code significantly simpler, precisely for this reason. That's not healthy for Python, or for 3rd party packages. A Why include this feature in pip?This functionality needs to be available to all Python installations. Otherwise there's a bootstrapping issue - it's hard to run scripts because you need to install stuff, so install stuff to make it easy to run scripts...? Experience with many tools (pip-run and pipx being good examples) shows that the one place where such tools struggle is in bootstrapping - the typical advice is to install them globally (using We can't add this to core Python - it fundamentally depends on package installation, and pip (the main package installer) isn't part of core Python (it's just bundled). We can't add it as a separate tool - as has been pointed out, that involves a bootstrapping issue, and pip-run has demonstrated that that hinders adoption. So really, the only place that is left is pip. You can't have a problem of dependency management without having pip available, so having the solution be part of pip as well, seems perfectly reasonable to me. I could elaborate further here, but I'm trying to be as concise as I can, and I want to address @chrahunt's points, so I'll leave it at this for now. I will say, though, that I have examples where I would use this functionality, multiple times, every day (and yes, I've tried pip-run, and the fact that it needs to be installed is a sufficient issue that it doesn't work for me as a solution). Responses to Chris' points
I don't think this is true. PEP 582 offers another way of bundling dependencies with a script. The approach is meant to be simpler than virtual environments, but the idea is still to install the dependencies along with the script, and distribute the whole lot.
I've explained above why I think that pip is the only place that can provide a helper for this use case. And I don't see what you intend as a "PEP 582 compatible workflow helper" that would address this use case2, so I'm not sure your comment is relevant.
This is valid. But we already have many cases of mis-targeted issues, and I hope no-one is suggesting that we block any features which could trigger such issues. The new resolver will definitely result in issues where people get what they thing is "the wrong" thing installed, which will be down to project metadata. Should we not add a new resolver? I believe that the extra support cost of such issues is justified by the value of the feature. Also, if we have serious concerns here, we could look at pip-run and see how many of its support issues have been of this nature (we could also look at similar projects like pipx).
Maybe. But is that such a bad thing? At the moment, there is no good solution3. Even if the proposed
This, I agree with (although I don't know what you mean by "future_fstrings compatibility", it's not something that is described as user-facing in the docs). The common factor here seems to be "how scripts specify metadata that's needed to run them". That seems to me to be something that should be standardised, for interoperability reasons (other tools may want that data). So I'd fully support a proposal to standardise those items. I'd also be OK with removing them from the initial implementation, or marking them as unsupported/experimental somehow, subject to a standard mechanism being agreed. But I don't think we need to discard the feature altogether just because of some details that need to be reviewed/amended. The functionality would still be more than adequate for the basic use case without them: $ python -m pip run -q requests html5lib -- my_scraper.py Clearly, things could be even simpler -
No more than vendoring any other package does. If, as suggested, we decide at a future date to migrate the functionality into core pip, then we can do so in a way that cleans up the code and the mechanisms. But in terms of this PR, any issues would just be "issue with a vendored library, please report downstream", just as with any other library we vendor. 1 As we all know, one-off scripts will be run many times 😉, so we need a longer-term answer to these questions. |
One further point I should add. I would like to see a bit more thought put into the UI of the |
Thank you @chrahunt for the thoughtful and thorough review. And thanks Paul for addressing some of them. Allow me to provide some clarification. Use casesThe design of pip-run allows for several use-cases and even some not specifically called out in the docs. Script executorIn my experience, the "script executor" construct, while useful, is less than 20% of the value of the design. Most of the time, I'm relying on the functionality for one of the other use cases. Multi-version interactions
I don't believe this behavior is possible with any other tool. Installer troubleshootingOften a person will file a bug report stating that "installing X produces unexpected outcome Y". Because pip-run doesn't alter any environment permanently, a maintainer can rapidly attempt to replicate the installer scenario by running FlexibilityThe fact that It's for this reason that Adapting the designSome of the advice above could be applied directly to I owe more attention to the previous responses and will specifically address some of them, but want to provide these examples and concerns for consideration in the interim. |
I feel the feature this provides is very useful. This would still be true even if PEP 582 (or an alternative “local environment detection”) is implemented, since the two use cases do not align perfectly. Local environments are generally intended to reused across multiple runs like virtual environments, while this feature, has an isolated, one-off emphasis by contrast, which would still be cumbersome to set up with PEP 582. I feel this would be better clarified if this feature is introduced under a different name, since |
Command Name
I like this suggestion, and my initial instinct is to agree. The The underlying behavior really does draw together two main behaviors. If length were not a factor, I might propose naming the command "install once and run", capturing the two main things the command does (first install temporarily the requirements, then run Python with those requirements in place). "install" is somewhat implied by "pip", so "once" and "run" both seem equally suitable to me. Choosing "run" implies the "once" and vice-versa. On balance, the two names seem equally suitable to me except for two factors:
While I appreciate the suggestion, I lean toward sticking with "run", but if others feel strongly, I'll add it to a list of blockers. |
f-strings supportThe f-strings compatibility is somewhat user-facing in the sense that if you try to invoke pip-run against a script on a version of Python older than 3.6 and and that script is using Inline requirementsSupport for inline requirements through things like I'm not wed to the syntax of I do agree it would be valuable to either settle on a standard for supporting inline requirements before establishing the implementation. I welcome anyone to take this torch and establish such a standard. In the meantime, this current implementation acts as a de-facto standard that should be supported for maximal compatibility, even if a new standard were to be adopted. Any new standard would almost necessarily be compatible with the current approach. I'd be okay with flagging this feature as "provisional" and "best effort", but I don't foresee any challenges with transitioning to a newer standard as available. Support burdenSpeaking to the support burden, I fully expect to be principally supporting this functionality and would consider it my responsibility to address user concerns emerging from this feature. @chrahunt I want to thank you again for the feedback. I feel like between myself and pfmoore, we've addressed your concerns. If you feel there are still issues remaining that are blockers, would you enumerate those? |
Just to clarify, I don't like the I'm not keen on But as I said above, I'm OK with them being included in the initial implementation, but subject to change or even removal in later versions. I'm also fine with them being removed. How we handle the removal or provisional nature of these items can be decided in PR review (a warning? documentation only?). Let's get the feature agreed (or not) in principle first. |
Flagging that I don't think this would catch the train for pip 20.1 (i.e. the coming 10 days). Please let me know if that's not the case. :) |
@pradyunsg Agreed, we shouldn't rush this or put deadline pressure on people. OTOH, I would like to see this resolved one way or another for 20.2. I think 3 months to reach consensus and get it in a state for release is a reasonable timeframe. |
I've been working hard on this the past few weeks (also presented it as a project at HackIllinois) to try to get this ready for the 20.1 release. If it can't be included then so-be-it. Agreed this should not be rushed.
One of my key design goals was to allow a single file to contain the entire implementation, including its dependency declarations. The tool is useful without this feature, but is also dramatically limited without it. Sure, one can write in the comments: # run this script with pip-run requests boto zope.interface 'pathlib2;python_version<"3.5"' -- script.py And then require the user to read the script and copy/paste the spec. It seems plainly obvious that it's valuable to the user that the run too honor some form of inline version specification for scripts. If you think this feature should be removed, would you file a bug with the project describing the reason the feature should be removed and what you expect users of the feature to do instead?
Isn't a de-facto standard better than some other arbitrary syntax? Are you proposing a different syntax? Would you consider filing a bug with pip-run describing a preferred syntax and motivations?
The current implementation is that the user may specify an index URL, but there are other cases where the script author would like to specify the index URL. Consider, for example, an internal environment where the resources are on a private PyPI server, and someone has thrown together a script to run that depends on that index server. They can add comments to the file:
Or they could send the script to a colleague and let people know that when they run the script, they should include But why create the foot gun? When the author knows that the only useful way to run the script is to specify a particular index URL, there should be a way for the author to specify that meaningful default in the script itself and for the tool to help enforce it. I agree there are situations where an index URL in the script is bad practice, but that doesn't obviate the legitimate uses that are good practice. It seems by this logic that
I think it's possible that
I'm fine with marking the functionality as provisional in the docs of pip-run, especially if there's an open issue to reference explaining why the syntax might change. If it's something likely to change, I'd even go as far as warning when the behavior is triggered. |
I'm OK with the feature staying, I was just saying that if someone else objected to it, and argued the case successfully, then I personally wouldn't defend it - I assumed that you would, though 😉
It's not a de-facto standard at the moment, it's an implementation-specific feature of a single tool. Pip, on the other hand, tries hard to be standards-based, and on that basis I'd consider "Syntax for scripts to specify their dependencies" to be a perfect example of something that should be standardised (just like we have a standard for how packages define their dependencies). Personally, I'm willing as an interim measure to use
No, that's why I'm OK with accepting the current syntax as an interim measure. Conversely, are you willing to propose the
... and I'm saying that this may not be something pip wants to support. Where pip looks for package dependencies is very much in the control of the user, not the package author - that's one of the reasons dependency links was deprecated. Letting scripts define their own index goes against that principle, and I don't think it's something that we should do without due consideration. Again, please understand that I'm not saying we'd never allow something like this, just that I'd rather add it as an enhancement later, with due consideration of the specifics of that feature, and an open discussion of how the feature would be exposed to the user, rather than have it pulled in as a side-effect of adding a
I don't see that - For me, the key functionality here is for a user who has written a script that uses a "standard" dependency like requests. They want to run that on another machine. At the moment, assuming that they follow the standard principle of not messing with the system Python, they have to do
With
Or assuming the script can declare that it depends on requests,
Frankly, that is the only use case that I view as essential. Anything beyond that is, in my view, at best nice to have. I'm happy to have Maybe we should initially restrict At the moment, this discussion is starting to feel uncomfortably "all or nothing" to me... 🙁 |
In jaraco/pip-run#41, I started dropping support for This finding does lead me to agree that a redesign of the "inline requirements" feature should be redesigned and aligned with what "pip install" supports. |
I'm happy to adapt That doesn't mean it has to be all or nothing - I'm very willing to remove cruft from pip-run (and have), but would like to support as many of the use-cases as makes sense. If a use-case is not valuable enough to be in Can we start from the assumption that |
I'm happy to do that. Can we also start from the assumption that not all of |
Of course. |
In the latest commit, I've added two decorator factories,
I found this approach didn't quite work on Python 2, so I updated it to unwrap and rewrap the classmethod and now it does.
I don't understand why the behavior is different on Python 3 and Python 2 for the logging configuration. Does this address the need to mark functionality as provisional and limit the supported interface? Are there other outstanding concerns that need to be marked as provisional? What other actions are necessary to move this effort forward? |
Hello! I am an automated bot and I have noticed that this pull request is not currently able to be merged. If you are able to either merge the |
Hello! I am an automated bot and I have noticed that this pull request is not currently able to be merged. If you are able to either merge the |
This is a resubmit of #3979 as proposed in #3971.
This approach takes a minimally-invasive approach, sharing the implementation with the pip-run. This vendored approach allows pip to adopt the functionality on a provisional basis and for updates to be made and proven rapidly in the
pip-run
project.Once the functionality has stabilized in the pip project, I'd fully expect for pip to fully adopt the code and for
pip-run
to be sunset, probably in 6-9 months.This command has provided me with immense value over the past four years and I'd like to share this functionality for others. By removing the barrier to first install pip-run, this functionality will become readily available for maintainers, teachers, and experimenters to use for a variety of one-off installation and testing scenarios.
I have not yet been able to run the tests, so this PR may require some massaging, but the basic functionality is working for me.
Blockers