It isn’t as if virtualenv is the only possible way to manage Python dependencies. It’s just one of the best. If you were experienced with Python before using virtualenv, you probably know some of the other hacks.
- You probably know that different versions of Python can usually coexist
as long as you use commands like
C:\Python27\Scripts\pip-2.7, etc.) to be specific about what you’re using. And on Unix-like OSes, rewrite all your hashbang lines accordingly (
- You probably know that you can specify the PYTHONPATH environment variable to force Python to search directories in the order you want.
- You probably know that you can modify sys.path before importing modules in order to control where they will be searched for first. (One of the worst.)
- You probably know that you can isolate dependencies and pin specific versions by ‘vendoring’ them (make copies of the packages you use in subdirectories right under the root directory you use for your project).
- You probably know that you can sidestep most conflicts by just avoiding upgrades forever and only developing against versions available in the system repos (the “head-in-sand” method).
But all of this is unnecessarily ugly and hard. It doesn’t even solve the problem completely: unless you use something similar to virtualenv, you will still end up with multiple projects whose dependencies are not isolated from each other. Deferring upgrades only makes them more painful when they become actually necessary.
virtualenv solves this entire class of problems at once, with very little effort. Everything can be upgraded as much as it needs, and nothing breaks because of dependencies. It mostly just does the right thing for Python, without more fuss than necessary. That’s part of why people even use it when they are also using virtual machines, containers or the like - though these could also serve as alternatives to virtualenv if you are comfortable with them. virtualenv has the small advantage of being the normal tool for this purpose in Python.