You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

req_install.py 43KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115
  1. from __future__ import absolute_import
  2. import logging
  3. import os
  4. import re
  5. import shutil
  6. import sys
  7. import sysconfig
  8. import traceback
  9. import warnings
  10. import zipfile
  11. from distutils.util import change_root
  12. from email.parser import FeedParser # type: ignore
  13. from pip._vendor import pkg_resources, pytoml, six
  14. from pip._vendor.packaging import specifiers
  15. from pip._vendor.packaging.markers import Marker
  16. from pip._vendor.packaging.requirements import InvalidRequirement, Requirement
  17. from pip._vendor.packaging.utils import canonicalize_name
  18. from pip._vendor.packaging.version import parse as parse_version
  19. from pip._vendor.packaging.version import Version
  20. from pip._vendor.pkg_resources import RequirementParseError, parse_requirements
  21. from pip._internal import wheel
  22. from pip._internal.build_env import BuildEnvironment
  23. from pip._internal.compat import native_str
  24. from pip._internal.download import (
  25. is_archive_file, is_url, path_to_url, url_to_path,
  26. )
  27. from pip._internal.exceptions import InstallationError, UninstallationError
  28. from pip._internal.locations import (
  29. PIP_DELETE_MARKER_FILENAME, running_under_virtualenv,
  30. )
  31. from pip._internal.req.req_uninstall import UninstallPathSet
  32. from pip._internal.utils.deprecation import RemovedInPip11Warning
  33. from pip._internal.utils.hashes import Hashes
  34. from pip._internal.utils.logging import indent_log
  35. from pip._internal.utils.misc import (
  36. _make_build_dir, ask_path_exists, backup_dir, call_subprocess,
  37. display_path, dist_in_site_packages, dist_in_usersite, ensure_dir,
  38. get_installed_version, is_installable_dir, read_text_file, rmtree,
  39. )
  40. from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM
  41. from pip._internal.utils.temp_dir import TempDirectory
  42. from pip._internal.utils.ui import open_spinner
  43. from pip._internal.vcs import vcs
  44. from pip._internal.wheel import Wheel, move_wheel_files
  45. logger = logging.getLogger(__name__)
  46. operators = specifiers.Specifier._operators.keys()
  47. def _strip_extras(path):
  48. m = re.match(r'^(.+)(\[[^\]]+\])$', path)
  49. extras = None
  50. if m:
  51. path_no_extras = m.group(1)
  52. extras = m.group(2)
  53. else:
  54. path_no_extras = path
  55. return path_no_extras, extras
  56. class InstallRequirement(object):
  57. """
  58. Represents something that may be installed later on, may have information
  59. about where to fetch the relavant requirement and also contains logic for
  60. installing the said requirement.
  61. """
  62. def __init__(self, req, comes_from, source_dir=None, editable=False,
  63. link=None, update=True, markers=None,
  64. isolated=False, options=None, wheel_cache=None,
  65. constraint=False, extras=()):
  66. assert req is None or isinstance(req, Requirement), req
  67. self.req = req
  68. self.comes_from = comes_from
  69. self.constraint = constraint
  70. if source_dir is not None:
  71. self.source_dir = os.path.normpath(os.path.abspath(source_dir))
  72. else:
  73. self.source_dir = None
  74. self.editable = editable
  75. self._wheel_cache = wheel_cache
  76. if link is not None:
  77. self.link = self.original_link = link
  78. else:
  79. from pip._internal.index import Link
  80. self.link = self.original_link = req and req.url and Link(req.url)
  81. if extras:
  82. self.extras = extras
  83. elif req:
  84. self.extras = {
  85. pkg_resources.safe_extra(extra) for extra in req.extras
  86. }
  87. else:
  88. self.extras = set()
  89. if markers is not None:
  90. self.markers = markers
  91. else:
  92. self.markers = req and req.marker
  93. self._egg_info_path = None
  94. # This holds the pkg_resources.Distribution object if this requirement
  95. # is already available:
  96. self.satisfied_by = None
  97. # This hold the pkg_resources.Distribution object if this requirement
  98. # conflicts with another installed distribution:
  99. self.conflicts_with = None
  100. # Temporary build location
  101. self._temp_build_dir = TempDirectory(kind="req-build")
  102. # Used to store the global directory where the _temp_build_dir should
  103. # have been created. Cf _correct_build_location method.
  104. self._ideal_build_dir = None
  105. # True if the editable should be updated:
  106. self.update = update
  107. # Set to True after successful installation
  108. self.install_succeeded = None
  109. # UninstallPathSet of uninstalled distribution (for possible rollback)
  110. self.uninstalled_pathset = None
  111. self.options = options if options else {}
  112. # Set to True after successful preparation of this requirement
  113. self.prepared = False
  114. self.is_direct = False
  115. self.isolated = isolated
  116. self.build_env = BuildEnvironment(no_clean=True)
  117. @classmethod
  118. def from_editable(cls, editable_req, comes_from=None, isolated=False,
  119. options=None, wheel_cache=None, constraint=False):
  120. from pip._internal.index import Link
  121. name, url, extras_override = parse_editable(editable_req)
  122. if url.startswith('file:'):
  123. source_dir = url_to_path(url)
  124. else:
  125. source_dir = None
  126. if name is not None:
  127. try:
  128. req = Requirement(name)
  129. except InvalidRequirement:
  130. raise InstallationError("Invalid requirement: '%s'" % name)
  131. else:
  132. req = None
  133. return cls(
  134. req, comes_from, source_dir=source_dir,
  135. editable=True,
  136. link=Link(url),
  137. constraint=constraint,
  138. isolated=isolated,
  139. options=options if options else {},
  140. wheel_cache=wheel_cache,
  141. extras=extras_override or (),
  142. )
  143. @classmethod
  144. def from_req(cls, req, comes_from=None, isolated=False, wheel_cache=None):
  145. try:
  146. req = Requirement(req)
  147. except InvalidRequirement:
  148. raise InstallationError("Invalid requirement: '%s'" % req)
  149. if req.url:
  150. raise InstallationError(
  151. "Direct url requirement (like %s) are not allowed for "
  152. "dependencies" % req
  153. )
  154. return cls(req, comes_from, isolated=isolated, wheel_cache=wheel_cache)
  155. @classmethod
  156. def from_line(
  157. cls, name, comes_from=None, isolated=False, options=None,
  158. wheel_cache=None, constraint=False):
  159. """Creates an InstallRequirement from a name, which might be a
  160. requirement, directory containing 'setup.py', filename, or URL.
  161. """
  162. from pip._internal.index import Link
  163. if is_url(name):
  164. marker_sep = '; '
  165. else:
  166. marker_sep = ';'
  167. if marker_sep in name:
  168. name, markers = name.split(marker_sep, 1)
  169. markers = markers.strip()
  170. if not markers:
  171. markers = None
  172. else:
  173. markers = Marker(markers)
  174. else:
  175. markers = None
  176. name = name.strip()
  177. req = None
  178. path = os.path.normpath(os.path.abspath(name))
  179. link = None
  180. extras = None
  181. if is_url(name):
  182. link = Link(name)
  183. else:
  184. p, extras = _strip_extras(path)
  185. looks_like_dir = os.path.isdir(p) and (
  186. os.path.sep in name or
  187. (os.path.altsep is not None and os.path.altsep in name) or
  188. name.startswith('.')
  189. )
  190. if looks_like_dir:
  191. if not is_installable_dir(p):
  192. raise InstallationError(
  193. "Directory %r is not installable. File 'setup.py' "
  194. "not found." % name
  195. )
  196. link = Link(path_to_url(p))
  197. elif is_archive_file(p):
  198. if not os.path.isfile(p):
  199. logger.warning(
  200. 'Requirement %r looks like a filename, but the '
  201. 'file does not exist',
  202. name
  203. )
  204. link = Link(path_to_url(p))
  205. # it's a local file, dir, or url
  206. if link:
  207. # Handle relative file URLs
  208. if link.scheme == 'file' and re.search(r'\.\./', link.url):
  209. link = Link(
  210. path_to_url(os.path.normpath(os.path.abspath(link.path))))
  211. # wheel file
  212. if link.is_wheel:
  213. wheel = Wheel(link.filename) # can raise InvalidWheelFilename
  214. req = "%s==%s" % (wheel.name, wheel.version)
  215. else:
  216. # set the req to the egg fragment. when it's not there, this
  217. # will become an 'unnamed' requirement
  218. req = link.egg_fragment
  219. # a requirement specifier
  220. else:
  221. req = name
  222. if extras:
  223. extras = Requirement("placeholder" + extras.lower()).extras
  224. else:
  225. extras = ()
  226. if req is not None:
  227. try:
  228. req = Requirement(req)
  229. except InvalidRequirement:
  230. if os.path.sep in req:
  231. add_msg = "It looks like a path."
  232. add_msg += deduce_helpful_msg(req)
  233. elif '=' in req and not any(op in req for op in operators):
  234. add_msg = "= is not a valid operator. Did you mean == ?"
  235. else:
  236. add_msg = traceback.format_exc()
  237. raise InstallationError(
  238. "Invalid requirement: '%s'\n%s" % (req, add_msg))
  239. return cls(
  240. req, comes_from, link=link, markers=markers,
  241. isolated=isolated,
  242. options=options if options else {},
  243. wheel_cache=wheel_cache,
  244. constraint=constraint,
  245. extras=extras,
  246. )
  247. def __str__(self):
  248. if self.req:
  249. s = str(self.req)
  250. if self.link:
  251. s += ' from %s' % self.link.url
  252. else:
  253. s = self.link.url if self.link else None
  254. if self.satisfied_by is not None:
  255. s += ' in %s' % display_path(self.satisfied_by.location)
  256. if self.comes_from:
  257. if isinstance(self.comes_from, six.string_types):
  258. comes_from = self.comes_from
  259. else:
  260. comes_from = self.comes_from.from_path()
  261. if comes_from:
  262. s += ' (from %s)' % comes_from
  263. return s
  264. def __repr__(self):
  265. return '<%s object: %s editable=%r>' % (
  266. self.__class__.__name__, str(self), self.editable)
  267. def populate_link(self, finder, upgrade, require_hashes):
  268. """Ensure that if a link can be found for this, that it is found.
  269. Note that self.link may still be None - if Upgrade is False and the
  270. requirement is already installed.
  271. If require_hashes is True, don't use the wheel cache, because cached
  272. wheels, always built locally, have different hashes than the files
  273. downloaded from the index server and thus throw false hash mismatches.
  274. Furthermore, cached wheels at present have undeterministic contents due
  275. to file modification times.
  276. """
  277. if self.link is None:
  278. self.link = finder.find_requirement(self, upgrade)
  279. if self._wheel_cache is not None and not require_hashes:
  280. old_link = self.link
  281. self.link = self._wheel_cache.get(self.link, self.name)
  282. if old_link != self.link:
  283. logger.debug('Using cached wheel link: %s', self.link)
  284. @property
  285. def specifier(self):
  286. return self.req.specifier
  287. @property
  288. def is_pinned(self):
  289. """Return whether I am pinned to an exact version.
  290. For example, some-package==1.2 is pinned; some-package>1.2 is not.
  291. """
  292. specifiers = self.specifier
  293. return (len(specifiers) == 1 and
  294. next(iter(specifiers)).operator in {'==', '==='})
  295. def from_path(self):
  296. if self.req is None:
  297. return None
  298. s = str(self.req)
  299. if self.comes_from:
  300. if isinstance(self.comes_from, six.string_types):
  301. comes_from = self.comes_from
  302. else:
  303. comes_from = self.comes_from.from_path()
  304. if comes_from:
  305. s += '->' + comes_from
  306. return s
  307. def build_location(self, build_dir):
  308. assert build_dir is not None
  309. if self._temp_build_dir.path is not None:
  310. return self._temp_build_dir.path
  311. if self.req is None:
  312. # for requirement via a path to a directory: the name of the
  313. # package is not available yet so we create a temp directory
  314. # Once run_egg_info will have run, we'll be able
  315. # to fix it via _correct_build_location
  316. # Some systems have /tmp as a symlink which confuses custom
  317. # builds (such as numpy). Thus, we ensure that the real path
  318. # is returned.
  319. self._temp_build_dir.create()
  320. self._ideal_build_dir = build_dir
  321. return self._temp_build_dir.path
  322. if self.editable:
  323. name = self.name.lower()
  324. else:
  325. name = self.name
  326. # FIXME: Is there a better place to create the build_dir? (hg and bzr
  327. # need this)
  328. if not os.path.exists(build_dir):
  329. logger.debug('Creating directory %s', build_dir)
  330. _make_build_dir(build_dir)
  331. return os.path.join(build_dir, name)
  332. def _correct_build_location(self):
  333. """Move self._temp_build_dir to self._ideal_build_dir/self.req.name
  334. For some requirements (e.g. a path to a directory), the name of the
  335. package is not available until we run egg_info, so the build_location
  336. will return a temporary directory and store the _ideal_build_dir.
  337. This is only called by self.egg_info_path to fix the temporary build
  338. directory.
  339. """
  340. if self.source_dir is not None:
  341. return
  342. assert self.req is not None
  343. assert self._temp_build_dir.path
  344. assert self._ideal_build_dir.path
  345. old_location = self._temp_build_dir.path
  346. self._temp_build_dir.path = None
  347. new_location = self.build_location(self._ideal_build_dir)
  348. if os.path.exists(new_location):
  349. raise InstallationError(
  350. 'A package already exists in %s; please remove it to continue'
  351. % display_path(new_location))
  352. logger.debug(
  353. 'Moving package %s from %s to new location %s',
  354. self, display_path(old_location), display_path(new_location),
  355. )
  356. shutil.move(old_location, new_location)
  357. self._temp_build_dir.path = new_location
  358. self._ideal_build_dir = None
  359. self.source_dir = os.path.normpath(os.path.abspath(new_location))
  360. self._egg_info_path = None
  361. @property
  362. def name(self):
  363. if self.req is None:
  364. return None
  365. return native_str(pkg_resources.safe_name(self.req.name))
  366. @property
  367. def setup_py_dir(self):
  368. return os.path.join(
  369. self.source_dir,
  370. self.link and self.link.subdirectory_fragment or '')
  371. @property
  372. def setup_py(self):
  373. assert self.source_dir, "No source dir for %s" % self
  374. setup_py = os.path.join(self.setup_py_dir, 'setup.py')
  375. # Python2 __file__ should not be unicode
  376. if six.PY2 and isinstance(setup_py, six.text_type):
  377. setup_py = setup_py.encode(sys.getfilesystemencoding())
  378. return setup_py
  379. @property
  380. def pyproject_toml(self):
  381. assert self.source_dir, "No source dir for %s" % self
  382. pp_toml = os.path.join(self.setup_py_dir, 'pyproject.toml')
  383. # Python2 __file__ should not be unicode
  384. if six.PY2 and isinstance(pp_toml, six.text_type):
  385. pp_toml = pp_toml.encode(sys.getfilesystemencoding())
  386. return pp_toml
  387. def get_pep_518_info(self):
  388. """Get a list of the packages required to build the project, if any,
  389. and a flag indicating whether pyproject.toml is present, indicating
  390. that the build should be isolated.
  391. Build requirements can be specified in a pyproject.toml, as described
  392. in PEP 518. If this file exists but doesn't specify build
  393. requirements, pip will default to installing setuptools and wheel.
  394. """
  395. if os.path.isfile(self.pyproject_toml):
  396. with open(self.pyproject_toml) as f:
  397. pp_toml = pytoml.load(f)
  398. build_sys = pp_toml.get('build-system', {})
  399. return (build_sys.get('requires', ['setuptools', 'wheel']), True)
  400. return (['setuptools', 'wheel'], False)
  401. def run_egg_info(self):
  402. assert self.source_dir
  403. if self.name:
  404. logger.debug(
  405. 'Running setup.py (path:%s) egg_info for package %s',
  406. self.setup_py, self.name,
  407. )
  408. else:
  409. logger.debug(
  410. 'Running setup.py (path:%s) egg_info for package from %s',
  411. self.setup_py, self.link,
  412. )
  413. with indent_log():
  414. script = SETUPTOOLS_SHIM % self.setup_py
  415. base_cmd = [sys.executable, '-c', script]
  416. if self.isolated:
  417. base_cmd += ["--no-user-cfg"]
  418. egg_info_cmd = base_cmd + ['egg_info']
  419. # We can't put the .egg-info files at the root, because then the
  420. # source code will be mistaken for an installed egg, causing
  421. # problems
  422. if self.editable:
  423. egg_base_option = []
  424. else:
  425. egg_info_dir = os.path.join(self.setup_py_dir, 'pip-egg-info')
  426. ensure_dir(egg_info_dir)
  427. egg_base_option = ['--egg-base', 'pip-egg-info']
  428. with self.build_env:
  429. call_subprocess(
  430. egg_info_cmd + egg_base_option,
  431. cwd=self.setup_py_dir,
  432. show_stdout=False,
  433. command_desc='python setup.py egg_info')
  434. if not self.req:
  435. if isinstance(parse_version(self.pkg_info()["Version"]), Version):
  436. op = "=="
  437. else:
  438. op = "==="
  439. self.req = Requirement(
  440. "".join([
  441. self.pkg_info()["Name"],
  442. op,
  443. self.pkg_info()["Version"],
  444. ])
  445. )
  446. self._correct_build_location()
  447. else:
  448. metadata_name = canonicalize_name(self.pkg_info()["Name"])
  449. if canonicalize_name(self.req.name) != metadata_name:
  450. logger.warning(
  451. 'Running setup.py (path:%s) egg_info for package %s '
  452. 'produced metadata for project name %s. Fix your '
  453. '#egg=%s fragments.',
  454. self.setup_py, self.name, metadata_name, self.name
  455. )
  456. self.req = Requirement(metadata_name)
  457. def egg_info_data(self, filename):
  458. if self.satisfied_by is not None:
  459. if not self.satisfied_by.has_metadata(filename):
  460. return None
  461. return self.satisfied_by.get_metadata(filename)
  462. assert self.source_dir
  463. filename = self.egg_info_path(filename)
  464. if not os.path.exists(filename):
  465. return None
  466. data = read_text_file(filename)
  467. return data
  468. def egg_info_path(self, filename):
  469. if self._egg_info_path is None:
  470. if self.editable:
  471. base = self.source_dir
  472. else:
  473. base = os.path.join(self.setup_py_dir, 'pip-egg-info')
  474. filenames = os.listdir(base)
  475. if self.editable:
  476. filenames = []
  477. for root, dirs, files in os.walk(base):
  478. for dir in vcs.dirnames:
  479. if dir in dirs:
  480. dirs.remove(dir)
  481. # Iterate over a copy of ``dirs``, since mutating
  482. # a list while iterating over it can cause trouble.
  483. # (See https://github.com/pypa/pip/pull/462.)
  484. for dir in list(dirs):
  485. # Don't search in anything that looks like a virtualenv
  486. # environment
  487. if (
  488. os.path.lexists(
  489. os.path.join(root, dir, 'bin', 'python')
  490. ) or
  491. os.path.exists(
  492. os.path.join(
  493. root, dir, 'Scripts', 'Python.exe'
  494. )
  495. )):
  496. dirs.remove(dir)
  497. # Also don't search through tests
  498. elif dir == 'test' or dir == 'tests':
  499. dirs.remove(dir)
  500. filenames.extend([os.path.join(root, dir)
  501. for dir in dirs])
  502. filenames = [f for f in filenames if f.endswith('.egg-info')]
  503. if not filenames:
  504. raise InstallationError(
  505. 'No files/directories in %s (from %s)' % (base, filename)
  506. )
  507. assert filenames, \
  508. "No files/directories in %s (from %s)" % (base, filename)
  509. # if we have more than one match, we pick the toplevel one. This
  510. # can easily be the case if there is a dist folder which contains
  511. # an extracted tarball for testing purposes.
  512. if len(filenames) > 1:
  513. filenames.sort(
  514. key=lambda x: x.count(os.path.sep) +
  515. (os.path.altsep and x.count(os.path.altsep) or 0)
  516. )
  517. self._egg_info_path = os.path.join(base, filenames[0])
  518. return os.path.join(self._egg_info_path, filename)
  519. def pkg_info(self):
  520. p = FeedParser()
  521. data = self.egg_info_data('PKG-INFO')
  522. if not data:
  523. logger.warning(
  524. 'No PKG-INFO file found in %s',
  525. display_path(self.egg_info_path('PKG-INFO')),
  526. )
  527. p.feed(data or '')
  528. return p.close()
  529. _requirements_section_re = re.compile(r'\[(.*?)\]')
  530. @property
  531. def installed_version(self):
  532. return get_installed_version(self.name)
  533. def assert_source_matches_version(self):
  534. assert self.source_dir
  535. version = self.pkg_info()['version']
  536. if self.req.specifier and version not in self.req.specifier:
  537. logger.warning(
  538. 'Requested %s, but installing version %s',
  539. self,
  540. version,
  541. )
  542. else:
  543. logger.debug(
  544. 'Source in %s has version %s, which satisfies requirement %s',
  545. display_path(self.source_dir),
  546. version,
  547. self,
  548. )
  549. def update_editable(self, obtain=True):
  550. if not self.link:
  551. logger.debug(
  552. "Cannot update repository at %s; repository location is "
  553. "unknown",
  554. self.source_dir,
  555. )
  556. return
  557. assert self.editable
  558. assert self.source_dir
  559. if self.link.scheme == 'file':
  560. # Static paths don't get updated
  561. return
  562. assert '+' in self.link.url, "bad url: %r" % self.link.url
  563. if not self.update:
  564. return
  565. vc_type, url = self.link.url.split('+', 1)
  566. backend = vcs.get_backend(vc_type)
  567. if backend:
  568. vcs_backend = backend(self.link.url)
  569. if obtain:
  570. vcs_backend.obtain(self.source_dir)
  571. else:
  572. vcs_backend.export(self.source_dir)
  573. else:
  574. assert 0, (
  575. 'Unexpected version control type (in %s): %s'
  576. % (self.link, vc_type))
  577. def uninstall(self, auto_confirm=False, verbose=False,
  578. use_user_site=False):
  579. """
  580. Uninstall the distribution currently satisfying this requirement.
  581. Prompts before removing or modifying files unless
  582. ``auto_confirm`` is True.
  583. Refuses to delete or modify files outside of ``sys.prefix`` -
  584. thus uninstallation within a virtual environment can only
  585. modify that virtual environment, even if the virtualenv is
  586. linked to global site-packages.
  587. """
  588. if not self.check_if_exists(use_user_site):
  589. logger.warning("Skipping %s as it is not installed.", self.name)
  590. return
  591. dist = self.satisfied_by or self.conflicts_with
  592. uninstalled_pathset = UninstallPathSet.from_dist(dist)
  593. uninstalled_pathset.remove(auto_confirm, verbose)
  594. return uninstalled_pathset
  595. def archive(self, build_dir):
  596. assert self.source_dir
  597. create_archive = True
  598. archive_name = '%s-%s.zip' % (self.name, self.pkg_info()["version"])
  599. archive_path = os.path.join(build_dir, archive_name)
  600. if os.path.exists(archive_path):
  601. response = ask_path_exists(
  602. 'The file %s exists. (i)gnore, (w)ipe, (b)ackup, (a)bort ' %
  603. display_path(archive_path), ('i', 'w', 'b', 'a'))
  604. if response == 'i':
  605. create_archive = False
  606. elif response == 'w':
  607. logger.warning('Deleting %s', display_path(archive_path))
  608. os.remove(archive_path)
  609. elif response == 'b':
  610. dest_file = backup_dir(archive_path)
  611. logger.warning(
  612. 'Backing up %s to %s',
  613. display_path(archive_path),
  614. display_path(dest_file),
  615. )
  616. shutil.move(archive_path, dest_file)
  617. elif response == 'a':
  618. sys.exit(-1)
  619. if create_archive:
  620. zip = zipfile.ZipFile(
  621. archive_path, 'w', zipfile.ZIP_DEFLATED,
  622. allowZip64=True
  623. )
  624. dir = os.path.normcase(os.path.abspath(self.setup_py_dir))
  625. for dirpath, dirnames, filenames in os.walk(dir):
  626. if 'pip-egg-info' in dirnames:
  627. dirnames.remove('pip-egg-info')
  628. for dirname in dirnames:
  629. dirname = os.path.join(dirpath, dirname)
  630. name = self._clean_zip_name(dirname, dir)
  631. zipdir = zipfile.ZipInfo(self.name + '/' + name + '/')
  632. zipdir.external_attr = 0x1ED << 16 # 0o755
  633. zip.writestr(zipdir, '')
  634. for filename in filenames:
  635. if filename == PIP_DELETE_MARKER_FILENAME:
  636. continue
  637. filename = os.path.join(dirpath, filename)
  638. name = self._clean_zip_name(filename, dir)
  639. zip.write(filename, self.name + '/' + name)
  640. zip.close()
  641. logger.info('Saved %s', display_path(archive_path))
  642. def _clean_zip_name(self, name, prefix):
  643. assert name.startswith(prefix + os.path.sep), (
  644. "name %r doesn't start with prefix %r" % (name, prefix)
  645. )
  646. name = name[len(prefix) + 1:]
  647. name = name.replace(os.path.sep, '/')
  648. return name
  649. def match_markers(self, extras_requested=None):
  650. if not extras_requested:
  651. # Provide an extra to safely evaluate the markers
  652. # without matching any extra
  653. extras_requested = ('',)
  654. if self.markers is not None:
  655. return any(
  656. self.markers.evaluate({'extra': extra})
  657. for extra in extras_requested)
  658. else:
  659. return True
  660. def install(self, install_options, global_options=None, root=None,
  661. home=None, prefix=None, warn_script_location=True,
  662. use_user_site=False, pycompile=True):
  663. global_options = global_options if global_options is not None else []
  664. if self.editable:
  665. self.install_editable(
  666. install_options, global_options, prefix=prefix,
  667. )
  668. return
  669. if self.is_wheel:
  670. version = wheel.wheel_version(self.source_dir)
  671. wheel.check_compatibility(version, self.name)
  672. self.move_wheel_files(
  673. self.source_dir, root=root, prefix=prefix, home=home,
  674. warn_script_location=warn_script_location,
  675. use_user_site=use_user_site, pycompile=pycompile,
  676. )
  677. self.install_succeeded = True
  678. return
  679. # Extend the list of global and install options passed on to
  680. # the setup.py call with the ones from the requirements file.
  681. # Options specified in requirements file override those
  682. # specified on the command line, since the last option given
  683. # to setup.py is the one that is used.
  684. global_options = list(global_options) + \
  685. self.options.get('global_options', [])
  686. install_options = list(install_options) + \
  687. self.options.get('install_options', [])
  688. if self.isolated:
  689. global_options = global_options + ["--no-user-cfg"]
  690. with TempDirectory(kind="record") as temp_dir:
  691. record_filename = os.path.join(temp_dir.path, 'install-record.txt')
  692. install_args = self.get_install_args(
  693. global_options, record_filename, root, prefix, pycompile,
  694. )
  695. msg = 'Running setup.py install for %s' % (self.name,)
  696. with open_spinner(msg) as spinner:
  697. with indent_log():
  698. with self.build_env:
  699. call_subprocess(
  700. install_args + install_options,
  701. cwd=self.setup_py_dir,
  702. show_stdout=False,
  703. spinner=spinner,
  704. )
  705. if not os.path.exists(record_filename):
  706. logger.debug('Record file %s not found', record_filename)
  707. return
  708. self.install_succeeded = True
  709. def prepend_root(path):
  710. if root is None or not os.path.isabs(path):
  711. return path
  712. else:
  713. return change_root(root, path)
  714. with open(record_filename) as f:
  715. for line in f:
  716. directory = os.path.dirname(line)
  717. if directory.endswith('.egg-info'):
  718. egg_info_dir = prepend_root(directory)
  719. break
  720. else:
  721. logger.warning(
  722. 'Could not find .egg-info directory in install record'
  723. ' for %s',
  724. self,
  725. )
  726. # FIXME: put the record somewhere
  727. # FIXME: should this be an error?
  728. return
  729. new_lines = []
  730. with open(record_filename) as f:
  731. for line in f:
  732. filename = line.strip()
  733. if os.path.isdir(filename):
  734. filename += os.path.sep
  735. new_lines.append(
  736. os.path.relpath(prepend_root(filename), egg_info_dir)
  737. )
  738. new_lines.sort()
  739. ensure_dir(egg_info_dir)
  740. inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt')
  741. with open(inst_files_path, 'w') as f:
  742. f.write('\n'.join(new_lines) + '\n')
  743. def ensure_has_source_dir(self, parent_dir):
  744. """Ensure that a source_dir is set.
  745. This will create a temporary build dir if the name of the requirement
  746. isn't known yet.
  747. :param parent_dir: The ideal pip parent_dir for the source_dir.
  748. Generally src_dir for editables and build_dir for sdists.
  749. :return: self.source_dir
  750. """
  751. if self.source_dir is None:
  752. self.source_dir = self.build_location(parent_dir)
  753. return self.source_dir
  754. def get_install_args(self, global_options, record_filename, root, prefix,
  755. pycompile):
  756. install_args = [sys.executable, "-u"]
  757. install_args.append('-c')
  758. install_args.append(SETUPTOOLS_SHIM % self.setup_py)
  759. install_args += list(global_options) + \
  760. ['install', '--record', record_filename]
  761. install_args += ['--single-version-externally-managed']
  762. if root is not None:
  763. install_args += ['--root', root]
  764. if prefix is not None:
  765. install_args += ['--prefix', prefix]
  766. if pycompile:
  767. install_args += ["--compile"]
  768. else:
  769. install_args += ["--no-compile"]
  770. if running_under_virtualenv():
  771. py_ver_str = 'python' + sysconfig.get_python_version()
  772. install_args += ['--install-headers',
  773. os.path.join(sys.prefix, 'include', 'site',
  774. py_ver_str, self.name)]
  775. return install_args
  776. def remove_temporary_source(self):
  777. """Remove the source files from this requirement, if they are marked
  778. for deletion"""
  779. if self.source_dir and os.path.exists(
  780. os.path.join(self.source_dir, PIP_DELETE_MARKER_FILENAME)):
  781. logger.debug('Removing source in %s', self.source_dir)
  782. rmtree(self.source_dir)
  783. self.source_dir = None
  784. self._temp_build_dir.cleanup()
  785. self.build_env.cleanup()
  786. def install_editable(self, install_options,
  787. global_options=(), prefix=None):
  788. logger.info('Running setup.py develop for %s', self.name)
  789. if self.isolated:
  790. global_options = list(global_options) + ["--no-user-cfg"]
  791. if prefix:
  792. prefix_param = ['--prefix={}'.format(prefix)]
  793. install_options = list(install_options) + prefix_param
  794. with indent_log():
  795. # FIXME: should we do --install-headers here too?
  796. with self.build_env:
  797. call_subprocess(
  798. [
  799. sys.executable,
  800. '-c',
  801. SETUPTOOLS_SHIM % self.setup_py
  802. ] +
  803. list(global_options) +
  804. ['develop', '--no-deps'] +
  805. list(install_options),
  806. cwd=self.setup_py_dir,
  807. show_stdout=False,
  808. )
  809. self.install_succeeded = True
  810. def check_if_exists(self, use_user_site):
  811. """Find an installed distribution that satisfies or conflicts
  812. with this requirement, and set self.satisfied_by or
  813. self.conflicts_with appropriately.
  814. """
  815. if self.req is None:
  816. return False
  817. try:
  818. # get_distribution() will resolve the entire list of requirements
  819. # anyway, and we've already determined that we need the requirement
  820. # in question, so strip the marker so that we don't try to
  821. # evaluate it.
  822. no_marker = Requirement(str(self.req))
  823. no_marker.marker = None
  824. self.satisfied_by = pkg_resources.get_distribution(str(no_marker))
  825. if self.editable and self.satisfied_by:
  826. self.conflicts_with = self.satisfied_by
  827. # when installing editables, nothing pre-existing should ever
  828. # satisfy
  829. self.satisfied_by = None
  830. return True
  831. except pkg_resources.DistributionNotFound:
  832. return False
  833. except pkg_resources.VersionConflict:
  834. existing_dist = pkg_resources.get_distribution(
  835. self.req.name
  836. )
  837. if use_user_site:
  838. if dist_in_usersite(existing_dist):
  839. self.conflicts_with = existing_dist
  840. elif (running_under_virtualenv() and
  841. dist_in_site_packages(existing_dist)):
  842. raise InstallationError(
  843. "Will not install to the user site because it will "
  844. "lack sys.path precedence to %s in %s" %
  845. (existing_dist.project_name, existing_dist.location)
  846. )
  847. else:
  848. self.conflicts_with = existing_dist
  849. return True
  850. @property
  851. def is_wheel(self):
  852. return self.link and self.link.is_wheel
  853. def move_wheel_files(self, wheeldir, root=None, home=None, prefix=None,
  854. warn_script_location=True, use_user_site=False,
  855. pycompile=True):
  856. move_wheel_files(
  857. self.name, self.req, wheeldir,
  858. user=use_user_site,
  859. home=home,
  860. root=root,
  861. prefix=prefix,
  862. pycompile=pycompile,
  863. isolated=self.isolated,
  864. warn_script_location=warn_script_location,
  865. )
  866. def get_dist(self):
  867. """Return a pkg_resources.Distribution built from self.egg_info_path"""
  868. egg_info = self.egg_info_path('').rstrip(os.path.sep)
  869. base_dir = os.path.dirname(egg_info)
  870. metadata = pkg_resources.PathMetadata(base_dir, egg_info)
  871. dist_name = os.path.splitext(os.path.basename(egg_info))[0]
  872. return pkg_resources.Distribution(
  873. os.path.dirname(egg_info),
  874. project_name=dist_name,
  875. metadata=metadata,
  876. )
  877. @property
  878. def has_hash_options(self):
  879. """Return whether any known-good hashes are specified as options.
  880. These activate --require-hashes mode; hashes specified as part of a
  881. URL do not.
  882. """
  883. return bool(self.options.get('hashes', {}))
  884. def hashes(self, trust_internet=True):
  885. """Return a hash-comparer that considers my option- and URL-based
  886. hashes to be known-good.
  887. Hashes in URLs--ones embedded in the requirements file, not ones
  888. downloaded from an index server--are almost peers with ones from
  889. flags. They satisfy --require-hashes (whether it was implicitly or
  890. explicitly activated) but do not activate it. md5 and sha224 are not
  891. allowed in flags, which should nudge people toward good algos. We
  892. always OR all hashes together, even ones from URLs.
  893. :param trust_internet: Whether to trust URL-based (#md5=...) hashes
  894. downloaded from the internet, as by populate_link()
  895. """
  896. good_hashes = self.options.get('hashes', {}).copy()
  897. link = self.link if trust_internet else self.original_link
  898. if link and link.hash:
  899. good_hashes.setdefault(link.hash_name, []).append(link.hash)
  900. return Hashes(good_hashes)
  901. def _strip_postfix(req):
  902. """
  903. Strip req postfix ( -dev, 0.2, etc )
  904. """
  905. # FIXME: use package_to_requirement?
  906. match = re.search(r'^(.*?)(?:-dev|-\d.*)$', req)
  907. if match:
  908. # Strip off -dev, -0.2, etc.
  909. warnings.warn(
  910. "#egg cleanup for editable urls will be dropped in the future",
  911. RemovedInPip11Warning,
  912. )
  913. req = match.group(1)
  914. return req
  915. def parse_editable(editable_req):
  916. """Parses an editable requirement into:
  917. - a requirement name
  918. - an URL
  919. - extras
  920. - editable options
  921. Accepted requirements:
  922. svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir
  923. .[some_extra]
  924. """
  925. from pip._internal.index import Link
  926. url = editable_req
  927. # If a file path is specified with extras, strip off the extras.
  928. url_no_extras, extras = _strip_extras(url)
  929. if os.path.isdir(url_no_extras):
  930. if not os.path.exists(os.path.join(url_no_extras, 'setup.py')):
  931. raise InstallationError(
  932. "Directory %r is not installable. File 'setup.py' not found." %
  933. url_no_extras
  934. )
  935. # Treating it as code that has already been checked out
  936. url_no_extras = path_to_url(url_no_extras)
  937. if url_no_extras.lower().startswith('file:'):
  938. package_name = Link(url_no_extras).egg_fragment
  939. if extras:
  940. return (
  941. package_name,
  942. url_no_extras,
  943. Requirement("placeholder" + extras.lower()).extras,
  944. )
  945. else:
  946. return package_name, url_no_extras, None
  947. for version_control in vcs:
  948. if url.lower().startswith('%s:' % version_control):
  949. url = '%s+%s' % (version_control, url)
  950. break
  951. if '+' not in url:
  952. raise InstallationError(
  953. '%s should either be a path to a local project or a VCS url '
  954. 'beginning with svn+, git+, hg+, or bzr+' %
  955. editable_req
  956. )
  957. vc_type = url.split('+', 1)[0].lower()
  958. if not vcs.get_backend(vc_type):
  959. error_message = 'For --editable=%s only ' % editable_req + \
  960. ', '.join([backend.name + '+URL' for backend in vcs.backends]) + \
  961. ' is currently supported'
  962. raise InstallationError(error_message)
  963. package_name = Link(url).egg_fragment
  964. if not package_name:
  965. raise InstallationError(
  966. "Could not detect requirement name for '%s', please specify one "
  967. "with #egg=your_package_name" % editable_req
  968. )
  969. return _strip_postfix(package_name), url, None
  970. def deduce_helpful_msg(req):
  971. """Returns helpful msg in case requirements file does not exist,
  972. or cannot be parsed.
  973. :params req: Requirements file path
  974. """
  975. msg = ""
  976. if os.path.exists(req):
  977. msg = " It does exist."
  978. # Try to parse and check if it is a requirements file.
  979. try:
  980. with open(req, 'r') as fp:
  981. # parse first line only
  982. next(parse_requirements(fp.read()))
  983. msg += " The argument you provided " + \
  984. "(%s) appears to be a" % (req) + \
  985. " requirements file. If that is the" + \
  986. " case, use the '-r' flag to install" + \
  987. " the packages specified within it."
  988. except RequirementParseError:
  989. logger.debug("Cannot parse '%s' as requirements \
  990. file" % (req), exc_info=1)
  991. else:
  992. msg += " File '%s' does not exist." % (req)
  993. return msg