]>
jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/pip/_internal/vcs/subversion.py
4 from typing
import List
, Optional
, Tuple
6 from pip
._internal
.utils
.misc
import (
9 is_console_interactive
,
11 split_auth_from_netloc
,
13 from pip
._internal
.utils
.subprocess
import CommandArgs
, make_command
14 from pip
._internal
.vcs
.versioncontrol
import (
22 logger
= logging
.getLogger(__name__
)
24 _svn_xml_url_re
= re
.compile('url="([^"]+)"')
25 _svn_rev_re
= re
.compile(r
'committed-rev="(\d+)"')
26 _svn_info_xml_rev_re
= re
.compile(r
'\s*revision="(\d+)"')
27 _svn_info_xml_url_re
= re
.compile(r
"<url>(.*)</url>")
30 class Subversion(VersionControl
):
33 repo_name
= "checkout"
34 schemes
= ("svn+ssh", "svn+http", "svn+https", "svn+svn", "svn+file")
37 def should_add_vcs_url_prefix(cls
, remote_url
: str) -> bool:
41 def get_base_rev_args(rev
: str) -> List
[str]:
45 def get_revision(cls
, location
: str) -> str:
47 Return the maximum revision for all files under a given location
49 # Note: taken from setuptools.command.egg_info
52 for base
, dirs
, _
in os
.walk(location
):
53 if cls
.dirname
not in dirs
:
55 continue # no sense walking uncontrolled subdirs
56 dirs
.remove(cls
.dirname
)
57 entries_fn
= os
.path
.join(base
, cls
.dirname
, "entries")
58 if not os
.path
.exists(entries_fn
):
59 # FIXME: should we warn?
62 dirurl
, localrev
= cls
._get
_svn
_url
_rev
(base
)
65 assert dirurl
is not None
66 base
= dirurl
+ "/" # save the root url
67 elif not dirurl
or not dirurl
.startswith(base
):
69 continue # not part of the same svn tree, skip it
70 revision
= max(revision
, localrev
)
74 def get_netloc_and_auth(
75 cls
, netloc
: str, scheme
: str
76 ) -> Tuple
[str, Tuple
[Optional
[str], Optional
[str]]]:
78 This override allows the auth information to be passed to svn via the
79 --username and --password options instead of via the URL.
82 # The --username and --password options can't be used for
83 # svn+ssh URLs, so keep the auth information in the URL.
84 return super().get_netloc_and_auth(netloc
, scheme
)
86 return split_auth_from_netloc(netloc
)
89 def get_url_rev_and_auth(cls
, url
: str) -> Tuple
[str, Optional
[str], AuthInfo
]:
90 # hotfix the URL scheme after removing svn+ from svn+ssh:// re-add it
91 url
, rev
, user_pass
= super().get_url_rev_and_auth(url
)
92 if url
.startswith("ssh://"):
94 return url
, rev
, user_pass
98 username
: Optional
[str], password
: Optional
[HiddenText
]
100 extra_args
: CommandArgs
= []
102 extra_args
+= ["--username", username
]
104 extra_args
+= ["--password", password
]
109 def get_remote_url(cls
, location
: str) -> str:
110 # In cases where the source is in a subdirectory, we have to look up in
111 # the location until we find a valid project root.
112 orig_location
= location
113 while not is_installable_dir(location
):
114 last_location
= location
115 location
= os
.path
.dirname(location
)
116 if location
== last_location
:
117 # We've traversed up to the root of the filesystem without
118 # finding a Python project.
120 "Could not find Python project for directory %s (tried all "
121 "parent directories)",
124 raise RemoteNotFoundError
126 url
, _rev
= cls
._get
_svn
_url
_rev
(location
)
128 raise RemoteNotFoundError
133 def _get_svn_url_rev(cls
, location
: str) -> Tuple
[Optional
[str], int]:
134 from pip
._internal
.exceptions
import InstallationError
136 entries_path
= os
.path
.join(location
, cls
.dirname
, "entries")
137 if os
.path
.exists(entries_path
):
138 with open(entries_path
) as f
:
140 else: # subversion >= 1.7 does not have the 'entries' file
144 if data
.startswith("8") or data
.startswith("9") or data
.startswith("10"):
145 entries
= list(map(str.splitlines
, data
.split("\n\x0c\n")))
146 del entries
[0][0] # get rid of the '8'
148 revs
= [int(d
[9]) for d
in entries
if len(d
) > 9 and d
[9]] + [0]
149 elif data
.startswith("<?xml"):
150 match
= _svn_xml_url_re
.search(data
)
152 raise ValueError(f
"Badly formatted data: {data!r}")
153 url
= match
.group(1) # get repository URL
154 revs
= [int(m
.group(1)) for m
in _svn_rev_re
.finditer(data
)] + [0]
158 # Note that using get_remote_call_options is not necessary here
159 # because `svn info` is being run against a local directory.
160 # We don't need to worry about making sure interactive mode
161 # is being used to prompt for passwords, because passwords
162 # are only potentially needed for remote server requests.
163 xml
= cls
.run_command(
164 ["info", "--xml", location
],
168 match
= _svn_info_xml_url_re
.search(xml
)
169 assert match
is not None
171 revs
= [int(m
.group(1)) for m
in _svn_info_xml_rev_re
.finditer(xml
)]
172 except InstallationError
:
183 def is_commit_id_equal(cls
, dest
: str, name
: Optional
[str]) -> bool:
184 """Always assume the versions don't match"""
187 def __init__(self
, use_interactive
: Optional
[bool] = None) -> None:
188 if use_interactive
is None:
189 use_interactive
= is_console_interactive()
190 self
.use_interactive
= use_interactive
192 # This member is used to cache the fetched version of the current
194 # Special value definitions:
195 # None: Not evaluated yet.
196 # Empty tuple: Could not parse version.
197 self
._vcs
_version
: Optional
[Tuple
[int, ...]] = None
201 def call_vcs_version(self
) -> Tuple
[int, ...]:
202 """Query the version of the currently installed Subversion client.
204 :return: A tuple containing the parts of the version information or
205 ``()`` if the version returned from ``svn`` could not be parsed.
206 :raises: BadCommand: If ``svn`` is not installed.
209 # svn, version 1.10.3 (r1842928)
210 # compiled Feb 25 2019, 14:20:39 on x86_64-apple-darwin17.0.0
211 # svn, version 1.7.14 (r1542130)
212 # compiled Mar 28 2018, 08:49:13 on x86_64-pc-linux-gnu
213 # svn, version 1.12.0-SlikSvn (SlikSvn/1.12.0)
214 # compiled May 28 2019, 13:44:56 on x86_64-microsoft-windows6.2
215 version_prefix
= "svn, version "
216 version
= self
.run_command(["--version"], show_stdout
=False, stdout_only
=True)
217 if not version
.startswith(version_prefix
):
220 version
= version
[len(version_prefix
) :].split()[0]
221 version_list
= version
.partition("-")[0].split(".")
223 parsed_version
= tuple(map(int, version_list
))
227 return parsed_version
229 def get_vcs_version(self
) -> Tuple
[int, ...]:
230 """Return the version of the currently installed Subversion client.
232 If the version of the Subversion client has already been queried,
233 a cached value will be used.
235 :return: A tuple containing the parts of the version information or
236 ``()`` if the version returned from ``svn`` could not be parsed.
237 :raises: BadCommand: If ``svn`` is not installed.
239 if self
._vcs
_version
is not None:
240 # Use cached version, if available.
241 # If parsing the version failed previously (empty tuple),
242 # do not attempt to parse it again.
243 return self
._vcs
_version
245 vcs_version
= self
.call_vcs_version()
246 self
._vcs
_version
= vcs_version
249 def get_remote_call_options(self
) -> CommandArgs
:
250 """Return options to be used on calls to Subversion that contact the server.
252 These options are applicable for the following ``svn`` subcommands used
259 :return: A list of command line arguments to pass to ``svn``.
261 if not self
.use_interactive
:
262 # --non-interactive switch is available since Subversion 0.14.4.
263 # Subversion < 1.8 runs in interactive mode by default.
264 return ["--non-interactive"]
266 svn_version
= self
.get_vcs_version()
267 # By default, Subversion >= 1.8 runs in non-interactive mode if
268 # stdin is not a TTY. Since that is how pip invokes SVN, in
269 # call_subprocess(), pip must pass --force-interactive to ensure
270 # the user can be prompted for a password, if required.
271 # SVN added the --force-interactive option in SVN 1.8. Since
272 # e.g. RHEL/CentOS 7, which is supported until 2024, ships with
273 # SVN 1.7, pip should continue to support SVN 1.7. Therefore, pip
274 # can't safely add the option if the SVN version is < 1.8 (or unknown).
275 if svn_version
>= (1, 8):
276 return ["--force-interactive"]
281 self
, dest
: str, url
: HiddenText
, rev_options
: RevOptions
, verbosity
: int
283 rev_display
= rev_options
.to_display()
285 "Checking out %s%s to %s",
294 cmd_args
= make_command(
297 self
.get_remote_call_options(),
298 rev_options
.to_args(),
302 self
.run_command(cmd_args
)
304 def switch(self
, dest
: str, url
: HiddenText
, rev_options
: RevOptions
) -> None:
305 cmd_args
= make_command(
307 self
.get_remote_call_options(),
308 rev_options
.to_args(),
312 self
.run_command(cmd_args
)
314 def update(self
, dest
: str, url
: HiddenText
, rev_options
: RevOptions
) -> None:
315 cmd_args
= make_command(
317 self
.get_remote_call_options(),
318 rev_options
.to_args(),
321 self
.run_command(cmd_args
)
324 vcs
.register(Subversion
)