__init__.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  1. from __future__ import absolute_import
  2. import contextlib
  3. import locale
  4. import logging
  5. import re
  6. import os
  7. import posixpath
  8. import shutil
  9. import stat
  10. import subprocess
  11. import sys
  12. import tarfile
  13. import zipfile
  14. from pip.exceptions import InstallationError
  15. from pip.compat import console_to_str, stdlib_pkgs
  16. from pip.locations import (
  17. site_packages, user_site, running_under_virtualenv, virtualenv_no_global,
  18. write_delete_marker_file,
  19. )
  20. from pip._vendor import pkg_resources
  21. from pip._vendor.six.moves import input
  22. from pip._vendor.six.moves import cStringIO
  23. from pip._vendor.six import PY2
  24. from pip._vendor.retrying import retry
  25. if PY2:
  26. from io import BytesIO as StringIO
  27. else:
  28. from io import StringIO
  29. __all__ = ['rmtree', 'display_path', 'backup_dir',
  30. 'ask', 'Inf', 'normalize_name', 'splitext',
  31. 'format_size', 'is_installable_dir',
  32. 'is_svn_page', 'file_contents',
  33. 'split_leading_dir', 'has_leading_dir',
  34. 'make_path_relative', 'normalize_path',
  35. 'renames', 'get_terminal_size', 'get_prog',
  36. 'unzip_file', 'untar_file', 'unpack_file', 'call_subprocess',
  37. 'captured_stdout', 'remove_tracebacks']
  38. logger = logging.getLogger(__name__)
  39. def import_or_raise(pkg_or_module_string, ExceptionType, *args, **kwargs):
  40. try:
  41. return __import__(pkg_or_module_string)
  42. except ImportError:
  43. raise ExceptionType(*args, **kwargs)
  44. def get_prog():
  45. try:
  46. if os.path.basename(sys.argv[0]) in ('__main__.py', '-c'):
  47. return "%s -m pip" % sys.executable
  48. except (AttributeError, TypeError, IndexError):
  49. pass
  50. return 'pip'
  51. # Retry every half second for up to 3 seconds
  52. @retry(stop_max_delay=3000, wait_fixed=500)
  53. def rmtree(dir, ignore_errors=False):
  54. shutil.rmtree(dir, ignore_errors=ignore_errors,
  55. onerror=rmtree_errorhandler)
  56. def rmtree_errorhandler(func, path, exc_info):
  57. """On Windows, the files in .svn are read-only, so when rmtree() tries to
  58. remove them, an exception is thrown. We catch that here, remove the
  59. read-only attribute, and hopefully continue without problems."""
  60. # if file type currently read only
  61. if os.stat(path).st_mode & stat.S_IREAD:
  62. # convert to read/write
  63. os.chmod(path, stat.S_IWRITE)
  64. # use the original function to repeat the operation
  65. func(path)
  66. return
  67. else:
  68. raise
  69. def display_path(path):
  70. """Gives the display value for a given path, making it relative to cwd
  71. if possible."""
  72. path = os.path.normcase(os.path.abspath(path))
  73. if sys.version_info[0] == 2:
  74. path = path.decode(sys.getfilesystemencoding(), 'replace')
  75. path = path.encode(sys.getdefaultencoding(), 'replace')
  76. if path.startswith(os.getcwd() + os.path.sep):
  77. path = '.' + path[len(os.getcwd()):]
  78. return path
  79. def backup_dir(dir, ext='.bak'):
  80. """Figure out the name of a directory to back up the given dir to
  81. (adding .bak, .bak2, etc)"""
  82. n = 1
  83. extension = ext
  84. while os.path.exists(dir + extension):
  85. n += 1
  86. extension = ext + str(n)
  87. return dir + extension
  88. def ask_path_exists(message, options):
  89. for action in os.environ.get('PIP_EXISTS_ACTION', '').split():
  90. if action in options:
  91. return action
  92. return ask(message, options)
  93. def ask(message, options):
  94. """Ask the message interactively, with the given possible responses"""
  95. while 1:
  96. if os.environ.get('PIP_NO_INPUT'):
  97. raise Exception(
  98. 'No input was expected ($PIP_NO_INPUT set); question: %s' %
  99. message
  100. )
  101. response = input(message)
  102. response = response.strip().lower()
  103. if response not in options:
  104. print(
  105. 'Your response (%r) was not one of the expected responses: '
  106. '%s' % (response, ', '.join(options))
  107. )
  108. else:
  109. return response
  110. class _Inf(object):
  111. """I am bigger than everything!"""
  112. def __eq__(self, other):
  113. if self is other:
  114. return True
  115. else:
  116. return False
  117. def __ne__(self, other):
  118. return not self.__eq__(other)
  119. def __lt__(self, other):
  120. return False
  121. def __le__(self, other):
  122. return False
  123. def __gt__(self, other):
  124. return True
  125. def __ge__(self, other):
  126. return True
  127. def __repr__(self):
  128. return 'Inf'
  129. Inf = _Inf() # this object is not currently used as a sortable in our code
  130. del _Inf
  131. _normalize_re = re.compile(r'[^a-z]', re.I)
  132. def normalize_name(name):
  133. return _normalize_re.sub('-', name.lower())
  134. def format_size(bytes):
  135. if bytes > 1000 * 1000:
  136. return '%.1fMB' % (bytes / 1000.0 / 1000)
  137. elif bytes > 10 * 1000:
  138. return '%ikB' % (bytes / 1000)
  139. elif bytes > 1000:
  140. return '%.1fkB' % (bytes / 1000.0)
  141. else:
  142. return '%ibytes' % bytes
  143. def is_installable_dir(path):
  144. """Return True if `path` is a directory containing a setup.py file."""
  145. if not os.path.isdir(path):
  146. return False
  147. setup_py = os.path.join(path, 'setup.py')
  148. if os.path.isfile(setup_py):
  149. return True
  150. return False
  151. def is_svn_page(html):
  152. """
  153. Returns true if the page appears to be the index page of an svn repository
  154. """
  155. return (re.search(r'<title>[^<]*Revision \d+:', html) and
  156. re.search(r'Powered by (?:<a[^>]*?>)?Subversion', html, re.I))
  157. def file_contents(filename):
  158. with open(filename, 'rb') as fp:
  159. return fp.read().decode('utf-8')
  160. def split_leading_dir(path):
  161. path = str(path)
  162. path = path.lstrip('/').lstrip('\\')
  163. if '/' in path and (('\\' in path and path.find('/') < path.find('\\')) or
  164. '\\' not in path):
  165. return path.split('/', 1)
  166. elif '\\' in path:
  167. return path.split('\\', 1)
  168. else:
  169. return path, ''
  170. def has_leading_dir(paths):
  171. """Returns true if all the paths have the same leading path name
  172. (i.e., everything is in one subdirectory in an archive)"""
  173. common_prefix = None
  174. for path in paths:
  175. prefix, rest = split_leading_dir(path)
  176. if not prefix:
  177. return False
  178. elif common_prefix is None:
  179. common_prefix = prefix
  180. elif prefix != common_prefix:
  181. return False
  182. return True
  183. def make_path_relative(path, rel_to):
  184. """
  185. Make a filename relative, where the filename path, and it is
  186. relative to rel_to
  187. >>> make_path_relative('/usr/share/something/a-file.pth',
  188. ... '/usr/share/another-place/src/Directory')
  189. '../../../something/a-file.pth'
  190. >>> make_path_relative('/usr/share/something/a-file.pth',
  191. ... '/home/user/src/Directory')
  192. '../../../usr/share/something/a-file.pth'
  193. >>> make_path_relative('/usr/share/a-file.pth', '/usr/share/')
  194. 'a-file.pth'
  195. """
  196. path_filename = os.path.basename(path)
  197. path = os.path.dirname(path)
  198. path = os.path.normpath(os.path.abspath(path))
  199. rel_to = os.path.normpath(os.path.abspath(rel_to))
  200. path_parts = path.strip(os.path.sep).split(os.path.sep)
  201. rel_to_parts = rel_to.strip(os.path.sep).split(os.path.sep)
  202. while path_parts and rel_to_parts and path_parts[0] == rel_to_parts[0]:
  203. path_parts.pop(0)
  204. rel_to_parts.pop(0)
  205. full_parts = ['..'] * len(rel_to_parts) + path_parts + [path_filename]
  206. if full_parts == ['']:
  207. return '.' + os.path.sep
  208. return os.path.sep.join(full_parts)
  209. def normalize_path(path, resolve_symlinks=True):
  210. """
  211. Convert a path to its canonical, case-normalized, absolute version.
  212. """
  213. path = os.path.expanduser(path)
  214. if resolve_symlinks:
  215. path = os.path.realpath(path)
  216. else:
  217. path = os.path.abspath(path)
  218. return os.path.normcase(path)
  219. def splitext(path):
  220. """Like os.path.splitext, but take off .tar too"""
  221. base, ext = posixpath.splitext(path)
  222. if base.lower().endswith('.tar'):
  223. ext = base[-4:] + ext
  224. base = base[:-4]
  225. return base, ext
  226. def renames(old, new):
  227. """Like os.renames(), but handles renaming across devices."""
  228. # Implementation borrowed from os.renames().
  229. head, tail = os.path.split(new)
  230. if head and tail and not os.path.exists(head):
  231. os.makedirs(head)
  232. shutil.move(old, new)
  233. head, tail = os.path.split(old)
  234. if head and tail:
  235. try:
  236. os.removedirs(head)
  237. except OSError:
  238. pass
  239. def is_local(path):
  240. """
  241. Return True if path is within sys.prefix, if we're running in a virtualenv.
  242. If we're not in a virtualenv, all paths are considered "local."
  243. """
  244. if not running_under_virtualenv():
  245. return True
  246. return normalize_path(path).startswith(normalize_path(sys.prefix))
  247. def dist_is_local(dist):
  248. """
  249. Return True if given Distribution object is installed locally
  250. (i.e. within current virtualenv).
  251. Always True if we're not in a virtualenv.
  252. """
  253. return is_local(dist_location(dist))
  254. def dist_in_usersite(dist):
  255. """
  256. Return True if given Distribution is installed in user site.
  257. """
  258. norm_path = normalize_path(dist_location(dist))
  259. return norm_path.startswith(normalize_path(user_site))
  260. def dist_in_site_packages(dist):
  261. """
  262. Return True if given Distribution is installed in
  263. distutils.sysconfig.get_python_lib().
  264. """
  265. return normalize_path(
  266. dist_location(dist)
  267. ).startswith(normalize_path(site_packages))
  268. def dist_is_editable(dist):
  269. """Is distribution an editable install?"""
  270. # TODO: factor out determining editableness out of FrozenRequirement
  271. from pip import FrozenRequirement
  272. req = FrozenRequirement.from_dist(dist, [])
  273. return req.editable
  274. def get_installed_distributions(local_only=True,
  275. skip=stdlib_pkgs,
  276. include_editables=True,
  277. editables_only=False,
  278. user_only=False):
  279. """
  280. Return a list of installed Distribution objects.
  281. If ``local_only`` is True (default), only return installations
  282. local to the current virtualenv, if in a virtualenv.
  283. ``skip`` argument is an iterable of lower-case project names to
  284. ignore; defaults to stdlib_pkgs
  285. If ``editables`` is False, don't report editables.
  286. If ``editables_only`` is True , only report editables.
  287. If ``user_only`` is True , only report installations in the user
  288. site directory.
  289. """
  290. if local_only:
  291. local_test = dist_is_local
  292. else:
  293. def local_test(d):
  294. return True
  295. if include_editables:
  296. def editable_test(d):
  297. return True
  298. else:
  299. def editable_test(d):
  300. return not dist_is_editable(d)
  301. if editables_only:
  302. def editables_only_test(d):
  303. return dist_is_editable(d)
  304. else:
  305. def editables_only_test(d):
  306. return True
  307. if user_only:
  308. user_test = dist_in_usersite
  309. else:
  310. def user_test(d):
  311. return True
  312. return [d for d in pkg_resources.working_set
  313. if local_test(d) and
  314. d.key not in skip and
  315. editable_test(d) and
  316. editables_only_test(d) and
  317. user_test(d)
  318. ]
  319. def egg_link_path(dist):
  320. """
  321. Return the path for the .egg-link file if it exists, otherwise, None.
  322. There's 3 scenarios:
  323. 1) not in a virtualenv
  324. try to find in site.USER_SITE, then site_packages
  325. 2) in a no-global virtualenv
  326. try to find in site_packages
  327. 3) in a yes-global virtualenv
  328. try to find in site_packages, then site.USER_SITE
  329. (don't look in global location)
  330. For #1 and #3, there could be odd cases, where there's an egg-link in 2
  331. locations.
  332. This method will just return the first one found.
  333. """
  334. sites = []
  335. if running_under_virtualenv():
  336. if virtualenv_no_global():
  337. sites.append(site_packages)
  338. else:
  339. sites.append(site_packages)
  340. if user_site:
  341. sites.append(user_site)
  342. else:
  343. if user_site:
  344. sites.append(user_site)
  345. sites.append(site_packages)
  346. for site in sites:
  347. egglink = os.path.join(site, dist.project_name) + '.egg-link'
  348. if os.path.isfile(egglink):
  349. return egglink
  350. def dist_location(dist):
  351. """
  352. Get the site-packages location of this distribution. Generally
  353. this is dist.location, except in the case of develop-installed
  354. packages, where dist.location is the source code location, and we
  355. want to know where the egg-link file is.
  356. """
  357. egg_link = egg_link_path(dist)
  358. if egg_link:
  359. return egg_link
  360. return dist.location
  361. def get_terminal_size():
  362. """Returns a tuple (x, y) representing the width(x) and the height(x)
  363. in characters of the terminal window."""
  364. def ioctl_GWINSZ(fd):
  365. try:
  366. import fcntl
  367. import termios
  368. import struct
  369. cr = struct.unpack(
  370. 'hh',
  371. fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')
  372. )
  373. except:
  374. return None
  375. if cr == (0, 0):
  376. return None
  377. return cr
  378. cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
  379. if not cr:
  380. try:
  381. fd = os.open(os.ctermid(), os.O_RDONLY)
  382. cr = ioctl_GWINSZ(fd)
  383. os.close(fd)
  384. except:
  385. pass
  386. if not cr:
  387. cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80))
  388. return int(cr[1]), int(cr[0])
  389. def current_umask():
  390. """Get the current umask which involves having to set it temporarily."""
  391. mask = os.umask(0)
  392. os.umask(mask)
  393. return mask
  394. def unzip_file(filename, location, flatten=True):
  395. """
  396. Unzip the file (with path `filename`) to the destination `location`. All
  397. files are written based on system defaults and umask (i.e. permissions are
  398. not preserved), except that regular file members with any execute
  399. permissions (user, group, or world) have "chmod +x" applied after being
  400. written. Note that for windows, any execute changes using os.chmod are
  401. no-ops per the python docs.
  402. """
  403. if not os.path.exists(location):
  404. os.makedirs(location)
  405. zipfp = open(filename, 'rb')
  406. try:
  407. zip = zipfile.ZipFile(zipfp, allowZip64=True)
  408. leading = has_leading_dir(zip.namelist()) and flatten
  409. for info in zip.infolist():
  410. name = info.filename
  411. data = zip.read(name)
  412. fn = name
  413. if leading:
  414. fn = split_leading_dir(name)[1]
  415. fn = os.path.join(location, fn)
  416. dir = os.path.dirname(fn)
  417. if not os.path.exists(dir):
  418. os.makedirs(dir)
  419. if fn.endswith('/') or fn.endswith('\\'):
  420. # A directory
  421. if not os.path.exists(fn):
  422. os.makedirs(fn)
  423. else:
  424. fp = open(fn, 'wb')
  425. try:
  426. fp.write(data)
  427. finally:
  428. fp.close()
  429. mode = info.external_attr >> 16
  430. # if mode and regular file and any execute permissions for
  431. # user/group/world?
  432. if mode and stat.S_ISREG(mode) and mode & 0o111:
  433. # make dest file have execute for user/group/world
  434. # (chmod +x) no-op on windows per python docs
  435. os.chmod(fn, (0o777 - current_umask() | 0o111))
  436. finally:
  437. zipfp.close()
  438. def untar_file(filename, location):
  439. """
  440. Untar the file (with path `filename`) to the destination `location`.
  441. All files are written based on system defaults and umask (i.e. permissions
  442. are not preserved), except that regular file members with any execute
  443. permissions (user, group, or world) have "chmod +x" applied after being
  444. written. Note that for windows, any execute changes using os.chmod are
  445. no-ops per the python docs.
  446. """
  447. if not os.path.exists(location):
  448. os.makedirs(location)
  449. if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'):
  450. mode = 'r:gz'
  451. elif (filename.lower().endswith('.bz2') or
  452. filename.lower().endswith('.tbz')):
  453. mode = 'r:bz2'
  454. elif filename.lower().endswith('.tar'):
  455. mode = 'r'
  456. else:
  457. logger.warning(
  458. 'Cannot determine compression type for file %s', filename,
  459. )
  460. mode = 'r:*'
  461. tar = tarfile.open(filename, mode)
  462. try:
  463. # note: python<=2.5 doesn't seem to know about pax headers, filter them
  464. leading = has_leading_dir([
  465. member.name for member in tar.getmembers()
  466. if member.name != 'pax_global_header'
  467. ])
  468. for member in tar.getmembers():
  469. fn = member.name
  470. if fn == 'pax_global_header':
  471. continue
  472. if leading:
  473. fn = split_leading_dir(fn)[1]
  474. path = os.path.join(location, fn)
  475. if member.isdir():
  476. if not os.path.exists(path):
  477. os.makedirs(path)
  478. elif member.issym():
  479. try:
  480. tar._extract_member(member, path)
  481. except Exception as exc:
  482. # Some corrupt tar files seem to produce this
  483. # (specifically bad symlinks)
  484. logger.warning(
  485. 'In the tar file %s the member %s is invalid: %s',
  486. filename, member.name, exc,
  487. )
  488. continue
  489. else:
  490. try:
  491. fp = tar.extractfile(member)
  492. except (KeyError, AttributeError) as exc:
  493. # Some corrupt tar files seem to produce this
  494. # (specifically bad symlinks)
  495. logger.warning(
  496. 'In the tar file %s the member %s is invalid: %s',
  497. filename, member.name, exc,
  498. )
  499. continue
  500. if not os.path.exists(os.path.dirname(path)):
  501. os.makedirs(os.path.dirname(path))
  502. destfp = open(path, 'wb')
  503. try:
  504. shutil.copyfileobj(fp, destfp)
  505. finally:
  506. destfp.close()
  507. fp.close()
  508. # member have any execute permissions for user/group/world?
  509. if member.mode & 0o111:
  510. # make dest file have execute for user/group/world
  511. # no-op on windows per python docs
  512. os.chmod(path, (0o777 - current_umask() | 0o111))
  513. finally:
  514. tar.close()
  515. def unpack_file(filename, location, content_type, link):
  516. filename = os.path.realpath(filename)
  517. if (content_type == 'application/zip' or
  518. filename.endswith('.zip') or
  519. filename.endswith('.whl') or
  520. zipfile.is_zipfile(filename)):
  521. unzip_file(
  522. filename,
  523. location,
  524. flatten=not filename.endswith('.whl')
  525. )
  526. elif (content_type == 'application/x-gzip' or
  527. tarfile.is_tarfile(filename) or
  528. splitext(filename)[1].lower() in (
  529. '.tar', '.tar.gz', '.tar.bz2', '.tgz', '.tbz')):
  530. untar_file(filename, location)
  531. elif (content_type and content_type.startswith('text/html') and
  532. is_svn_page(file_contents(filename))):
  533. # We don't really care about this
  534. from pip.vcs.subversion import Subversion
  535. Subversion('svn+' + link.url).unpack(location)
  536. else:
  537. # FIXME: handle?
  538. # FIXME: magic signatures?
  539. logger.critical(
  540. 'Cannot unpack file %s (downloaded from %s, content-type: %s); '
  541. 'cannot detect archive format',
  542. filename, location, content_type,
  543. )
  544. raise InstallationError(
  545. 'Cannot determine archive format of %s' % location
  546. )
  547. def remove_tracebacks(output):
  548. pattern = (r'(?:\W+File "(?:.*)", line (?:.*)\W+(?:.*)\W+\^\W+)?'
  549. r'Syntax(?:Error|Warning): (?:.*)')
  550. output = re.sub(pattern, '', output)
  551. if PY2:
  552. return output
  553. # compileall.compile_dir() prints different messages to stdout
  554. # in Python 3
  555. return re.sub(r"\*\*\* Error compiling (?:.*)", '', output)
  556. def call_subprocess(cmd, show_stdout=True,
  557. filter_stdout=None, cwd=None,
  558. raise_on_returncode=True,
  559. command_level=logging.DEBUG, command_desc=None,
  560. extra_environ=None):
  561. if command_desc is None:
  562. cmd_parts = []
  563. for part in cmd:
  564. if ' ' in part or '\n' in part or '"' in part or "'" in part:
  565. part = '"%s"' % part.replace('"', '\\"')
  566. cmd_parts.append(part)
  567. command_desc = ' '.join(cmd_parts)
  568. if show_stdout:
  569. stdout = None
  570. else:
  571. stdout = subprocess.PIPE
  572. logger.log(command_level, "Running command %s", command_desc)
  573. env = os.environ.copy()
  574. if extra_environ:
  575. env.update(extra_environ)
  576. try:
  577. proc = subprocess.Popen(
  578. cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout,
  579. cwd=cwd, env=env)
  580. except Exception as exc:
  581. logger.critical(
  582. "Error %s while executing command %s", exc, command_desc,
  583. )
  584. raise
  585. all_output = []
  586. if stdout is not None:
  587. stdout = remove_tracebacks(console_to_str(proc.stdout.read()))
  588. stdout = cStringIO(stdout)
  589. all_output = stdout.readlines()
  590. if show_stdout:
  591. while 1:
  592. line = stdout.readline()
  593. if not line:
  594. break
  595. line = line.rstrip()
  596. all_output.append(line + '\n')
  597. if filter_stdout:
  598. level = filter_stdout(line)
  599. if isinstance(level, tuple):
  600. level, line = level
  601. logger.log(level, line)
  602. # if not logger.stdout_level_matches(level) and False:
  603. # # TODO(dstufft): Handle progress bar.
  604. # logger.show_progress()
  605. else:
  606. logger.debug(line)
  607. if not all_output:
  608. returned_stdout, returned_stderr = proc.communicate()
  609. all_output = [returned_stdout or '']
  610. proc.wait()
  611. if proc.returncode:
  612. if raise_on_returncode:
  613. if all_output:
  614. logger.info(
  615. 'Complete output from command %s:', command_desc,
  616. )
  617. logger.info(
  618. ''.join(all_output) +
  619. '\n----------------------------------------'
  620. )
  621. raise InstallationError(
  622. 'Command "%s" failed with error code %s in %s'
  623. % (command_desc, proc.returncode, cwd))
  624. else:
  625. logger.warning(
  626. 'Command "%s" had error code %s in %s',
  627. command_desc, proc.returncode, cwd,
  628. )
  629. if stdout is not None:
  630. return remove_tracebacks(''.join(all_output))
  631. def read_text_file(filename):
  632. """Return the contents of *filename*.
  633. Try to decode the file contents with utf-8, the preferred system encoding
  634. (e.g., cp1252 on some Windows machines), and latin1, in that order.
  635. Decoding a byte string with latin1 will never raise an error. In the worst
  636. case, the returned string will contain some garbage characters.
  637. """
  638. with open(filename, 'rb') as fp:
  639. data = fp.read()
  640. encodings = ['utf-8', locale.getpreferredencoding(False), 'latin1']
  641. for enc in encodings:
  642. try:
  643. data = data.decode(enc)
  644. except UnicodeDecodeError:
  645. continue
  646. break
  647. assert type(data) != bytes # Latin1 should have worked.
  648. return data
  649. def _make_build_dir(build_dir):
  650. os.makedirs(build_dir)
  651. write_delete_marker_file(build_dir)
  652. class FakeFile(object):
  653. """Wrap a list of lines in an object with readline() to make
  654. ConfigParser happy."""
  655. def __init__(self, lines):
  656. self._gen = (l for l in lines)
  657. def readline(self):
  658. try:
  659. try:
  660. return next(self._gen)
  661. except NameError:
  662. return self._gen.next()
  663. except StopIteration:
  664. return ''
  665. def __iter__(self):
  666. return self._gen
  667. class StreamWrapper(StringIO):
  668. @classmethod
  669. def from_stream(cls, orig_stream):
  670. cls.orig_stream = orig_stream
  671. return cls()
  672. # compileall.compile_dir() needs stdout.encoding to print to stdout
  673. @property
  674. def encoding(self):
  675. return self.orig_stream.encoding
  676. @contextlib.contextmanager
  677. def captured_output(stream_name):
  678. """Return a context manager used by captured_stdout/stdin/stderr
  679. that temporarily replaces the sys stream *stream_name* with a StringIO.
  680. Taken from Lib/support/__init__.py in the CPython repo.
  681. """
  682. orig_stdout = getattr(sys, stream_name)
  683. setattr(sys, stream_name, StreamWrapper.from_stream(orig_stdout))
  684. try:
  685. yield getattr(sys, stream_name)
  686. finally:
  687. setattr(sys, stream_name, orig_stdout)
  688. def captured_stdout():
  689. """Capture the output of sys.stdout:
  690. with captured_stdout() as stdout:
  691. print('hello')
  692. self.assertEqual(stdout.getvalue(), 'hello\n')
  693. Taken from Lib/support/__init__.py in the CPython repo.
  694. """
  695. return captured_output('stdout')
  696. class cached_property(object):
  697. """A property that is only computed once per instance and then replaces
  698. itself with an ordinary attribute. Deleting the attribute resets the
  699. property.
  700. Source: https://github.com/bottlepy/bottle/blob/0.11.5/bottle.py#L175
  701. """
  702. def __init__(self, func):
  703. self.__doc__ = getattr(func, '__doc__')
  704. self.func = func
  705. def __get__(self, obj, cls):
  706. if obj is None:
  707. # We're being accessed from the class itself, not from an object
  708. return self
  709. value = obj.__dict__[self.func.__name__] = self.func(obj)
  710. return value