Managing Python packages is something every developer eventually faces. When I started working on Python projects, I realized very quickly that without proper organization, things could get messy fast. Dependencies could conflict, environments would break, and it felt like I was spending more time untangling version issues than writing actual code. Sound familiar?
In this post, I want to share the journey I went through to manage Python packages the right way—whether you're building apps for development, production, or testing. I'll walk through the essentials: virtual environments, dependency management, and how to use tools like pip
, pipenv
, poetry
, and even pyenv
to make Python package management as smooth as possible.
I can’t stress this enough: you should always use virtual environments. These isolated environments are like silos for your Python projects. Each project gets its own clean workspace, so the dependencies for one don’t conflict with the dependencies of another. This is especially crucial if you’re juggling multiple projects, each with its own requirements.
Let’s say you’re working on a project that uses Django 2.x, and another one that uses Django 3.x. Without virtual environments, trying to run both would be a disaster. You'd install Django 3.x globally, only to find that it broke your Django 2.x project. This is where virtual environments save the day.
venv
with pip
: The Built-In WayThe first time I created a virtual environment, I used the venv
module, which comes built into Python. It's perfect for lightweight project isolation and easy to use.
venv
Here’s how I typically create a virtual environment:
python3 -m venv env
This command sets up a virtual environment in the env
directory. Inside this environment, pip
is ready to install packages that are specific to the project. Anything I install here won’t affect my system Python installation.
To activate the environment in bash/zsh:
. env/bin/activate
For fish shell:
. env/bin/activate.fish
For Windows users, activation looks like this:
.\env\Scripts activate
Once activated, the command prompt changes (usually showing (env)
), letting me know that I’m working inside the virtual environment.
pip
Now that the virtual environment is active, I can install packages:
pip install requests
The installed packages go straight into the virtual environment’s directories, leaving my global environment untouched. Running pip list
inside the environment will show the packages installed for this specific project.
When I’m done, I deactivate the environment:
deactivate
Boom! I’m back in the global environment. The beauty of this setup is that each project has its own dependencies neatly isolated.
pip
Here are some essential pip
commands that I use regularly:
Installing a package:
pip install <package-name>
Uninstalling a package:
pip uninstall <package-name>
Upgrading a package:
pip install --upgrade <package-name>
Checking outdated packages:
pip list --outdated
Installing from a requirements.txt
:
pip install -r requirements.txt
Generating requirements.txt
:
pip freeze > requirements.txt
Viewing installed packages:
pip list
Checking package details (location, version, etc.):
pip show <package-name>
Installing a package for a specific Python version:
python3 -m pip install <package-name>
Installing in user scope (non-system-wide):
python3 -m pip install --user <package-name>
virtualenv
: The Older but Gold StandardBefore venv
was available, we had virtualenv
, which is still widely used, especially in environments that use older Python versions (< 3.3). Though similar to venv
, virtualenv
has some extra features, like being able to create environments for different versions of Python.
virtualenv
Installing virtualenv
:
pip install virtualenv
Creating a virtual environment:
virtualenv <env-name>
Creating a virtual environment with a specific Python version:
virtualenv -p python3.8 <env-name>
Activating the environment:
. <env-name>/bin/activate
.\<env-name>\Scriptsctivate
Deactivating the environment:
deactivate
Checking Python version inside the virtual environment:
python --version
List installed packages:
pip list
Install packages in virtualenv:
pip install <package-name>
Remove the virtual environment: Just delete the directory:
rm -rf <env-name>
Recreate the environment from a requirements.txt
file:
virtualenv <env-name> && pip install -r requirements.txt
pipenv
: Making Virtual Environments and Dependency Management EasyI came across Pipenv when I started managing more complex projects, and I wanted a tool that combined virtual environments with dependency management. Pipenv does just that—handling both with minimal hassle.
pipenv
Installing a package and creating a virtual environment:
pipenv install <package-name>
Installing a package for development only:
pipenv install <package-name> --dev
Activating the virtual environment:
pipenv shell
Checking installed packages:
pipenv graph
Uninstalling a package:
pipenv uninstall <package-name>
Uninstalling all packages:
pipenv uninstall --all
Generating a Pipfile.lock
:
pipenv lock
Installing from Pipfile.lock
:
pipenv sync
Running a command inside the virtual environment without activating:
pipenv run python script.py
Checking security vulnerabilities in dependencies:
pipenv check
poetry
: A Modern Take on Package ManagementAt some point, I wanted more control over how my projects were packaged and distributed. That’s when I found Poetry, a tool that takes dependency management and virtual environments up a notch.
poetry
Creating a new project:
poetry new <project-name>
Adding a dependency:
poetry add <package-name>
Adding a development dependency:
poetry add --dev <package-name>
Installing project dependencies:
poetry install
Activating the virtual environment:
poetry shell
Running a command without activating:
poetry run python script.py
Generating poetry.lock
:
poetry lock
Building a package for distribution:
poetry build
Publishing a package to PyPI:
poetry publish
Checking package dependencies:
poetry show --tree
tox
: Testing Across Multiple EnvironmentsTox is a tool that automates testing in multiple environments, making it perfect for ensuring compatibility across different Python versions.
tox
Install tox:
pip install tox
Generate tox.ini
configuration file:
Create a basic tox.ini
file for running tests on multiple Python versions.
[tox]
envlist = py36, py37, py38
[testenv]
deps = pytest
commands = pytest
Run tests:
tox
Run tests for a specific environment:
tox -e py37
Recreate virtual environments (clean up):
tox --recreate
List environments:
tox -l
Skip environment recreation:
tox --skip-missing-interpreters
Linting with tox (e.g., using flake8
):
[testenv:lint]
deps = flake8
commands = flake8 your_project
Run linting:
tox -e lint
Install extra dependencies for testing:
tox --installpkg <package-name>
In my journey of managing Python packages, I realized there’s no one-size-fits-all solution. Sometimes pip
with virtual environments is all I need, but for more complex projects, tools like pipenv
and poetry
make life easier. And if you’re like me, juggling multiple Python versions, pyenv
will save you a ton of headaches.
Package management in Python is all about finding what works best for your workflow. Whether you’re maintaining small scripts or full-blown web applications, these tools will help you stay organized and keep your projects running smoothly.