Skip to content

Latest commit

 

History

History
464 lines (325 loc) · 12.8 KB

File metadata and controls

464 lines (325 loc) · 12.8 KB

Version Specifiers

This guide explains how to specify versions of packages and Python interpreters in Pipenv, including syntax, best practices, and advanced usage patterns.

Package Version Specifiers

Pipenv uses the same version specifier format as pip, following PEP 440 standards. These specifiers allow you to control exactly which versions of packages are installed.

Basic Version Specifiers

Specifier Example Meaning
== requests==2.28.1 Exact version
>= requests>=2.20.0 Minimum version
<= requests<=2.30.0 Maximum version
> requests>2.0.0 Greater than version
< requests<3.0.0 Less than version
!= requests!=2.29.0 Not equal to version
~= requests~=2.28.0 Compatible release (equivalent to >=2.28.0,<2.29.0)
* requests==* Any version (not recommended for production)

Combining Version Specifiers

You can combine specifiers to create version ranges:

# Install any version between 2.20.0 and 3.0.0
$ pipenv install "requests>=2.20.0,<3.0.0"
The use of double quotes around the package and version specification (i.e. `"requests>=2.20.0,<3.0.0"`) is highly recommended to avoid issues with shell interpretation, especially on Unix-based systems.

Compatible Release Operator

The ~= operator (compatible release) is particularly useful:

# Install version 2.28.* (>=2.28.0,<2.29.0)
$ pipenv install "requests~=2.28.0"

# Install version 2.* (>=2.0.0,<3.0.0)
$ pipenv install "requests~=2.0"

This ensures you get bug fixes (micro version updates) but not potentially breaking changes (major or minor version updates, depending on how you specify it).

Wildcard Versions

While not recommended for production, you can use wildcards:

# Install the latest version
$ pipenv install "requests==*"
# or simply
$ pipenv install requests

Pre-release Versions

By default, Pipenv doesn't install pre-release versions. To include them:

# Command line flag
$ pipenv install --pre "requests>=2.0.0"

# Or in Pipfile
# [pipenv]
# allow_prereleases = true

Python Version Specifiers

Specifying Python Version for a Project

You can specify which Python version to use when creating a virtual environment:

# Use Python 3
$ pipenv --python 3

# Use Python 3.10 specifically
$ pipenv --python 3.10

# Use a specific Python executable
$ pipenv --python /usr/local/bin/python3.10

This creates a Pipfile with a [requires] section:

[requires]
python_version = "3.10"

Python Version vs. Full Version

You can specify either a Python version or a full version:

# In your Pipfile
[requires]
python_version = "3.10"  # Any 3.10.x version

Or for more specific control:

# In your Pipfile
[requires]
python_full_version = "3.10.4"  # Exactly Python 3.10.4
**`python_version` only accepts an exact version string**, such as `"3.10"` or `"3"`.
It does **not** accept version range specifiers like `">= 3.6"` or `"~= 3.9"`.

If you write `python_version = ">= 3.6"`, Pipenv will warn that the required Python
version was not found and may behave unexpectedly:

    Warning: Python >= 3.6 was not found on your system...
    Warning: Your Pipfile requires python_version >= 3.6, but you are using 3.9.2 (...)

To express a minimum Python requirement, simply set the lowest version you support
as the exact `python_version` (e.g. `python_version = "3.9"`). If you need
a fully exact pin, use `python_full_version` instead (e.g. `python_full_version = "3.9.7"`).

Python Version Selection Logic

When you run pipenv install without specifying a Python version:

  1. Pipenv checks the [requires] section in the Pipfile
  2. If python_full_version is specified, it tries to use that exact version
  3. If python_version is specified, it tries to find a compatible version
  4. If neither is specified, it uses the default Python interpreter

Advanced Version Specifiers

VCS Dependencies

You can install packages directly from version control systems:

# From a Git repository
$ pipenv install -e git+https://github.com/requests/requests.git@v2.31.0#egg=requests

