Pip -t: A simple and transparent alternative to virtualenv in Python
blog.zoomeranalytics.com
blog.zoomeranalytics.com
- `virtualenv` works by creating an overlay of the `site-packages` in `$VIRTUALENV`, and change the shebang of scripts to use absolute path of python in the overlay. That way, not only do you get dependencies isolation, but you can also call directly your programs from outside the virtualenv and it will work.
What you propose is both bad practice (relative path in `$PYTHONPATH`) and rather annoying (you need to be in a specific directory to run everything). Trying to automate that will sure end up in pure hell!
- As in "you need to `cd` to _this_ directory then run _that_
- Or "WTF it worked on my machine when I was in this directory but now it doesn't"
The things you care about may be different from what others care about: I'm guessing don't care about Windows compatibility or you wouldn't talk about shebangs, and others may not find only running the scripts from the project root an issue (I don't for example).
Why is a relative path in the PYTHONPATH necessarily bad practice? It's not a rhetorical question, I am genuinely curious.
If you're happy with virtualenv then continue using it - all we have done is propose an alternative.
I found a writeup [1] on how to write your own virtualenv. It doesn't look like there's much overhead there.
I suppose one could justify not using virtualenv if they're on a system with limited resources or network access. I've known people who spent hours building a compiling python 2.7 from source on embedded systems because they rolled their own distro, and in that situation it would make sense to not want to mess with virtualenv if you just spent hours getting pip working.
1: https://www.recurse.com/blog/14-there-is-no-magic-virtualenv...
With this you just have one tool, and it's just easier for me to understand what's going on compared with virtualenv.
I'm not too sure that this would end up being any easier, but maybe...
[1] http://streetsign.org.uk/ [2] https://bitbucket.org/dfairhead/streetsign-server/src/5b359a...
$ /path/to/.virtualenv/my_project/bin/python SomeScript.py
Among other things, what happens if your Python code changes its working directory, then subsequently tries to import a module? Consequences range from "doesn't work" to "introduces a security vulnerability by running arbitrary Python code from an untrusted directory".
/tmp $ PYTHONPATH='./test' /usr/bin/python -c 'import sys; print sys.path[:3]'
['', '/private/tmp/test', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip']It's still odd for software to only work when launched from a specific directory, but not necessarily a critical security vulnerability then.
find . -type d -name node_modules | wc -l
from any reasonably large project's root directory -- the results will defy your sense of parsimony.Packages which require other modules pegged to a specific version will install that package within their own node_modules directory, when they are already deep within a subdirectory of another node_modules directory. If two packages both require bar@X.Y.Z, they will both install that bar package in their own node_modules directory.
It is literally the most naive way to write a package manager.
What exactly is the overhead of a virtualenv? A few symlinks and a script which adds a few variables to your environment? Your calculation of what is and isn't complex is exactly backwards from the traditional Python perspective.
As a Pythonista, I can't help but object to the homebrew/npm school of roll-your-own package management. It eschews parsimony, deliberate architecture planning, and learning from the mistakes of other projects in favor of aesthetics and (sorry, I can't think of another name for this) hipster-DIYism.
But I don't know enough about JS development to say whether this really happens and/or if it is an issue (some pretty cool stuff has been done with JS so I guess it's possible to live with it).
BTW I share your dislike of hipsters, though I am a bit jealous those moustaches are just so cool.
Let's say you reimplement ls in Python. You then use your shiny new program to have a look at some downloaded files:
$ ../utils/ls.py
['hello.txt', 'os.py']
So far so good. Now let's try it with PYTHONPATH set to include the current directory: $ PYTHONPATH="." ../utils/ls.py
Boom! Your box is mine!
['no', 'files', 'for', 'you']This is apparently an unacceptable limitation for some people, judging by the responses to the article - however for others it's not.
I end up doing some real crazy stuff:
* build in docker
* using virtualenv /usr/local/mypackage
* then manually copy over the init scripts "cp /usr/local/mypackage/bin/* /usr/local/bin"
* and debianize "/usr/local/mypackage/" and "/usr/local/bin".
Except it doesn't work:
* I need root do create the venv in /usr/local.
* This technique copies garbage like "activate" script to bin.
* virtualenv ships its own python binary. If the python version on host differ, it won't work.
* The workaround is to rewrite the bin scripts.
Total mess. All I need is a deb with some bin scripts, and all dependencies bundled. Is it _that_ hard...
I have some hopes to wrap a pex into deb/rpm, but I would not call this approach simple.
That's unfortunate since Python is a wonderful language for many data-sciency tasks - Python makes things possible and pleasant, that would be a pain in other languages.
* [1] https://github.com/jordansissel/fpm
* [2] https://github.com/kevinconway/rpmvenv
* [3] https://pex.readthedocs.org/en/latest/
* Another tool: https://github.com/spotify/dh-virtualenv
virtualenv -p python2.7 env_name or virtualenv --python=/some/path/to/python env_name
You've made the best argument for using pip like this, however. Instead of having to install a virtualenvironment and install dependencies and all of that, you could just ship your program with the pip -t libraries, though you'd have to include the path somehow on the install so python knew where to find those files.
How about using dh_python? And if you're building in docker anyway, why use virtualenv?
> All I need is a deb with some bin scripts, and all dependencies bundled.
Ah, that would be a problem. Python .debs are supposed to depend on separate python module packages, not bundle everything.
For what you're doing, have you considered turning your entire program into a single executable Python zipfile? See youtube-dl for an example.
I can't see why that cannot be adapted to .deb packages as well...
python setup.py --command-packages=stdeb.command bdist_deb
I'm pretty sure this works without requiring some special module.Virtualenv installs some .so files in its thing, so venv directory is tightly coupled with the python binary it runs. Ie: you need to sort it out on the moment you build venv, and shipping venv directory is not correct.
Isn't this exactly what virtualenv does anyway? Plus, with virtualenv you don't have to muck around in your $PYTHONPATH and you can still choose which Python version is installed for each virtualenv.
This article doesn't seem to explicitly mention any, but what are the compelling reasons to use pip over pyenv/virtualenv for project isolation? I guess using 1 tool to install dependencies and isolate your working environment is handy. I'm unsure if the overhead of virtualenv is so high that it offers a compelling reason to switch (at least for me).
If the activate/deactivate bothers you, you can also just call the interpreter directly: env/bin/python ; you can also call any pip-installed binaries similarly. For example, env/bin/ipython if you have that installed. Or env/bin/pip.
I have a small utility[1] to magic that to "vpython" and "vipython" and "venv pip", respectively. (The last one is the general "execute this binary in the virtualenv" form; the utility makes some assumptions about how you name your envs, however, but also searches up the tree (I can cd into a dir, so I don't need to do ../../env/bin/python… or source the env.) I believe there's another implementation of this idea out there, but I can't find it at the moment.
(My vim is/was a bit python-heavy, and sourcing envs utterly breaks it if I attempt to start vim with an env sourced, since it gets the right vimrc, but the wrong pip installation)
If you share a file system with untrusted users (like /tmp or an NFS mount) or extract an untrusted archive (zip, tar, rar) then you might inadvertently execute python code that another user placed there!
It's the same reason why you shouldn't use PATH=. in your shell.
This poor man's virtualenv doesn't seem like a good idea...
For example, if you're looking through various home directories for some file, you might be doing something like:
$ cd /home/foo
$ ls
$ cd /home/bar
$ ls
If your PATH contained ".useful_stuff/tools" and the user bar happened to have a file called "/home/bar/.useful_stuff/tools/ls", that file has now been executed instead of /bin/ls, with your privileges. Your account has now been compromised.Try this:
[ptx@hn downloads]$ python3 -c 'print("hello")'
hello
[ptx@hn downloads]$ export PYTHONPATH="./.pip"
[ptx@hn downloads]$ python3 -c 'print("hello")'
You have been owned! Have a nice day.
hello
[ptx@hn downloads]$ cat .pip/site.py
print("You have been owned! Have a nice day.")
For this same reason, if you use Mercurial on a repository owned by someone else, you get something like: [root@host foo]# hg stat
not trusting file /home/ptx/foo/.hg/hgrc from untrusted user ptx, group users
Microsoft had a related security problem a few years back where they would load DLL files from the current directory:
https://isc.sans.edu/diary/DLL+hijacking+vulnerabilities/944...The relative path is an interesting trick, I'll have to keep that in mind in the future.
There are so many little details that need to be fleshed out in such an infrastructure tool, we can't solve all these boring problems multiple times.
The idea of having different multiple versions of dependencies on a single system is not exactly a new problem, and is solved by many other package managers. Is there some inherent weakness in python modules that forces them to be managed this way? Do the maintainers of pip insist on this for other reasons?
It seems like one of the bigger complaints I hear about python so I feel like it must be harder then it seems.
1. You create a virtualenv 2. You enter the virtualenv 3. You install dependencies in it
Redo these steps for every isolated set of dependencies you want.
The solution proposed here does not solve anything. It only provides some half-working equivalent of virtualenv that works with a hidden directory to run your scripts...
1. Your 'node_modules' is automatically created by npm. No need for a separate tool.
2. You don't have to 'enter' an npm environment other than changing to the directory where it exists.
These really aren't huge issues, but I can see it being an annoyance for some, especially people coming from Node.js to Python.
A -> B, C
B -> D == 0.1
C -> D == 0.2
This dependency graph is handled easily by npm and other tools (at the expense of disk space, memory usage, etc.) and potentially small incompatibilities/gotchas, but is completely impossible to satisfy with pip.The complexity arises from differing system-wide dependencies that are out of scope of various package managers or virtual environments, and from development environment (assuming we’re addressing the need to have multiple apps co-exist on one machine for development) not matching production.
Unlike this pip solution, vex extends your PATH to include installed scripts. It also works well on Windows and is really easy to use.
Doesn't matter how many times others learn this lesson, I personally see it over and over.