Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hhhrrrttt222111
GitHub Repository: hhhrrrttt222111/Dorkify
Path: blob/master/venv/Lib/site-packages/pip/_internal/vcs/subversion.py
811 views
1
# The following comment should be removed at some point in the future.
2
# mypy: disallow-untyped-defs=False
3
4
from __future__ import absolute_import
5
6
import logging
7
import os
8
import re
9
10
from pip._internal.utils.logging import indent_log
11
from pip._internal.utils.misc import (
12
display_path,
13
is_console_interactive,
14
rmtree,
15
split_auth_from_netloc,
16
)
17
from pip._internal.utils.subprocess import make_command
18
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
19
from pip._internal.vcs.versioncontrol import VersionControl, vcs
20
21
_svn_xml_url_re = re.compile('url="([^"]+)"')
22
_svn_rev_re = re.compile(r'committed-rev="(\d+)"')
23
_svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"')
24
_svn_info_xml_url_re = re.compile(r'<url>(.*)</url>')
25
26
27
if MYPY_CHECK_RUNNING:
28
from typing import Optional, Tuple
29
from pip._internal.utils.subprocess import CommandArgs
30
from pip._internal.utils.misc import HiddenText
31
from pip._internal.vcs.versioncontrol import AuthInfo, RevOptions
32
33
34
logger = logging.getLogger(__name__)
35
36
37
class Subversion(VersionControl):
38
name = 'svn'
39
dirname = '.svn'
40
repo_name = 'checkout'
41
schemes = ('svn', 'svn+ssh', 'svn+http', 'svn+https', 'svn+svn')
42
43
@classmethod
44
def should_add_vcs_url_prefix(cls, remote_url):
45
return True
46
47
@staticmethod
48
def get_base_rev_args(rev):
49
return ['-r', rev]
50
51
@classmethod
52
def get_revision(cls, location):
53
"""
54
Return the maximum revision for all files under a given location
55
"""
56
# Note: taken from setuptools.command.egg_info
57
revision = 0
58
59
for base, dirs, files in os.walk(location):
60
if cls.dirname not in dirs:
61
dirs[:] = []
62
continue # no sense walking uncontrolled subdirs
63
dirs.remove(cls.dirname)
64
entries_fn = os.path.join(base, cls.dirname, 'entries')
65
if not os.path.exists(entries_fn):
66
# FIXME: should we warn?
67
continue
68
69
dirurl, localrev = cls._get_svn_url_rev(base)
70
71
if base == location:
72
base = dirurl + '/' # save the root url
73
elif not dirurl or not dirurl.startswith(base):
74
dirs[:] = []
75
continue # not part of the same svn tree, skip it
76
revision = max(revision, localrev)
77
return revision
78
79
@classmethod
80
def get_netloc_and_auth(cls, netloc, scheme):
81
"""
82
This override allows the auth information to be passed to svn via the
83
--username and --password options instead of via the URL.
84
"""
85
if scheme == 'ssh':
86
# The --username and --password options can't be used for
87
# svn+ssh URLs, so keep the auth information in the URL.
88
return super(Subversion, cls).get_netloc_and_auth(netloc, scheme)
89
90
return split_auth_from_netloc(netloc)
91
92
@classmethod
93
def get_url_rev_and_auth(cls, url):
94
# type: (str) -> Tuple[str, Optional[str], AuthInfo]
95
# hotfix the URL scheme after removing svn+ from svn+ssh:// readd it
96
url, rev, user_pass = super(Subversion, cls).get_url_rev_and_auth(url)
97
if url.startswith('ssh://'):
98
url = 'svn+' + url
99
return url, rev, user_pass
100
101
@staticmethod
102
def make_rev_args(username, password):
103
# type: (Optional[str], Optional[HiddenText]) -> CommandArgs
104
extra_args = [] # type: CommandArgs
105
if username:
106
extra_args += ['--username', username]
107
if password:
108
extra_args += ['--password', password]
109
110
return extra_args
111
112
@classmethod
113
def get_remote_url(cls, location):
114
# In cases where the source is in a subdirectory, not alongside
115
# setup.py we have to look up in the location until we find a real
116
# setup.py
117
orig_location = location
118
while not os.path.exists(os.path.join(location, 'setup.py')):
119
last_location = location
120
location = os.path.dirname(location)
121
if location == last_location:
122
# We've traversed up to the root of the filesystem without
123
# finding setup.py
124
logger.warning(
125
"Could not find setup.py for directory %s (tried all "
126
"parent directories)",
127
orig_location,
128
)
129
return None
130
131
return cls._get_svn_url_rev(location)[0]
132
133
@classmethod
134
def _get_svn_url_rev(cls, location):
135
from pip._internal.exceptions import InstallationError
136
137
entries_path = os.path.join(location, cls.dirname, 'entries')
138
if os.path.exists(entries_path):
139
with open(entries_path) as f:
140
data = f.read()
141
else: # subversion >= 1.7 does not have the 'entries' file
142
data = ''
143
144
if (data.startswith('8') or
145
data.startswith('9') or
146
data.startswith('10')):
147
data = list(map(str.splitlines, data.split('\n\x0c\n')))
148
del data[0][0] # get rid of the '8'
149
url = data[0][3]
150
revs = [int(d[9]) for d in data if len(d) > 9 and d[9]] + [0]
151
elif data.startswith('<?xml'):
152
match = _svn_xml_url_re.search(data)
153
if not match:
154
raise ValueError(
155
'Badly formatted data: {data!r}'.format(**locals()))
156
url = match.group(1) # get repository URL
157
revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)] + [0]
158
else:
159
try:
160
# subversion >= 1.7
161
# Note that using get_remote_call_options is not necessary here
162
# because `svn info` is being run against a local directory.
163
# We don't need to worry about making sure interactive mode
164
# is being used to prompt for passwords, because passwords
165
# are only potentially needed for remote server requests.
166
xml = cls.run_command(
167
['info', '--xml', location],
168
show_stdout=False,
169
)
170
url = _svn_info_xml_url_re.search(xml).group(1)
171
revs = [
172
int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml)
173
]
174
except InstallationError:
175
url, revs = None, []
176
177
if revs:
178
rev = max(revs)
179
else:
180
rev = 0
181
182
return url, rev
183
184
@classmethod
185
def is_commit_id_equal(cls, dest, name):
186
"""Always assume the versions don't match"""
187
return False
188
189
def __init__(self, use_interactive=None):
190
# type: (bool) -> None
191
if use_interactive is None:
192
use_interactive = is_console_interactive()
193
self.use_interactive = use_interactive
194
195
# This member is used to cache the fetched version of the current
196
# ``svn`` client.
197
# Special value definitions:
198
# None: Not evaluated yet.
199
# Empty tuple: Could not parse version.
200
self._vcs_version = None # type: Optional[Tuple[int, ...]]
201
202
super(Subversion, self).__init__()
203
204
def call_vcs_version(self):
205
# type: () -> Tuple[int, ...]
206
"""Query the version of the currently installed Subversion client.
207
208
:return: A tuple containing the parts of the version information or
209
``()`` if the version returned from ``svn`` could not be parsed.
210
:raises: BadCommand: If ``svn`` is not installed.
211
"""
212
# Example versions:
213
# svn, version 1.10.3 (r1842928)
214
# compiled Feb 25 2019, 14:20:39 on x86_64-apple-darwin17.0.0
215
# svn, version 1.7.14 (r1542130)
216
# compiled Mar 28 2018, 08:49:13 on x86_64-pc-linux-gnu
217
version_prefix = 'svn, version '
218
version = self.run_command(['--version'], show_stdout=False)
219
if not version.startswith(version_prefix):
220
return ()
221
222
version = version[len(version_prefix):].split()[0]
223
version_list = version.split('.')
224
try:
225
parsed_version = tuple(map(int, version_list))
226
except ValueError:
227
return ()
228
229
return parsed_version
230
231
def get_vcs_version(self):
232
# type: () -> Tuple[int, ...]
233
"""Return the version of the currently installed Subversion client.
234
235
If the version of the Subversion client has already been queried,
236
a cached value will be used.
237
238
:return: A tuple containing the parts of the version information or
239
``()`` if the version returned from ``svn`` could not be parsed.
240
:raises: BadCommand: If ``svn`` is not installed.
241
"""
242
if self._vcs_version is not None:
243
# Use cached version, if available.
244
# If parsing the version failed previously (empty tuple),
245
# do not attempt to parse it again.
246
return self._vcs_version
247
248
vcs_version = self.call_vcs_version()
249
self._vcs_version = vcs_version
250
return vcs_version
251
252
def get_remote_call_options(self):
253
# type: () -> CommandArgs
254
"""Return options to be used on calls to Subversion that contact the server.
255
256
These options are applicable for the following ``svn`` subcommands used
257
in this class.
258
259
- checkout
260
- export
261
- switch
262
- update
263
264
:return: A list of command line arguments to pass to ``svn``.
265
"""
266
if not self.use_interactive:
267
# --non-interactive switch is available since Subversion 0.14.4.
268
# Subversion < 1.8 runs in interactive mode by default.
269
return ['--non-interactive']
270
271
svn_version = self.get_vcs_version()
272
# By default, Subversion >= 1.8 runs in non-interactive mode if
273
# stdin is not a TTY. Since that is how pip invokes SVN, in
274
# call_subprocess(), pip must pass --force-interactive to ensure
275
# the user can be prompted for a password, if required.
276
# SVN added the --force-interactive option in SVN 1.8. Since
277
# e.g. RHEL/CentOS 7, which is supported until 2024, ships with
278
# SVN 1.7, pip should continue to support SVN 1.7. Therefore, pip
279
# can't safely add the option if the SVN version is < 1.8 (or unknown).
280
if svn_version >= (1, 8):
281
return ['--force-interactive']
282
283
return []
284
285
def export(self, location, url):
286
# type: (str, HiddenText) -> None
287
"""Export the svn repository at the url to the destination location"""
288
url, rev_options = self.get_url_rev_options(url)
289
290
logger.info('Exporting svn repository %s to %s', url, location)
291
with indent_log():
292
if os.path.exists(location):
293
# Subversion doesn't like to check out over an existing
294
# directory --force fixes this, but was only added in svn 1.5
295
rmtree(location)
296
cmd_args = make_command(
297
'export', self.get_remote_call_options(),
298
rev_options.to_args(), url, location,
299
)
300
self.run_command(cmd_args, show_stdout=False)
301
302
def fetch_new(self, dest, url, rev_options):
303
# type: (str, HiddenText, RevOptions) -> None
304
rev_display = rev_options.to_display()
305
logger.info(
306
'Checking out %s%s to %s',
307
url,
308
rev_display,
309
display_path(dest),
310
)
311
cmd_args = make_command(
312
'checkout', '-q', self.get_remote_call_options(),
313
rev_options.to_args(), url, dest,
314
)
315
self.run_command(cmd_args)
316
317
def switch(self, dest, url, rev_options):
318
# type: (str, HiddenText, RevOptions) -> None
319
cmd_args = make_command(
320
'switch', self.get_remote_call_options(), rev_options.to_args(),
321
url, dest,
322
)
323
self.run_command(cmd_args)
324
325
def update(self, dest, url, rev_options):
326
# type: (str, HiddenText, RevOptions) -> None
327
cmd_args = make_command(
328
'update', self.get_remote_call_options(), rev_options.to_args(),
329
dest,
330
)
331
self.run_command(cmd_args)
332
333
334
vcs.register(Subversion)
335
336