req.py 82 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931
  1. from email.parser import FeedParser
  2. import os
  3. import imp
  4. import locale
  5. import re
  6. import sys
  7. import shutil
  8. import tempfile
  9. import textwrap
  10. import zipfile
  11. from distutils.util import change_root
  12. from pip.locations import (bin_py, running_under_virtualenv,PIP_DELETE_MARKER_FILENAME,
  13. write_delete_marker_file, bin_user)
  14. from pip.exceptions import (InstallationError, UninstallationError, UnsupportedWheel,
  15. BestVersionAlreadyInstalled, InvalidWheelFilename,
  16. DistributionNotFound, PreviousBuildDirError)
  17. from pip.vcs import vcs
  18. from pip.log import logger
  19. from pip.util import (display_path, rmtree, ask, ask_path_exists, backup_dir,
  20. is_installable_dir, is_local, dist_is_local,
  21. dist_in_usersite, dist_in_site_packages, renames,
  22. normalize_path, egg_link_path, make_path_relative,
  23. call_subprocess, is_prerelease, normalize_name)
  24. from pip.backwardcompat import (urlparse, urllib, uses_pycache,
  25. ConfigParser, string_types, HTTPError,
  26. get_python_version, b)
  27. from pip.index import Link
  28. from pip.locations import build_prefix
  29. from pip.download import (PipSession, get_file_content, is_url, url_to_path,
  30. path_to_url, is_archive_file,
  31. unpack_vcs_link, is_vcs_url, is_file_url,
  32. unpack_file_url, unpack_http_url)
  33. import pip.wheel
  34. from pip.wheel import move_wheel_files, Wheel, wheel_ext
  35. from pip._vendor import pkg_resources, six
  36. def read_text_file(filename):
  37. """Return the contents of *filename*.
  38. Try to decode the file contents with utf-8, the preffered system encoding
  39. (e.g., cp1252 on some Windows machines) and latin1, in that order. Decoding
  40. a byte string with latin1 will never raise an error. In the worst case, the
  41. returned string will contain some garbage characters.
  42. """
  43. with open(filename, 'rb') as fp:
  44. data = fp.read()
  45. encodings = ['utf-8', locale.getpreferredencoding(False), 'latin1']
  46. for enc in encodings:
  47. try:
  48. data = data.decode(enc)
  49. except UnicodeDecodeError:
  50. continue
  51. break
  52. assert type(data) != bytes # Latin1 should have worked.
  53. return data
  54. class InstallRequirement(object):
  55. def __init__(self, req, comes_from, source_dir=None, editable=False,
  56. url=None, as_egg=False, update=True, prereleases=None,
  57. editable_options=None, from_bundle=False, pycompile=True):
  58. self.extras = ()
  59. if isinstance(req, string_types):
  60. req = pkg_resources.Requirement.parse(req)
  61. self.extras = req.extras
  62. self.req = req
  63. self.comes_from = comes_from
  64. self.source_dir = source_dir
  65. self.editable = editable
  66. if editable_options is None:
  67. editable_options = {}
  68. self.editable_options = editable_options
  69. self.url = url
  70. self.as_egg = as_egg
  71. self._egg_info_path = None
  72. # This holds the pkg_resources.Distribution object if this requirement
  73. # is already available:
  74. self.satisfied_by = None
  75. # This hold the pkg_resources.Distribution object if this requirement
  76. # conflicts with another installed distribution:
  77. self.conflicts_with = None
  78. self._temp_build_dir = None
  79. self._is_bundle = None
  80. # True if the editable should be updated:
  81. self.update = update
  82. # Set to True after successful installation
  83. self.install_succeeded = None
  84. # UninstallPathSet of uninstalled distribution (for possible rollback)
  85. self.uninstalled = None
  86. self.use_user_site = False
  87. self.target_dir = None
  88. self.from_bundle = from_bundle
  89. self.pycompile = pycompile
  90. # True if pre-releases are acceptable
  91. if prereleases:
  92. self.prereleases = True
  93. elif self.req is not None:
  94. self.prereleases = any([is_prerelease(x[1]) and x[0] != "!=" for x in self.req.specs])
  95. else:
  96. self.prereleases = False
  97. @classmethod
  98. def from_editable(cls, editable_req, comes_from=None, default_vcs=None):
  99. name, url, extras_override = parse_editable(editable_req, default_vcs)
  100. if url.startswith('file:'):
  101. source_dir = url_to_path(url)
  102. else:
  103. source_dir = None
  104. res = cls(name, comes_from, source_dir=source_dir,
  105. editable=True,
  106. url=url,
  107. editable_options=extras_override,
  108. prereleases=True)
  109. if extras_override is not None:
  110. res.extras = extras_override
  111. return res
  112. @classmethod
  113. def from_line(cls, name, comes_from=None, prereleases=None):
  114. """Creates an InstallRequirement from a name, which might be a
  115. requirement, directory containing 'setup.py', filename, or URL.
  116. """
  117. url = None
  118. name = name.strip()
  119. req = None
  120. path = os.path.normpath(os.path.abspath(name))
  121. link = None
  122. if is_url(name):
  123. link = Link(name)
  124. elif os.path.isdir(path) and (os.path.sep in name or name.startswith('.')):
  125. if not is_installable_dir(path):
  126. raise InstallationError("Directory %r is not installable. File 'setup.py' not found." % name)
  127. link = Link(path_to_url(name))
  128. elif is_archive_file(path):
  129. if not os.path.isfile(path):
  130. logger.warn('Requirement %r looks like a filename, but the file does not exist', name)
  131. link = Link(path_to_url(name))
  132. # If the line has an egg= definition, but isn't editable, pull the requirement out.
  133. # Otherwise, assume the name is the req for the non URL/path/archive case.
  134. if link and req is None:
  135. url = link.url_without_fragment
  136. req = link.egg_fragment #when fragment is None, this will become an 'unnamed' requirement
  137. # Handle relative file URLs
  138. if link.scheme == 'file' and re.search(r'\.\./', url):
  139. url = path_to_url(os.path.normpath(os.path.abspath(link.path)))
  140. # fail early for invalid or unsupported wheels
  141. if link.ext == wheel_ext:
  142. wheel = Wheel(link.filename) # can raise InvalidWheelFilename
  143. if not wheel.supported():
  144. raise UnsupportedWheel("%s is not a supported wheel on this platform." % wheel.filename)
  145. else:
  146. req = name
  147. return cls(req, comes_from, url=url, prereleases=prereleases)
  148. def __str__(self):
  149. if self.req:
  150. s = str(self.req)
  151. if self.url:
  152. s += ' from %s' % self.url
  153. else:
  154. s = self.url
  155. if self.satisfied_by is not None:
  156. s += ' in %s' % display_path(self.satisfied_by.location)
  157. if self.comes_from:
  158. if isinstance(self.comes_from, string_types):
  159. comes_from = self.comes_from
  160. else:
  161. comes_from = self.comes_from.from_path()
  162. if comes_from:
  163. s += ' (from %s)' % comes_from
  164. return s
  165. def from_path(self):
  166. if self.req is None:
  167. return None
  168. s = str(self.req)
  169. if self.comes_from:
  170. if isinstance(self.comes_from, string_types):
  171. comes_from = self.comes_from
  172. else:
  173. comes_from = self.comes_from.from_path()
  174. if comes_from:
  175. s += '->' + comes_from
  176. return s
  177. def build_location(self, build_dir, unpack=True):
  178. if self._temp_build_dir is not None:
  179. return self._temp_build_dir
  180. if self.req is None:
  181. self._temp_build_dir = tempfile.mkdtemp('-build', 'pip-')
  182. self._ideal_build_dir = build_dir
  183. return self._temp_build_dir
  184. if self.editable:
  185. name = self.name.lower()
  186. else:
  187. name = self.name
  188. # FIXME: Is there a better place to create the build_dir? (hg and bzr need this)
  189. if not os.path.exists(build_dir):
  190. _make_build_dir(build_dir)
  191. return os.path.join(build_dir, name)
  192. def correct_build_location(self):
  193. """If the build location was a temporary directory, this will move it
  194. to a new more permanent location"""
  195. if self.source_dir is not None:
  196. return
  197. assert self.req is not None
  198. assert self._temp_build_dir
  199. old_location = self._temp_build_dir
  200. new_build_dir = self._ideal_build_dir
  201. del self._ideal_build_dir
  202. if self.editable:
  203. name = self.name.lower()
  204. else:
  205. name = self.name
  206. new_location = os.path.join(new_build_dir, name)
  207. if not os.path.exists(new_build_dir):
  208. logger.debug('Creating directory %s' % new_build_dir)
  209. _make_build_dir(new_build_dir)
  210. if os.path.exists(new_location):
  211. raise InstallationError(
  212. 'A package already exists in %s; please remove it to continue'
  213. % display_path(new_location))
  214. logger.debug('Moving package %s from %s to new location %s'
  215. % (self, display_path(old_location), display_path(new_location)))
  216. shutil.move(old_location, new_location)
  217. self._temp_build_dir = new_location
  218. self.source_dir = new_location
  219. self._egg_info_path = None
  220. @property
  221. def name(self):
  222. if self.req is None:
  223. return None
  224. return self.req.project_name
  225. @property
  226. def url_name(self):
  227. if self.req is None:
  228. return None
  229. return urllib.quote(self.req.unsafe_name)
  230. @property
  231. def setup_py(self):
  232. try:
  233. import setuptools
  234. except ImportError:
  235. # Setuptools is not available
  236. raise InstallationError(
  237. "setuptools must be installed to install from a source "
  238. "distribution"
  239. )
  240. setup_file = 'setup.py'
  241. if self.editable_options and 'subdirectory' in self.editable_options:
  242. setup_py = os.path.join(self.source_dir,
  243. self.editable_options['subdirectory'],
  244. setup_file)
  245. else:
  246. setup_py = os.path.join(self.source_dir, setup_file)
  247. # Python2 __file__ should not be unicode
  248. if six.PY2 and isinstance(setup_py, six.text_type):
  249. setup_py = setup_py.encode(sys.getfilesystemencoding())
  250. return setup_py
  251. def run_egg_info(self, force_root_egg_info=False):
  252. assert self.source_dir
  253. if self.name:
  254. logger.notify('Running setup.py (path:%s) egg_info for package %s' % (self.setup_py, self.name))
  255. else:
  256. logger.notify('Running setup.py (path:%s) egg_info for package from %s' % (self.setup_py, self.url))
  257. logger.indent += 2
  258. try:
  259. # if it's distribute>=0.7, it won't contain an importable
  260. # setuptools, and having an egg-info dir blocks the ability of
  261. # setup.py to find setuptools plugins, so delete the egg-info dir if
  262. # no setuptools. it will get recreated by the run of egg_info
  263. # NOTE: this self.name check only works when installing from a specifier
  264. # (not archive path/urls)
  265. # TODO: take this out later
  266. if self.name == 'distribute' and not os.path.isdir(os.path.join(self.source_dir, 'setuptools')):
  267. rmtree(os.path.join(self.source_dir, 'distribute.egg-info'))
  268. script = self._run_setup_py
  269. script = script.replace('__SETUP_PY__', repr(self.setup_py))
  270. script = script.replace('__PKG_NAME__', repr(self.name))
  271. egg_info_cmd = [sys.executable, '-c', script, 'egg_info']
  272. # We can't put the .egg-info files at the root, because then the source code will be mistaken
  273. # for an installed egg, causing problems
  274. if self.editable or force_root_egg_info:
  275. egg_base_option = []
  276. else:
  277. egg_info_dir = os.path.join(self.source_dir, 'pip-egg-info')
  278. if not os.path.exists(egg_info_dir):
  279. os.makedirs(egg_info_dir)
  280. egg_base_option = ['--egg-base', 'pip-egg-info']
  281. call_subprocess(
  282. egg_info_cmd + egg_base_option,
  283. cwd=self.source_dir, filter_stdout=self._filter_install, show_stdout=False,
  284. command_level=logger.VERBOSE_DEBUG,
  285. command_desc='python setup.py egg_info')
  286. finally:
  287. logger.indent -= 2
  288. if not self.req:
  289. self.req = pkg_resources.Requirement.parse(
  290. "%(Name)s==%(Version)s" % self.pkg_info())
  291. self.correct_build_location()
  292. ## FIXME: This is a lame hack, entirely for PasteScript which has
  293. ## a self-provided entry point that causes this awkwardness
  294. _run_setup_py = """
  295. __file__ = __SETUP_PY__
  296. from setuptools.command import egg_info
  297. import pkg_resources
  298. import os
  299. import tokenize
  300. def replacement_run(self):
  301. self.mkpath(self.egg_info)
  302. installer = self.distribution.fetch_build_egg
  303. for ep in pkg_resources.iter_entry_points('egg_info.writers'):
  304. # require=False is the change we're making:
  305. writer = ep.load(require=False)
  306. if writer:
  307. writer(self, ep.name, os.path.join(self.egg_info,ep.name))
  308. self.find_sources()
  309. egg_info.egg_info.run = replacement_run
  310. exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))
  311. """
  312. def egg_info_data(self, filename):
  313. if self.satisfied_by is not None:
  314. if not self.satisfied_by.has_metadata(filename):
  315. return None
  316. return self.satisfied_by.get_metadata(filename)
  317. assert self.source_dir
  318. filename = self.egg_info_path(filename)
  319. if not os.path.exists(filename):
  320. return None
  321. data = read_text_file(filename)
  322. return data
  323. def egg_info_path(self, filename):
  324. if self._egg_info_path is None:
  325. if self.editable:
  326. base = self.source_dir
  327. else:
  328. base = os.path.join(self.source_dir, 'pip-egg-info')
  329. filenames = os.listdir(base)
  330. if self.editable:
  331. filenames = []
  332. for root, dirs, files in os.walk(base):
  333. for dir in vcs.dirnames:
  334. if dir in dirs:
  335. dirs.remove(dir)
  336. # Iterate over a copy of ``dirs``, since mutating
  337. # a list while iterating over it can cause trouble.
  338. # (See https://github.com/pypa/pip/pull/462.)
  339. for dir in list(dirs):
  340. # Don't search in anything that looks like a virtualenv environment
  341. if (os.path.exists(os.path.join(root, dir, 'bin', 'python'))
  342. or os.path.exists(os.path.join(root, dir, 'Scripts', 'Python.exe'))):
  343. dirs.remove(dir)
  344. # Also don't search through tests
  345. if dir == 'test' or dir == 'tests':
  346. dirs.remove(dir)
  347. filenames.extend([os.path.join(root, dir)
  348. for dir in dirs])
  349. filenames = [f for f in filenames if f.endswith('.egg-info')]
  350. if not filenames:
  351. raise InstallationError('No files/directories in %s (from %s)' % (base, filename))
  352. assert filenames, "No files/directories in %s (from %s)" % (base, filename)
  353. # if we have more than one match, we pick the toplevel one. This can
  354. # easily be the case if there is a dist folder which contains an
  355. # extracted tarball for testing purposes.
  356. if len(filenames) > 1:
  357. filenames.sort(key=lambda x: x.count(os.path.sep) +
  358. (os.path.altsep and
  359. x.count(os.path.altsep) or 0))
  360. self._egg_info_path = os.path.join(base, filenames[0])
  361. return os.path.join(self._egg_info_path, filename)
  362. def egg_info_lines(self, filename):
  363. data = self.egg_info_data(filename)
  364. if not data:
  365. return []
  366. result = []
  367. for line in data.splitlines():
  368. line = line.strip()
  369. if not line or line.startswith('#'):
  370. continue
  371. result.append(line)
  372. return result
  373. def pkg_info(self):
  374. p = FeedParser()
  375. data = self.egg_info_data('PKG-INFO')
  376. if not data:
  377. logger.warn('No PKG-INFO file found in %s' % display_path(self.egg_info_path('PKG-INFO')))
  378. p.feed(data or '')
  379. return p.close()
  380. @property
  381. def dependency_links(self):
  382. return self.egg_info_lines('dependency_links.txt')
  383. _requirements_section_re = re.compile(r'\[(.*?)\]')
  384. def requirements(self, extras=()):
  385. in_extra = None
  386. for line in self.egg_info_lines('requires.txt'):
  387. match = self._requirements_section_re.match(line.lower())
  388. if match:
  389. in_extra = match.group(1)
  390. continue
  391. if in_extra and in_extra not in extras:
  392. logger.debug('skipping extra %s' % in_extra)
  393. # Skip requirement for an extra we aren't requiring
  394. continue
  395. yield line
  396. @property
  397. def absolute_versions(self):
  398. for qualifier, version in self.req.specs:
  399. if qualifier == '==':
  400. yield version
  401. @property
  402. def installed_version(self):
  403. return self.pkg_info()['version']
  404. def assert_source_matches_version(self):
  405. assert self.source_dir
  406. version = self.installed_version
  407. if version not in self.req:
  408. logger.warn('Requested %s, but installing version %s' % (self, self.installed_version))
  409. else:
  410. logger.debug('Source in %s has version %s, which satisfies requirement %s'
  411. % (display_path(self.source_dir), version, self))
  412. def update_editable(self, obtain=True):
  413. if not self.url:
  414. logger.info("Cannot update repository at %s; repository location is unknown" % self.source_dir)
  415. return
  416. assert self.editable
  417. assert self.source_dir
  418. if self.url.startswith('file:'):
  419. # Static paths don't get updated
  420. return
  421. assert '+' in self.url, "bad url: %r" % self.url
  422. if not self.update:
  423. return
  424. vc_type, url = self.url.split('+', 1)
  425. backend = vcs.get_backend(vc_type)
  426. if backend:
  427. vcs_backend = backend(self.url)
  428. if obtain:
  429. vcs_backend.obtain(self.source_dir)
  430. else:
  431. vcs_backend.export(self.source_dir)
  432. else:
  433. assert 0, (
  434. 'Unexpected version control type (in %s): %s'
  435. % (self.url, vc_type))
  436. def uninstall(self, auto_confirm=False):
  437. """
  438. Uninstall the distribution currently satisfying this requirement.
  439. Prompts before removing or modifying files unless
  440. ``auto_confirm`` is True.
  441. Refuses to delete or modify files outside of ``sys.prefix`` -
  442. thus uninstallation within a virtual environment can only
  443. modify that virtual environment, even if the virtualenv is
  444. linked to global site-packages.
  445. """
  446. if not self.check_if_exists():
  447. raise UninstallationError("Cannot uninstall requirement %s, not installed" % (self.name,))
  448. dist = self.satisfied_by or self.conflicts_with
  449. paths_to_remove = UninstallPathSet(dist)
  450. pip_egg_info_path = os.path.join(dist.location,
  451. dist.egg_name()) + '.egg-info'
  452. dist_info_path = os.path.join(dist.location,
  453. '-'.join(dist.egg_name().split('-')[:2])
  454. ) + '.dist-info'
  455. # workaround for http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=618367
  456. debian_egg_info_path = pip_egg_info_path.replace(
  457. '-py%s' % pkg_resources.PY_MAJOR, '')
  458. easy_install_egg = dist.egg_name() + '.egg'
  459. develop_egg_link = egg_link_path(dist)
  460. pip_egg_info_exists = os.path.exists(pip_egg_info_path)
  461. debian_egg_info_exists = os.path.exists(debian_egg_info_path)
  462. dist_info_exists = os.path.exists(dist_info_path)
  463. if pip_egg_info_exists or debian_egg_info_exists:
  464. # package installed by pip
  465. if pip_egg_info_exists:
  466. egg_info_path = pip_egg_info_path
  467. else:
  468. egg_info_path = debian_egg_info_path
  469. paths_to_remove.add(egg_info_path)
  470. if dist.has_metadata('installed-files.txt'):
  471. for installed_file in dist.get_metadata('installed-files.txt').splitlines():
  472. path = os.path.normpath(os.path.join(egg_info_path, installed_file))
  473. paths_to_remove.add(path)
  474. #FIXME: need a test for this elif block
  475. #occurs with --single-version-externally-managed/--record outside of pip
  476. elif dist.has_metadata('top_level.txt'):
  477. if dist.has_metadata('namespace_packages.txt'):
  478. namespaces = dist.get_metadata('namespace_packages.txt')
  479. else:
  480. namespaces = []
  481. for top_level_pkg in [p for p
  482. in dist.get_metadata('top_level.txt').splitlines()
  483. if p and p not in namespaces]:
  484. path = os.path.join(dist.location, top_level_pkg)
  485. paths_to_remove.add(path)
  486. paths_to_remove.add(path + '.py')
  487. paths_to_remove.add(path + '.pyc')
  488. elif dist.location.endswith(easy_install_egg):
  489. # package installed by easy_install
  490. paths_to_remove.add(dist.location)
  491. easy_install_pth = os.path.join(os.path.dirname(dist.location),
  492. 'easy-install.pth')
  493. paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg)
  494. elif develop_egg_link:
  495. # develop egg
  496. fh = open(develop_egg_link, 'r')
  497. link_pointer = os.path.normcase(fh.readline().strip())
  498. fh.close()
  499. assert (link_pointer == dist.location), 'Egg-link %s does not match installed location of %s (at %s)' % (link_pointer, self.name, dist.location)
  500. paths_to_remove.add(develop_egg_link)
  501. easy_install_pth = os.path.join(os.path.dirname(develop_egg_link),
  502. 'easy-install.pth')
  503. paths_to_remove.add_pth(easy_install_pth, dist.location)
  504. elif dist_info_exists:
  505. for path in pip.wheel.uninstallation_paths(dist):
  506. paths_to_remove.add(path)
  507. # find distutils scripts= scripts
  508. if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'):
  509. for script in dist.metadata_listdir('scripts'):
  510. if dist_in_usersite(dist):
  511. bin_dir = bin_user
  512. else:
  513. bin_dir = bin_py
  514. paths_to_remove.add(os.path.join(bin_dir, script))
  515. if sys.platform == 'win32':
  516. paths_to_remove.add(os.path.join(bin_dir, script) + '.bat')
  517. # find console_scripts
  518. if dist.has_metadata('entry_points.txt'):
  519. config = ConfigParser.SafeConfigParser()
  520. config.readfp(FakeFile(dist.get_metadata_lines('entry_points.txt')))
  521. if config.has_section('console_scripts'):
  522. for name, value in config.items('console_scripts'):
  523. if dist_in_usersite(dist):
  524. bin_dir = bin_user
  525. else:
  526. bin_dir = bin_py
  527. paths_to_remove.add(os.path.join(bin_dir, name))
  528. if sys.platform == 'win32':
  529. paths_to_remove.add(os.path.join(bin_dir, name) + '.exe')
  530. paths_to_remove.add(os.path.join(bin_dir, name) + '.exe.manifest')
  531. paths_to_remove.add(os.path.join(bin_dir, name) + '-script.py')
  532. paths_to_remove.remove(auto_confirm)
  533. self.uninstalled = paths_to_remove
  534. def rollback_uninstall(self):
  535. if self.uninstalled:
  536. self.uninstalled.rollback()
  537. else:
  538. logger.error("Can't rollback %s, nothing uninstalled."
  539. % (self.project_name,))
  540. def commit_uninstall(self):
  541. if self.uninstalled:
  542. self.uninstalled.commit()
  543. else:
  544. logger.error("Can't commit %s, nothing uninstalled."
  545. % (self.project_name,))
  546. def archive(self, build_dir):
  547. assert self.source_dir
  548. create_archive = True
  549. archive_name = '%s-%s.zip' % (self.name, self.installed_version)
  550. archive_path = os.path.join(build_dir, archive_name)
  551. if os.path.exists(archive_path):
  552. response = ask_path_exists(
  553. 'The file %s exists. (i)gnore, (w)ipe, (b)ackup ' %
  554. display_path(archive_path), ('i', 'w', 'b'))
  555. if response == 'i':
  556. create_archive = False
  557. elif response == 'w':
  558. logger.warn('Deleting %s' % display_path(archive_path))
  559. os.remove(archive_path)
  560. elif response == 'b':
  561. dest_file = backup_dir(archive_path)
  562. logger.warn('Backing up %s to %s'
  563. % (display_path(archive_path), display_path(dest_file)))
  564. shutil.move(archive_path, dest_file)
  565. if create_archive:
  566. zip = zipfile.ZipFile(archive_path, 'w', zipfile.ZIP_DEFLATED)
  567. dir = os.path.normcase(os.path.abspath(self.source_dir))
  568. for dirpath, dirnames, filenames in os.walk(dir):
  569. if 'pip-egg-info' in dirnames:
  570. dirnames.remove('pip-egg-info')
  571. for dirname in dirnames:
  572. dirname = os.path.join(dirpath, dirname)
  573. name = self._clean_zip_name(dirname, dir)
  574. zipdir = zipfile.ZipInfo(self.name + '/' + name + '/')
  575. zipdir.external_attr = 0x1ED << 16 # 0o755
  576. zip.writestr(zipdir, '')
  577. for filename in filenames:
  578. if filename == PIP_DELETE_MARKER_FILENAME:
  579. continue
  580. filename = os.path.join(dirpath, filename)
  581. name = self._clean_zip_name(filename, dir)
  582. zip.write(filename, self.name + '/' + name)
  583. zip.close()
  584. logger.indent -= 2
  585. logger.notify('Saved %s' % display_path(archive_path))
  586. def _clean_zip_name(self, name, prefix):
  587. assert name.startswith(prefix+os.path.sep), (
  588. "name %r doesn't start with prefix %r" % (name, prefix))
  589. name = name[len(prefix)+1:]
  590. name = name.replace(os.path.sep, '/')
  591. return name
  592. def install(self, install_options, global_options=(), root=None):
  593. if self.editable:
  594. self.install_editable(install_options, global_options)
  595. return
  596. if self.is_wheel:
  597. version = pip.wheel.wheel_version(self.source_dir)
  598. pip.wheel.check_compatibility(version, self.name)
  599. self.move_wheel_files(self.source_dir, root=root)
  600. self.install_succeeded = True
  601. return
  602. temp_location = tempfile.mkdtemp('-record', 'pip-')
  603. record_filename = os.path.join(temp_location, 'install-record.txt')
  604. try:
  605. install_args = [sys.executable]
  606. install_args.append('-c')
  607. install_args.append(
  608. "import setuptools, tokenize;__file__=%r;"\
  609. "exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))" % self.setup_py)
  610. install_args += list(global_options) + ['install','--record', record_filename]
  611. if not self.as_egg:
  612. install_args += ['--single-version-externally-managed']
  613. if root is not None:
  614. install_args += ['--root', root]
  615. if self.pycompile:
  616. install_args += ["--compile"]
  617. else:
  618. install_args += ["--no-compile"]
  619. if running_under_virtualenv():
  620. ## FIXME: I'm not sure if this is a reasonable location; probably not
  621. ## but we can't put it in the default location, as that is a virtualenv symlink that isn't writable
  622. install_args += ['--install-headers',
  623. os.path.join(sys.prefix, 'include', 'site',
  624. 'python' + get_python_version())]
  625. logger.notify('Running setup.py install for %s' % self.name)
  626. logger.indent += 2
  627. try:
  628. call_subprocess(install_args + install_options,
  629. cwd=self.source_dir, filter_stdout=self._filter_install, show_stdout=False)
  630. finally:
  631. logger.indent -= 2
  632. if not os.path.exists(record_filename):
  633. logger.notify('Record file %s not found' % record_filename)
  634. return
  635. self.install_succeeded = True
  636. if self.as_egg:
  637. # there's no --always-unzip option we can pass to install command
  638. # so we unable to save the installed-files.txt
  639. return
  640. def prepend_root(path):
  641. if root is None or not os.path.isabs(path):
  642. return path
  643. else:
  644. return change_root(root, path)
  645. f = open(record_filename)
  646. for line in f:
  647. line = line.strip()
  648. if line.endswith('.egg-info'):
  649. egg_info_dir = prepend_root(line)
  650. break
  651. else:
  652. logger.warn('Could not find .egg-info directory in install record for %s' % self)
  653. ## FIXME: put the record somewhere
  654. ## FIXME: should this be an error?
  655. return
  656. f.close()
  657. new_lines = []
  658. f = open(record_filename)
  659. for line in f:
  660. filename = line.strip()
  661. if os.path.isdir(filename):
  662. filename += os.path.sep
  663. new_lines.append(make_path_relative(prepend_root(filename), egg_info_dir))
  664. f.close()
  665. f = open(os.path.join(egg_info_dir, 'installed-files.txt'), 'w')
  666. f.write('\n'.join(new_lines)+'\n')
  667. f.close()
  668. finally:
  669. if os.path.exists(record_filename):
  670. os.remove(record_filename)
  671. os.rmdir(temp_location)
  672. def remove_temporary_source(self):
  673. """Remove the source files from this requirement, if they are marked
  674. for deletion"""
  675. if self.is_bundle or os.path.exists(self.delete_marker_filename):
  676. logger.info('Removing source in %s' % self.source_dir)
  677. if self.source_dir:
  678. rmtree(self.source_dir)
  679. self.source_dir = None
  680. if self._temp_build_dir and os.path.exists(self._temp_build_dir):
  681. rmtree(self._temp_build_dir)
  682. self._temp_build_dir = None
  683. def install_editable(self, install_options, global_options=()):
  684. logger.notify('Running setup.py develop for %s' % self.name)
  685. logger.indent += 2
  686. try:
  687. ## FIXME: should we do --install-headers here too?
  688. call_subprocess(
  689. [sys.executable, '-c',
  690. "import setuptools, tokenize; __file__=%r; exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))" % self.setup_py]
  691. + list(global_options) + ['develop', '--no-deps'] + list(install_options),
  692. cwd=self.source_dir, filter_stdout=self._filter_install,
  693. show_stdout=False)
  694. finally:
  695. logger.indent -= 2
  696. self.install_succeeded = True
  697. def _filter_install(self, line):
  698. level = logger.NOTIFY
  699. for regex in [r'^running .*', r'^writing .*', '^creating .*', '^[Cc]opying .*',
  700. r'^reading .*', r"^removing .*\.egg-info' \(and everything under it\)$",
  701. r'^byte-compiling ',
  702. # Not sure what this warning is, but it seems harmless:
  703. r"^warning: manifest_maker: standard file '-c' not found$"]:
  704. if re.search(regex, line.strip()):
  705. level = logger.INFO
  706. break
  707. return (level, line)
  708. def check_if_exists(self):
  709. """Find an installed distribution that satisfies or conflicts
  710. with this requirement, and set self.satisfied_by or
  711. self.conflicts_with appropriately."""
  712. if self.req is None:
  713. return False
  714. try:
  715. # DISTRIBUTE TO SETUPTOOLS UPGRADE HACK (1 of 3 parts)
  716. # if we've already set distribute as a conflict to setuptools
  717. # then this check has already run before. we don't want it to
  718. # run again, and return False, since it would block the uninstall
  719. # TODO: remove this later
  720. if (self.req.project_name == 'setuptools'
  721. and self.conflicts_with
  722. and self.conflicts_with.project_name == 'distribute'):
  723. return True
  724. else:
  725. self.satisfied_by = pkg_resources.get_distribution(self.req)
  726. except pkg_resources.DistributionNotFound:
  727. return False
  728. except pkg_resources.VersionConflict:
  729. existing_dist = pkg_resources.get_distribution(self.req.project_name)
  730. if self.use_user_site:
  731. if dist_in_usersite(existing_dist):
  732. self.conflicts_with = existing_dist
  733. elif running_under_virtualenv() and dist_in_site_packages(existing_dist):
  734. raise InstallationError("Will not install to the user site because it will lack sys.path precedence to %s in %s"
  735. %(existing_dist.project_name, existing_dist.location))
  736. else:
  737. self.conflicts_with = existing_dist
  738. return True
  739. @property
  740. def is_wheel(self):
  741. return self.url and '.whl' in self.url
  742. @property
  743. def is_bundle(self):
  744. if self._is_bundle is not None:
  745. return self._is_bundle
  746. base = self._temp_build_dir
  747. if not base:
  748. ## FIXME: this doesn't seem right:
  749. return False
  750. self._is_bundle = (os.path.exists(os.path.join(base, 'pip-manifest.txt'))
  751. or os.path.exists(os.path.join(base, 'pyinstall-manifest.txt')))
  752. return self._is_bundle
  753. def bundle_requirements(self):
  754. for dest_dir in self._bundle_editable_dirs:
  755. package = os.path.basename(dest_dir)
  756. ## FIXME: svnism:
  757. for vcs_backend in vcs.backends:
  758. url = rev = None
  759. vcs_bundle_file = os.path.join(
  760. dest_dir, vcs_backend.bundle_file)
  761. if os.path.exists(vcs_bundle_file):
  762. vc_type = vcs_backend.name
  763. fp = open(vcs_bundle_file)
  764. content = fp.read()
  765. fp.close()
  766. url, rev = vcs_backend().parse_vcs_bundle_file(content)
  767. break
  768. if url:
  769. url = '%s+%s@%s' % (vc_type, url, rev)
  770. else:
  771. url = None
  772. yield InstallRequirement(
  773. package, self, editable=True, url=url,
  774. update=False, source_dir=dest_dir, from_bundle=True)
  775. for dest_dir in self._bundle_build_dirs:
  776. package = os.path.basename(dest_dir)
  777. yield InstallRequirement(package, self,source_dir=dest_dir, from_bundle=True)
  778. def move_bundle_files(self, dest_build_dir, dest_src_dir):
  779. base = self._temp_build_dir
  780. assert base
  781. src_dir = os.path.join(base, 'src')
  782. build_dir = os.path.join(base, 'build')
  783. bundle_build_dirs = []
  784. bundle_editable_dirs = []
  785. for source_dir, dest_dir, dir_collection in [
  786. (src_dir, dest_src_dir, bundle_editable_dirs),
  787. (build_dir, dest_build_dir, bundle_build_dirs)]:
  788. if os.path.exists(source_dir):
  789. for dirname in os.listdir(source_dir):
  790. dest = os.path.join(dest_dir, dirname)
  791. dir_collection.append(dest)
  792. if os.path.exists(dest):
  793. logger.warn('The directory %s (containing package %s) already exists; cannot move source from bundle %s'
  794. % (dest, dirname, self))
  795. continue
  796. if not os.path.exists(dest_dir):
  797. logger.info('Creating directory %s' % dest_dir)
  798. os.makedirs(dest_dir)
  799. shutil.move(os.path.join(source_dir, dirname), dest)
  800. if not os.listdir(source_dir):
  801. os.rmdir(source_dir)
  802. self._temp_build_dir = None
  803. self._bundle_build_dirs = bundle_build_dirs
  804. self._bundle_editable_dirs = bundle_editable_dirs
  805. def move_wheel_files(self, wheeldir, root=None):
  806. move_wheel_files(
  807. self.name, self.req, wheeldir,
  808. user=self.use_user_site,
  809. home=self.target_dir,
  810. root=root,
  811. pycompile=self.pycompile,
  812. )
  813. @property
  814. def delete_marker_filename(self):
  815. assert self.source_dir
  816. return os.path.join(self.source_dir, PIP_DELETE_MARKER_FILENAME)
  817. class Requirements(object):
  818. def __init__(self):
  819. self._keys = []
  820. self._dict = {}
  821. def keys(self):
  822. return self._keys
  823. def values(self):
  824. return [self._dict[key] for key in self._keys]
  825. def __contains__(self, item):
  826. return item in self._keys
  827. def __setitem__(self, key, value):
  828. if key not in self._keys:
  829. self._keys.append(key)
  830. self._dict[key] = value
  831. def __getitem__(self, key):
  832. return self._dict[key]
  833. def __repr__(self):
  834. values = ['%s: %s' % (repr(k), repr(self[k])) for k in self.keys()]
  835. return 'Requirements({%s})' % ', '.join(values)
  836. class RequirementSet(object):
  837. def __init__(self, build_dir, src_dir, download_dir, download_cache=None,
  838. upgrade=False, ignore_installed=False, as_egg=False,
  839. target_dir=None, ignore_dependencies=False,
  840. force_reinstall=False, use_user_site=False, session=None,
  841. pycompile=True, wheel_download_dir=None):
  842. self.build_dir = build_dir
  843. self.src_dir = src_dir
  844. self.download_dir = download_dir
  845. if download_cache:
  846. download_cache = os.path.expanduser(download_cache)
  847. self.download_cache = download_cache
  848. self.upgrade = upgrade
  849. self.ignore_installed = ignore_installed
  850. self.force_reinstall = force_reinstall
  851. self.requirements = Requirements()
  852. # Mapping of alias: real_name
  853. self.requirement_aliases = {}
  854. self.unnamed_requirements = []
  855. self.ignore_dependencies = ignore_dependencies
  856. self.successfully_downloaded = []
  857. self.successfully_installed = []
  858. self.reqs_to_cleanup = []
  859. self.as_egg = as_egg
  860. self.use_user_site = use_user_site
  861. self.target_dir = target_dir #set from --target option
  862. self.session = session or PipSession()
  863. self.pycompile = pycompile
  864. self.wheel_download_dir = wheel_download_dir
  865. def __str__(self):
  866. reqs = [req for req in self.requirements.values()
  867. if not req.comes_from]
  868. reqs.sort(key=lambda req: req.name.lower())
  869. return ' '.join([str(req.req) for req in reqs])
  870. def add_requirement(self, install_req):
  871. name = install_req.name
  872. install_req.as_egg = self.as_egg
  873. install_req.use_user_site = self.use_user_site
  874. install_req.target_dir = self.target_dir
  875. install_req.pycompile = self.pycompile
  876. if not name:
  877. #url or path requirement w/o an egg fragment
  878. self.unnamed_requirements.append(install_req)
  879. else:
  880. if self.has_requirement(name):
  881. raise InstallationError(
  882. 'Double requirement given: %s (already in %s, name=%r)'
  883. % (install_req, self.get_requirement(name), name))
  884. self.requirements[name] = install_req
  885. ## FIXME: what about other normalizations? E.g., _ vs. -?
  886. if name.lower() != name:
  887. self.requirement_aliases[name.lower()] = name
  888. def has_requirement(self, project_name):
  889. for name in project_name, project_name.lower():
  890. if name in self.requirements or name in self.requirement_aliases:
  891. return True
  892. return False
  893. @property
  894. def has_requirements(self):
  895. return list(self.requirements.values()) or self.unnamed_requirements
  896. @property
  897. def has_editables(self):
  898. if any(req.editable for req in self.requirements.values()):
  899. return True
  900. if any(req.editable for req in self.unnamed_requirements):
  901. return True
  902. return False
  903. @property
  904. def is_download(self):
  905. if self.download_dir:
  906. self.download_dir = os.path.expanduser(self.download_dir)
  907. if os.path.exists(self.download_dir):
  908. return True
  909. else:
  910. logger.fatal('Could not find download directory')
  911. raise InstallationError(
  912. "Could not find or access download directory '%s'"
  913. % display_path(self.download_dir))
  914. return False
  915. def get_requirement(self, project_name):
  916. for name in project_name, project_name.lower():
  917. if name in self.requirements:
  918. return self.requirements[name]
  919. if name in self.requirement_aliases:
  920. return self.requirements[self.requirement_aliases[name]]
  921. raise KeyError("No project with the name %r" % project_name)
  922. def uninstall(self, auto_confirm=False):
  923. for req in self.requirements.values():
  924. req.uninstall(auto_confirm=auto_confirm)
  925. req.commit_uninstall()
  926. def locate_files(self):
  927. ## FIXME: duplicates code from prepare_files; relevant code should
  928. ## probably be factored out into a separate method
  929. unnamed = list(self.unnamed_requirements)
  930. reqs = list(self.requirements.values())
  931. while reqs or unnamed:
  932. if unnamed:
  933. req_to_install = unnamed.pop(0)
  934. else:
  935. req_to_install = reqs.pop(0)
  936. install_needed = True
  937. if not self.ignore_installed and not req_to_install.editable:
  938. req_to_install.check_if_exists()
  939. if req_to_install.satisfied_by:
  940. if self.upgrade:
  941. #don't uninstall conflict if user install and and conflict is not user install
  942. if not (self.use_user_site and not dist_in_usersite(req_to_install.satisfied_by)):
  943. req_to_install.conflicts_with = req_to_install.satisfied_by
  944. req_to_install.satisfied_by = None
  945. else:
  946. install_needed = False
  947. if req_to_install.satisfied_by:
  948. logger.notify('Requirement already satisfied '
  949. '(use --upgrade to upgrade): %s'
  950. % req_to_install)
  951. if req_to_install.editable:
  952. if req_to_install.source_dir is None:
  953. req_to_install.source_dir = req_to_install.build_location(self.src_dir)
  954. elif install_needed:
  955. req_to_install.source_dir = req_to_install.build_location(self.build_dir, not self.is_download)
  956. if req_to_install.source_dir is not None and not os.path.isdir(req_to_install.source_dir):
  957. raise InstallationError('Could not install requirement %s '
  958. 'because source folder %s does not exist '
  959. '(perhaps --no-download was used without first running '
  960. 'an equivalent install with --no-install?)'
  961. % (req_to_install, req_to_install.source_dir))
  962. def prepare_files(self, finder, force_root_egg_info=False, bundle=False):
  963. """Prepare process. Create temp directories, download and/or unpack files."""
  964. unnamed = list(self.unnamed_requirements)
  965. reqs = list(self.requirements.values())
  966. while reqs or unnamed:
  967. if unnamed:
  968. req_to_install = unnamed.pop(0)
  969. else:
  970. req_to_install = reqs.pop(0)
  971. install = True
  972. best_installed = False
  973. not_found = None
  974. if not self.ignore_installed and not req_to_install.editable:
  975. req_to_install.check_if_exists()
  976. if req_to_install.satisfied_by:
  977. if self.upgrade:
  978. if not self.force_reinstall and not req_to_install.url:
  979. try:
  980. url = finder.find_requirement(
  981. req_to_install, self.upgrade)
  982. except BestVersionAlreadyInstalled:
  983. best_installed = True
  984. install = False
  985. except DistributionNotFound:
  986. not_found = sys.exc_info()[1]
  987. else:
  988. # Avoid the need to call find_requirement again
  989. req_to_install.url = url.url
  990. if not best_installed:
  991. #don't uninstall conflict if user install and conflict is not user install
  992. if not (self.use_user_site and not dist_in_usersite(req_to_install.satisfied_by)):
  993. req_to_install.conflicts_with = req_to_install.satisfied_by
  994. req_to_install.satisfied_by = None
  995. else:
  996. install = False
  997. if req_to_install.satisfied_by:
  998. if best_installed:
  999. logger.notify('Requirement already up-to-date: %s'
  1000. % req_to_install)
  1001. else:
  1002. logger.notify('Requirement already satisfied '
  1003. '(use --upgrade to upgrade): %s'
  1004. % req_to_install)
  1005. if req_to_install.editable:
  1006. logger.notify('Obtaining %s' % req_to_install)
  1007. elif install:
  1008. if req_to_install.url and req_to_install.url.lower().startswith('file:'):
  1009. logger.notify('Unpacking %s' % display_path(url_to_path(req_to_install.url)))
  1010. else:
  1011. logger.notify('Downloading/unpacking %s' % req_to_install)
  1012. logger.indent += 2
  1013. try:
  1014. is_bundle = False
  1015. is_wheel = False
  1016. if req_to_install.editable:
  1017. if req_to_install.source_dir is None:
  1018. location = req_to_install.build_location(self.src_dir)
  1019. req_to_install.source_dir = location
  1020. else:
  1021. location = req_to_install.source_dir
  1022. if not os.path.exists(self.build_dir):
  1023. _make_build_dir(self.build_dir)
  1024. req_to_install.update_editable(not self.is_download)
  1025. if self.is_download:
  1026. req_to_install.run_egg_info()
  1027. req_to_install.archive(self.download_dir)
  1028. else:
  1029. req_to_install.run_egg_info()
  1030. elif install:
  1031. ##@@ if filesystem packages are not marked
  1032. ##editable in a req, a non deterministic error
  1033. ##occurs when the script attempts to unpack the
  1034. ##build directory
  1035. # NB: This call can result in the creation of a temporary build directory
  1036. location = req_to_install.build_location(self.build_dir, not self.is_download)
  1037. unpack = True
  1038. url = None
  1039. # In the case where the req comes from a bundle, we should
  1040. # assume a build dir exists and move on
  1041. if req_to_install.from_bundle:
  1042. pass
  1043. # If a checkout exists, it's unwise to keep going. version
  1044. # inconsistencies are logged later, but do not fail the
  1045. # installation.
  1046. elif os.path.exists(os.path.join(location, 'setup.py')):
  1047. raise PreviousBuildDirError(textwrap.dedent("""
  1048. pip can't proceed with requirement '%s' due to a pre-existing build directory.
  1049. location: %s
  1050. This is likely due to a previous installation that failed.
  1051. pip is being responsible and not assuming it can delete this.
  1052. Please delete it and try again.
  1053. """ % (req_to_install, location)))
  1054. else:
  1055. ## FIXME: this won't upgrade when there's an existing package unpacked in `location`
  1056. if req_to_install.url is None:
  1057. if not_found:
  1058. raise not_found
  1059. url = finder.find_requirement(req_to_install, upgrade=self.upgrade)
  1060. else:
  1061. ## FIXME: should req_to_install.url already be a link?
  1062. url = Link(req_to_install.url)
  1063. assert url
  1064. if url:
  1065. try:
  1066. if (
  1067. url.filename.endswith(wheel_ext)
  1068. and self.wheel_download_dir
  1069. ):
  1070. # when doing 'pip wheel`
  1071. download_dir = self.wheel_download_dir
  1072. do_download = True
  1073. else:
  1074. download_dir = self.download_dir
  1075. do_download = self.is_download
  1076. self.unpack_url(
  1077. url, location, download_dir,
  1078. do_download,
  1079. )
  1080. except HTTPError as exc:
  1081. logger.fatal(
  1082. 'Could not install requirement %s because '
  1083. 'of error %s' % (req_to_install, exc)
  1084. )
  1085. raise InstallationError(
  1086. 'Could not install requirement %s because of HTTP error %s for URL %s'
  1087. % (req_to_install, e, url))
  1088. else:
  1089. unpack = False
  1090. if unpack:
  1091. is_bundle = req_to_install.is_bundle
  1092. is_wheel = url and url.filename.endswith(wheel_ext)
  1093. if is_bundle:
  1094. req_to_install.move_bundle_files(self.build_dir, self.src_dir)
  1095. for subreq in req_to_install.bundle_requirements():
  1096. reqs.append(subreq)
  1097. self.add_requirement(subreq)
  1098. elif self.is_download:
  1099. req_to_install.source_dir = location
  1100. if not is_wheel:
  1101. # FIXME: see https://github.com/pypa/pip/issues/1112
  1102. req_to_install.run_egg_info()
  1103. if url and url.scheme in vcs.all_schemes:
  1104. req_to_install.archive(self.download_dir)
  1105. elif is_wheel:
  1106. req_to_install.source_dir = location
  1107. req_to_install.url = url.url
  1108. else:
  1109. req_to_install.source_dir = location
  1110. req_to_install.run_egg_info()
  1111. if force_root_egg_info:
  1112. # We need to run this to make sure that the .egg-info/
  1113. # directory is created for packing in the bundle
  1114. req_to_install.run_egg_info(force_root_egg_info=True)
  1115. req_to_install.assert_source_matches_version()
  1116. #@@ sketchy way of identifying packages not grabbed from an index
  1117. if bundle and req_to_install.url:
  1118. self.copy_to_build_dir(req_to_install)
  1119. install = False
  1120. # req_to_install.req is only avail after unpack for URL pkgs
  1121. # repeat check_if_exists to uninstall-on-upgrade (#14)
  1122. if not self.ignore_installed:
  1123. req_to_install.check_if_exists()
  1124. if req_to_install.satisfied_by:
  1125. if self.upgrade or self.ignore_installed:
  1126. #don't uninstall conflict if user install and and conflict is not user install
  1127. if not (self.use_user_site and not dist_in_usersite(req_to_install.satisfied_by)):
  1128. req_to_install.conflicts_with = req_to_install.satisfied_by
  1129. req_to_install.satisfied_by = None
  1130. else:
  1131. logger.notify(
  1132. 'Requirement already satisfied (use '
  1133. '--upgrade to upgrade): %s' %
  1134. req_to_install
  1135. )
  1136. install = False
  1137. if is_wheel:
  1138. dist = list(
  1139. pkg_resources.find_distributions(location)
  1140. )[0]
  1141. if not req_to_install.req:
  1142. req_to_install.req = dist.as_requirement()
  1143. self.add_requirement(req_to_install)
  1144. if not self.ignore_dependencies:
  1145. for subreq in dist.requires(
  1146. req_to_install.extras):
  1147. if self.has_requirement(
  1148. subreq.project_name):
  1149. continue
  1150. subreq = InstallRequirement(str(subreq),
  1151. req_to_install)
  1152. reqs.append(subreq)
  1153. self.add_requirement(subreq)
  1154. # sdists
  1155. elif not is_bundle:
  1156. ## FIXME: shouldn't be globally added:
  1157. finder.add_dependency_links(req_to_install.dependency_links)
  1158. if (req_to_install.extras):
  1159. logger.notify("Installing extra requirements: %r" % ','.join(req_to_install.extras))
  1160. if not self.ignore_dependencies:
  1161. for req in req_to_install.requirements(req_to_install.extras):
  1162. try:
  1163. name = pkg_resources.Requirement.parse(req).project_name
  1164. except ValueError:
  1165. e = sys.exc_info()[1]
  1166. ## FIXME: proper warning
  1167. logger.error('Invalid requirement: %r (%s) in requirement %s' % (req, e, req_to_install))
  1168. continue
  1169. if self.has_requirement(name):
  1170. ## FIXME: check for conflict
  1171. continue
  1172. subreq = InstallRequirement(req, req_to_install)
  1173. reqs.append(subreq)
  1174. self.add_requirement(subreq)
  1175. if not self.has_requirement(req_to_install.name):
  1176. #'unnamed' requirements will get added here
  1177. self.add_requirement(req_to_install)
  1178. # cleanup tmp src
  1179. if not is_bundle:
  1180. if (
  1181. self.is_download or
  1182. req_to_install._temp_build_dir is not None
  1183. ):
  1184. self.reqs_to_cleanup.append(req_to_install)
  1185. if install:
  1186. self.successfully_downloaded.append(req_to_install)
  1187. if bundle and (req_to_install.url and req_to_install.url.startswith('file:///')):
  1188. self.copy_to_build_dir(req_to_install)
  1189. finally:
  1190. logger.indent -= 2
  1191. def cleanup_files(self, bundle=False):
  1192. """Clean up files, remove builds."""
  1193. logger.notify('Cleaning up...')
  1194. logger.indent += 2
  1195. for req in self.reqs_to_cleanup:
  1196. req.remove_temporary_source()
  1197. remove_dir = []
  1198. if self._pip_has_created_build_dir():
  1199. remove_dir.append(self.build_dir)
  1200. # The source dir of a bundle can always be removed.
  1201. # FIXME: not if it pre-existed the bundle!
  1202. if bundle:
  1203. remove_dir.append(self.src_dir)
  1204. for dir in remove_dir:
  1205. if os.path.exists(dir):
  1206. logger.info('Removing temporary dir %s...' % dir)
  1207. rmtree(dir)
  1208. logger.indent -= 2
  1209. def _pip_has_created_build_dir(self):
  1210. return (self.build_dir == build_prefix and
  1211. os.path.exists(os.path.join(self.build_dir, PIP_DELETE_MARKER_FILENAME)))
  1212. def copy_to_build_dir(self, req_to_install):
  1213. target_dir = req_to_install.editable and self.src_dir or self.build_dir
  1214. logger.info("Copying %s to %s" % (req_to_install.name, target_dir))
  1215. dest = os.path.join(target_dir, req_to_install.name)
  1216. shutil.copytree(req_to_install.source_dir, dest)
  1217. call_subprocess(["python", "%s/setup.py" % dest, "clean"], cwd=dest,
  1218. command_desc='python setup.py clean')
  1219. def unpack_url(self, link, location, download_dir=None,
  1220. only_download=False):
  1221. if download_dir is None:
  1222. download_dir = self.download_dir
  1223. # non-editable vcs urls
  1224. if is_vcs_url(link):
  1225. if only_download:
  1226. loc = download_dir
  1227. else:
  1228. loc = location
  1229. unpack_vcs_link(link, loc, only_download)
  1230. # file urls
  1231. elif is_file_url(link):
  1232. unpack_file_url(link, location, download_dir)
  1233. if only_download:
  1234. write_delete_marker_file(location)
  1235. # http urls
  1236. else:
  1237. unpack_http_url(
  1238. link,
  1239. location,
  1240. self.download_cache,
  1241. download_dir,
  1242. self.session,
  1243. )
  1244. if only_download:
  1245. write_delete_marker_file(location)
  1246. def install(self, install_options, global_options=(), *args, **kwargs):
  1247. """Install everything in this set (after having downloaded and unpacked the packages)"""
  1248. to_install = [r for r in self.requirements.values()
  1249. if not r.satisfied_by]
  1250. # DISTRIBUTE TO SETUPTOOLS UPGRADE HACK (1 of 3 parts)
  1251. # move the distribute-0.7.X wrapper to the end because it does not
  1252. # install a setuptools package. by moving it to the end, we ensure it's
  1253. # setuptools dependency is handled first, which will provide the
  1254. # setuptools package
  1255. # TODO: take this out later
  1256. distribute_req = pkg_resources.Requirement.parse("distribute>=0.7")
  1257. for req in to_install:
  1258. if req.name == 'distribute' and req.installed_version in distribute_req:
  1259. to_install.remove(req)
  1260. to_install.append(req)
  1261. if to_install:
  1262. logger.notify('Installing collected packages: %s' % ', '.join([req.name for req in to_install]))
  1263. logger.indent += 2
  1264. try:
  1265. for requirement in to_install:
  1266. # DISTRIBUTE TO SETUPTOOLS UPGRADE HACK (1 of 3 parts)
  1267. # when upgrading from distribute-0.6.X to the new merged
  1268. # setuptools in py2, we need to force setuptools to uninstall
  1269. # distribute. In py3, which is always using distribute, this
  1270. # conversion is already happening in distribute's pkg_resources.
  1271. # It's ok *not* to check if setuptools>=0.7 because if someone
  1272. # were actually trying to ugrade from distribute to setuptools
  1273. # 0.6.X, then all this could do is actually help, although that
  1274. # upgade path was certainly never "supported"
  1275. # TODO: remove this later
  1276. if requirement.name == 'setuptools':
  1277. try:
  1278. # only uninstall distribute<0.7. For >=0.7, setuptools
  1279. # will also be present, and that's what we need to
  1280. # uninstall
  1281. distribute_requirement = pkg_resources.Requirement.parse("distribute<0.7")
  1282. existing_distribute = pkg_resources.get_distribution("distribute")
  1283. if existing_distribute in distribute_requirement:
  1284. requirement.conflicts_with = existing_distribute
  1285. except pkg_resources.DistributionNotFound:
  1286. # distribute wasn't installed, so nothing to do
  1287. pass
  1288. if requirement.conflicts_with:
  1289. logger.notify('Found existing installation: %s'
  1290. % requirement.conflicts_with)
  1291. logger.indent += 2
  1292. try:
  1293. requirement.uninstall(auto_confirm=True)
  1294. finally:
  1295. logger.indent -= 2
  1296. try:
  1297. requirement.install(install_options, global_options, *args, **kwargs)
  1298. except:
  1299. # if install did not succeed, rollback previous uninstall
  1300. if requirement.conflicts_with and not requirement.install_succeeded:
  1301. requirement.rollback_uninstall()
  1302. raise
  1303. else:
  1304. if requirement.conflicts_with and requirement.install_succeeded:
  1305. requirement.commit_uninstall()
  1306. requirement.remove_temporary_source()
  1307. finally:
  1308. logger.indent -= 2
  1309. self.successfully_installed = to_install
  1310. def create_bundle(self, bundle_filename):
  1311. ## FIXME: can't decide which is better; zip is easier to read
  1312. ## random files from, but tar.bz2 is smaller and not as lame a
  1313. ## format.
  1314. ## FIXME: this file should really include a manifest of the
  1315. ## packages, maybe some other metadata files. It would make
  1316. ## it easier to detect as well.
  1317. zip = zipfile.ZipFile(bundle_filename, 'w', zipfile.ZIP_DEFLATED)
  1318. vcs_dirs = []
  1319. for dir, basename in (self.build_dir, 'build'), (self.src_dir, 'src'):
  1320. dir = os.path.normcase(os.path.abspath(dir))
  1321. for dirpath, dirnames, filenames in os.walk(dir):
  1322. for backend in vcs.backends:
  1323. vcs_backend = backend()
  1324. vcs_url = vcs_rev = None
  1325. if vcs_backend.dirname in dirnames:
  1326. for vcs_dir in vcs_dirs:
  1327. if dirpath.startswith(vcs_dir):
  1328. # vcs bundle file already in parent directory
  1329. break
  1330. else:
  1331. vcs_url, vcs_rev = vcs_backend.get_info(
  1332. os.path.join(dir, dirpath))
  1333. vcs_dirs.append(dirpath)
  1334. vcs_bundle_file = vcs_backend.bundle_file
  1335. vcs_guide = vcs_backend.guide % {'url': vcs_url,
  1336. 'rev': vcs_rev}
  1337. dirnames.remove(vcs_backend.dirname)
  1338. break
  1339. if 'pip-egg-info' in dirnames:
  1340. dirnames.remove('pip-egg-info')
  1341. for dirname in dirnames:
  1342. dirname = os.path.join(dirpath, dirname)
  1343. name = self._clean_zip_name(dirname, dir)
  1344. zip.writestr(basename + '/' + name + '/', '')
  1345. for filename in filenames:
  1346. if filename == PIP_DELETE_MARKER_FILENAME:
  1347. continue
  1348. filename = os.path.join(dirpath, filename)
  1349. name = self._clean_zip_name(filename, dir)
  1350. zip.write(filename, basename + '/' + name)
  1351. if vcs_url:
  1352. name = os.path.join(dirpath, vcs_bundle_file)
  1353. name = self._clean_zip_name(name, dir)
  1354. zip.writestr(basename + '/' + name, vcs_guide)
  1355. zip.writestr('pip-manifest.txt', self.bundle_requirements())
  1356. zip.close()
  1357. BUNDLE_HEADER = '''\
  1358. # This is a pip bundle file, that contains many source packages
  1359. # that can be installed as a group. You can install this like:
  1360. # pip this_file.zip
  1361. # The rest of the file contains a list of all the packages included:
  1362. '''
  1363. def bundle_requirements(self):
  1364. parts = [self.BUNDLE_HEADER]
  1365. for req in [req for req in self.requirements.values()
  1366. if not req.comes_from]:
  1367. parts.append('%s==%s\n' % (req.name, req.installed_version))
  1368. parts.append('# These packages were installed to satisfy the above requirements:\n')
  1369. for req in [req for req in self.requirements.values()
  1370. if req.comes_from]:
  1371. parts.append('%s==%s\n' % (req.name, req.installed_version))
  1372. ## FIXME: should we do something with self.unnamed_requirements?
  1373. return ''.join(parts)
  1374. def _clean_zip_name(self, name, prefix):
  1375. assert name.startswith(prefix+os.path.sep), (
  1376. "name %r doesn't start with prefix %r" % (name, prefix))
  1377. name = name[len(prefix)+1:]
  1378. name = name.replace(os.path.sep, '/')
  1379. return name
  1380. def _make_build_dir(build_dir):
  1381. os.makedirs(build_dir)
  1382. write_delete_marker_file(build_dir)
  1383. _scheme_re = re.compile(r'^(http|https|file):', re.I)
  1384. def parse_requirements(filename, finder=None, comes_from=None, options=None,
  1385. session=None):
  1386. if session is None:
  1387. session = PipSession()
  1388. skip_match = None
  1389. skip_regex = options.skip_requirements_regex if options else None
  1390. if skip_regex:
  1391. skip_match = re.compile(skip_regex)
  1392. reqs_file_dir = os.path.dirname(os.path.abspath(filename))
  1393. filename, content = get_file_content(filename,
  1394. comes_from=comes_from,
  1395. session=session,
  1396. )
  1397. for line_number, line in enumerate(content.splitlines()):
  1398. line_number += 1
  1399. line = line.strip()
  1400. # Remove comments from file
  1401. line = re.sub(r"(^|\s)#.*$", "", line)
  1402. if not line or line.startswith('#'):
  1403. continue
  1404. if skip_match and skip_match.search(line):
  1405. continue
  1406. if line.startswith('-r') or line.startswith('--requirement'):
  1407. if line.startswith('-r'):
  1408. req_url = line[2:].strip()
  1409. else:
  1410. req_url = line[len('--requirement'):].strip().strip('=')
  1411. if _scheme_re.search(filename):
  1412. # Relative to a URL
  1413. req_url = urlparse.urljoin(filename, req_url)
  1414. elif not _scheme_re.search(req_url):
  1415. req_url = os.path.join(os.path.dirname(filename), req_url)
  1416. for item in parse_requirements(req_url, finder, comes_from=filename, options=options, session=session):
  1417. yield item
  1418. elif line.startswith('-Z') or line.startswith('--always-unzip'):
  1419. # No longer used, but previously these were used in
  1420. # requirement files, so we'll ignore.
  1421. pass
  1422. elif line.startswith('-f') or line.startswith('--find-links'):
  1423. if line.startswith('-f'):
  1424. line = line[2:].strip()
  1425. else:
  1426. line = line[len('--find-links'):].strip().lstrip('=')
  1427. ## FIXME: it would be nice to keep track of the source of
  1428. ## the find_links:
  1429. # support a find-links local path relative to a requirements file
  1430. relative_to_reqs_file = os.path.join(reqs_file_dir, line)
  1431. if os.path.exists(relative_to_reqs_file):
  1432. line = relative_to_reqs_file
  1433. if finder:
  1434. finder.find_links.append(line)
  1435. elif line.startswith('-i') or line.startswith('--index-url'):
  1436. if line.startswith('-i'):
  1437. line = line[2:].strip()
  1438. else:
  1439. line = line[len('--index-url'):].strip().lstrip('=')
  1440. if finder:
  1441. finder.index_urls = [line]
  1442. elif line.startswith('--extra-index-url'):
  1443. line = line[len('--extra-index-url'):].strip().lstrip('=')
  1444. if finder:
  1445. finder.index_urls.append(line)
  1446. elif line.startswith('--use-wheel'):
  1447. finder.use_wheel = True
  1448. elif line.startswith('--no-index'):
  1449. finder.index_urls = []
  1450. elif line.startswith("--allow-external"):
  1451. line = line[len("--allow-external"):].strip().lstrip("=")
  1452. finder.allow_external |= set([normalize_name(line).lower()])
  1453. elif line.startswith("--allow-all-external"):
  1454. finder.allow_all_external = True
  1455. # Remove in 1.7
  1456. elif line.startswith("--no-allow-external"):
  1457. pass
  1458. # Remove in 1.7
  1459. elif line.startswith("--no-allow-insecure"):
  1460. pass
  1461. # Remove after 1.7
  1462. elif line.startswith("--allow-insecure"):
  1463. line = line[len("--allow-insecure"):].strip().lstrip("=")
  1464. finder.allow_unverified |= set([normalize_name(line).lower()])
  1465. elif line.startswith("--allow-unverified"):
  1466. line = line[len("--allow-unverified"):].strip().lstrip("=")
  1467. finder.allow_unverified |= set([normalize_name(line).lower()])
  1468. else:
  1469. comes_from = '-r %s (line %s)' % (filename, line_number)
  1470. if line.startswith('-e') or line.startswith('--editable'):
  1471. if line.startswith('-e'):
  1472. line = line[2:].strip()
  1473. else:
  1474. line = line[len('--editable'):].strip().lstrip('=')
  1475. req = InstallRequirement.from_editable(
  1476. line, comes_from=comes_from, default_vcs=options.default_vcs if options else None)
  1477. else:
  1478. req = InstallRequirement.from_line(line, comes_from, prereleases=getattr(options, "pre", None))
  1479. yield req
  1480. def _strip_postfix(req):
  1481. """
  1482. Strip req postfix ( -dev, 0.2, etc )
  1483. """
  1484. ## FIXME: use package_to_requirement?
  1485. match = re.search(r'^(.*?)(?:-dev|-\d.*)$', req)
  1486. if match:
  1487. # Strip off -dev, -0.2, etc.
  1488. req = match.group(1)
  1489. return req
  1490. def _build_req_from_url(url):
  1491. parts = [p for p in url.split('#', 1)[0].split('/') if p]
  1492. req = None
  1493. if parts[-2] in ('tags', 'branches', 'tag', 'branch'):
  1494. req = parts[-3]
  1495. elif parts[-1] == 'trunk':
  1496. req = parts[-2]
  1497. return req
  1498. def _build_editable_options(req):
  1499. """
  1500. This method generates a dictionary of the query string
  1501. parameters contained in a given editable URL.
  1502. """
  1503. regexp = re.compile(r"[\?#&](?P<name>[^&=]+)=(?P<value>[^&=]+)")
  1504. matched = regexp.findall(req)
  1505. if matched:
  1506. ret = dict()
  1507. for option in matched:
  1508. (name, value) = option
  1509. if name in ret:
  1510. raise Exception("%s option already defined" % name)
  1511. ret[name] = value
  1512. return ret
  1513. return None
  1514. def parse_editable(editable_req, default_vcs=None):
  1515. """Parses svn+http://blahblah@rev#egg=Foobar into a requirement
  1516. (Foobar) and a URL"""
  1517. url = editable_req
  1518. extras = None
  1519. # If a file path is specified with extras, strip off the extras.
  1520. m = re.match(r'^(.+)(\[[^\]]+\])$', url)
  1521. if m:
  1522. url_no_extras = m.group(1)
  1523. extras = m.group(2)
  1524. else:
  1525. url_no_extras = url
  1526. if os.path.isdir(url_no_extras):
  1527. if not os.path.exists(os.path.join(url_no_extras, 'setup.py')):
  1528. raise InstallationError("Directory %r is not installable. File 'setup.py' not found." % url_no_extras)
  1529. # Treating it as code that has already been checked out
  1530. url_no_extras = path_to_url(url_no_extras)
  1531. if url_no_extras.lower().startswith('file:'):
  1532. if extras:
  1533. return None, url_no_extras, pkg_resources.Requirement.parse('__placeholder__' + extras).extras
  1534. else:
  1535. return None, url_no_extras, None
  1536. for version_control in vcs:
  1537. if url.lower().startswith('%s:' % version_control):
  1538. url = '%s+%s' % (version_control, url)
  1539. break
  1540. if '+' not in url:
  1541. if default_vcs:
  1542. url = default_vcs + '+' + url
  1543. else:
  1544. raise InstallationError(
  1545. '%s should either be a path to a local project or a VCS url beginning with svn+, git+, hg+, or bzr+' % editable_req)
  1546. vc_type = url.split('+', 1)[0].lower()
  1547. if not vcs.get_backend(vc_type):
  1548. error_message = 'For --editable=%s only ' % editable_req + \
  1549. ', '.join([backend.name + '+URL' for backend in vcs.backends]) + \
  1550. ' is currently supported'
  1551. raise InstallationError(error_message)
  1552. try:
  1553. options = _build_editable_options(editable_req)
  1554. except Exception:
  1555. message = sys.exc_info()[1]
  1556. raise InstallationError(
  1557. '--editable=%s error in editable options:%s' % (editable_req, message))
  1558. if not options or 'egg' not in options:
  1559. req = _build_req_from_url(editable_req)
  1560. if not req:
  1561. raise InstallationError('--editable=%s is not the right format; it must have #egg=Package' % editable_req)
  1562. else:
  1563. req = options['egg']
  1564. package = _strip_postfix(req)
  1565. return package, url, options
  1566. class UninstallPathSet(object):
  1567. """A set of file paths to be removed in the uninstallation of a
  1568. requirement."""
  1569. def __init__(self, dist):
  1570. self.paths = set()
  1571. self._refuse = set()
  1572. self.pth = {}
  1573. self.dist = dist
  1574. self.save_dir = None
  1575. self._moved_paths = []
  1576. def _permitted(self, path):
  1577. """
  1578. Return True if the given path is one we are permitted to
  1579. remove/modify, False otherwise.
  1580. """
  1581. return is_local(path)
  1582. def _can_uninstall(self):
  1583. if not dist_is_local(self.dist):
  1584. logger.notify("Not uninstalling %s at %s, outside environment %s"
  1585. % (self.dist.project_name, normalize_path(self.dist.location), sys.prefix))
  1586. return False
  1587. return True
  1588. def add(self, path):
  1589. path = normalize_path(path)
  1590. if not os.path.exists(path):
  1591. return
  1592. if self._permitted(path):
  1593. self.paths.add(path)
  1594. else:
  1595. self._refuse.add(path)
  1596. # __pycache__ files can show up after 'installed-files.txt' is created, due to imports
  1597. if os.path.splitext(path)[1] == '.py' and uses_pycache:
  1598. self.add(imp.cache_from_source(path))
  1599. def add_pth(self, pth_file, entry):
  1600. pth_file = normalize_path(pth_file)
  1601. if self._permitted(pth_file):
  1602. if pth_file not in self.pth:
  1603. self.pth[pth_file] = UninstallPthEntries(pth_file)
  1604. self.pth[pth_file].add(entry)
  1605. else:
  1606. self._refuse.add(pth_file)
  1607. def compact(self, paths):
  1608. """Compact a path set to contain the minimal number of paths
  1609. necessary to contain all paths in the set. If /a/path/ and
  1610. /a/path/to/a/file.txt are both in the set, leave only the
  1611. shorter path."""
  1612. short_paths = set()
  1613. for path in sorted(paths, key=len):
  1614. if not any([(path.startswith(shortpath) and
  1615. path[len(shortpath.rstrip(os.path.sep))] == os.path.sep)
  1616. for shortpath in short_paths]):
  1617. short_paths.add(path)
  1618. return short_paths
  1619. def _stash(self, path):
  1620. return os.path.join(
  1621. self.save_dir, os.path.splitdrive(path)[1].lstrip(os.path.sep))
  1622. def remove(self, auto_confirm=False):
  1623. """Remove paths in ``self.paths`` with confirmation (unless
  1624. ``auto_confirm`` is True)."""
  1625. if not self._can_uninstall():
  1626. return
  1627. if not self.paths:
  1628. logger.notify("Can't uninstall '%s'. No files were found to uninstall." % self.dist.project_name)
  1629. return
  1630. logger.notify('Uninstalling %s:' % self.dist.project_name)
  1631. logger.indent += 2
  1632. paths = sorted(self.compact(self.paths))
  1633. try:
  1634. if auto_confirm:
  1635. response = 'y'
  1636. else:
  1637. for path in paths:
  1638. logger.notify(path)
  1639. response = ask('Proceed (y/n)? ', ('y', 'n'))
  1640. if self._refuse:
  1641. logger.notify('Not removing or modifying (outside of prefix):')
  1642. for path in self.compact(self._refuse):
  1643. logger.notify(path)
  1644. if response == 'y':
  1645. self.save_dir = tempfile.mkdtemp(suffix='-uninstall',
  1646. prefix='pip-')
  1647. for path in paths:
  1648. new_path = self._stash(path)
  1649. logger.info('Removing file or directory %s' % path)
  1650. self._moved_paths.append(path)
  1651. renames(path, new_path)
  1652. for pth in self.pth.values():
  1653. pth.remove()
  1654. logger.notify('Successfully uninstalled %s' % self.dist.project_name)
  1655. finally:
  1656. logger.indent -= 2
  1657. def rollback(self):
  1658. """Rollback the changes previously made by remove()."""
  1659. if self.save_dir is None:
  1660. logger.error("Can't roll back %s; was not uninstalled" % self.dist.project_name)
  1661. return False
  1662. logger.notify('Rolling back uninstall of %s' % self.dist.project_name)
  1663. for path in self._moved_paths:
  1664. tmp_path = self._stash(path)
  1665. logger.info('Replacing %s' % path)
  1666. renames(tmp_path, path)
  1667. for pth in self.pth:
  1668. pth.rollback()
  1669. def commit(self):
  1670. """Remove temporary save dir: rollback will no longer be possible."""
  1671. if self.save_dir is not None:
  1672. rmtree(self.save_dir)
  1673. self.save_dir = None
  1674. self._moved_paths = []
  1675. class UninstallPthEntries(object):
  1676. def __init__(self, pth_file):
  1677. if not os.path.isfile(pth_file):
  1678. raise UninstallationError("Cannot remove entries from nonexistent file %s" % pth_file)
  1679. self.file = pth_file
  1680. self.entries = set()
  1681. self._saved_lines = None
  1682. def add(self, entry):
  1683. entry = os.path.normcase(entry)
  1684. # On Windows, os.path.normcase converts the entry to use
  1685. # backslashes. This is correct for entries that describe absolute
  1686. # paths outside of site-packages, but all the others use forward
  1687. # slashes.
  1688. if sys.platform == 'win32' and not os.path.splitdrive(entry)[0]:
  1689. entry = entry.replace('\\', '/')
  1690. self.entries.add(entry)
  1691. def remove(self):
  1692. logger.info('Removing pth entries from %s:' % self.file)
  1693. fh = open(self.file, 'rb')
  1694. # windows uses '\r\n' with py3k, but uses '\n' with py2.x
  1695. lines = fh.readlines()
  1696. self._saved_lines = lines
  1697. fh.close()
  1698. if any(b('\r\n') in line for line in lines):
  1699. endline = '\r\n'
  1700. else:
  1701. endline = '\n'
  1702. for entry in self.entries:
  1703. try:
  1704. logger.info('Removing entry: %s' % entry)
  1705. lines.remove(b(entry + endline))
  1706. except ValueError:
  1707. pass
  1708. fh = open(self.file, 'wb')
  1709. fh.writelines(lines)
  1710. fh.close()
  1711. def rollback(self):
  1712. if self._saved_lines is None:
  1713. logger.error('Cannot roll back changes to %s, none were made' % self.file)
  1714. return False
  1715. logger.info('Rolling %s back to previous state' % self.file)
  1716. fh = open(self.file, 'wb')
  1717. fh.writelines(self._saved_lines)
  1718. fh.close()
  1719. return True
  1720. class FakeFile(object):
  1721. """Wrap a list of lines in an object with readline() to make
  1722. ConfigParser happy."""
  1723. def __init__(self, lines):
  1724. self._gen = (l for l in lines)
  1725. def readline(self):
  1726. try:
  1727. try:
  1728. return next(self._gen)
  1729. except NameError:
  1730. return self._gen.next()
  1731. except StopIteration:
  1732. return ''
  1733. def __iter__(self):
  1734. return self._gen