The format follows this pattern:

<vcs_type>+<scheme>://<location>/<user_or_organization>/<repository>@<branch_or_tag>#egg=<package_name>

Where:

  • <vcs_type> can be git, bzr, svn, or hg
  • <scheme> can be http, https, ssh, or file
  • @<branch_or_tag> is optional and specifies a specific branch, tag, or commit

This will be reflected in your Pipfile:

[packages]
requests = {editable = true, git = "https://github.com/requests/requests.git", ref = "v2.31.0"}

Local Path Dependencies

You can install packages from a local path using the path attribute:

# Install a local package in editable mode
$ pipenv install -e ./path/to/package

# Install the current directory as an editable package
$ pipenv install -e .

This will be reflected in your Pipfile:

[packages]
my-package = {editable = true, path = "./path/to/package"}
# or, for the current directory:
my-package = {editable = true, path = "."}

To install a local package without editable mode:

$ pipenv install ./path/to/package
[packages]
my-package = {path = "./path/to/package"}

Remote File URL Dependencies

You can install packages directly from a remote URL (wheel or source distribution) using the file attribute:

$ pipenv install https://example.com/packages/my-package-1.0.tar.gz

This will be reflected in your Pipfile:

[packages]
my-package = {file = "https://example.com/packages/my-package-1.0.tar.gz"}

path vs file: use path for local filesystem locations (e.g., . or ./libs/my-package) and file for remote HTTP/HTTPS URLs.

Platform-Specific Dependencies

You can specify that a package should only be installed on certain platforms using PEP 508 markers:

# Install pywinusb only on Windows
$ pipenv install "pywinusb ; sys_platform == 'win32'"

This will be reflected in your Pipfile:

[packages]
pywinusb = {version = "*", markers = "sys_platform == 'win32'"}

Shorthand Marker Keys

In addition to the full markers syntax, Pipenv supports shorthand keys for common markers directly in Pipfile entries:

[packages]
# These two forms are equivalent:
pywinusb = {version = "*", markers = "sys_platform == 'win32'"}
pywinusb = {version = "*", sys_platform = "== 'win32'"}

# Platform-specific using platform_machine:
special-arm-lib = {version = "*", platform_machine = "== 'arm64'"}

# macOS-only dependency:
pyobjc = {version = "*", sys_platform = "== 'darwin'"}

Any key from the PEP 508 environment markers can be used as a shorthand.

Platform Markers and Locking

When `pipenv lock` runs, pip resolves **all** packages in your `Pipfile`, regardless of
platform markers. If a package with a `sys_platform == 'win32'` marker is listed and
pip cannot find a matching distribution for it on your current platform (e.g. Linux),
you may see an error like:

    ERROR: No matching distribution found for pywin32

This happens because pip's resolver attempts to download metadata for all listed packages
and some Windows-only packages do not publish distributions for Linux/macOS.

**Workaround:** Ensure the package supports all platforms at the *metadata* level, or
split Windows-specific packages into a separate Pipfile or category. For packages that
truly have no Linux/macOS releases at all, consider managing them outside of Pipenv for
the non-Windows environments.

Architecture-Specific Dependencies

Use the platform_machine marker to target specific CPU architectures:

[packages]
# Only install on ARM64 (e.g., Apple Silicon Macs, ARM Linux)
arm-optimized = {version = "*", platform_machine = "== 'arm64'"}

# Only install on x86_64 systems
x86-optimized = {version = "*", platform_machine = "== 'x86_64'"}

# macOS on Apple Silicon only
macos-arm = {version = "*", markers = "sys_platform == 'darwin' and platform_machine == 'arm64'"}

Common platform_machine values:

  • x86_64 — 64-bit Intel/AMD (Linux, macOS)
  • arm64 — ARM 64-bit (Apple Silicon macOS)
  • aarch64 — ARM 64-bit (Linux)
  • AMD64 — 64-bit Intel/AMD (Windows)

