PageRenderTime 51ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/pip/util.py

https://github.com/ptthiem/pip
Python | 719 lines | 674 code | 15 blank | 30 comment | 21 complexity | 2953b8381c17c8e64957a817e53d9497 MD5 | raw file
  1. import sys
  2. import shutil
  3. import os
  4. import stat
  5. import re
  6. import posixpath
  7. import pkg_resources
  8. import zipfile
  9. import tarfile
  10. import subprocess
  11. import textwrap
  12. from pip.exceptions import InstallationError, BadCommand, PipError
  13. from pip.backwardcompat import(WindowsError, string_types, raw_input,
  14. console_to_str, user_site, PermissionError)
  15. from pip.locations import site_packages, running_under_virtualenv, virtualenv_no_global
  16. from pip.log import logger
  17. from pip._vendor.distlib import version
  18. __all__ = ['rmtree', 'display_path', 'backup_dir',
  19. 'find_command', 'ask', 'Inf',
  20. 'normalize_name', 'splitext',
  21. 'format_size', 'is_installable_dir',
  22. 'is_svn_page', 'file_contents',
  23. 'split_leading_dir', 'has_leading_dir',
  24. 'make_path_relative', 'normalize_path',
  25. 'renames', 'get_terminal_size', 'get_prog',
  26. 'unzip_file', 'untar_file', 'create_download_cache_folder',
  27. 'cache_download', 'unpack_file', 'call_subprocess']
  28. def get_prog():
  29. try:
  30. if os.path.basename(sys.argv[0]) in ('__main__.py', '-c'):
  31. return "%s -m pip" % sys.executable
  32. except (AttributeError, TypeError, IndexError):
  33. pass
  34. return 'pip'
  35. def rmtree(dir, ignore_errors=False):
  36. shutil.rmtree(dir, ignore_errors=ignore_errors,
  37. onerror=rmtree_errorhandler)
  38. def rmtree_errorhandler(func, path, exc_info):
  39. """On Windows, the files in .svn are read-only, so when rmtree() tries to
  40. remove them, an exception is thrown. We catch that here, remove the
  41. read-only attribute, and hopefully continue without problems."""
  42. exctype, value = exc_info[:2]
  43. if not ((exctype is WindowsError and value.args[0] == 5) or #others
  44. (exctype is OSError and value.args[0] == 13) or #python2.4
  45. (exctype is PermissionError and value.args[3] == 5) #python3.3
  46. ):
  47. raise
  48. # file type should currently be read only
  49. if ((os.stat(path).st_mode & stat.S_IREAD) != stat.S_IREAD):
  50. raise
  51. # convert to read/write
  52. os.chmod(path, stat.S_IWRITE)
  53. # use the original function to repeat the operation
  54. func(path)
  55. def display_path(path):
  56. """Gives the display value for a given path, making it relative to cwd
  57. if possible."""
  58. path = os.path.normcase(os.path.abspath(path))
  59. if path.startswith(os.getcwd() + os.path.sep):
  60. path = '.' + path[len(os.getcwd()):]
  61. return path
  62. def backup_dir(dir, ext='.bak'):
  63. """Figure out the name of a directory to back up the given dir to
  64. (adding .bak, .bak2, etc)"""
  65. n = 1
  66. extension = ext
  67. while os.path.exists(dir + extension):
  68. n += 1
  69. extension = ext + str(n)
  70. return dir + extension
  71. def find_command(cmd, paths=None, pathext=None):
  72. """Searches the PATH for the given command and returns its path"""
  73. if paths is None:
  74. paths = os.environ.get('PATH', '').split(os.pathsep)
  75. if isinstance(paths, string_types):
  76. paths = [paths]
  77. # check if there are funny path extensions for executables, e.g. Windows
  78. if pathext is None:
  79. pathext = get_pathext()
  80. pathext = [ext for ext in pathext.lower().split(os.pathsep) if len(ext)]
  81. # don't use extensions if the command ends with one of them
  82. if os.path.splitext(cmd)[1].lower() in pathext:
  83. pathext = ['']
  84. # check if we find the command on PATH
  85. for path in paths:
  86. # try without extension first
  87. cmd_path = os.path.join(path, cmd)
  88. for ext in pathext:
  89. # then including the extension
  90. cmd_path_ext = cmd_path + ext
  91. if os.path.isfile(cmd_path_ext):
  92. return cmd_path_ext
  93. if os.path.isfile(cmd_path):
  94. return cmd_path
  95. raise BadCommand('Cannot find command %r' % cmd)
  96. def get_pathext(default_pathext=None):
  97. """Returns the path extensions from environment or a default"""
  98. if default_pathext is None:
  99. default_pathext = os.pathsep.join(['.COM', '.EXE', '.BAT', '.CMD'])
  100. pathext = os.environ.get('PATHEXT', default_pathext)
  101. return pathext
  102. def ask_path_exists(message, options):
  103. for action in os.environ.get('PIP_EXISTS_ACTION', '').split():
  104. if action in options:
  105. return action
  106. return ask(message, options)
  107. def ask(message, options):
  108. """Ask the message interactively, with the given possible responses"""
  109. while 1:
  110. if os.environ.get('PIP_NO_INPUT'):
  111. raise Exception('No input was expected ($PIP_NO_INPUT set); question: %s' % message)
  112. response = raw_input(message)
  113. response = response.strip().lower()
  114. if response not in options:
  115. print('Your response (%r) was not one of the expected responses: %s' % (
  116. response, ', '.join(options)))
  117. else:
  118. return response
  119. class _Inf(object):
  120. """I am bigger than everything!"""
  121. def __eq__(self, other):
  122. if self is other:
  123. return True
  124. else:
  125. return False
  126. def __ne__(self, other):
  127. return not self.__eq__(other)
  128. def __lt__(self, other):
  129. return False
  130. def __le__(self, other):
  131. return False
  132. def __gt__(self, other):
  133. return True
  134. def __ge__(self, other):
  135. return True
  136. def __repr__(self):
  137. return 'Inf'
  138. Inf = _Inf() #this object is not currently used as a sortable in our code
  139. del _Inf
  140. _normalize_re = re.compile(r'[^a-z]', re.I)
  141. def normalize_name(name):
  142. return _normalize_re.sub('-', name.lower())
  143. def format_size(bytes):
  144. if bytes > 1000*1000:
  145. return '%.1fMB' % (bytes/1000.0/1000)
  146. elif bytes > 10*1000:
  147. return '%ikB' % (bytes/1000)
  148. elif bytes > 1000:
  149. return '%.1fkB' % (bytes/1000.0)
  150. else:
  151. return '%ibytes' % bytes
  152. def is_installable_dir(path):
  153. """Return True if `path` is a directory containing a setup.py file."""
  154. if not os.path.isdir(path):
  155. return False
  156. setup_py = os.path.join(path, 'setup.py')
  157. if os.path.isfile(setup_py):
  158. return True
  159. return False
  160. def is_svn_page(html):
  161. """Returns true if the page appears to be the index page of an svn repository"""
  162. return (re.search(r'<title>[^<]*Revision \d+:', html)
  163. and re.search(r'Powered by (?:<a[^>]*?>)?Subversion', html, re.I))
  164. def file_contents(filename):
  165. fp = open(filename, 'rb')
  166. try:
  167. return fp.read().decode('utf-8')
  168. finally:
  169. fp.close()
  170. def split_leading_dir(path):
  171. path = str(path)
  172. path = path.lstrip('/').lstrip('\\')
  173. if '/' in path and (('\\' in path and path.find('/') < path.find('\\'))
  174. or '\\' not in path):
  175. return path.split('/', 1)
  176. elif '\\' in path:
  177. return path.split('\\', 1)
  178. else:
  179. return path, ''
  180. def has_leading_dir(paths):
  181. """Returns true if all the paths have the same leading path name
  182. (i.e., everything is in one subdirectory in an archive)"""
  183. common_prefix = None
  184. for path in paths:
  185. prefix, rest = split_leading_dir(path)
  186. if not prefix:
  187. return False
  188. elif common_prefix is None:
  189. common_prefix = prefix
  190. elif prefix != common_prefix:
  191. return False
  192. return True
  193. def make_path_relative(path, rel_to):
  194. """
  195. Make a filename relative, where the filename path, and it is
  196. relative to rel_to
  197. >>> make_relative_path('/usr/share/something/a-file.pth',
  198. ... '/usr/share/another-place/src/Directory')
  199. '../../../something/a-file.pth'
  200. >>> make_relative_path('/usr/share/something/a-file.pth',
  201. ... '/home/user/src/Directory')
  202. '../../../usr/share/something/a-file.pth'
  203. >>> make_relative_path('/usr/share/a-file.pth', '/usr/share/')
  204. 'a-file.pth'
  205. """
  206. path_filename = os.path.basename(path)
  207. path = os.path.dirname(path)
  208. path = os.path.normpath(os.path.abspath(path))
  209. rel_to = os.path.normpath(os.path.abspath(rel_to))
  210. path_parts = path.strip(os.path.sep).split(os.path.sep)
  211. rel_to_parts = rel_to.strip(os.path.sep).split(os.path.sep)
  212. while path_parts and rel_to_parts and path_parts[0] == rel_to_parts[0]:
  213. path_parts.pop(0)
  214. rel_to_parts.pop(0)
  215. full_parts = ['..']*len(rel_to_parts) + path_parts + [path_filename]
  216. if full_parts == ['']:
  217. return '.' + os.path.sep
  218. return os.path.sep.join(full_parts)
  219. def normalize_path(path):
  220. """
  221. Convert a path to its canonical, case-normalized, absolute version.
  222. """
  223. return os.path.normcase(os.path.realpath(os.path.expanduser(path)))
  224. def splitext(path):
  225. """Like os.path.splitext, but take off .tar too"""
  226. base, ext = posixpath.splitext(path)
  227. if base.lower().endswith('.tar'):
  228. ext = base[-4:] + ext
  229. base = base[:-4]
  230. return base, ext
  231. def renames(old, new):
  232. """Like os.renames(), but handles renaming across devices."""
  233. # Implementation borrowed from os.renames().
  234. head, tail = os.path.split(new)
  235. if head and tail and not os.path.exists(head):
  236. os.makedirs(head)
  237. shutil.move(old, new)
  238. head, tail = os.path.split(old)
  239. if head and tail:
  240. try:
  241. os.removedirs(head)
  242. except OSError:
  243. pass
  244. def is_local(path):
  245. """
  246. Return True if path is within sys.prefix, if we're running in a virtualenv.
  247. If we're not in a virtualenv, all paths are considered "local."
  248. """
  249. if not running_under_virtualenv():
  250. return True
  251. return normalize_path(path).startswith(normalize_path(sys.prefix))
  252. def dist_is_local(dist):
  253. """
  254. Return True if given Distribution object is installed locally
  255. (i.e. within current virtualenv).
  256. Always True if we're not in a virtualenv.
  257. """
  258. return is_local(dist_location(dist))
  259. def dist_in_usersite(dist):
  260. """
  261. Return True if given Distribution is installed in user site.
  262. """
  263. if user_site:
  264. return normalize_path(dist_location(dist)).startswith(normalize_path(user_site))
  265. else:
  266. return False
  267. def dist_in_site_packages(dist):
  268. """
  269. Return True if given Distribution is installed in distutils.sysconfig.get_python_lib().
  270. """
  271. return normalize_path(dist_location(dist)).startswith(normalize_path(site_packages))
  272. def dist_is_editable(dist):
  273. """Is distribution an editable install?"""
  274. #TODO: factor out determining editableness out of FrozenRequirement
  275. from pip import FrozenRequirement
  276. req = FrozenRequirement.from_dist(dist, [])
  277. return req.editable
  278. def get_installed_distributions(local_only=True,
  279. skip=('setuptools', 'pip', 'python', 'distribute', 'wsgiref'),
  280. include_editables=True,
  281. editables_only=False):
  282. """
  283. Return a list of installed Distribution objects.
  284. If ``local_only`` is True (default), only return installations
  285. local to the current virtualenv, if in a virtualenv.
  286. ``skip`` argument is an iterable of lower-case project names to
  287. ignore; defaults to ('setuptools', 'pip', 'python'). [FIXME also
  288. skip virtualenv?]
  289. If ``editables`` is False, don't report editables.
  290. If ``editables_only`` is True , only report editables.
  291. """
  292. if local_only:
  293. local_test = dist_is_local
  294. else:
  295. local_test = lambda d: True
  296. if include_editables:
  297. editable_test = lambda d: True
  298. else:
  299. editable_test = lambda d: not dist_is_editable(d)
  300. if editables_only:
  301. editables_only_test = lambda d: dist_is_editable(d)
  302. else:
  303. editables_only_test = lambda d: True
  304. return [d for d in pkg_resources.working_set
  305. if local_test(d)
  306. and d.key not in skip
  307. and editable_test(d)
  308. and editables_only_test(d)
  309. ]
  310. def egg_link_path(dist):
  311. """
  312. Return the path for the .egg-link file if it exists, otherwise, None.
  313. There's 3 scenarios:
  314. 1) not in a virtualenv
  315. try to find in site.USER_SITE, then site_packages
  316. 2) in a no-global virtualenv
  317. try to find in site_packages
  318. 3) in a yes-global virtualenv
  319. try to find in site_packages, then site.USER_SITE (don't look in global location)
  320. For #1 and #3, there could be odd cases, where there's an egg-link in 2 locations.
  321. This method will just return the first one found.
  322. """
  323. sites = []
  324. if running_under_virtualenv():
  325. if virtualenv_no_global():
  326. sites.append(site_packages)
  327. else:
  328. sites.append(site_packages)
  329. if user_site:
  330. sites.append(user_site)
  331. else:
  332. if user_site:
  333. sites.append(user_site)
  334. sites.append(site_packages)
  335. for site in sites:
  336. egglink = os.path.join(site, dist.project_name) + '.egg-link'
  337. if os.path.isfile(egglink):
  338. return egglink
  339. def dist_location(dist):
  340. """
  341. Get the site-packages location of this distribution. Generally
  342. this is dist.location, except in the case of develop-installed
  343. packages, where dist.location is the source code location, and we
  344. want to know where the egg-link file is.
  345. """
  346. egg_link = egg_link_path(dist)
  347. if egg_link:
  348. return egg_link
  349. return dist.location
  350. def get_terminal_size():
  351. """Returns a tuple (x, y) representing the width(x) and the height(x)
  352. in characters of the terminal window."""
  353. def ioctl_GWINSZ(fd):
  354. try:
  355. import fcntl
  356. import termios
  357. import struct
  358. cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,
  359. '1234'))
  360. except:
  361. return None
  362. if cr == (0, 0):
  363. return None
  364. if cr == (0, 0):
  365. return None
  366. return cr
  367. cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
  368. if not cr:
  369. try:
  370. fd = os.open(os.ctermid(), os.O_RDONLY)
  371. cr = ioctl_GWINSZ(fd)
  372. os.close(fd)
  373. except:
  374. pass
  375. if not cr:
  376. cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80))
  377. return int(cr[1]), int(cr[0])
  378. def current_umask():
  379. """Get the current umask which involves having to set it temporarily."""
  380. mask = os.umask(0)
  381. os.umask(mask)
  382. return mask
  383. def unzip_file(filename, location, flatten=True):
  384. """
  385. Unzip the file (with path `filename`) to the destination `location`. All
  386. files are written based on system defaults and umask (i.e. permissions are
  387. not preserved), except that regular file members with any execute
  388. permissions (user, group, or world) have "chmod +x" applied after being
  389. written. Note that for windows, any execute changes using os.chmod are
  390. no-ops per the python docs.
  391. """
  392. if not os.path.exists(location):
  393. os.makedirs(location)
  394. zipfp = open(filename, 'rb')
  395. try:
  396. zip = zipfile.ZipFile(zipfp)
  397. leading = has_leading_dir(zip.namelist()) and flatten
  398. for info in zip.infolist():
  399. name = info.filename
  400. data = zip.read(name)
  401. fn = name
  402. if leading:
  403. fn = split_leading_dir(name)[1]
  404. fn = os.path.join(location, fn)
  405. dir = os.path.dirname(fn)
  406. if not os.path.exists(dir):
  407. os.makedirs(dir)
  408. if fn.endswith('/') or fn.endswith('\\'):
  409. # A directory
  410. if not os.path.exists(fn):
  411. os.makedirs(fn)
  412. else:
  413. fp = open(fn, 'wb')
  414. try:
  415. fp.write(data)
  416. finally:
  417. fp.close()
  418. mode = info.external_attr >> 16
  419. # if mode and regular file and any execute permissions for user/group/world?
  420. if mode and stat.S_ISREG(mode) and mode & 0o111:
  421. # make dest file have execute for user/group/world (chmod +x)
  422. # no-op on windows per python docs
  423. os.chmod(fn, (0o777-current_umask() | 0o111))
  424. finally:
  425. zipfp.close()
  426. def untar_file(filename, location):
  427. """
  428. Untar the file (with path `filename`) to the destination `location`.
  429. All files are written based on system defaults and umask (i.e. permissions
  430. are not preserved), except that regular file members with any execute
  431. permissions (user, group, or world) have "chmod +x" applied after being
  432. written. Note that for windows, any execute changes using os.chmod are
  433. no-ops per the python docs.
  434. """
  435. if not os.path.exists(location):
  436. os.makedirs(location)
  437. if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'):
  438. mode = 'r:gz'
  439. elif filename.lower().endswith('.bz2') or filename.lower().endswith('.tbz'):
  440. mode = 'r:bz2'
  441. elif filename.lower().endswith('.tar'):
  442. mode = 'r'
  443. else:
  444. logger.warn('Cannot determine compression type for file %s' % filename)
  445. mode = 'r:*'
  446. tar = tarfile.open(filename, mode)
  447. try:
  448. # note: python<=2.5 doesnt seem to know about pax headers, filter them
  449. leading = has_leading_dir([
  450. member.name for member in tar.getmembers()
  451. if member.name != 'pax_global_header'
  452. ])
  453. for member in tar.getmembers():
  454. fn = member.name
  455. if fn == 'pax_global_header':
  456. continue
  457. if leading:
  458. fn = split_leading_dir(fn)[1]
  459. path = os.path.join(location, fn)
  460. if member.isdir():
  461. if not os.path.exists(path):
  462. os.makedirs(path)
  463. elif member.issym():
  464. try:
  465. tar._extract_member(member, path)
  466. except:
  467. e = sys.exc_info()[1]
  468. # Some corrupt tar files seem to produce this
  469. # (specifically bad symlinks)
  470. logger.warn(
  471. 'In the tar file %s the member %s is invalid: %s'
  472. % (filename, member.name, e))
  473. continue
  474. else:
  475. try:
  476. fp = tar.extractfile(member)
  477. except (KeyError, AttributeError):
  478. e = sys.exc_info()[1]
  479. # Some corrupt tar files seem to produce this
  480. # (specifically bad symlinks)
  481. logger.warn(
  482. 'In the tar file %s the member %s is invalid: %s'
  483. % (filename, member.name, e))
  484. continue
  485. if not os.path.exists(os.path.dirname(path)):
  486. os.makedirs(os.path.dirname(path))
  487. destfp = open(path, 'wb')
  488. try:
  489. shutil.copyfileobj(fp, destfp)
  490. finally:
  491. destfp.close()
  492. fp.close()
  493. # member have any execute permissions for user/group/world?
  494. if member.mode & 0o111:
  495. # make dest file have execute for user/group/world
  496. # no-op on windows per python docs
  497. os.chmod(path, (0o777-current_umask() | 0o111))
  498. finally:
  499. tar.close()
  500. def create_download_cache_folder(folder):
  501. logger.indent -= 2
  502. logger.notify('Creating supposed download cache at %s' % folder)
  503. logger.indent += 2
  504. os.makedirs(folder)
  505. def cache_download(target_file, temp_location, content_type):
  506. logger.notify('Storing download in cache at %s' % display_path(target_file))
  507. shutil.copyfile(temp_location, target_file)
  508. fp = open(target_file+'.content-type', 'w')
  509. fp.write(content_type)
  510. fp.close()
  511. def unpack_file(filename, location, content_type, link):
  512. filename = os.path.realpath(filename)
  513. if (content_type == 'application/zip'
  514. or filename.endswith('.zip')
  515. or filename.endswith('.pybundle')
  516. or filename.endswith('.whl')
  517. or zipfile.is_zipfile(filename)):
  518. unzip_file(filename, location, flatten=not filename.endswith(('.pybundle', '.whl')))
  519. elif (content_type == 'application/x-gzip'
  520. or tarfile.is_tarfile(filename)
  521. or splitext(filename)[1].lower() in ('.tar', '.tar.gz', '.tar.bz2', '.tgz', '.tbz')):
  522. untar_file(filename, location)
  523. elif (content_type and content_type.startswith('text/html')
  524. and is_svn_page(file_contents(filename))):
  525. # We don't really care about this
  526. from pip.vcs.subversion import Subversion
  527. Subversion('svn+' + link.url).unpack(location)
  528. else:
  529. ## FIXME: handle?
  530. ## FIXME: magic signatures?
  531. logger.fatal('Cannot unpack file %s (downloaded from %s, content-type: %s); cannot detect archive format'
  532. % (filename, location, content_type))
  533. raise InstallationError('Cannot determine archive format of %s' % location)
  534. def call_subprocess(cmd, show_stdout=True,
  535. filter_stdout=None, cwd=None,
  536. raise_on_returncode=True,
  537. command_level=logger.DEBUG, command_desc=None,
  538. extra_environ=None):
  539. if command_desc is None:
  540. cmd_parts = []
  541. for part in cmd:
  542. if ' ' in part or '\n' in part or '"' in part or "'" in part:
  543. part = '"%s"' % part.replace('"', '\\"')
  544. cmd_parts.append(part)
  545. command_desc = ' '.join(cmd_parts)
  546. if show_stdout:
  547. stdout = None
  548. else:
  549. stdout = subprocess.PIPE
  550. logger.log(command_level, "Running command %s" % command_desc)
  551. env = os.environ.copy()
  552. if extra_environ:
  553. env.update(extra_environ)
  554. try:
  555. proc = subprocess.Popen(
  556. cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout,
  557. cwd=cwd, env=env)
  558. except Exception:
  559. e = sys.exc_info()[1]
  560. logger.fatal(
  561. "Error %s while executing command %s" % (e, command_desc))
  562. raise
  563. all_output = []
  564. if stdout is not None:
  565. stdout = proc.stdout
  566. while 1:
  567. line = console_to_str(stdout.readline())
  568. if not line:
  569. break
  570. line = line.rstrip()
  571. all_output.append(line + '\n')
  572. if filter_stdout:
  573. level = filter_stdout(line)
  574. if isinstance(level, tuple):
  575. level, line = level
  576. logger.log(level, line)
  577. if not logger.stdout_level_matches(level):
  578. logger.show_progress()
  579. else:
  580. logger.info(line)
  581. else:
  582. returned_stdout, returned_stderr = proc.communicate()
  583. all_output = [returned_stdout or '']
  584. proc.wait()
  585. if proc.returncode:
  586. if raise_on_returncode:
  587. if all_output:
  588. logger.notify('Complete output from command %s:' % command_desc)
  589. logger.notify('\n'.join(all_output) + '\n----------------------------------------')
  590. raise InstallationError(
  591. "Command %s failed with error code %s in %s"
  592. % (command_desc, proc.returncode, cwd))
  593. else:
  594. logger.warn(
  595. "Command %s had error code %s in %s"
  596. % (command_desc, proc.returncode, cwd))
  597. if stdout is not None:
  598. return ''.join(all_output)
  599. def is_prerelease(vers):
  600. """
  601. Attempt to determine if this is a pre-release using PEP386/PEP426 rules.
  602. Will return True if it is a pre-release and False if not. Versions are
  603. assumed to be a pre-release if they cannot be parsed.
  604. """
  605. normalized = version._suggest_normalized_version(vers)
  606. if normalized is None:
  607. # Cannot normalize, assume it is a pre-release
  608. return True
  609. parsed = version._normalized_key(normalized)
  610. return any([any([y in set(["a", "b", "c", "rc", "dev"]) for y in x]) for x in parsed])