Development of an internal social media platform with personalised dashboards for students
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.

subversion.py 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. from __future__ import absolute_import
  2. import logging
  3. import os
  4. import re
  5. from pip._internal.models.link import Link
  6. from pip._internal.utils.logging import indent_log
  7. from pip._internal.utils.misc import (
  8. display_path, make_vcs_requirement_url, rmtree, split_auth_from_netloc,
  9. )
  10. from pip._internal.vcs import VersionControl, vcs
  11. _svn_xml_url_re = re.compile('url="([^"]+)"')
  12. _svn_rev_re = re.compile(r'committed-rev="(\d+)"')
  13. _svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"')
  14. _svn_info_xml_url_re = re.compile(r'<url>(.*)</url>')
  15. logger = logging.getLogger(__name__)
  16. class Subversion(VersionControl):
  17. name = 'svn'
  18. dirname = '.svn'
  19. repo_name = 'checkout'
  20. schemes = ('svn', 'svn+ssh', 'svn+http', 'svn+https', 'svn+svn')
  21. def get_base_rev_args(self, rev):
  22. return ['-r', rev]
  23. def export(self, location):
  24. """Export the svn repository at the url to the destination location"""
  25. url, rev_options = self.get_url_rev_options(self.url)
  26. logger.info('Exporting svn repository %s to %s', url, location)
  27. with indent_log():
  28. if os.path.exists(location):
  29. # Subversion doesn't like to check out over an existing
  30. # directory --force fixes this, but was only added in svn 1.5
  31. rmtree(location)
  32. cmd_args = ['export'] + rev_options.to_args() + [url, location]
  33. self.run_command(cmd_args, show_stdout=False)
  34. def fetch_new(self, dest, url, rev_options):
  35. rev_display = rev_options.to_display()
  36. logger.info(
  37. 'Checking out %s%s to %s',
  38. url,
  39. rev_display,
  40. display_path(dest),
  41. )
  42. cmd_args = ['checkout', '-q'] + rev_options.to_args() + [url, dest]
  43. self.run_command(cmd_args)
  44. def switch(self, dest, url, rev_options):
  45. cmd_args = ['switch'] + rev_options.to_args() + [url, dest]
  46. self.run_command(cmd_args)
  47. def update(self, dest, url, rev_options):
  48. cmd_args = ['update'] + rev_options.to_args() + [dest]
  49. self.run_command(cmd_args)
  50. def get_location(self, dist, dependency_links):
  51. for url in dependency_links:
  52. egg_fragment = Link(url).egg_fragment
  53. if not egg_fragment:
  54. continue
  55. if '-' in egg_fragment:
  56. # FIXME: will this work when a package has - in the name?
  57. key = '-'.join(egg_fragment.split('-')[:-1]).lower()
  58. else:
  59. key = egg_fragment
  60. if key == dist.key:
  61. return url.split('#', 1)[0]
  62. return None
  63. def get_revision(self, location):
  64. """
  65. Return the maximum revision for all files under a given location
  66. """
  67. # Note: taken from setuptools.command.egg_info
  68. revision = 0
  69. for base, dirs, files in os.walk(location):
  70. if self.dirname not in dirs:
  71. dirs[:] = []
  72. continue # no sense walking uncontrolled subdirs
  73. dirs.remove(self.dirname)
  74. entries_fn = os.path.join(base, self.dirname, 'entries')
  75. if not os.path.exists(entries_fn):
  76. # FIXME: should we warn?
  77. continue
  78. dirurl, localrev = self._get_svn_url_rev(base)
  79. if base == location:
  80. base = dirurl + '/' # save the root url
  81. elif not dirurl or not dirurl.startswith(base):
  82. dirs[:] = []
  83. continue # not part of the same svn tree, skip it
  84. revision = max(revision, localrev)
  85. return revision
  86. def get_netloc_and_auth(self, netloc, scheme):
  87. """
  88. This override allows the auth information to be passed to svn via the
  89. --username and --password options instead of via the URL.
  90. """
  91. if scheme == 'ssh':
  92. # The --username and --password options can't be used for
  93. # svn+ssh URLs, so keep the auth information in the URL.
  94. return super(Subversion, self).get_netloc_and_auth(
  95. netloc, scheme)
  96. return split_auth_from_netloc(netloc)
  97. def get_url_rev_and_auth(self, url):
  98. # hotfix the URL scheme after removing svn+ from svn+ssh:// readd it
  99. url, rev, user_pass = super(Subversion, self).get_url_rev_and_auth(url)
  100. if url.startswith('ssh://'):
  101. url = 'svn+' + url
  102. return url, rev, user_pass
  103. def make_rev_args(self, username, password):
  104. extra_args = []
  105. if username:
  106. extra_args += ['--username', username]
  107. if password:
  108. extra_args += ['--password', password]
  109. return extra_args
  110. def get_url(self, location):
  111. # In cases where the source is in a subdirectory, not alongside
  112. # setup.py we have to look up in the location until we find a real
  113. # setup.py
  114. orig_location = location
  115. while not os.path.exists(os.path.join(location, 'setup.py')):
  116. last_location = location
  117. location = os.path.dirname(location)
  118. if location == last_location:
  119. # We've traversed up to the root of the filesystem without
  120. # finding setup.py
  121. logger.warning(
  122. "Could not find setup.py for directory %s (tried all "
  123. "parent directories)",
  124. orig_location,
  125. )
  126. return None
  127. return self._get_svn_url_rev(location)[0]
  128. def _get_svn_url_rev(self, location):
  129. from pip._internal.exceptions import InstallationError
  130. entries_path = os.path.join(location, self.dirname, 'entries')
  131. if os.path.exists(entries_path):
  132. with open(entries_path) as f:
  133. data = f.read()
  134. else: # subversion >= 1.7 does not have the 'entries' file
  135. data = ''
  136. if (data.startswith('8') or
  137. data.startswith('9') or
  138. data.startswith('10')):
  139. data = list(map(str.splitlines, data.split('\n\x0c\n')))
  140. del data[0][0] # get rid of the '8'
  141. url = data[0][3]
  142. revs = [int(d[9]) for d in data if len(d) > 9 and d[9]] + [0]
  143. elif data.startswith('<?xml'):
  144. match = _svn_xml_url_re.search(data)
  145. if not match:
  146. raise ValueError('Badly formatted data: %r' % data)
  147. url = match.group(1) # get repository URL
  148. revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)] + [0]
  149. else:
  150. try:
  151. # subversion >= 1.7
  152. xml = self.run_command(
  153. ['info', '--xml', location],
  154. show_stdout=False,
  155. )
  156. url = _svn_info_xml_url_re.search(xml).group(1)
  157. revs = [
  158. int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml)
  159. ]
  160. except InstallationError:
  161. url, revs = None, []
  162. if revs:
  163. rev = max(revs)
  164. else:
  165. rev = 0
  166. return url, rev
  167. def get_src_requirement(self, dist, location):
  168. repo = self.get_url(location)
  169. if repo is None:
  170. return None
  171. repo = 'svn+' + repo
  172. rev = self.get_revision(location)
  173. # FIXME: why not project name?
  174. egg_project_name = dist.egg_name().split('-', 1)[0]
  175. return make_vcs_requirement_url(repo, rev, egg_project_name)
  176. def is_commit_id_equal(self, dest, name):
  177. """Always assume the versions don't match"""
  178. return False
  179. vcs.register(Subversion)