Python Development Workflow for Humans.
—
Utility functions for dependency parsing, shell operations, file handling, and system integration. These modules provide helper functions, constants, and lower-level operations that support pipenv's core functionality.
Parse and manipulate package dependencies and version specifications.
# From pipenv.utils.dependencies
def get_version(pipfile_entry):
"""
Extract version specification from Pipfile entry.
Parameters:
pipfile_entry: Pipfile package entry (str or dict)
Returns:
str: Version specification
"""
def python_version(path_to_python):
"""
Get Python version from executable path.
Parameters:
path_to_python (str): Path to Python executable
Returns:
str: Python version string
"""
def clean_pkg_version(version):
"""
Clean and normalize package version string.
Parameters:
version (str): Raw version string
Returns:
str: Cleaned version string
"""
def expansive_install_req_from_line(line, extras_to_install=None):
"""
Parse pip install line into InstallRequirement.
Parameters:
line (str): Pip install line
extras_to_install (list, optional): Extra requirements
Returns:
InstallRequirement: Parsed requirement
"""
def determine_package_name(package):
"""
Determine canonical package name.
Parameters:
package: Package specification
Returns:
str: Package name
"""
def get_canonical_names(packages):
"""
Get canonical names for packages.
Parameters:
packages (list): List of package specifications
Returns:
list: Canonical package names
"""Usage examples:
from pipenv.utils.dependencies import (
get_version, python_version, clean_pkg_version,
determine_package_name, get_canonical_names
)
# Extract version from Pipfile entries
version1 = get_version("requests>=2.25.0") # ">=2.25.0"
version2 = get_version({"version": "==1.0.0"}) # "==1.0.0"
version3 = get_version("*") # "*"
# Get Python version
py_version = python_version("/usr/bin/python3.9")
print(f"Python version: {py_version}") # "3.9.7"
# Clean version strings
clean_version = clean_pkg_version("==2.25.1 ") # "==2.25.1"
# Parse install requirements
from pipenv.utils.dependencies import expansive_install_req_from_line
req = expansive_install_req_from_line("requests>=2.25.0")
print(f"Package: {req.name}, Version: {req.specifier}")
# Determine package names
name1 = determine_package_name("requests>=2.25.0") # "requests"
name2 = determine_package_name("git+https://...") # Extracted from URL
# Get canonical names
packages = ["Django", "REQUESTS", "click"]
canonical = get_canonical_names(packages)
print(f"Canonical names: {canonical}") # ["django", "requests", "click"]Handle shell operations, path manipulation, and directory management.
# From pipenv.utils.shell
def make_posix(path):
"""
Convert path to POSIX format.
Parameters:
path (str): File system path
Returns:
str: POSIX-formatted path
"""
def chdir(path):
"""
Context manager for changing directory.
Parameters:
path (str): Directory path to change to
Returns:
ContextManager: Directory change context
"""
def looks_like_dir(path):
"""
Check if path looks like a directory.
Parameters:
path (str): Path to check
Returns:
bool: True if path looks like directory
"""
def load_path(python):
"""
Load Python sys.path for given executable.
Parameters:
python (str): Python executable path
Returns:
list: Python sys.path entries
"""Usage examples:
from pipenv.utils.shell import make_posix, chdir, looks_like_dir, load_path
import os
# Convert to POSIX paths
posix_path = make_posix("C:\\Users\\Name\\Project") # "/c/Users/Name/Project"
posix_path2 = make_posix("/already/posix/path") # "/already/posix/path"
# Change directory context
current_dir = os.getcwd()
with chdir("/tmp"):
print(f"Inside context: {os.getcwd()}") # "/tmp"
# Do work in /tmp
print(f"Outside context: {os.getcwd()}") # Back to original
# Check if path looks like directory
is_dir1 = looks_like_dir("/path/to/directory/") # True
is_dir2 = looks_like_dir("/path/to/file.py") # False
is_dir3 = looks_like_dir("relative_dir") # True
# Load Python path
python_paths = load_path("/usr/bin/python3.9")
print(f"Python sys.path: {python_paths}")Handle Pipfile discovery, creation, and manipulation.
# From pipenv.utils.pipfile
def find_pipfile(max_depth=3):
"""
Find Pipfile in directory tree.
Parameters:
max_depth (int): Maximum directories to search up
Returns:
str|None: Path to Pipfile or None if not found
"""
def ensure_pipfile():
"""
Ensure Pipfile exists in current directory.
Returns:
str: Path to Pipfile
"""
class Pipfile:
"""
Pipfile manipulation class.
Methods:
load(): Load Pipfile content
write(): Write Pipfile content
add_package(): Add package to Pipfile
remove_package(): Remove package from Pipfile
"""Usage examples:
from pipenv.utils.pipfile import find_pipfile, ensure_pipfile, Pipfile
import os
# Find Pipfile in current or parent directories
pipfile_path = find_pipfile(max_depth=5)
if pipfile_path:
print(f"Found Pipfile at: {pipfile_path}")
else:
print("No Pipfile found")
# Ensure Pipfile exists
pipfile_path = ensure_pipfile()
print(f"Pipfile location: {pipfile_path}")
# Work with Pipfile class
pipfile = Pipfile()
pipfile_data = pipfile.load()
print(f"Pipfile packages: {pipfile_data.get('packages', {}).keys()}")
# Modify Pipfile
pipfile.add_package("requests", ">=2.25.0")
pipfile.remove_package("unused-package")
pipfile.write()Handle lock file operations and atomic writes.
# From pipenv.utils.locking
def prepare_lockfile(lockfile_data):
"""
Prepare lockfile data for writing.
Parameters:
lockfile_data (dict): Raw lockfile data
Returns:
dict: Prepared lockfile data
"""
def atomic_open_for_write(filename):
"""
Atomic file writing context manager.
Parameters:
filename (str): File to write atomically
Returns:
ContextManager: Atomic write context
"""
class Lockfile:
"""
Lockfile operations class.
Methods:
load(): Load lockfile content
write(): Write lockfile content
verify(): Verify lockfile integrity
"""Usage examples:
from pipenv.utils.locking import prepare_lockfile, atomic_open_for_write, Lockfile
import json
# Prepare lockfile data
raw_lockfile = {
"default": {"requests": {"version": "==2.25.1"}},
"develop": {"pytest": {"version": "==6.2.2"}}
}
prepared = prepare_lockfile(raw_lockfile)
# Atomic file writing
with atomic_open_for_write("Pipfile.lock") as f:
json.dump(prepared, f, indent=4)
# Work with Lockfile class
lockfile = Lockfile()
lockfile_data = lockfile.load()
print(f"Lockfile hash: {lockfile_data.get('_meta', {}).get('hash')}")
# Verify lockfile
is_valid = lockfile.verify()
print(f"Lockfile valid: {is_valid}")Important constants and type definitions used throughout pipenv.
# From pipenv.utils.constants
VCS_LIST: tuple # ("git", "svn", "hg", "bzr")
SCHEME_LIST: tuple # ("http://", "https://", "ftp://", ...)
FALSE_VALUES: tuple # ("0", "false", "no", "off")
TRUE_VALUES: tuple # ("1", "true", "yes", "on")
REMOTE_SCHEMES: tuple # Remote URL schemes
RELEVANT_PROJECT_FILES: tuple # Project configuration files
INSTALLABLE_EXTENSIONS: tuple # Package file extensions
# Additional constants
DEFAULT_NEWLINES: str # Default line endings
PIPFILE_SPEC: dict # Pipfile specification
LOCKFILE_VERSION: str # Lock file format versionUsage examples:
from pipenv.utils.constants import (
VCS_LIST, SCHEME_LIST, FALSE_VALUES, TRUE_VALUES,
RELEVANT_PROJECT_FILES, INSTALLABLE_EXTENSIONS
)
# Check VCS systems
if "git" in VCS_LIST:
print("Git is supported")
# Validate URL schemes
def is_remote_url(url):
return any(url.startswith(scheme) for scheme in SCHEME_LIST)
# Parse boolean values
def parse_bool(value):
if str(value).lower() in TRUE_VALUES:
return True
elif str(value).lower() in FALSE_VALUES:
return False
else:
raise ValueError(f"Invalid boolean value: {value}")
# Check for project files
import os
def find_project_files():
found_files = []
for filename in RELEVANT_PROJECT_FILES:
if os.path.exists(filename):
found_files.append(filename)
return found_files
# Check installable files
def is_installable_file(filename):
return any(filename.endswith(ext) for ext in INSTALLABLE_EXTENSIONS)
print(f"VCS systems: {VCS_LIST}")
print(f"URL schemes: {SCHEME_LIST}")
print(f"Project files: {find_project_files()}")Parse and execute Pipfile scripts with support for call-based script definitions.
class Script:
"""
Parse a script line from Pipfile's [scripts] section.
Always works in POSIX mode, even on Windows.
"""
script_types: list # ["call"]
def __init__(self, command, args=None):
"""
Initialize Script with command and arguments.
Parameters:
command (str): The command to execute
args (list, optional): Additional arguments
"""
@classmethod
def parse(cls, value):
"""
Parse script value from Pipfile.
Parameters:
value: Script definition (str, list, or inline table)
Returns:
Script: Parsed script instance
"""
def cmdify(self):
"""
Convert script to executable command format.
Returns:
str: Executable command string
"""
class ScriptEmptyError(ValueError):
"""Raised when script definition is empty."""
class ScriptParseError(ValueError):
"""Raised when script definition cannot be parsed."""Usage examples:
from pipenv.cmdparse import Script, ScriptParseError
# Parse simple string script
script = Script.parse("python setup.py test")
command = script.cmdify()
print(f"Command: {command}")
# Parse call-based script
call_script = Script.parse({"call": "mypackage.module:function('arg')"})
call_command = call_script.cmdify()
print(f"Call command: {call_command}")
# Handle script parsing errors
try:
invalid_script = Script.parse({"invalid": "not supported"})
except ScriptParseError as e:
print(f"Script parse error: {e}")
# Create script programmatically
custom_script = Script("pytest", ["-v", "--cov=mypackage"])
test_command = custom_script.cmdify()
print(f"Test command: {test_command}")Additional file and path manipulation utilities.
# File operations
def normalize_path(path):
"""Normalize file system path."""
def is_file_url(url):
"""Check if URL is a file:// URL."""
def is_vcs_url(url):
"""Check if URL is a VCS URL."""
def temp_environ():
"""Context manager for temporary environment variables."""
def safe_expandvars(value):
"""Safely expand environment variables in string."""Usage examples:
from pipenv.utils import normalize_path, is_file_url, is_vcs_url, temp_environ
import os
# Normalize paths
normalized = normalize_path("./relative/../path/to/file")
print(f"Normalized: {normalized}")
# Check URL types
file_url = is_file_url("file:///path/to/file") # True
vcs_url = is_vcs_url("git+https://github.com/user/repo") # True
# Temporary environment
with temp_environ():
os.environ["TEMP_VAR"] = "temporary_value"
print(f"Temp var: {os.environ.get('TEMP_VAR')}")
# Variable is removed after contextComplete examples showing utility integration.
from pipenv.utils.dependencies import (
get_version, determine_package_name, get_canonical_names
)
from pipenv.utils.pipfile import Pipfile
def analyze_dependencies():
"""Analyze project dependencies."""
pipfile = Pipfile()
pipfile_data = pipfile.load()
packages = pipfile_data.get("packages", {})
dev_packages = pipfile_data.get("dev-packages", {})
print("Production Dependencies:")
for pkg, spec in packages.items():
version = get_version(spec)
canonical = determine_package_name(pkg)
print(f" {canonical}: {version}")
print("\nDevelopment Dependencies:")
for pkg, spec in dev_packages.items():
version = get_version(spec)
canonical = determine_package_name(pkg)
print(f" {canonical}: {version}")
# analyze_dependencies()from pipenv.utils.shell import chdir, load_path
from pipenv.utils.pipfile import find_pipfile, ensure_pipfile
import os
def setup_project_environment(project_path):
"""Set up pipenv project environment."""
with chdir(project_path):
# Find or create Pipfile
pipfile_path = find_pipfile()
if not pipfile_path:
pipfile_path = ensure_pipfile()
print(f"Created new Pipfile at: {pipfile_path}")
else:
print(f"Using existing Pipfile: {pipfile_path}")
# Load Python path information
python_exe = "/usr/bin/python3"
if os.path.exists(python_exe):
sys_path = load_path(python_exe)
print(f"Python sys.path has {len(sys_path)} entries")
# setup_project_environment("/path/to/project")from pipenv.utils.locking import prepare_lockfile, atomic_open_for_write, Lockfile
import json
import hashlib
def update_lockfile(packages_data):
"""Update lock file with new package data."""
# Prepare lockfile data
lockfile_data = {
"_meta": {
"hash": {"sha256": hashlib.sha256(json.dumps(packages_data, sort_keys=True).encode()).hexdigest()},
"pipfile-spec": 6,
"requires": {"python_version": "3.9"},
"sources": [{"name": "pypi", "url": "https://pypi.org/simple", "verify_ssl": True}]
},
"default": packages_data.get("packages", {}),
"develop": packages_data.get("dev-packages", {})
}
prepared = prepare_lockfile(lockfile_data)
# Write atomically
with atomic_open_for_write("Pipfile.lock") as f:
json.dump(prepared, f, indent=4, sort_keys=True)
print("Lockfile updated successfully")
# Example usage
# packages = {
# "packages": {"requests": {"version": "==2.25.1"}},
# "dev-packages": {"pytest": {"version": "==6.2.2"}}
# }
# update_lockfile(packages)Install with Tessl CLI
npx tessl i tessl/pypi-pipenv