misc.py 27 KB

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