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

848 lines
26 KiB

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