forked from conda/conda
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpip_util.py
113 lines (100 loc) · 3.63 KB
/
pip_util.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
"""
Functions related to core conda functionality that relates to pip
NOTE: This modules used to in conda, as conda/pip.py
"""
from __future__ import absolute_import, print_function
from os.path import isfile, join
import re
import subprocess
import sys
def pip_args(prefix):
"""
return the arguments required to invoke pip (in prefix), or None if pip
is not installed
"""
if sys.platform == 'win32':
pip_path = join(prefix, 'Scripts', 'pip-script.py')
py_path = join(prefix, 'python.exe')
else:
pip_path = join(prefix, 'bin', 'pip')
py_path = join(prefix, 'bin', 'python')
if isfile(pip_path) and isfile(py_path):
ret = [py_path, pip_path]
# Check the version of pip
# --disable-pip-version-check was introduced in pip 6.0
# If older than that, they should probably get the warning anyway.
pip_version = subprocess.check_output(ret + ['-V']).decode('utf-8').split()[1]
major_ver = pip_version.split('.')[0]
if int(major_ver) >= 6:
ret.append('--disable-pip-version-check')
return ret
else:
return None
class PipPackage(dict):
def __str__(self):
if 'path' in self:
return '%s (%s)-%s-<pip>' % (
self['name'],
self['path'],
self['version']
)
return '%s-%s-<pip>' % (self['name'], self['version'])
def installed(prefix, output=True):
args = pip_args(prefix)
if args is None:
return
args.append('list')
try:
pipinst = subprocess.check_output(
args, universal_newlines=True
).splitlines()
except Exception:
# Any error should just be ignored
if output:
print("# Warning: subprocess call to pip failed")
return
# For every package in pipinst that is not already represented
# in installed append a fake name to installed with 'pip'
# as the build string
pat = re.compile('([\w.-]+)\s+\((.+)\)')
for line in pipinst:
line = line.strip()
if not line:
continue
m = pat.match(line)
if m is None:
if output:
print('Could not extract name and version from: %r' % line)
continue
name, version = m.groups()
name = name.lower()
kwargs = {
'name': name,
'version': version,
}
if ', ' in version:
# Packages installed with setup.py develop will include a path in
# the version. They should be included here, even if they are
# installed with conda, as they are preferred over the conda
# version. We still include the conda version, though, because it
# is still installed.
version, path = version.split(', ')
# We do this because the code below uses rsplit('-', 2)
version = version.replace('-', ' ')
kwargs.update({
'path': path,
'version': version,
})
yield PipPackage(**kwargs)
def add_pip_installed(prefix, installed_pkgs, json=None, output=True):
# Defer to json for backwards compatibility
if isinstance(json, bool):
output = not json
# TODO Refactor so installed is a real list of objects/dicts
# instead of strings allowing for direct comparison
# split :: to get rid of channel info
conda_names = {d.quad[0] for d in installed_pkgs}
for pip_pkg in installed(prefix, output=output):
if pip_pkg['name'] in conda_names and 'path' not in pip_pkg:
continue
installed_pkgs.add(str(pip_pkg))