-
-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathdeps.py
135 lines (113 loc) · 2.68 KB
/
deps.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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
from __future__ import print_function
import os
import sys
import contextlib
import subprocess
import tempfile
import shutil
import itertools
import functools
try:
from pip._vendor import pkg_resources
except ImportError:
import pkg_resources
filterfalse = getattr(itertools, 'filterfalse', None) or itertools.ifilterfalse
@contextlib.contextmanager
def _update_working_set():
"""
Update the master working_set to include these new packages.
TODO: would be better to use an officially-supported API,
but no suitable API is apparent.
"""
try:
pkg_resources = sys.modules['pkg_resources']
pkg_resources._initialize_master_working_set()
except KeyError:
# it's unnecessary to re-initialize when it hasn't
# yet been initialized.
pass
yield
def _installable(args):
"""
Return True only if the args to pip install
indicate something to install.
>>> _installable(['inflect'])
True
>>> _installable(['-q'])
False
>>> _installable(['-q', 'inflect'])
True
>>> _installable(['-rfoo.txt'])
True
>>> _installable(['projects/inflect'])
True
>>> _installable(['~/projects/inflect'])
True
"""
return any(
not arg.startswith('-')
or arg.startswith('-r')
or arg.startswith('--requirement')
for arg in args
)
@contextlib.contextmanager
def load(*args):
target = tempfile.mkdtemp(prefix='pip-run-')
cmd = (
sys.executable,
'-m', 'pip',
'install',
'-t', target,
) + args
with _patch_prefix():
_installable(args) and subprocess.check_call(cmd)
try:
yield target
finally:
shutil.rmtree(target)
@contextlib.contextmanager
def _patch_prefix():
"""
To workaround pypa/pip#4106, override the system prefix with
a user prefix, restoring the original file after.
"""
cfg_fn = os.path.expanduser('~/.pydistutils.cfg')
with _save_file(cfg_fn):
with open(cfg_fn, 'w') as cfg:
cfg.write('[install]\nprefix=\n')
yield
@contextlib.contextmanager
def _save_file(filename):
"""
Capture the state of filename and restore it after the context
exits.
"""
# For now, only supports a missing filename.
if os.path.exists(filename):
tmpl = "Unsupported with extant {filename}"
raise NotImplementedError(tmpl.format(**locals()))
try:
yield
finally:
if os.path.exists(filename):
os.remove(filename)
@contextlib.contextmanager
def on_sys_path(*args):
"""
Install dependencies via args to pip and ensure they have precedence
on sys.path.
"""
with load(*args) as target:
sys.path.insert(0, target)
try:
with _update_working_set():
yield target
finally:
sys.path.remove(target)
def pkg_installed(spec):
try:
pkg_resources.require(spec)
except Exception:
return False
return True
not_installed = functools.partial(filterfalse, pkg_installed)