summaryrefslogtreecommitdiff
path: root/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req
diff options
context:
space:
mode:
authorShubham Saini <shubham6405@gmail.com>2019-08-05 08:32:33 +0000
committerShubham Saini <shubham6405@gmail.com>2019-08-05 08:32:33 +0000
commit227b2d30a8675b44918f9d9ca89b24144a938215 (patch)
tree9f8e6a28724514b6fdf463a9ab2067a7ef309b72 /venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req
parent842a8cfbbbdb1f92889d892e4859dbd5d40c5be8 (diff)
removing venv files
Diffstat (limited to 'venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req')
-rw-r--r--venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/__init__.py69
-rw-r--r--venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/req_file.py338
-rw-r--r--venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/req_install.py1115
-rw-r--r--venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/req_set.py164
-rw-r--r--venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/req_uninstall.py455
5 files changed, 0 insertions, 2141 deletions
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/__init__.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/__init__.py
deleted file mode 100644
index 07ae607..0000000
--- a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/__init__.py
+++ /dev/null
@@ -1,69 +0,0 @@
1from __future__ import absolute_import
2
3import logging
4
5from .req_install import InstallRequirement
6from .req_set import RequirementSet
7from .req_file import parse_requirements
8from pip._internal.utils.logging import indent_log
9
10
11__all__ = [
12 "RequirementSet", "InstallRequirement",
13 "parse_requirements", "install_given_reqs",
14]
15
16logger = logging.getLogger(__name__)
17
18
19def install_given_reqs(to_install, install_options, global_options=(),
20 *args, **kwargs):
21 """
22 Install everything in the given list.
23
24 (to be called after having downloaded and unpacked the packages)
25 """
26
27 if to_install:
28 logger.info(
29 'Installing collected packages: %s',
30 ', '.join([req.name for req in to_install]),
31 )
32
33 with indent_log():
34 for requirement in to_install:
35 if requirement.conflicts_with:
36 logger.info(
37 'Found existing installation: %s',
38 requirement.conflicts_with,
39 )
40 with indent_log():
41 uninstalled_pathset = requirement.uninstall(
42 auto_confirm=True
43 )
44 try:
45 requirement.install(
46 install_options,
47 global_options,
48 *args,
49 **kwargs
50 )
51 except:
52 should_rollback = (
53 requirement.conflicts_with and
54 not requirement.install_succeeded
55 )
56 # if install did not succeed, rollback previous uninstall
57 if should_rollback:
58 uninstalled_pathset.rollback()
59 raise
60 else:
61 should_commit = (
62 requirement.conflicts_with and
63 requirement.install_succeeded
64 )
65 if should_commit:
66 uninstalled_pathset.commit()
67 requirement.remove_temporary_source()
68
69 return to_install
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/req_file.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/req_file.py
deleted file mode 100644
index 9e6ef41..0000000
--- a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/req_file.py
+++ /dev/null
@@ -1,338 +0,0 @@
1"""
2Requirements file parsing
3"""
4
5from __future__ import absolute_import
6
7import optparse
8import os
9import re
10import shlex
11import sys
12
13from pip._vendor.six.moves import filterfalse
14from pip._vendor.six.moves.urllib import parse as urllib_parse
15
16from pip._internal import cmdoptions
17from pip._internal.download import get_file_content
18from pip._internal.exceptions import RequirementsFileParseError
19from pip._internal.req.req_install import InstallRequirement
20
21__all__ = ['parse_requirements']
22
23SCHEME_RE = re.compile(r'^(http|https|file):', re.I)
24COMMENT_RE = re.compile(r'(^|\s)+#.*$')
25
26# Matches environment variable-style values in '${MY_VARIABLE_1}' with the
27# variable name consisting of only uppercase letters, digits or the '_'
28# (underscore). This follows the POSIX standard defined in IEEE Std 1003.1,
29# 2013 Edition.
30ENV_VAR_RE = re.compile(r'(?P<var>\$\{(?P<name>[A-Z0-9_]+)\})')
31
32SUPPORTED_OPTIONS = [
33 cmdoptions.constraints,
34 cmdoptions.editable,
35 cmdoptions.requirements,
36 cmdoptions.no_index,
37 cmdoptions.index_url,
38 cmdoptions.find_links,
39 cmdoptions.extra_index_url,
40 cmdoptions.always_unzip,
41 cmdoptions.no_binary,
42 cmdoptions.only_binary,
43 cmdoptions.pre,
44 cmdoptions.process_dependency_links,
45 cmdoptions.trusted_host,
46 cmdoptions.require_hashes,
47]
48
49# options to be passed to requirements
50SUPPORTED_OPTIONS_REQ = [
51 cmdoptions.install_options,
52 cmdoptions.global_options,
53 cmdoptions.hash,
54]
55
56# the 'dest' string values
57SUPPORTED_OPTIONS_REQ_DEST = [o().dest for o in SUPPORTED_OPTIONS_REQ]
58
59
60def parse_requirements(filename, finder=None, comes_from=None, options=None,
61 session=None, constraint=False, wheel_cache=None):
62 """Parse a requirements file and yield InstallRequirement instances.
63
64 :param filename: Path or url of requirements file.
65 :param finder: Instance of pip.index.PackageFinder.
66 :param comes_from: Origin description of requirements.
67 :param options: cli options.
68 :param session: Instance of pip.download.PipSession.
69 :param constraint: If true, parsing a constraint file rather than
70 requirements file.
71 :param wheel_cache: Instance of pip.wheel.WheelCache
72 """
73 if session is None:
74 raise TypeError(
75 "parse_requirements() missing 1 required keyword argument: "
76 "'session'"
77 )
78
79 _, content = get_file_content(
80 filename, comes_from=comes_from, session=session
81 )
82
83 lines_enum = preprocess(content, options)
84
85 for line_number, line in lines_enum:
86 req_iter = process_line(line, filename, line_number, finder,
87 comes_from, options, session, wheel_cache,
88 constraint=constraint)
89 for req in req_iter:
90 yield req
91
92
93def preprocess(content, options):
94 """Split, filter, and join lines, and return a line iterator
95
96 :param content: the content of the requirements file
97 :param options: cli options
98 """
99 lines_enum = enumerate(content.splitlines(), start=1)
100 lines_enum = join_lines(lines_enum)
101 lines_enum = ignore_comments(lines_enum)
102 lines_enum = skip_regex(lines_enum, options)
103 lines_enum = expand_env_variables(lines_enum)
104 return lines_enum
105
106
107def process_line(line, filename, line_number, finder=None, comes_from=None,
108 options=None, session=None, wheel_cache=None,
109 constraint=False):
110 """Process a single requirements line; This can result in creating/yielding
111 requirements, or updating the finder.
112
113 For lines that contain requirements, the only options that have an effect
114 are from SUPPORTED_OPTIONS_REQ, and they are scoped to the
115 requirement. Other options from SUPPORTED_OPTIONS may be present, but are
116 ignored.
117
118 For lines that do not contain requirements, the only options that have an
119 effect are from SUPPORTED_OPTIONS. Options from SUPPORTED_OPTIONS_REQ may
120 be present, but are ignored. These lines may contain multiple options
121 (although our docs imply only one is supported), and all our parsed and
122 affect the finder.
123
124 :param constraint: If True, parsing a constraints file.
125 :param options: OptionParser options that we may update
126 """
127 parser = build_parser(line)
128 defaults = parser.get_default_values()
129 defaults.index_url = None
130 if finder:
131 # `finder.format_control` will be updated during parsing
132 defaults.format_control = finder.format_control
133 args_str, options_str = break_args_options(line)
134 if sys.version_info < (2, 7, 3):
135 # Prior to 2.7.3, shlex cannot deal with unicode entries
136 options_str = options_str.encode('utf8')
137 opts, _ = parser.parse_args(shlex.split(options_str), defaults)
138
139 # preserve for the nested code path
140 line_comes_from = '%s %s (line %s)' % (
141 '-c' if constraint else '-r', filename, line_number,
142 )
143
144 # yield a line requirement
145 if args_str:
146 isolated = options.isolated_mode if options else False
147 if options:
148 cmdoptions.check_install_build_global(options, opts)
149 # get the options that apply to requirements
150 req_options = {}
151 for dest in SUPPORTED_OPTIONS_REQ_DEST:
152 if dest in opts.__dict__ and opts.__dict__[dest]:
153 req_options[dest] = opts.__dict__[dest]
154 yield InstallRequirement.from_line(
155 args_str, line_comes_from, constraint=constraint,
156 isolated=isolated, options=req_options, wheel_cache=wheel_cache
157 )
158
159 # yield an editable requirement
160 elif opts.editables:
161 isolated = options.isolated_mode if options else False
162 yield InstallRequirement.from_editable(
163 opts.editables[0], comes_from=line_comes_from,
164 constraint=constraint, isolated=isolated, wheel_cache=wheel_cache
165 )
166
167 # parse a nested requirements file
168 elif opts.requirements or opts.constraints:
169 if opts.requirements:
170 req_path = opts.requirements[0]
171 nested_constraint = False
172 else:
173 req_path = opts.constraints[0]
174 nested_constraint = True
175 # original file is over http
176 if SCHEME_RE.search(filename):
177 # do a url join so relative paths work
178 req_path = urllib_parse.urljoin(filename, req_path)
179 # original file and nested file are paths
180 elif not SCHEME_RE.search(req_path):
181 # do a join so relative paths work
182 req_path = os.path.join(os.path.dirname(filename), req_path)
183 # TODO: Why not use `comes_from='-r {} (line {})'` here as well?
184 parser = parse_requirements(
185 req_path, finder, comes_from, options, session,
186 constraint=nested_constraint, wheel_cache=wheel_cache
187 )
188 for req in parser:
189 yield req
190
191 # percolate hash-checking option upward
192 elif opts.require_hashes:
193 options.require_hashes = opts.require_hashes
194
195 # set finder options
196 elif finder:
197 if opts.index_url:
198 finder.index_urls = [opts.index_url]
199 if opts.no_index is True:
200 finder.index_urls = []
201 if opts.extra_index_urls:
202 finder.index_urls.extend(opts.extra_index_urls)
203 if opts.find_links:
204 # FIXME: it would be nice to keep track of the source
205 # of the find_links: support a find-links local path
206 # relative to a requirements file.
207 value = opts.find_links[0]
208 req_dir = os.path.dirname(os.path.abspath(filename))
209 relative_to_reqs_file = os.path.join(req_dir, value)
210 if os.path.exists(relative_to_reqs_file):
211 value = relative_to_reqs_file
212 finder.find_links.append(value)
213 if opts.pre:
214 finder.allow_all_prereleases = True
215 if opts.process_dependency_links:
216 finder.process_dependency_links = True
217 if opts.trusted_hosts:
218 finder.secure_origins.extend(
219 ("*", host, "*") for host in opts.trusted_hosts)
220
221
222def break_args_options(line):
223 """Break up the line into an args and options string. We only want to shlex
224 (and then optparse) the options, not the args. args can contain markers
225 which are corrupted by shlex.
226 """
227 tokens = line.split(' ')
228 args = []
229 options = tokens[:]
230 for token in tokens:
231 if token.startswith('-') or token.startswith('--'):
232 break
233 else:
234 args.append(token)
235 options.pop(0)
236 return ' '.join(args), ' '.join(options)
237
238
239def build_parser(line):
240 """
241 Return a parser for parsing requirement lines
242 """
243 parser = optparse.OptionParser(add_help_option=False)
244
245 option_factories = SUPPORTED_OPTIONS + SUPPORTED_OPTIONS_REQ
246 for option_factory in option_factories:
247 option = option_factory()
248 parser.add_option(option)
249
250 # By default optparse sys.exits on parsing errors. We want to wrap
251 # that in our own exception.
252 def parser_exit(self, msg):
253 # add offending line
254 msg = 'Invalid requirement: %s\n%s' % (line, msg)
255 raise RequirementsFileParseError(msg)
256 parser.exit = parser_exit
257
258 return parser
259
260
261def join_lines(lines_enum):
262 """Joins a line ending in '\' with the previous line (except when following
263 comments). The joined line takes on the index of the first line.
264 """
265 primary_line_number = None
266 new_line = []
267 for line_number, line in lines_enum:
268 if not line.endswith('\\') or COMMENT_RE.match(line):
269 if COMMENT_RE.match(line):
270 # this ensures comments are always matched later
271 line = ' ' + line
272 if new_line:
273 new_line.append(line)
274 yield primary_line_number, ''.join(new_line)
275 new_line = []
276 else:
277 yield line_number, line
278 else:
279 if not new_line:
280 primary_line_number = line_number
281 new_line.append(line.strip('\\'))
282
283 # last line contains \
284 if new_line:
285 yield primary_line_number, ''.join(new_line)
286
287 # TODO: handle space after '\'.
288
289
290def ignore_comments(lines_enum):
291 """
292 Strips comments and filter empty lines.
293 """
294 for line_number, line in lines_enum:
295 line = COMMENT_RE.sub('', line)
296 line = line.strip()
297 if line:
298 yield line_number, line
299
300
301def skip_regex(lines_enum, options):
302 """
303 Skip lines that match '--skip-requirements-regex' pattern
304
305 Note: the regex pattern is only built once
306 """
307 skip_regex = options.skip_requirements_regex if options else None
308 if skip_regex:
309 pattern = re.compile(skip_regex)
310 lines_enum = filterfalse(lambda e: pattern.search(e[1]), lines_enum)
311 return lines_enum
312
313
314def expand_env_variables(lines_enum):
315 """Replace all environment variables that can be retrieved via `os.getenv`.
316
317 The only allowed format for environment variables defined in the
318 requirement file is `${MY_VARIABLE_1}` to ensure two things:
319
320 1. Strings that contain a `$` aren't accidentally (partially) expanded.
321 2. Ensure consistency across platforms for requirement files.
322
323 These points are the result of a discusssion on the `github pull
324 request #3514 <https://github.com/pypa/pip/pull/3514>`_.
325
326 Valid characters in variable names follow the `POSIX standard
327 <http://pubs.opengroup.org/onlinepubs/9699919799/>`_ and are limited
328 to uppercase letter, digits and the `_` (underscore).
329 """
330 for line_number, line in lines_enum:
331 for env_var, var_name in ENV_VAR_RE.findall(line):
332 value = os.getenv(var_name)
333 if not value:
334 continue
335
336 line = line.replace(env_var, value)
337
338 yield line_number, line
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/req_install.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/req_install.py
deleted file mode 100644
index 9dd1523..0000000
--- a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/req_install.py
+++ /dev/null
@@ -1,1115 +0,0 @@
1from __future__ import absolute_import
2
3import logging
4import os
5import re
6import shutil
7import sys
8import sysconfig
9import traceback
10import warnings
11import zipfile
12from distutils.util import change_root
13from email.parser import FeedParser # type: ignore
14
15from pip._vendor import pkg_resources, pytoml, six
16from pip._vendor.packaging import specifiers
17from pip._vendor.packaging.markers import Marker
18from pip._vendor.packaging.requirements import InvalidRequirement, Requirement
19from pip._vendor.packaging.utils import canonicalize_name
20from pip._vendor.packaging.version import parse as parse_version
21from pip._vendor.packaging.version import Version
22from pip._vendor.pkg_resources import RequirementParseError, parse_requirements
23
24from pip._internal import wheel
25from pip._internal.build_env import BuildEnvironment
26from pip._internal.compat import native_str
27from pip._internal.download import (
28 is_archive_file, is_url, path_to_url, url_to_path,
29)
30from pip._internal.exceptions import InstallationError, UninstallationError
31from pip._internal.locations import (
32 PIP_DELETE_MARKER_FILENAME, running_under_virtualenv,
33)
34from pip._internal.req.req_uninstall import UninstallPathSet
35from pip._internal.utils.deprecation import RemovedInPip11Warning
36from pip._internal.utils.hashes import Hashes
37from pip._internal.utils.logging import indent_log
38from pip._internal.utils.misc import (
39 _make_build_dir, ask_path_exists, backup_dir, call_subprocess,
40 display_path, dist_in_site_packages, dist_in_usersite, ensure_dir,
41 get_installed_version, is_installable_dir, read_text_file, rmtree,
42)
43from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM
44from pip._internal.utils.temp_dir import TempDirectory
45from pip._internal.utils.ui import open_spinner
46from pip._internal.vcs import vcs
47from pip._internal.wheel import Wheel, move_wheel_files
48
49logger = logging.getLogger(__name__)
50
51operators = specifiers.Specifier._operators.keys()
52
53
54def _strip_extras(path):
55 m = re.match(r'^(.+)(\[[^\]]+\])$', path)
56 extras = None
57 if m:
58 path_no_extras = m.group(1)
59 extras = m.group(2)
60 else:
61 path_no_extras = path
62
63 return path_no_extras, extras
64
65
66class InstallRequirement(object):
67 """
68 Represents something that may be installed later on, may have information
69 about where to fetch the relavant requirement and also contains logic for
70 installing the said requirement.
71 """
72
73 def __init__(self, req, comes_from, source_dir=None, editable=False,
74 link=None, update=True, markers=None,
75 isolated=False, options=None, wheel_cache=None,
76 constraint=False, extras=()):
77 assert req is None or isinstance(req, Requirement), req
78 self.req = req
79 self.comes_from = comes_from
80 self.constraint = constraint
81 if source_dir is not None:
82 self.source_dir = os.path.normpath(os.path.abspath(source_dir))
83 else:
84 self.source_dir = None
85 self.editable = editable
86
87 self._wheel_cache = wheel_cache
88 if link is not None:
89 self.link = self.original_link = link
90 else:
91 from pip._internal.index import Link
92 self.link = self.original_link = req and req.url and Link(req.url)
93
94 if extras:
95 self.extras = extras
96 elif req:
97 self.extras = {
98 pkg_resources.safe_extra(extra) for extra in req.extras
99 }
100 else:
101 self.extras = set()
102 if markers is not None:
103 self.markers = markers
104 else:
105 self.markers = req and req.marker
106 self._egg_info_path = None
107 # This holds the pkg_resources.Distribution object if this requirement
108 # is already available:
109 self.satisfied_by = None
110 # This hold the pkg_resources.Distribution object if this requirement
111 # conflicts with another installed distribution:
112 self.conflicts_with = None
113 # Temporary build location
114 self._temp_build_dir = TempDirectory(kind="req-build")
115 # Used to store the global directory where the _temp_build_dir should
116 # have been created. Cf _correct_build_location method.
117 self._ideal_build_dir = None
118 # True if the editable should be updated:
119 self.update = update
120 # Set to True after successful installation
121 self.install_succeeded = None
122 # UninstallPathSet of uninstalled distribution (for possible rollback)
123 self.uninstalled_pathset = None
124 self.options = options if options else {}
125 # Set to True after successful preparation of this requirement
126 self.prepared = False
127 self.is_direct = False
128
129 self.isolated = isolated
130 self.build_env = BuildEnvironment(no_clean=True)
131
132 @classmethod
133 def from_editable(cls, editable_req, comes_from=None, isolated=False,
134 options=None, wheel_cache=None, constraint=False):
135 from pip._internal.index import Link
136
137 name, url, extras_override = parse_editable(editable_req)
138 if url.startswith('file:'):
139 source_dir = url_to_path(url)
140 else:
141 source_dir = None
142
143 if name is not None:
144 try:
145 req = Requirement(name)
146 except InvalidRequirement:
147 raise InstallationError("Invalid requirement: '%s'" % name)
148 else:
149 req = None
150 return cls(
151 req, comes_from, source_dir=source_dir,
152 editable=True,
153 link=Link(url),
154 constraint=constraint,
155 isolated=isolated,
156 options=options if options else {},
157 wheel_cache=wheel_cache,
158 extras=extras_override or (),
159 )
160
161 @classmethod
162 def from_req(cls, req, comes_from=None, isolated=False, wheel_cache=None):
163 try:
164 req = Requirement(req)
165 except InvalidRequirement:
166 raise InstallationError("Invalid requirement: '%s'" % req)
167 if req.url:
168 raise InstallationError(
169 "Direct url requirement (like %s) are not allowed for "
170 "dependencies" % req
171 )
172 return cls(req, comes_from, isolated=isolated, wheel_cache=wheel_cache)
173
174 @classmethod
175 def from_line(
176 cls, name, comes_from=None, isolated=False, options=None,
177 wheel_cache=None, constraint=False):
178 """Creates an InstallRequirement from a name, which might be a
179 requirement, directory containing 'setup.py', filename, or URL.
180 """
181 from pip._internal.index import Link
182
183 if is_url(name):
184 marker_sep = '; '
185 else:
186 marker_sep = ';'
187 if marker_sep in name:
188 name, markers = name.split(marker_sep, 1)
189 markers = markers.strip()
190 if not markers:
191 markers = None
192 else:
193 markers = Marker(markers)
194 else:
195 markers = None
196 name = name.strip()
197 req = None
198 path = os.path.normpath(os.path.abspath(name))
199 link = None
200 extras = None
201
202 if is_url(name):
203 link = Link(name)
204 else:
205 p, extras = _strip_extras(path)
206 looks_like_dir = os.path.isdir(p) and (
207 os.path.sep in name or
208 (os.path.altsep is not None and os.path.altsep in name) or
209 name.startswith('.')
210 )
211 if looks_like_dir:
212 if not is_installable_dir(p):
213 raise InstallationError(
214 "Directory %r is not installable. File 'setup.py' "
215 "not found." % name
216 )
217 link = Link(path_to_url(p))
218 elif is_archive_file(p):
219 if not os.path.isfile(p):
220 logger.warning(
221 'Requirement %r looks like a filename, but the '
222 'file does not exist',
223 name
224 )
225 link = Link(path_to_url(p))
226
227 # it's a local file, dir, or url
228 if link:
229 # Handle relative file URLs
230 if link.scheme == 'file' and re.search(r'\.\./', link.url):
231 link = Link(
232 path_to_url(os.path.normpath(os.path.abspath(link.path))))
233 # wheel file
234 if link.is_wheel:
235 wheel = Wheel(link.filename) # can raise InvalidWheelFilename
236 req = "%s==%s" % (wheel.name, wheel.version)
237 else:
238 # set the req to the egg fragment. when it's not there, this
239 # will become an 'unnamed' requirement
240 req = link.egg_fragment
241
242 # a requirement specifier
243 else:
244 req = name
245
246 if extras:
247 extras = Requirement("placeholder" + extras.lower()).extras
248 else:
249 extras = ()
250 if req is not None:
251 try:
252 req = Requirement(req)
253 except InvalidRequirement:
254 if os.path.sep in req:
255 add_msg = "It looks like a path."
256 add_msg += deduce_helpful_msg(req)
257 elif '=' in req and not any(op in req for op in operators):
258 add_msg = "= is not a valid operator. Did you mean == ?"
259 else:
260 add_msg = traceback.format_exc()
261 raise InstallationError(
262 "Invalid requirement: '%s'\n%s" % (req, add_msg))
263 return cls(
264 req, comes_from, link=link, markers=markers,
265 isolated=isolated,
266 options=options if options else {},
267 wheel_cache=wheel_cache,
268 constraint=constraint,
269 extras=extras,
270 )
271
272 def __str__(self):
273 if self.req:
274 s = str(self.req)
275 if self.link:
276 s += ' from %s' % self.link.url
277 else:
278 s = self.link.url if self.link else None
279 if self.satisfied_by is not None:
280 s += ' in %s' % display_path(self.satisfied_by.location)
281 if self.comes_from:
282 if isinstance(self.comes_from, six.string_types):
283 comes_from = self.comes_from
284 else:
285 comes_from = self.comes_from.from_path()
286 if comes_from:
287 s += ' (from %s)' % comes_from
288 return s
289
290 def __repr__(self):
291 return '<%s object: %s editable=%r>' % (
292 self.__class__.__name__, str(self), self.editable)
293
294 def populate_link(self, finder, upgrade, require_hashes):
295 """Ensure that if a link can be found for this, that it is found.
296
297 Note that self.link may still be None - if Upgrade is False and the
298 requirement is already installed.
299
300 If require_hashes is True, don't use the wheel cache, because cached
301 wheels, always built locally, have different hashes than the files
302 downloaded from the index server and thus throw false hash mismatches.
303 Furthermore, cached wheels at present have undeterministic contents due
304 to file modification times.
305 """
306 if self.link is None:
307 self.link = finder.find_requirement(self, upgrade)
308 if self._wheel_cache is not None and not require_hashes:
309 old_link = self.link
310 self.link = self._wheel_cache.get(self.link, self.name)
311 if old_link != self.link:
312 logger.debug('Using cached wheel link: %s', self.link)
313
314 @property
315 def specifier(self):
316 return self.req.specifier
317
318 @property
319 def is_pinned(self):
320 """Return whether I am pinned to an exact version.
321
322 For example, some-package==1.2 is pinned; some-package>1.2 is not.
323 """
324 specifiers = self.specifier
325 return (len(specifiers) == 1 and
326 next(iter(specifiers)).operator in {'==', '==='})
327
328 def from_path(self):
329 if self.req is None:
330 return None
331 s = str(self.req)
332 if self.comes_from:
333 if isinstance(self.comes_from, six.string_types):
334 comes_from = self.comes_from
335 else:
336 comes_from = self.comes_from.from_path()
337 if comes_from:
338 s += '->' + comes_from
339 return s
340
341 def build_location(self, build_dir):
342 assert build_dir is not None
343 if self._temp_build_dir.path is not None:
344 return self._temp_build_dir.path
345 if self.req is None:
346 # for requirement via a path to a directory: the name of the
347 # package is not available yet so we create a temp directory
348 # Once run_egg_info will have run, we'll be able
349 # to fix it via _correct_build_location
350 # Some systems have /tmp as a symlink which confuses custom
351 # builds (such as numpy). Thus, we ensure that the real path
352 # is returned.
353 self._temp_build_dir.create()
354 self._ideal_build_dir = build_dir
355
356 return self._temp_build_dir.path
357 if self.editable:
358 name = self.name.lower()
359 else:
360 name = self.name
361 # FIXME: Is there a better place to create the build_dir? (hg and bzr
362 # need this)
363 if not os.path.exists(build_dir):
364 logger.debug('Creating directory %s', build_dir)
365 _make_build_dir(build_dir)
366 return os.path.join(build_dir, name)
367
368 def _correct_build_location(self):
369 """Move self._temp_build_dir to self._ideal_build_dir/self.req.name
370
371 For some requirements (e.g. a path to a directory), the name of the
372 package is not available until we run egg_info, so the build_location
373 will return a temporary directory and store the _ideal_build_dir.
374
375 This is only called by self.egg_info_path to fix the temporary build
376 directory.
377 """
378 if self.source_dir is not None:
379 return
380 assert self.req is not None
381 assert self._temp_build_dir.path
382 assert self._ideal_build_dir.path
383 old_location = self._temp_build_dir.path
384 self._temp_build_dir.path = None
385
386 new_location = self.build_location(self._ideal_build_dir)
387 if os.path.exists(new_location):
388 raise InstallationError(
389 'A package already exists in %s; please remove it to continue'
390 % display_path(new_location))
391 logger.debug(
392 'Moving package %s from %s to new location %s',
393 self, display_path(old_location), display_path(new_location),
394 )
395 shutil.move(old_location, new_location)
396 self._temp_build_dir.path = new_location
397 self._ideal_build_dir = None
398 self.source_dir = os.path.normpath(os.path.abspath(new_location))
399 self._egg_info_path = None
400
401 @property
402 def name(self):
403 if self.req is None:
404 return None
405 return native_str(pkg_resources.safe_name(self.req.name))
406
407 @property
408 def setup_py_dir(self):
409 return os.path.join(
410 self.source_dir,
411 self.link and self.link.subdirectory_fragment or '')
412
413 @property
414 def setup_py(self):
415 assert self.source_dir, "No source dir for %s" % self
416
417 setup_py = os.path.join(self.setup_py_dir, 'setup.py')
418
419 # Python2 __file__ should not be unicode
420 if six.PY2 and isinstance(setup_py, six.text_type):
421 setup_py = setup_py.encode(sys.getfilesystemencoding())
422
423 return setup_py
424
425 @property
426 def pyproject_toml(self):
427 assert self.source_dir, "No source dir for %s" % self
428
429 pp_toml = os.path.join(self.setup_py_dir, 'pyproject.toml')
430
431 # Python2 __file__ should not be unicode
432 if six.PY2 and isinstance(pp_toml, six.text_type):
433 pp_toml = pp_toml.encode(sys.getfilesystemencoding())
434
435 return pp_toml
436
437 def get_pep_518_info(self):
438 """Get a list of the packages required to build the project, if any,
439 and a flag indicating whether pyproject.toml is present, indicating
440 that the build should be isolated.
441
442 Build requirements can be specified in a pyproject.toml, as described
443 in PEP 518. If this file exists but doesn't specify build
444 requirements, pip will default to installing setuptools and wheel.
445 """
446 if os.path.isfile(self.pyproject_toml):
447 with open(self.pyproject_toml) as f:
448 pp_toml = pytoml.load(f)
449 build_sys = pp_toml.get('build-system', {})
450 return (build_sys.get('requires', ['setuptools', 'wheel']), True)
451 return (['setuptools', 'wheel'], False)
452
453 def run_egg_info(self):
454 assert self.source_dir
455 if self.name:
456 logger.debug(
457 'Running setup.py (path:%s) egg_info for package %s',
458 self.setup_py, self.name,
459 )
460 else:
461 logger.debug(
462 'Running setup.py (path:%s) egg_info for package from %s',
463 self.setup_py, self.link,
464 )
465
466 with indent_log():
467 script = SETUPTOOLS_SHIM % self.setup_py
468 base_cmd = [sys.executable, '-c', script]
469 if self.isolated:
470 base_cmd += ["--no-user-cfg"]
471 egg_info_cmd = base_cmd + ['egg_info']
472 # We can't put the .egg-info files at the root, because then the
473 # source code will be mistaken for an installed egg, causing
474 # problems
475 if self.editable:
476 egg_base_option = []
477 else:
478 egg_info_dir = os.path.join(self.setup_py_dir, 'pip-egg-info')
479 ensure_dir(egg_info_dir)
480 egg_base_option = ['--egg-base', 'pip-egg-info']
481 with self.build_env:
482 call_subprocess(
483 egg_info_cmd + egg_base_option,
484 cwd=self.setup_py_dir,
485 show_stdout=False,
486 command_desc='python setup.py egg_info')
487
488 if not self.req:
489 if isinstance(parse_version(self.pkg_info()["Version"]), Version):
490 op = "=="
491 else:
492 op = "==="
493 self.req = Requirement(
494 "".join([
495 self.pkg_info()["Name"],
496 op,
497 self.pkg_info()["Version"],
498 ])
499 )
500 self._correct_build_location()
501 else:
502 metadata_name = canonicalize_name(self.pkg_info()["Name"])
503 if canonicalize_name(self.req.name) != metadata_name:
504 logger.warning(
505 'Running setup.py (path:%s) egg_info for package %s '
506 'produced metadata for project name %s. Fix your '
507 '#egg=%s fragments.',
508 self.setup_py, self.name, metadata_name, self.name
509 )
510 self.req = Requirement(metadata_name)
511
512 def egg_info_data(self, filename):
513 if self.satisfied_by is not None:
514 if not self.satisfied_by.has_metadata(filename):
515 return None
516 return self.satisfied_by.get_metadata(filename)
517 assert self.source_dir
518 filename = self.egg_info_path(filename)
519 if not os.path.exists(filename):
520 return None
521 data = read_text_file(filename)
522 return data
523
524 def egg_info_path(self, filename):
525 if self._egg_info_path is None:
526 if self.editable:
527 base = self.source_dir
528 else:
529 base = os.path.join(self.setup_py_dir, 'pip-egg-info')
530 filenames = os.listdir(base)
531 if self.editable:
532 filenames = []
533 for root, dirs, files in os.walk(base):
534 for dir in vcs.dirnames:
535 if dir in dirs:
536 dirs.remove(dir)
537 # Iterate over a copy of ``dirs``, since mutating
538 # a list while iterating over it can cause trouble.
539 # (See https://github.com/pypa/pip/pull/462.)
540 for dir in list(dirs):
541 # Don't search in anything that looks like a virtualenv
542 # environment
543 if (
544 os.path.lexists(
545 os.path.join(root, dir, 'bin', 'python')
546 ) or
547 os.path.exists(
548 os.path.join(
549 root, dir, 'Scripts', 'Python.exe'
550 )
551 )):
552 dirs.remove(dir)
553 # Also don't search through tests
554 elif dir == 'test' or dir == 'tests':
555 dirs.remove(dir)
556 filenames.extend([os.path.join(root, dir)
557 for dir in dirs])
558 filenames = [f for f in filenames if f.endswith('.egg-info')]
559
560 if not filenames:
561 raise InstallationError(
562 'No files/directories in %s (from %s)' % (base, filename)
563 )
564 assert filenames, \
565 "No files/directories in %s (from %s)" % (base, filename)
566
567 # if we have more than one match, we pick the toplevel one. This
568 # can easily be the case if there is a dist folder which contains
569 # an extracted tarball for testing purposes.
570 if len(filenames) > 1:
571 filenames.sort(
572 key=lambda x: x.count(os.path.sep) +
573 (os.path.altsep and x.count(os.path.altsep) or 0)
574 )
575 self._egg_info_path = os.path.join(base, filenames[0])
576 return os.path.join(self._egg_info_path, filename)
577
578 def pkg_info(self):
579 p = FeedParser()
580 data = self.egg_info_data('PKG-INFO')
581 if not data:
582 logger.warning(
583 'No PKG-INFO file found in %s',
584 display_path(self.egg_info_path('PKG-INFO')),
585 )
586 p.feed(data or '')
587 return p.close()
588
589 _requirements_section_re = re.compile(r'\[(.*?)\]')
590
591 @property
592 def installed_version(self):
593 return get_installed_version(self.name)
594
595 def assert_source_matches_version(self):
596 assert self.source_dir
597 version = self.pkg_info()['version']
598 if self.req.specifier and version not in self.req.specifier:
599 logger.warning(
600 'Requested %s, but installing version %s',
601 self,
602 version,
603 )
604 else:
605 logger.debug(
606 'Source in %s has version %s, which satisfies requirement %s',
607 display_path(self.source_dir),
608 version,
609 self,
610 )
611
612 def update_editable(self, obtain=True):
613 if not self.link:
614 logger.debug(
615 "Cannot update repository at %s; repository location is "
616 "unknown",
617 self.source_dir,
618 )
619 return
620 assert self.editable
621 assert self.source_dir
622 if self.link.scheme == 'file':
623 # Static paths don't get updated
624 return
625 assert '+' in self.link.url, "bad url: %r" % self.link.url
626 if not self.update:
627 return
628 vc_type, url = self.link.url.split('+', 1)
629 backend = vcs.get_backend(vc_type)
630 if backend:
631 vcs_backend = backend(self.link.url)
632 if obtain:
633 vcs_backend.obtain(self.source_dir)
634 else:
635 vcs_backend.export(self.source_dir)
636 else:
637 assert 0, (
638 'Unexpected version control type (in %s): %s'
639 % (self.link, vc_type))
640
641 def uninstall(self, auto_confirm=False, verbose=False,
642 use_user_site=False):
643 """
644 Uninstall the distribution currently satisfying this requirement.
645
646 Prompts before removing or modifying files unless
647 ``auto_confirm`` is True.
648
649 Refuses to delete or modify files outside of ``sys.prefix`` -
650 thus uninstallation within a virtual environment can only
651 modify that virtual environment, even if the virtualenv is
652 linked to global site-packages.
653
654 """
655 if not self.check_if_exists(use_user_site):
656 logger.warning("Skipping %s as it is not installed.", self.name)
657 return
658 dist = self.satisfied_by or self.conflicts_with
659
660 uninstalled_pathset = UninstallPathSet.from_dist(dist)
661 uninstalled_pathset.remove(auto_confirm, verbose)
662 return uninstalled_pathset
663
664 def archive(self, build_dir):
665 assert self.source_dir
666 create_archive = True
667 archive_name = '%s-%s.zip' % (self.name, self.pkg_info()["version"])
668 archive_path = os.path.join(build_dir, archive_name)
669 if os.path.exists(archive_path):
670 response = ask_path_exists(
671 'The file %s exists. (i)gnore, (w)ipe, (b)ackup, (a)bort ' %
672 display_path(archive_path), ('i', 'w', 'b', 'a'))
673 if response == 'i':
674 create_archive = False
675 elif response == 'w':
676 logger.warning('Deleting %s', display_path(archive_path))
677 os.remove(archive_path)
678 elif response == 'b':
679 dest_file = backup_dir(archive_path)
680 logger.warning(
681 'Backing up %s to %s',
682 display_path(archive_path),
683 display_path(dest_file),
684 )
685 shutil.move(archive_path, dest_file)
686 elif response == 'a':
687 sys.exit(-1)
688 if create_archive:
689 zip = zipfile.ZipFile(
690 archive_path, 'w', zipfile.ZIP_DEFLATED,
691 allowZip64=True
692 )
693 dir = os.path.normcase(os.path.abspath(self.setup_py_dir))
694 for dirpath, dirnames, filenames in os.walk(dir):
695 if 'pip-egg-info' in dirnames:
696 dirnames.remove('pip-egg-info')
697 for dirname in dirnames:
698 dirname = os.path.join(dirpath, dirname)
699 name = self._clean_zip_name(dirname, dir)
700 zipdir = zipfile.ZipInfo(self.name + '/' + name + '/')
701 zipdir.external_attr = 0x1ED << 16 # 0o755
702 zip.writestr(zipdir, '')
703 for filename in filenames:
704 if filename == PIP_DELETE_MARKER_FILENAME:
705 continue
706 filename = os.path.join(dirpath, filename)
707 name = self._clean_zip_name(filename, dir)
708 zip.write(filename, self.name + '/' + name)
709 zip.close()
710 logger.info('Saved %s', display_path(archive_path))
711
712 def _clean_zip_name(self, name, prefix):
713 assert name.startswith(prefix + os.path.sep), (
714 "name %r doesn't start with prefix %r" % (name, prefix)
715 )
716 name = name[len(prefix) + 1:]
717 name = name.replace(os.path.sep, '/')
718 return name
719
720 def match_markers(self, extras_requested=None):
721 if not extras_requested:
722 # Provide an extra to safely evaluate the markers
723 # without matching any extra
724 extras_requested = ('',)
725 if self.markers is not None:
726 return any(
727 self.markers.evaluate({'extra': extra})
728 for extra in extras_requested)
729 else:
730 return True
731
732 def install(self, install_options, global_options=None, root=None,
733 home=None, prefix=None, warn_script_location=True,
734 use_user_site=False, pycompile=True):
735 global_options = global_options if global_options is not None else []
736 if self.editable:
737 self.install_editable(
738 install_options, global_options, prefix=prefix,
739 )
740 return
741 if self.is_wheel:
742 version = wheel.wheel_version(self.source_dir)
743 wheel.check_compatibility(version, self.name)
744
745 self.move_wheel_files(
746 self.source_dir, root=root, prefix=prefix, home=home,
747 warn_script_location=warn_script_location,
748 use_user_site=use_user_site, pycompile=pycompile,
749 )
750 self.install_succeeded = True
751 return
752
753 # Extend the list of global and install options passed on to
754 # the setup.py call with the ones from the requirements file.
755 # Options specified in requirements file override those
756 # specified on the command line, since the last option given
757 # to setup.py is the one that is used.
758 global_options = list(global_options) + \
759 self.options.get('global_options', [])
760 install_options = list(install_options) + \
761 self.options.get('install_options', [])
762
763 if self.isolated:
764 global_options = global_options + ["--no-user-cfg"]
765
766 with TempDirectory(kind="record") as temp_dir:
767 record_filename = os.path.join(temp_dir.path, 'install-record.txt')
768 install_args = self.get_install_args(
769 global_options, record_filename, root, prefix, pycompile,
770 )
771 msg = 'Running setup.py install for %s' % (self.name,)
772 with open_spinner(msg) as spinner:
773 with indent_log():
774 with self.build_env:
775 call_subprocess(
776 install_args + install_options,
777 cwd=self.setup_py_dir,
778 show_stdout=False,
779 spinner=spinner,
780 )
781
782 if not os.path.exists(record_filename):
783 logger.debug('Record file %s not found', record_filename)
784 return
785 self.install_succeeded = True
786
787 def prepend_root(path):
788 if root is None or not os.path.isabs(path):
789 return path
790 else:
791 return change_root(root, path)
792
793 with open(record_filename) as f:
794 for line in f:
795 directory = os.path.dirname(line)
796 if directory.endswith('.egg-info'):
797 egg_info_dir = prepend_root(directory)
798 break
799 else:
800 logger.warning(
801 'Could not find .egg-info directory in install record'
802 ' for %s',
803 self,
804 )
805 # FIXME: put the record somewhere
806 # FIXME: should this be an error?
807 return
808 new_lines = []
809 with open(record_filename) as f:
810 for line in f:
811 filename = line.strip()
812 if os.path.isdir(filename):
813 filename += os.path.sep
814 new_lines.append(
815 os.path.relpath(prepend_root(filename), egg_info_dir)
816 )
817 new_lines.sort()
818 ensure_dir(egg_info_dir)
819 inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt')
820 with open(inst_files_path, 'w') as f:
821 f.write('\n'.join(new_lines) + '\n')
822
823 def ensure_has_source_dir(self, parent_dir):
824 """Ensure that a source_dir is set.
825
826 This will create a temporary build dir if the name of the requirement
827 isn't known yet.
828
829 :param parent_dir: The ideal pip parent_dir for the source_dir.
830 Generally src_dir for editables and build_dir for sdists.
831 :return: self.source_dir
832 """
833 if self.source_dir is None:
834 self.source_dir = self.build_location(parent_dir)
835 return self.source_dir
836
837 def get_install_args(self, global_options, record_filename, root, prefix,
838 pycompile):
839 install_args = [sys.executable, "-u"]
840 install_args.append('-c')
841 install_args.append(SETUPTOOLS_SHIM % self.setup_py)
842 install_args += list(global_options) + \
843 ['install', '--record', record_filename]
844 install_args += ['--single-version-externally-managed']
845
846 if root is not None:
847 install_args += ['--root', root]
848 if prefix is not None:
849 install_args += ['--prefix', prefix]
850
851 if pycompile:
852 install_args += ["--compile"]
853 else:
854 install_args += ["--no-compile"]
855
856 if running_under_virtualenv():
857 py_ver_str = 'python' + sysconfig.get_python_version()
858 install_args += ['--install-headers',
859 os.path.join(sys.prefix, 'include', 'site',
860 py_ver_str, self.name)]
861
862 return install_args
863
864 def remove_temporary_source(self):
865 """Remove the source files from this requirement, if they are marked
866 for deletion"""
867 if self.source_dir and os.path.exists(
868 os.path.join(self.source_dir, PIP_DELETE_MARKER_FILENAME)):
869 logger.debug('Removing source in %s', self.source_dir)
870 rmtree(self.source_dir)
871 self.source_dir = None
872 self._temp_build_dir.cleanup()
873 self.build_env.cleanup()
874
875 def install_editable(self, install_options,
876 global_options=(), prefix=None):
877 logger.info('Running setup.py develop for %s', self.name)
878
879 if self.isolated:
880 global_options = list(global_options) + ["--no-user-cfg"]
881
882 if prefix:
883 prefix_param = ['--prefix={}'.format(prefix)]
884 install_options = list(install_options) + prefix_param
885
886 with indent_log():
887 # FIXME: should we do --install-headers here too?
888 with self.build_env:
889 call_subprocess(
890 [
891 sys.executable,
892 '-c',
893 SETUPTOOLS_SHIM % self.setup_py
894 ] +
895 list(global_options) +
896 ['develop', '--no-deps'] +
897 list(install_options),
898
899 cwd=self.setup_py_dir,
900 show_stdout=False,
901 )
902
903 self.install_succeeded = True
904
905 def check_if_exists(self, use_user_site):
906 """Find an installed distribution that satisfies or conflicts
907 with this requirement, and set self.satisfied_by or
908 self.conflicts_with appropriately.
909 """
910 if self.req is None:
911 return False
912 try:
913 # get_distribution() will resolve the entire list of requirements
914 # anyway, and we've already determined that we need the requirement
915 # in question, so strip the marker so that we don't try to
916 # evaluate it.
917 no_marker = Requirement(str(self.req))
918 no_marker.marker = None
919 self.satisfied_by = pkg_resources.get_distribution(str(no_marker))
920 if self.editable and self.satisfied_by:
921 self.conflicts_with = self.satisfied_by
922 # when installing editables, nothing pre-existing should ever
923 # satisfy
924 self.satisfied_by = None
925 return True
926 except pkg_resources.DistributionNotFound:
927 return False
928 except pkg_resources.VersionConflict:
929 existing_dist = pkg_resources.get_distribution(
930 self.req.name
931 )
932 if use_user_site:
933 if dist_in_usersite(existing_dist):
934 self.conflicts_with = existing_dist
935 elif (running_under_virtualenv() and
936 dist_in_site_packages(existing_dist)):
937 raise InstallationError(
938 "Will not install to the user site because it will "
939 "lack sys.path precedence to %s in %s" %
940 (existing_dist.project_name, existing_dist.location)
941 )
942 else:
943 self.conflicts_with = existing_dist
944 return True
945
946 @property
947 def is_wheel(self):
948 return self.link and self.link.is_wheel
949
950 def move_wheel_files(self, wheeldir, root=None, home=None, prefix=None,
951 warn_script_location=True, use_user_site=False,
952 pycompile=True):
953 move_wheel_files(
954 self.name, self.req, wheeldir,
955 user=use_user_site,
956 home=home,
957 root=root,
958 prefix=prefix,
959 pycompile=pycompile,
960 isolated=self.isolated,
961 warn_script_location=warn_script_location,
962 )
963
964 def get_dist(self):
965 """Return a pkg_resources.Distribution built from self.egg_info_path"""
966 egg_info = self.egg_info_path('').rstrip(os.path.sep)
967 base_dir = os.path.dirname(egg_info)
968 metadata = pkg_resources.PathMetadata(base_dir, egg_info)
969 dist_name = os.path.splitext(os.path.basename(egg_info))[0]
970 return pkg_resources.Distribution(
971 os.path.dirname(egg_info),
972 project_name=dist_name,
973 metadata=metadata,
974 )
975
976 @property
977 def has_hash_options(self):
978 """Return whether any known-good hashes are specified as options.
979
980 These activate --require-hashes mode; hashes specified as part of a
981 URL do not.
982
983 """
984 return bool(self.options.get('hashes', {}))
985
986 def hashes(self, trust_internet=True):
987 """Return a hash-comparer that considers my option- and URL-based
988 hashes to be known-good.
989
990 Hashes in URLs--ones embedded in the requirements file, not ones
991 downloaded from an index server--are almost peers with ones from
992 flags. They satisfy --require-hashes (whether it was implicitly or
993 explicitly activated) but do not activate it. md5 and sha224 are not
994 allowed in flags, which should nudge people toward good algos. We
995 always OR all hashes together, even ones from URLs.
996
997 :param trust_internet: Whether to trust URL-based (#md5=...) hashes
998 downloaded from the internet, as by populate_link()
999
1000 """
1001 good_hashes = self.options.get('hashes', {}).copy()
1002 link = self.link if trust_internet else self.original_link
1003 if link and link.hash:
1004 good_hashes.setdefault(link.hash_name, []).append(link.hash)
1005 return Hashes(good_hashes)
1006
1007
1008def _strip_postfix(req):
1009 """
1010 Strip req postfix ( -dev, 0.2, etc )
1011 """
1012 # FIXME: use package_to_requirement?
1013 match = re.search(r'^(.*?)(?:-dev|-\d.*)$', req)
1014 if match:
1015 # Strip off -dev, -0.2, etc.
1016 warnings.warn(
1017 "#egg cleanup for editable urls will be dropped in the future",
1018 RemovedInPip11Warning,
1019 )
1020 req = match.group(1)
1021 return req
1022
1023
1024def parse_editable(editable_req):
1025 """Parses an editable requirement into:
1026 - a requirement name
1027 - an URL
1028 - extras
1029 - editable options
1030 Accepted requirements:
1031 svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir
1032 .[some_extra]
1033 """
1034
1035 from pip._internal.index import Link
1036
1037 url = editable_req
1038
1039 # If a file path is specified with extras, strip off the extras.
1040 url_no_extras, extras = _strip_extras(url)
1041
1042 if os.path.isdir(url_no_extras):
1043 if not os.path.exists(os.path.join(url_no_extras, 'setup.py')):
1044 raise InstallationError(
1045 "Directory %r is not installable. File 'setup.py' not found." %
1046 url_no_extras
1047 )
1048 # Treating it as code that has already been checked out
1049 url_no_extras = path_to_url(url_no_extras)
1050
1051 if url_no_extras.lower().startswith('file:'):
1052 package_name = Link(url_no_extras).egg_fragment
1053 if extras:
1054 return (
1055 package_name,
1056 url_no_extras,
1057 Requirement("placeholder" + extras.lower()).extras,
1058 )
1059 else:
1060 return package_name, url_no_extras, None
1061
1062 for version_control in vcs:
1063 if url.lower().startswith('%s:' % version_control):
1064 url = '%s+%s' % (version_control, url)
1065 break
1066
1067 if '+' not in url:
1068 raise InstallationError(
1069 '%s should either be a path to a local project or a VCS url '
1070 'beginning with svn+, git+, hg+, or bzr+' %
1071 editable_req
1072 )
1073
1074 vc_type = url.split('+', 1)[0].lower()
1075
1076 if not vcs.get_backend(vc_type):
1077 error_message = 'For --editable=%s only ' % editable_req + \
1078 ', '.join([backend.name + '+URL' for backend in vcs.backends]) + \
1079 ' is currently supported'
1080 raise InstallationError(error_message)
1081
1082 package_name = Link(url).egg_fragment
1083 if not package_name:
1084 raise InstallationError(
1085 "Could not detect requirement name for '%s', please specify one "
1086 "with #egg=your_package_name" % editable_req
1087 )
1088 return _strip_postfix(package_name), url, None
1089
1090
1091def deduce_helpful_msg(req):
1092 """Returns helpful msg in case requirements file does not exist,
1093 or cannot be parsed.
1094
1095 :params req: Requirements file path
1096 """
1097 msg = ""
1098 if os.path.exists(req):
1099 msg = " It does exist."
1100 # Try to parse and check if it is a requirements file.
1101 try:
1102 with open(req, 'r') as fp:
1103 # parse first line only
1104 next(parse_requirements(fp.read()))
1105 msg += " The argument you provided " + \
1106 "(%s) appears to be a" % (req) + \
1107 " requirements file. If that is the" + \
1108 " case, use the '-r' flag to install" + \
1109 " the packages specified within it."
1110 except RequirementParseError:
1111 logger.debug("Cannot parse '%s' as requirements \
1112 file" % (req), exc_info=1)
1113 else:
1114 msg += " File '%s' does not exist." % (req)
1115 return msg
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/req_set.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/req_set.py
deleted file mode 100644
index 78b7d32..0000000
--- a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/req_set.py
+++ /dev/null
@@ -1,164 +0,0 @@
1from __future__ import absolute_import
2
3import logging
4from collections import OrderedDict
5
6from pip._internal.exceptions import InstallationError
7from pip._internal.utils.logging import indent_log
8from pip._internal.wheel import Wheel
9
10logger = logging.getLogger(__name__)
11
12
13class RequirementSet(object):
14
15 def __init__(self, require_hashes=False):
16 """Create a RequirementSet.
17
18 :param wheel_cache: The pip wheel cache, for passing to
19 InstallRequirement.
20 """
21
22 self.requirements = OrderedDict()
23 self.require_hashes = require_hashes
24
25 # Mapping of alias: real_name
26 self.requirement_aliases = {}
27 self.unnamed_requirements = []
28 self.successfully_downloaded = []
29 self.reqs_to_cleanup = []
30
31 def __str__(self):
32 reqs = [req for req in self.requirements.values()
33 if not req.comes_from]
34 reqs.sort(key=lambda req: req.name.lower())
35 return ' '.join([str(req.req) for req in reqs])
36
37 def __repr__(self):
38 reqs = [req for req in self.requirements.values()]
39 reqs.sort(key=lambda req: req.name.lower())
40 reqs_str = ', '.join([str(req.req) for req in reqs])
41 return ('<%s object; %d requirement(s): %s>'
42 % (self.__class__.__name__, len(reqs), reqs_str))
43
44 def add_requirement(self, install_req, parent_req_name=None,
45 extras_requested=None):
46 """Add install_req as a requirement to install.
47
48 :param parent_req_name: The name of the requirement that needed this
49 added. The name is used because when multiple unnamed requirements
50 resolve to the same name, we could otherwise end up with dependency
51 links that point outside the Requirements set. parent_req must
52 already be added. Note that None implies that this is a user
53 supplied requirement, vs an inferred one.
54 :param extras_requested: an iterable of extras used to evaluate the
55 environment markers.
56 :return: Additional requirements to scan. That is either [] if
57 the requirement is not applicable, or [install_req] if the
58 requirement is applicable and has just been added.
59 """
60 name = install_req.name
61 if not install_req.match_markers(extras_requested):
62 logger.info("Ignoring %s: markers '%s' don't match your "
63 "environment", install_req.name,
64 install_req.markers)
65 return [], None
66
67 # This check has to come after we filter requirements with the
68 # environment markers.
69 if install_req.link and install_req.link.is_wheel:
70 wheel = Wheel(install_req.link.filename)
71 if not wheel.supported():
72 raise InstallationError(
73 "%s is not a supported wheel on this platform." %
74 wheel.filename
75 )
76
77 # This next bit is really a sanity check.
78 assert install_req.is_direct == (parent_req_name is None), (
79 "a direct req shouldn't have a parent and also, "
80 "a non direct req should have a parent"
81 )
82
83 if not name:
84 # url or path requirement w/o an egg fragment
85 self.unnamed_requirements.append(install_req)
86 return [install_req], None
87 else:
88 try:
89 existing_req = self.get_requirement(name)
90 except KeyError:
91 existing_req = None
92 if (parent_req_name is None and existing_req and not
93 existing_req.constraint and
94 existing_req.extras == install_req.extras and not
95 existing_req.req.specifier == install_req.req.specifier):
96 raise InstallationError(
97 'Double requirement given: %s (already in %s, name=%r)'
98 % (install_req, existing_req, name))
99 if not existing_req:
100 # Add requirement
101 self.requirements[name] = install_req
102 # FIXME: what about other normalizations? E.g., _ vs. -?
103 if name.lower() != name:
104 self.requirement_aliases[name.lower()] = name
105 result = [install_req]
106 else:
107 # Assume there's no need to scan, and that we've already
108 # encountered this for scanning.
109 result = []
110 if not install_req.constraint and existing_req.constraint:
111 if (install_req.link and not (existing_req.link and
112 install_req.link.path == existing_req.link.path)):
113 self.reqs_to_cleanup.append(install_req)
114 raise InstallationError(
115 "Could not satisfy constraints for '%s': "
116 "installation from path or url cannot be "
117 "constrained to a version" % name,
118 )
119 # If we're now installing a constraint, mark the existing
120 # object for real installation.
121 existing_req.constraint = False
122 existing_req.extras = tuple(
123 sorted(set(existing_req.extras).union(
124 set(install_req.extras))))
125 logger.debug("Setting %s extras to: %s",
126 existing_req, existing_req.extras)
127 # And now we need to scan this.
128 result = [existing_req]
129 # Canonicalise to the already-added object for the backref
130 # check below.
131 install_req = existing_req
132
133 # We return install_req here to allow for the caller to add it to
134 # the dependency information for the parent package.
135 return result, install_req
136
137 def has_requirement(self, project_name):
138 name = project_name.lower()
139 if (name in self.requirements and
140 not self.requirements[name].constraint or
141 name in self.requirement_aliases and
142 not self.requirements[self.requirement_aliases[name]].constraint):
143 return True
144 return False
145
146 @property
147 def has_requirements(self):
148 return list(req for req in self.requirements.values() if not
149 req.constraint) or self.unnamed_requirements
150
151 def get_requirement(self, project_name):
152 for name in project_name, project_name.lower():
153 if name in self.requirements:
154 return self.requirements[name]
155 if name in self.requirement_aliases:
156 return self.requirements[self.requirement_aliases[name]]
157 raise KeyError("No project with the name %r" % project_name)
158
159 def cleanup_files(self):
160 """Clean up files, remove builds."""
161 logger.debug('Cleaning up...')
162 with indent_log():
163 for req in self.reqs_to_cleanup:
164 req.remove_temporary_source()
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/req_uninstall.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/req_uninstall.py
deleted file mode 100644
index a47520f..0000000
--- a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/req/req_uninstall.py
+++ /dev/null
@@ -1,455 +0,0 @@
1from __future__ import absolute_import
2
3import csv
4import functools
5import logging
6import os
7import sys
8import sysconfig
9
10from pip._vendor import pkg_resources
11
12from pip._internal.compat import WINDOWS, cache_from_source, uses_pycache
13from pip._internal.exceptions import UninstallationError
14from pip._internal.locations import bin_py, bin_user
15from pip._internal.utils.logging import indent_log
16from pip._internal.utils.misc import (
17 FakeFile, ask, dist_in_usersite, dist_is_local, egg_link_path, is_local,
18 normalize_path, renames,
19)
20from pip._internal.utils.temp_dir import TempDirectory
21
22logger = logging.getLogger(__name__)
23
24
25def _script_names(dist, script_name, is_gui):
26 """Create the fully qualified name of the files created by
27 {console,gui}_scripts for the given ``dist``.
28 Returns the list of file names
29 """
30 if dist_in_usersite(dist):
31 bin_dir = bin_user
32 else:
33 bin_dir = bin_py
34 exe_name = os.path.join(bin_dir, script_name)
35 paths_to_remove = [exe_name]
36 if WINDOWS:
37 paths_to_remove.append(exe_name + '.exe')
38 paths_to_remove.append(exe_name + '.exe.manifest')
39 if is_gui:
40 paths_to_remove.append(exe_name + '-script.pyw')
41 else:
42 paths_to_remove.append(exe_name + '-script.py')
43 return paths_to_remove
44
45
46def _unique(fn):
47 @functools.wraps(fn)
48 def unique(*args, **kw):
49 seen = set()
50 for item in fn(*args, **kw):
51 if item not in seen:
52 seen.add(item)
53 yield item
54 return unique
55
56
57@_unique
58def uninstallation_paths(dist):
59 """
60 Yield all the uninstallation paths for dist based on RECORD-without-.pyc
61
62 Yield paths to all the files in RECORD. For each .py file in RECORD, add
63 the .pyc in the same directory.
64
65 UninstallPathSet.add() takes care of the __pycache__ .pyc.
66 """
67 r = csv.reader(FakeFile(dist.get_metadata_lines('RECORD')))
68 for row in r:
69 path = os.path.join(dist.location, row[0])
70 yield path
71 if path.endswith('.py'):
72 dn, fn = os.path.split(path)
73 base = fn[:-3]
74 path = os.path.join(dn, base + '.pyc')
75 yield path
76
77
78def compact(paths):
79 """Compact a path set to contain the minimal number of paths
80 necessary to contain all paths in the set. If /a/path/ and
81 /a/path/to/a/file.txt are both in the set, leave only the
82 shorter path."""
83
84 sep = os.path.sep
85 short_paths = set()
86 for path in sorted(paths, key=len):
87 should_add = any(
88 path.startswith(shortpath.rstrip("*")) and
89 path[len(shortpath.rstrip("*").rstrip(sep))] == sep
90 for shortpath in short_paths
91 )
92 if not should_add:
93 short_paths.add(path)
94 return short_paths
95
96
97def compress_for_output_listing(paths):
98 """Returns a tuple of 2 sets of which paths to display to user
99
100 The first set contains paths that would be deleted. Files of a package
101 are not added and the top-level directory of the package has a '*' added
102 at the end - to signify that all it's contents are removed.
103
104 The second set contains files that would have been skipped in the above
105 folders.
106 """
107
108 will_remove = list(paths)
109 will_skip = set()
110
111 # Determine folders and files
112 folders = set()
113 files = set()
114 for path in will_remove:
115 if path.endswith(".pyc"):
116 continue
117 if path.endswith("__init__.py") or ".dist-info" in path:
118 folders.add(os.path.dirname(path))
119 files.add(path)
120
121 folders = compact(folders)
122
123 # This walks the tree using os.walk to not miss extra folders
124 # that might get added.
125 for folder in folders:
126 for dirpath, _, dirfiles in os.walk(folder):
127 for fname in dirfiles:
128 if fname.endswith(".pyc"):
129 continue
130
131 file_ = os.path.normcase(os.path.join(dirpath, fname))
132 if os.path.isfile(file_) and file_ not in files:
133 # We are skipping this file. Add it to the set.
134 will_skip.add(file_)
135
136 will_remove = files | {
137 os.path.join(folder, "*") for folder in folders
138 }
139
140 return will_remove, will_skip
141
142
143class UninstallPathSet(object):
144 """A set of file paths to be removed in the uninstallation of a
145 requirement."""
146 def __init__(self, dist):
147 self.paths = set()
148 self._refuse = set()
149 self.pth = {}
150 self.dist = dist
151 self.save_dir = TempDirectory(kind="uninstall")
152 self._moved_paths = []
153
154 def _permitted(self, path):
155 """
156 Return True if the given path is one we are permitted to
157 remove/modify, False otherwise.
158
159 """
160 return is_local(path)
161
162 def add(self, path):
163 head, tail = os.path.split(path)
164
165 # we normalize the head to resolve parent directory symlinks, but not
166 # the tail, since we only want to uninstall symlinks, not their targets
167 path = os.path.join(normalize_path(head), os.path.normcase(tail))
168
169 if not os.path.exists(path):
170 return
171 if self._permitted(path):
172 self.paths.add(path)
173 else:
174 self._refuse.add(path)
175
176 # __pycache__ files can show up after 'installed-files.txt' is created,
177 # due to imports
178 if os.path.splitext(path)[1] == '.py' and uses_pycache:
179 self.add(cache_from_source(path))
180
181 def add_pth(self, pth_file, entry):
182 pth_file = normalize_path(pth_file)
183 if self._permitted(pth_file):
184 if pth_file not in self.pth:
185 self.pth[pth_file] = UninstallPthEntries(pth_file)
186 self.pth[pth_file].add(entry)
187 else:
188 self._refuse.add(pth_file)
189
190 def _stash(self, path):
191 return os.path.join(
192 self.save_dir.path, os.path.splitdrive(path)[1].lstrip(os.path.sep)
193 )
194
195 def remove(self, auto_confirm=False, verbose=False):
196 """Remove paths in ``self.paths`` with confirmation (unless
197 ``auto_confirm`` is True)."""
198
199 if not self.paths:
200 logger.info(
201 "Can't uninstall '%s'. No files were found to uninstall.",
202 self.dist.project_name,
203 )
204 return
205
206 dist_name_version = (
207 self.dist.project_name + "-" + self.dist.version
208 )
209 logger.info('Uninstalling %s:', dist_name_version)
210
211 with indent_log():
212 if auto_confirm or self._allowed_to_proceed(verbose):
213 self.save_dir.create()
214
215 for path in sorted(compact(self.paths)):
216 new_path = self._stash(path)
217 logger.debug('Removing file or directory %s', path)
218 self._moved_paths.append(path)
219 renames(path, new_path)
220 for pth in self.pth.values():
221 pth.remove()
222
223 logger.info('Successfully uninstalled %s', dist_name_version)
224
225 def _allowed_to_proceed(self, verbose):
226 """Display which files would be deleted and prompt for confirmation
227 """
228
229 def _display(msg, paths):
230 if not paths:
231 return
232
233 logger.info(msg)
234 with indent_log():
235 for path in sorted(compact(paths)):
236 logger.info(path)
237
238 if not verbose:
239 will_remove, will_skip = compress_for_output_listing(self.paths)
240 else:
241 # In verbose mode, display all the files that are going to be
242 # deleted.
243 will_remove = list(self.paths)
244 will_skip = set()
245
246 _display('Would remove:', will_remove)
247 _display('Would not remove (might be manually added):', will_skip)
248 _display('Would not remove (outside of prefix):', self._refuse)
249
250 return ask('Proceed (y/n)? ', ('y', 'n')) == 'y'
251
252 def rollback(self):
253 """Rollback the changes previously made by remove()."""
254 if self.save_dir.path is None:
255 logger.error(
256 "Can't roll back %s; was not uninstalled",
257 self.dist.project_name,
258 )
259 return False
260 logger.info('Rolling back uninstall of %s', self.dist.project_name)
261 for path in self._moved_paths:
262 tmp_path = self._stash(path)
263 logger.debug('Replacing %s', path)
264 renames(tmp_path, path)
265 for pth in self.pth.values():
266 pth.rollback()
267
268 def commit(self):
269 """Remove temporary save dir: rollback will no longer be possible."""
270 self.save_dir.cleanup()
271 self._moved_paths = []
272
273 @classmethod
274 def from_dist(cls, dist):
275 dist_path = normalize_path(dist.location)
276 if not dist_is_local(dist):
277 logger.info(
278 "Not uninstalling %s at %s, outside environment %s",
279 dist.key,
280 dist_path,
281 sys.prefix,
282 )
283 return cls(dist)
284
285 if dist_path in {p for p in {sysconfig.get_path("stdlib"),
286 sysconfig.get_path("platstdlib")}
287 if p}:
288 logger.info(
289 "Not uninstalling %s at %s, as it is in the standard library.",
290 dist.key,
291 dist_path,
292 )
293 return cls(dist)
294
295 paths_to_remove = cls(dist)
296 develop_egg_link = egg_link_path(dist)
297 develop_egg_link_egg_info = '{}.egg-info'.format(
298 pkg_resources.to_filename(dist.project_name))
299 egg_info_exists = dist.egg_info and os.path.exists(dist.egg_info)
300 # Special case for distutils installed package
301 distutils_egg_info = getattr(dist._provider, 'path', None)
302
303 # Uninstall cases order do matter as in the case of 2 installs of the
304 # same package, pip needs to uninstall the currently detected version
305 if (egg_info_exists and dist.egg_info.endswith('.egg-info') and
306 not dist.egg_info.endswith(develop_egg_link_egg_info)):
307 # if dist.egg_info.endswith(develop_egg_link_egg_info), we
308 # are in fact in the develop_egg_link case
309 paths_to_remove.add(dist.egg_info)
310 if dist.has_metadata('installed-files.txt'):
311 for installed_file in dist.get_metadata(
312 'installed-files.txt').splitlines():
313 path = os.path.normpath(
314 os.path.join(dist.egg_info, installed_file)
315 )
316 paths_to_remove.add(path)
317 # FIXME: need a test for this elif block
318 # occurs with --single-version-externally-managed/--record outside
319 # of pip
320 elif dist.has_metadata('top_level.txt'):
321 if dist.has_metadata('namespace_packages.txt'):
322 namespaces = dist.get_metadata('namespace_packages.txt')
323 else:
324 namespaces = []
325 for top_level_pkg in [
326 p for p
327 in dist.get_metadata('top_level.txt').splitlines()
328 if p and p not in namespaces]:
329 path = os.path.join(dist.location, top_level_pkg)
330 paths_to_remove.add(path)
331 paths_to_remove.add(path + '.py')
332 paths_to_remove.add(path + '.pyc')
333 paths_to_remove.add(path + '.pyo')
334
335 elif distutils_egg_info:
336 raise UninstallationError(
337 "Cannot uninstall {!r}. It is a distutils installed project "
338 "and thus we cannot accurately determine which files belong "
339 "to it which would lead to only a partial uninstall.".format(
340 dist.project_name,
341 )
342 )
343
344 elif dist.location.endswith('.egg'):
345 # package installed by easy_install
346 # We cannot match on dist.egg_name because it can slightly vary
347 # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg
348 paths_to_remove.add(dist.location)
349 easy_install_egg = os.path.split(dist.location)[1]
350 easy_install_pth = os.path.join(os.path.dirname(dist.location),
351 'easy-install.pth')
352 paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg)
353
354 elif egg_info_exists and dist.egg_info.endswith('.dist-info'):
355 for path in uninstallation_paths(dist):
356 paths_to_remove.add(path)
357
358 elif develop_egg_link:
359 # develop egg
360 with open(develop_egg_link, 'r') as fh:
361 link_pointer = os.path.normcase(fh.readline().strip())
362 assert (link_pointer == dist.location), (
363 'Egg-link %s does not match installed location of %s '
364 '(at %s)' % (link_pointer, dist.project_name, dist.location)
365 )
366 paths_to_remove.add(develop_egg_link)
367 easy_install_pth = os.path.join(os.path.dirname(develop_egg_link),
368 'easy-install.pth')
369 paths_to_remove.add_pth(easy_install_pth, dist.location)
370
371 else:
372 logger.debug(
373 'Not sure how to uninstall: %s - Check: %s',
374 dist, dist.location,
375 )
376
377 # find distutils scripts= scripts
378 if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'):
379 for script in dist.metadata_listdir('scripts'):
380 if dist_in_usersite(dist):
381 bin_dir = bin_user
382 else:
383 bin_dir = bin_py
384 paths_to_remove.add(os.path.join(bin_dir, script))
385 if WINDOWS:
386 paths_to_remove.add(os.path.join(bin_dir, script) + '.bat')
387
388 # find console_scripts
389 _scripts_to_remove = []
390 console_scripts = dist.get_entry_map(group='console_scripts')
391 for name in console_scripts.keys():
392 _scripts_to_remove.extend(_script_names(dist, name, False))
393 # find gui_scripts
394 gui_scripts = dist.get_entry_map(group='gui_scripts')
395 for name in gui_scripts.keys():
396 _scripts_to_remove.extend(_script_names(dist, name, True))
397
398 for s in _scripts_to_remove:
399 paths_to_remove.add(s)
400
401 return paths_to_remove
402
403
404class UninstallPthEntries(object):
405 def __init__(self, pth_file):
406 if not os.path.isfile(pth_file):
407 raise UninstallationError(
408 "Cannot remove entries from nonexistent file %s" % pth_file
409 )
410 self.file = pth_file
411 self.entries = set()
412 self._saved_lines = None
413
414 def add(self, entry):
415 entry = os.path.normcase(entry)
416 # On Windows, os.path.normcase converts the entry to use
417 # backslashes. This is correct for entries that describe absolute
418 # paths outside of site-packages, but all the others use forward
419 # slashes.
420 if WINDOWS and not os.path.splitdrive(entry)[0]:
421 entry = entry.replace('\\', '/')
422 self.entries.add(entry)
423
424 def remove(self):
425 logger.debug('Removing pth entries from %s:', self.file)
426 with open(self.file, 'rb') as fh:
427 # windows uses '\r\n' with py3k, but uses '\n' with py2.x
428 lines = fh.readlines()
429 self._saved_lines = lines
430 if any(b'\r\n' in line for line in lines):
431 endline = '\r\n'
432 else:
433 endline = '\n'
434 # handle missing trailing newline
435 if lines and not lines[-1].endswith(endline.encode("utf-8")):
436 lines[-1] = lines[-1] + endline.encode("utf-8")
437 for entry in self.entries:
438 try:
439 logger.debug('Removing entry: %s', entry)
440 lines.remove((entry + endline).encode("utf-8"))
441 except ValueError:
442 pass
443 with open(self.file, 'wb') as fh:
444 fh.writelines(lines)
445
446 def rollback(self):
447 if self._saved_lines is None:
448 logger.error(
449 'Cannot roll back changes to %s, none were made', self.file
450 )
451 return False
452 logger.debug('Rolling %s back to previous state', self.file)
453 with open(self.file, 'wb') as fh:
454 fh.writelines(self._saved_lines)
455 return True