Note: platform_machine controls whether a package is installed based on the current machine's architecture. It does not control which wheel variant pip selects (e.g., it cannot force pip to choose a universal2 wheel over an arm64 wheel). To install a specific wheel file, use the file or path attribute instead — see Local and Remote File Dependencies.

Python Version-Specific Dependencies

You can specify dependencies that are only needed for certain Python versions:

[packages]
typing = {version = ">=3.6.2", markers = "python_version < '3.5'"}
dataclasses = {version = ">=0.8", markers = "python_version < '3.7'"}

Complex Markers

You can use complex logical expressions in markers:

[packages]
unittest2 = {version = ">=1.0,<3.0", markers = "python_version < '2.7.9' or (python_version >= '3.0' and python_version < '3.4')"}

Common markers include:

  • python_version: Python version in 'X.Y' format
  • python_full_version: Python version in 'X.Y.Z' format
  • sys_platform: Platform name (e.g., 'win32', 'linux', 'darwin')
  • platform_machine: Machine type (e.g., 'x86_64', 'arm64', 'aarch64', 'AMD64')
  • platform_system: Operating system name (e.g., 'Linux', 'Darwin', 'Windows')
  • platform_python_implementation: Python implementation (e.g., 'CPython', 'PyPy')
  • os_name: Name of the operating system (e.g., 'posix', 'nt')

Package Extras

Many packages provide optional features as "extras":

# Install requests with the 'security' and 'socks' extras
$ pipenv install "requests[security,socks]"

This will be reflected in your Pipfile:

[packages]
requests = {version = "*", extras = ["security", "socks"]}

Package Categories

Pipenv supports organizing dependencies into different categories beyond the standard packages and dev-packages.

Defining Custom Categories

In your Pipfile:

[packages]
requests = "*"

[dev-packages]
pytest = "*"

[docs]
sphinx = "*"
sphinx-rtd-theme = "*"

[tests]
pytest-cov = "*"

Installing Specific Categories

# Install a package in a specific category
$ pipenv install sphinx --categories="docs"

# Install all packages from specific categories
$ pipenv install --categories="docs,tests"

Locking Specific Categories

# Lock only specific categories
$ pipenv lock --categories="docs,tests"

Best Practices

For Applications

For applications, use specific versions to ensure stability:

[packages]
# Exact version for critical dependencies
requests = "==2.28.1"
# Compatible release for less critical dependencies
flask = "~=2.0.1"
# Version range for flexible dependencies
urllib3 = ">=1.26.0,<2.0.0"

For Libraries

For libraries, use more flexible version constraints:

[packages]
# Minimum version only
requests = ">=2.20.0"
# Upper bound to prevent incompatible versions
urllib3 = "<2.0.0"

Security Considerations

  • Regularly update dependencies to get security fixes
  • Use pipenv scan to check for vulnerabilities
  • Avoid using * or very loose constraints in production
  • Consider using hash verification with pipenv lock

Dependency Resolution

When specifying versions, consider:

  1. Compatibility: Ensure your constraints don't conflict with other packages
  2. Flexibility: Overly strict constraints can make dependency resolution difficult
  3. Security: Too loose constraints might introduce vulnerabilities
  4. Stability: Balance between getting updates and maintaining stability

Troubleshooting

Common Issues

Version Conflict Resolution

If you encounter version conflicts:

# Try with verbose output to see the conflict
$ pipenv install --verbose

# Try relaxing version constraints
# Instead of requests==2.28.1, try requests~=2.28.0

No Matching Distribution Found

If pip can't find a matching distribution:

  1. Check that the version exists on PyPI
  2. Verify your version specifier syntax
  3. Consider if you need pre-release versions (--pre)

Dependency Resolution Failures

If Pipenv can't resolve dependencies:

# Clear the cache and try again
$ pipenv lock --clear

Conclusion

Understanding version specifiers is crucial for effective dependency management with Pipenv. By using the right specifiers, you can balance stability, security, and flexibility in your Python projects.