-
Notifications
You must be signed in to change notification settings - Fork 412
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
Allow running scripts with dependencies using pipx #916
Conversation
I have an alternative idea for handling the venv issue. Rather than try to deal with the complexities of having two different types of environment, I can punt on the problem, and simply note that if I call
then I have a valid If I then add a new I'm not completely happy with this solution, but it's honestly no worse than someone creating a |
Hmm, I don't think the error on the macos CI is caused by this change:
|
There's probably still work to do on this, but IMO it's ready for an initial round of reviews. If anyone knows how I can fix the MacOS test failure, I'd appreciate it. By the way, the code creates the environment name based on the requirements, not based on the script name. That's deliberate, because it means that if you (for example) use requests in your scripts a lot, the same environment can get used for multiple scripts. I don't know if it would be worth documenting this somewhere - at a minimum, I could add a comment explaining this in the code, but I couldn't find any docs where this sort of detail seemed to fit. |
It's fixed in #917. |
I believe this resolves all of the outstanding issues. I had to refactor the |
Gentle ping on this. Is there anything I can do to help get this merged and into a released version? |
Hi and apologies for the lack of feedback. I really appreciate your work here (and elsewhere in the Python world), but I simply don’t have the bandwidth in my personal life to devote to open source as much as I used to. I’ll do my best to take a look soon, but can’t make any promises. |
No worries, thanks for the update. |
pipx run https://example.com/test.py | ||
pipx run https://example.com/test.py 1 2 3 |
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’m not sure we want to mention this in documentation since running arbitrary code directly from the internet is very dangerous to attacks such as MITM. This is similar to e.g. what we used to recommend (curl .../get-pip.py | python
) but now split the command into two. It is OK for this to be supported (use it at your own risk if you managed to find it in --help
) but I would like to see this removed from documentation. Or at least we should move it to a separate section and mention very prominently this is dangerous.
Also, while I’m writing this, I wonder if pipx run - [args]
is a good thing to support.
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 can already run arbitrary code, see here. All this feature adds is the ability to define requirements.
Also, while I’m writing this, I wonder if
pipx run - [args]
is a good thing to support.
I don't have a strong opinion on this, but I do think it can be handled in a follow-up PR if you think it's worth doing.
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.
@uranusjr is there anything you want me to do to address this? I could just remove the two examples with URLs, but as I said this isn't a new feature, and it's already documented in the last line of the block of examples jutst above what I added.
People interested in this feature and pull request may be interested to learn about PEP 723 Inline script metadata, which is currently provisionally accepted. It specifies the syntax for a special comment that specifies dependencies. Here's an example: # /// pyproject
# [run]
# requires-python = ">=3.11"
# dependencies = [
# "requests<3",
# "rich",
# ]
# ///
import requests
from rich.pretty import pprint
resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10]) There are still collecting feedback from the community. See this forum thread, and this one. To be clear, no version of pipx currently implements this, or any other tool that I am aware of. |
@Flimm #!/usr/bin/env pipx run
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "requests<3",
# "rich",
# ]
# ///
import requests
from rich.pretty import pprint
resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])
# vi: ft=python |
Wow, that's wonderful! I'm glad to see this PEP got approved and that pipx supports it 😁 By the way, on Linux, the shebang line you mentioned doesn't work, because Linux doesn't support shebang lines with spaces the way that one would expect. To work around this, you need to pass
|
@Flimm Thanks, MacOS' |
The PEP is final. It was accepted here. |
Fixes #913
TODO
docs/changelog.md
Venv
class to handle app-less venvs properlySummary of changes
This PR adds functionality to the
pipx run <script>
command to allow running scripts that have non-stdlib dependencies.The script is parsed for a comment block that has the form
The block is terminated by a blank comment line, or by a non-comment line. Requirements may be anything that can be a top-level requirement for pip.
Before running a script, the code checks for a requirements block. If one is present, it creates a temporary virtual environment (managed the same way as the existing
pip run app
handles its venvs), installs the requirements there, and runs the script in that environment.Implementation issues
The existing implementation of the
Venv
class inpipx
assumes every environment will have a "main app". The code to write the pipx metadata file enforces this. This is not the case for temporary environments supporting scripts.Rather than changing the implementation of
Venv
to allow environments without apps (which would be an extensive change, as it changes a fundamental invariant of the existing class) I plan on splitting theVenv
class into two subclasses -AppVenv
andScriptVenv
. All existing uses would becomeAppVenv
instances, withScriptVenv
being for the new functionality.One subtlety here is that the code that lists venvs simply scans directories and assumes what it finds is a venv (
VenvContainer.iter_venv_dirs
). In practice that isn't an issue, asScriptVenv
environments will only be found in the cache directory, which is never scanned in this way (as far as I can tell) but it is a potential risk. I will decide how to handle this when I do the refactoring.Test plan
The PR includes tests of the functionality.
Also, it can be manually tested by creating a file
test.py
containingThen run the command
Output should show the executable being in a temporary virtual environment, and the pip output will show requests being present (with its dependencies).