0
# Utilities
1
2
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.
3
4
## Capabilities
5
6
### Dependency Utilities
7
8
Parse and manipulate package dependencies and version specifications.
9
10
```python { .api }
11
# From pipenv.utils.dependencies
12
def get_version(pipfile_entry):
13
"""
14
Extract version specification from Pipfile entry.
15
16
Parameters:
17
pipfile_entry: Pipfile package entry (str or dict)
18
19
Returns:
20
str: Version specification
21
"""
22
23
def python_version(path_to_python):
24
"""
25
Get Python version from executable path.
26
27
Parameters:
28
path_to_python (str): Path to Python executable
29
30
Returns:
31
str: Python version string
32
"""
33
34
def clean_pkg_version(version):
35
"""
36
Clean and normalize package version string.
37
38
Parameters:
39
version (str): Raw version string
40
41
Returns:
42
str: Cleaned version string
43
"""
44
45
def expansive_install_req_from_line(line, extras_to_install=None):
46
"""
47
Parse pip install line into InstallRequirement.
48
49
Parameters:
50
line (str): Pip install line
51
extras_to_install (list, optional): Extra requirements
52
53
Returns:
54
InstallRequirement: Parsed requirement
55
"""
56
57
def determine_package_name(package):
58
"""
59
Determine canonical package name.
60
61
Parameters:
62
package: Package specification
63
64
Returns:
65
str: Package name
66
"""
67
68
def get_canonical_names(packages):
69
"""
70
Get canonical names for packages.
71
72
Parameters:
73
packages (list): List of package specifications
74
75
Returns:
76
list: Canonical package names
77
"""
78
```
79
80
Usage examples:
81
```python
82
from pipenv.utils.dependencies import (
83
get_version, python_version, clean_pkg_version,
84
determine_package_name, get_canonical_names
85
)
86
87
# Extract version from Pipfile entries
88
version1 = get_version("requests>=2.25.0") # ">=2.25.0"
89
version2 = get_version({"version": "==1.0.0"}) # "==1.0.0"
90
version3 = get_version("*") # "*"
91
92
# Get Python version
93
py_version = python_version("/usr/bin/python3.9")
94
print(f"Python version: {py_version}") # "3.9.7"
95
96
# Clean version strings
97
clean_version = clean_pkg_version("==2.25.1 ") # "==2.25.1"
98
99
# Parse install requirements
100
from pipenv.utils.dependencies import expansive_install_req_from_line
101
req = expansive_install_req_from_line("requests>=2.25.0")
102
print(f"Package: {req.name}, Version: {req.specifier}")
103
104
# Determine package names
105
name1 = determine_package_name("requests>=2.25.0") # "requests"
106
name2 = determine_package_name("git+https://...") # Extracted from URL
107
108
# Get canonical names
109
packages = ["Django", "REQUESTS", "click"]
110
canonical = get_canonical_names(packages)
111
print(f"Canonical names: {canonical}") # ["django", "requests", "click"]
112
```
113
114
### Shell Utilities
115
116
Handle shell operations, path manipulation, and directory management.
117
118
```python { .api }
119
# From pipenv.utils.shell
120
def make_posix(path):
121
"""
122
Convert path to POSIX format.
123
124
Parameters:
125
path (str): File system path
126
127
Returns:
128
str: POSIX-formatted path
129
"""
130
131
def chdir(path):
132
"""
133
Context manager for changing directory.
134
135
Parameters:
136
path (str): Directory path to change to
137
138
Returns:
139
ContextManager: Directory change context
140
"""
141
142
def looks_like_dir(path):
143
"""
144
Check if path looks like a directory.
145
146
Parameters:
147
path (str): Path to check
148
149
Returns:
150
bool: True if path looks like directory
151
"""
152
153
def load_path(python):
154
"""
155
Load Python sys.path for given executable.
156
157
Parameters:
158
python (str): Python executable path
159
160
Returns:
161
list: Python sys.path entries
162
"""
163
```
164
165
Usage examples:
166
```python
167
from pipenv.utils.shell import make_posix, chdir, looks_like_dir, load_path
168
import os
169
170
# Convert to POSIX paths
171
posix_path = make_posix("C:\\Users\\Name\\Project") # "/c/Users/Name/Project"
172
posix_path2 = make_posix("/already/posix/path") # "/already/posix/path"
173
174
# Change directory context
175
current_dir = os.getcwd()
176
with chdir("/tmp"):
177
print(f"Inside context: {os.getcwd()}") # "/tmp"
178
# Do work in /tmp
179
print(f"Outside context: {os.getcwd()}") # Back to original
180
181
# Check if path looks like directory
182
is_dir1 = looks_like_dir("/path/to/directory/") # True
183
is_dir2 = looks_like_dir("/path/to/file.py") # False
184
is_dir3 = looks_like_dir("relative_dir") # True
185
186
# Load Python path
187
python_paths = load_path("/usr/bin/python3.9")
188
print(f"Python sys.path: {python_paths}")
189
```
190
191
### Pipfile Utilities
192
193
Handle Pipfile discovery, creation, and manipulation.
194
195
```python { .api }
196
# From pipenv.utils.pipfile
197
def find_pipfile(max_depth=3):
198
"""
199
Find Pipfile in directory tree.
200
201
Parameters:
202
max_depth (int): Maximum directories to search up
203
204
Returns:
205
str|None: Path to Pipfile or None if not found
206
"""
207
208
def ensure_pipfile():
209
"""
210
Ensure Pipfile exists in current directory.
211
212
Returns:
213
str: Path to Pipfile
214
"""
215
216
class Pipfile:
217
"""
218
Pipfile manipulation class.
219
220
Methods:
221
load(): Load Pipfile content
222
write(): Write Pipfile content
223
add_package(): Add package to Pipfile
224
remove_package(): Remove package from Pipfile
225
"""
226
```
227
228
Usage examples:
229
```python
230
from pipenv.utils.pipfile import find_pipfile, ensure_pipfile, Pipfile
231
import os
232
233
# Find Pipfile in current or parent directories
234
pipfile_path = find_pipfile(max_depth=5)
235
if pipfile_path:
236
print(f"Found Pipfile at: {pipfile_path}")
237
else:
238
print("No Pipfile found")
239
240
# Ensure Pipfile exists
241
pipfile_path = ensure_pipfile()
242
print(f"Pipfile location: {pipfile_path}")
243
244
# Work with Pipfile class
245
pipfile = Pipfile()
246
pipfile_data = pipfile.load()
247
print(f"Pipfile packages: {pipfile_data.get('packages', {}).keys()}")
248
249
# Modify Pipfile
250
pipfile.add_package("requests", ">=2.25.0")
251
pipfile.remove_package("unused-package")
252
pipfile.write()
253
```
254
255
### Locking Utilities
256
257
Handle lock file operations and atomic writes.
258
259
```python { .api }
260
# From pipenv.utils.locking
261
def prepare_lockfile(lockfile_data):
262
"""
263
Prepare lockfile data for writing.
264
265
Parameters:
266
lockfile_data (dict): Raw lockfile data
267
268
Returns:
269
dict: Prepared lockfile data
270
"""
271
272
def atomic_open_for_write(filename):
273
"""
274
Atomic file writing context manager.
275
276
Parameters:
277
filename (str): File to write atomically
278
279
Returns:
280
ContextManager: Atomic write context
281
"""
282
283
class Lockfile:
284
"""
285
Lockfile operations class.
286
287
Methods:
288
load(): Load lockfile content
289
write(): Write lockfile content
290
verify(): Verify lockfile integrity
291
"""
292
```
293
294
Usage examples:
295
```python
296
from pipenv.utils.locking import prepare_lockfile, atomic_open_for_write, Lockfile
297
import json
298
299
# Prepare lockfile data
300
raw_lockfile = {
301
"default": {"requests": {"version": "==2.25.1"}},
302
"develop": {"pytest": {"version": "==6.2.2"}}
303
}
304
prepared = prepare_lockfile(raw_lockfile)
305
306
# Atomic file writing
307
with atomic_open_for_write("Pipfile.lock") as f:
308
json.dump(prepared, f, indent=4)
309
310
# Work with Lockfile class
311
lockfile = Lockfile()
312
lockfile_data = lockfile.load()
313
print(f"Lockfile hash: {lockfile_data.get('_meta', {}).get('hash')}")
314
315
# Verify lockfile
316
is_valid = lockfile.verify()
317
print(f"Lockfile valid: {is_valid}")
318
```
319
320
### Constants and Definitions
321
322
Important constants and type definitions used throughout pipenv.
323
324
```python { .api }
325
# From pipenv.utils.constants
326
VCS_LIST: tuple # ("git", "svn", "hg", "bzr")
327
SCHEME_LIST: tuple # ("http://", "https://", "ftp://", ...)
328
FALSE_VALUES: tuple # ("0", "false", "no", "off")
329
TRUE_VALUES: tuple # ("1", "true", "yes", "on")
330
REMOTE_SCHEMES: tuple # Remote URL schemes
331
RELEVANT_PROJECT_FILES: tuple # Project configuration files
332
INSTALLABLE_EXTENSIONS: tuple # Package file extensions
333
334
# Additional constants
335
DEFAULT_NEWLINES: str # Default line endings
336
PIPFILE_SPEC: dict # Pipfile specification
337
LOCKFILE_VERSION: str # Lock file format version
338
```
339
340
Usage examples:
341
```python
342
from pipenv.utils.constants import (
343
VCS_LIST, SCHEME_LIST, FALSE_VALUES, TRUE_VALUES,
344
RELEVANT_PROJECT_FILES, INSTALLABLE_EXTENSIONS
345
)
346
347
# Check VCS systems
348
if "git" in VCS_LIST:
349
print("Git is supported")
350
351
# Validate URL schemes
352
def is_remote_url(url):
353
return any(url.startswith(scheme) for scheme in SCHEME_LIST)
354
355
# Parse boolean values
356
def parse_bool(value):
357
if str(value).lower() in TRUE_VALUES:
358
return True
359
elif str(value).lower() in FALSE_VALUES:
360
return False
361
else:
362
raise ValueError(f"Invalid boolean value: {value}")
363
364
# Check for project files
365
import os
366
def find_project_files():
367
found_files = []
368
for filename in RELEVANT_PROJECT_FILES:
369
if os.path.exists(filename):
370
found_files.append(filename)
371
return found_files
372
373
# Check installable files
374
def is_installable_file(filename):
375
return any(filename.endswith(ext) for ext in INSTALLABLE_EXTENSIONS)
376
377
print(f"VCS systems: {VCS_LIST}")
378
print(f"URL schemes: {SCHEME_LIST}")
379
print(f"Project files: {find_project_files()}")
380
```
381
382
### Script Parsing
383
384
Parse and execute Pipfile scripts with support for call-based script definitions.
385
386
```python { .api }
387
class Script:
388
"""
389
Parse a script line from Pipfile's [scripts] section.
390
Always works in POSIX mode, even on Windows.
391
"""
392
393
script_types: list # ["call"]
394
395
def __init__(self, command, args=None):
396
"""
397
Initialize Script with command and arguments.
398
399
Parameters:
400
command (str): The command to execute
401
args (list, optional): Additional arguments
402
"""
403
404
@classmethod
405
def parse(cls, value):
406
"""
407
Parse script value from Pipfile.
408
409
Parameters:
410
value: Script definition (str, list, or inline table)
411
412
Returns:
413
Script: Parsed script instance
414
"""
415
416
def cmdify(self):
417
"""
418
Convert script to executable command format.
419
420
Returns:
421
str: Executable command string
422
"""
423
424
class ScriptEmptyError(ValueError):
425
"""Raised when script definition is empty."""
426
427
class ScriptParseError(ValueError):
428
"""Raised when script definition cannot be parsed."""
429
```
430
431
Usage examples:
432
```python
433
from pipenv.cmdparse import Script, ScriptParseError
434
435
# Parse simple string script
436
script = Script.parse("python setup.py test")
437
command = script.cmdify()
438
print(f"Command: {command}")
439
440
# Parse call-based script
441
call_script = Script.parse({"call": "mypackage.module:function('arg')"})
442
call_command = call_script.cmdify()
443
print(f"Call command: {call_command}")
444
445
# Handle script parsing errors
446
try:
447
invalid_script = Script.parse({"invalid": "not supported"})
448
except ScriptParseError as e:
449
print(f"Script parse error: {e}")
450
451
# Create script programmatically
452
custom_script = Script("pytest", ["-v", "--cov=mypackage"])
453
test_command = custom_script.cmdify()
454
print(f"Test command: {test_command}")
455
```
456
457
### File and Path Utilities
458
459
Additional file and path manipulation utilities.
460
461
```python { .api }
462
# File operations
463
def normalize_path(path):
464
"""Normalize file system path."""
465
466
def is_file_url(url):
467
"""Check if URL is a file:// URL."""
468
469
def is_vcs_url(url):
470
"""Check if URL is a VCS URL."""
471
472
def temp_environ():
473
"""Context manager for temporary environment variables."""
474
475
def safe_expandvars(value):
476
"""Safely expand environment variables in string."""
477
```
478
479
Usage examples:
480
```python
481
from pipenv.utils import normalize_path, is_file_url, is_vcs_url, temp_environ
482
import os
483
484
# Normalize paths
485
normalized = normalize_path("./relative/../path/to/file")
486
print(f"Normalized: {normalized}")
487
488
# Check URL types
489
file_url = is_file_url("file:///path/to/file") # True
490
vcs_url = is_vcs_url("git+https://github.com/user/repo") # True
491
492
# Temporary environment
493
with temp_environ():
494
os.environ["TEMP_VAR"] = "temporary_value"
495
print(f"Temp var: {os.environ.get('TEMP_VAR')}")
496
# Variable is removed after context
497
```
498
499
## Integration Examples
500
501
Complete examples showing utility integration.
502
503
### Dependency Analysis
504
505
```python
506
from pipenv.utils.dependencies import (
507
get_version, determine_package_name, get_canonical_names
508
)
509
from pipenv.utils.pipfile import Pipfile
510
511
def analyze_dependencies():
512
"""Analyze project dependencies."""
513
pipfile = Pipfile()
514
pipfile_data = pipfile.load()
515
516
packages = pipfile_data.get("packages", {})
517
dev_packages = pipfile_data.get("dev-packages", {})
518
519
print("Production Dependencies:")
520
for pkg, spec in packages.items():
521
version = get_version(spec)
522
canonical = determine_package_name(pkg)
523
print(f" {canonical}: {version}")
524
525
print("\nDevelopment Dependencies:")
526
for pkg, spec in dev_packages.items():
527
version = get_version(spec)
528
canonical = determine_package_name(pkg)
529
print(f" {canonical}: {version}")
530
531
# analyze_dependencies()
532
```
533
534
### Environment Setup
535
536
```python
537
from pipenv.utils.shell import chdir, load_path
538
from pipenv.utils.pipfile import find_pipfile, ensure_pipfile
539
import os
540
541
def setup_project_environment(project_path):
542
"""Set up pipenv project environment."""
543
with chdir(project_path):
544
# Find or create Pipfile
545
pipfile_path = find_pipfile()
546
if not pipfile_path:
547
pipfile_path = ensure_pipfile()
548
print(f"Created new Pipfile at: {pipfile_path}")
549
else:
550
print(f"Using existing Pipfile: {pipfile_path}")
551
552
# Load Python path information
553
python_exe = "/usr/bin/python3"
554
if os.path.exists(python_exe):
555
sys_path = load_path(python_exe)
556
print(f"Python sys.path has {len(sys_path)} entries")
557
558
# setup_project_environment("/path/to/project")
559
```
560
561
### Lock File Management
562
563
```python
564
from pipenv.utils.locking import prepare_lockfile, atomic_open_for_write, Lockfile
565
import json
566
import hashlib
567
568
def update_lockfile(packages_data):
569
"""Update lock file with new package data."""
570
# Prepare lockfile data
571
lockfile_data = {
572
"_meta": {
573
"hash": {"sha256": hashlib.sha256(json.dumps(packages_data, sort_keys=True).encode()).hexdigest()},
574
"pipfile-spec": 6,
575
"requires": {"python_version": "3.9"},
576
"sources": [{"name": "pypi", "url": "https://pypi.org/simple", "verify_ssl": True}]
577
},
578
"default": packages_data.get("packages", {}),
579
"develop": packages_data.get("dev-packages", {})
580
}
581
582
prepared = prepare_lockfile(lockfile_data)
583
584
# Write atomically
585
with atomic_open_for_write("Pipfile.lock") as f:
586
json.dump(prepared, f, indent=4, sort_keys=True)
587
588
print("Lockfile updated successfully")
589
590
# Example usage
591
# packages = {
592
# "packages": {"requests": {"version": "==2.25.1"}},
593
# "dev-packages": {"pytest": {"version": "==6.2.2"}}
594
# }
595
# update_lockfile(packages)
596
```