Show More
@@ -0,0 +1,249 b'' | |||
|
1 | # utils.urlutil - code related to [paths] management | |
|
2 | # | |
|
3 | # Copyright 2005-2021 Olivia Mackall <olivia@selenic.com> and others | |
|
4 | # | |
|
5 | # This software may be used and distributed according to the terms of the | |
|
6 | # GNU General Public License version 2 or any later version. | |
|
7 | import os | |
|
8 | ||
|
9 | from ..i18n import _ | |
|
10 | from ..pycompat import ( | |
|
11 | getattr, | |
|
12 | setattr, | |
|
13 | ) | |
|
14 | from .. import ( | |
|
15 | error, | |
|
16 | pycompat, | |
|
17 | util, | |
|
18 | ) | |
|
19 | ||
|
20 | ||
|
21 | class paths(dict): | |
|
22 | """Represents a collection of paths and their configs. | |
|
23 | ||
|
24 | Data is initially derived from ui instances and the config files they have | |
|
25 | loaded. | |
|
26 | """ | |
|
27 | ||
|
28 | def __init__(self, ui): | |
|
29 | dict.__init__(self) | |
|
30 | ||
|
31 | for name, loc in ui.configitems(b'paths', ignoresub=True): | |
|
32 | # No location is the same as not existing. | |
|
33 | if not loc: | |
|
34 | continue | |
|
35 | loc, sub_opts = ui.configsuboptions(b'paths', name) | |
|
36 | self[name] = path(ui, name, rawloc=loc, suboptions=sub_opts) | |
|
37 | ||
|
38 | for name, p in sorted(self.items()): | |
|
39 | p.chain_path(ui, self) | |
|
40 | ||
|
41 | def getpath(self, ui, name, default=None): | |
|
42 | """Return a ``path`` from a string, falling back to default. | |
|
43 | ||
|
44 | ``name`` can be a named path or locations. Locations are filesystem | |
|
45 | paths or URIs. | |
|
46 | ||
|
47 | Returns None if ``name`` is not a registered path, a URI, or a local | |
|
48 | path to a repo. | |
|
49 | """ | |
|
50 | # Only fall back to default if no path was requested. | |
|
51 | if name is None: | |
|
52 | if not default: | |
|
53 | default = () | |
|
54 | elif not isinstance(default, (tuple, list)): | |
|
55 | default = (default,) | |
|
56 | for k in default: | |
|
57 | try: | |
|
58 | return self[k] | |
|
59 | except KeyError: | |
|
60 | continue | |
|
61 | return None | |
|
62 | ||
|
63 | # Most likely empty string. | |
|
64 | # This may need to raise in the future. | |
|
65 | if not name: | |
|
66 | return None | |
|
67 | ||
|
68 | try: | |
|
69 | return self[name] | |
|
70 | except KeyError: | |
|
71 | # Try to resolve as a local path or URI. | |
|
72 | try: | |
|
73 | # we pass the ui instance are warning might need to be issued | |
|
74 | return path(ui, None, rawloc=name) | |
|
75 | except ValueError: | |
|
76 | raise error.RepoError(_(b'repository %s does not exist') % name) | |
|
77 | ||
|
78 | ||
|
79 | _pathsuboptions = {} | |
|
80 | ||
|
81 | ||
|
82 | def pathsuboption(option, attr): | |
|
83 | """Decorator used to declare a path sub-option. | |
|
84 | ||
|
85 | Arguments are the sub-option name and the attribute it should set on | |
|
86 | ``path`` instances. | |
|
87 | ||
|
88 | The decorated function will receive as arguments a ``ui`` instance, | |
|
89 | ``path`` instance, and the string value of this option from the config. | |
|
90 | The function should return the value that will be set on the ``path`` | |
|
91 | instance. | |
|
92 | ||
|
93 | This decorator can be used to perform additional verification of | |
|
94 | sub-options and to change the type of sub-options. | |
|
95 | """ | |
|
96 | ||
|
97 | def register(func): | |
|
98 | _pathsuboptions[option] = (attr, func) | |
|
99 | return func | |
|
100 | ||
|
101 | return register | |
|
102 | ||
|
103 | ||
|
104 | @pathsuboption(b'pushurl', b'pushloc') | |
|
105 | def pushurlpathoption(ui, path, value): | |
|
106 | u = util.url(value) | |
|
107 | # Actually require a URL. | |
|
108 | if not u.scheme: | |
|
109 | ui.warn(_(b'(paths.%s:pushurl not a URL; ignoring)\n') % path.name) | |
|
110 | return None | |
|
111 | ||
|
112 | # Don't support the #foo syntax in the push URL to declare branch to | |
|
113 | # push. | |
|
114 | if u.fragment: | |
|
115 | ui.warn( | |
|
116 | _( | |
|
117 | b'("#fragment" in paths.%s:pushurl not supported; ' | |
|
118 | b'ignoring)\n' | |
|
119 | ) | |
|
120 | % path.name | |
|
121 | ) | |
|
122 | u.fragment = None | |
|
123 | ||
|
124 | return bytes(u) | |
|
125 | ||
|
126 | ||
|
127 | @pathsuboption(b'pushrev', b'pushrev') | |
|
128 | def pushrevpathoption(ui, path, value): | |
|
129 | return value | |
|
130 | ||
|
131 | ||
|
132 | class path(object): | |
|
133 | """Represents an individual path and its configuration.""" | |
|
134 | ||
|
135 | def __init__(self, ui, name, rawloc=None, suboptions=None): | |
|
136 | """Construct a path from its config options. | |
|
137 | ||
|
138 | ``ui`` is the ``ui`` instance the path is coming from. | |
|
139 | ``name`` is the symbolic name of the path. | |
|
140 | ``rawloc`` is the raw location, as defined in the config. | |
|
141 | ``pushloc`` is the raw locations pushes should be made to. | |
|
142 | ||
|
143 | If ``name`` is not defined, we require that the location be a) a local | |
|
144 | filesystem path with a .hg directory or b) a URL. If not, | |
|
145 | ``ValueError`` is raised. | |
|
146 | """ | |
|
147 | if not rawloc: | |
|
148 | raise ValueError(b'rawloc must be defined') | |
|
149 | ||
|
150 | # Locations may define branches via syntax <base>#<branch>. | |
|
151 | u = util.url(rawloc) | |
|
152 | branch = None | |
|
153 | if u.fragment: | |
|
154 | branch = u.fragment | |
|
155 | u.fragment = None | |
|
156 | ||
|
157 | self.url = u | |
|
158 | # the url from the config/command line before dealing with `path://` | |
|
159 | self.raw_url = u.copy() | |
|
160 | self.branch = branch | |
|
161 | ||
|
162 | self.name = name | |
|
163 | self.rawloc = rawloc | |
|
164 | self.loc = b'%s' % u | |
|
165 | ||
|
166 | self._validate_path() | |
|
167 | ||
|
168 | _path, sub_opts = ui.configsuboptions(b'paths', b'*') | |
|
169 | self._own_sub_opts = {} | |
|
170 | if suboptions is not None: | |
|
171 | self._own_sub_opts = suboptions.copy() | |
|
172 | sub_opts.update(suboptions) | |
|
173 | self._all_sub_opts = sub_opts.copy() | |
|
174 | ||
|
175 | self._apply_suboptions(ui, sub_opts) | |
|
176 | ||
|
177 | def chain_path(self, ui, paths): | |
|
178 | if self.url.scheme == b'path': | |
|
179 | assert self.url.path is None | |
|
180 | try: | |
|
181 | subpath = paths[self.url.host] | |
|
182 | except KeyError: | |
|
183 | m = _('cannot use `%s`, "%s" is not a known path') | |
|
184 | m %= (self.rawloc, self.url.host) | |
|
185 | raise error.Abort(m) | |
|
186 | if subpath.raw_url.scheme == b'path': | |
|
187 | m = _('cannot use `%s`, "%s" is also define as a `path://`') | |
|
188 | m %= (self.rawloc, self.url.host) | |
|
189 | raise error.Abort(m) | |
|
190 | self.url = subpath.url | |
|
191 | self.rawloc = subpath.rawloc | |
|
192 | self.loc = subpath.loc | |
|
193 | if self.branch is None: | |
|
194 | self.branch = subpath.branch | |
|
195 | else: | |
|
196 | base = self.rawloc.rsplit(b'#', 1)[0] | |
|
197 | self.rawloc = b'%s#%s' % (base, self.branch) | |
|
198 | suboptions = subpath._all_sub_opts.copy() | |
|
199 | suboptions.update(self._own_sub_opts) | |
|
200 | self._apply_suboptions(ui, suboptions) | |
|
201 | ||
|
202 | def _validate_path(self): | |
|
203 | # When given a raw location but not a symbolic name, validate the | |
|
204 | # location is valid. | |
|
205 | if ( | |
|
206 | not self.name | |
|
207 | and not self.url.scheme | |
|
208 | and not self._isvalidlocalpath(self.loc) | |
|
209 | ): | |
|
210 | raise ValueError( | |
|
211 | b'location is not a URL or path to a local ' | |
|
212 | b'repo: %s' % self.rawloc | |
|
213 | ) | |
|
214 | ||
|
215 | def _apply_suboptions(self, ui, sub_options): | |
|
216 | # Now process the sub-options. If a sub-option is registered, its | |
|
217 | # attribute will always be present. The value will be None if there | |
|
218 | # was no valid sub-option. | |
|
219 | for suboption, (attr, func) in pycompat.iteritems(_pathsuboptions): | |
|
220 | if suboption not in sub_options: | |
|
221 | setattr(self, attr, None) | |
|
222 | continue | |
|
223 | ||
|
224 | value = func(ui, self, sub_options[suboption]) | |
|
225 | setattr(self, attr, value) | |
|
226 | ||
|
227 | def _isvalidlocalpath(self, path): | |
|
228 | """Returns True if the given path is a potentially valid repository. | |
|
229 | This is its own function so that extensions can change the definition of | |
|
230 | 'valid' in this case (like when pulling from a git repo into a hg | |
|
231 | one).""" | |
|
232 | try: | |
|
233 | return os.path.isdir(os.path.join(path, b'.hg')) | |
|
234 | # Python 2 may return TypeError. Python 3, ValueError. | |
|
235 | except (TypeError, ValueError): | |
|
236 | return False | |
|
237 | ||
|
238 | @property | |
|
239 | def suboptions(self): | |
|
240 | """Return sub-options and their values for this path. | |
|
241 | ||
|
242 | This is intended to be used for presentation purposes. | |
|
243 | """ | |
|
244 | d = {} | |
|
245 | for subopt, (attr, _func) in pycompat.iteritems(_pathsuboptions): | |
|
246 | value = getattr(self, attr) | |
|
247 | if value is not None: | |
|
248 | d[subopt] = value | |
|
249 | return d |
@@ -26,7 +26,6 b' from .node import hex' | |||
|
26 | 26 | from .pycompat import ( |
|
27 | 27 | getattr, |
|
28 | 28 | open, |
|
29 | setattr, | |
|
30 | 29 | ) |
|
31 | 30 | |
|
32 | 31 | from . import ( |
@@ -48,6 +47,7 b' from .utils import (' | |||
|
48 | 47 | procutil, |
|
49 | 48 | resourceutil, |
|
50 | 49 | stringutil, |
|
50 | urlutil, | |
|
51 | 51 | ) |
|
52 | 52 | |
|
53 | 53 | urlreq = util.urlreq |
@@ -1049,7 +1049,7 b' class ui(object):' | |||
|
1049 | 1049 | |
|
1050 | 1050 | @util.propertycache |
|
1051 | 1051 | def paths(self): |
|
1052 | return paths(self) | |
|
1052 | return urlutil.paths(self) | |
|
1053 | 1053 | |
|
1054 | 1054 | def getpath(self, *args, **kwargs): |
|
1055 | 1055 | """see paths.getpath for details |
@@ -2180,237 +2180,6 b' class ui(object):' | |||
|
2180 | 2180 | return util._estimatememory() |
|
2181 | 2181 | |
|
2182 | 2182 | |
|
2183 | class paths(dict): | |
|
2184 | """Represents a collection of paths and their configs. | |
|
2185 | ||
|
2186 | Data is initially derived from ui instances and the config files they have | |
|
2187 | loaded. | |
|
2188 | """ | |
|
2189 | ||
|
2190 | def __init__(self, ui): | |
|
2191 | dict.__init__(self) | |
|
2192 | ||
|
2193 | for name, loc in ui.configitems(b'paths', ignoresub=True): | |
|
2194 | # No location is the same as not existing. | |
|
2195 | if not loc: | |
|
2196 | continue | |
|
2197 | loc, sub_opts = ui.configsuboptions(b'paths', name) | |
|
2198 | self[name] = path(ui, name, rawloc=loc, suboptions=sub_opts) | |
|
2199 | ||
|
2200 | for name, p in sorted(self.items()): | |
|
2201 | p.chain_path(ui, self) | |
|
2202 | ||
|
2203 | def getpath(self, ui, name, default=None): | |
|
2204 | """Return a ``path`` from a string, falling back to default. | |
|
2205 | ||
|
2206 | ``name`` can be a named path or locations. Locations are filesystem | |
|
2207 | paths or URIs. | |
|
2208 | ||
|
2209 | Returns None if ``name`` is not a registered path, a URI, or a local | |
|
2210 | path to a repo. | |
|
2211 | """ | |
|
2212 | # Only fall back to default if no path was requested. | |
|
2213 | if name is None: | |
|
2214 | if not default: | |
|
2215 | default = () | |
|
2216 | elif not isinstance(default, (tuple, list)): | |
|
2217 | default = (default,) | |
|
2218 | for k in default: | |
|
2219 | try: | |
|
2220 | return self[k] | |
|
2221 | except KeyError: | |
|
2222 | continue | |
|
2223 | return None | |
|
2224 | ||
|
2225 | # Most likely empty string. | |
|
2226 | # This may need to raise in the future. | |
|
2227 | if not name: | |
|
2228 | return None | |
|
2229 | ||
|
2230 | try: | |
|
2231 | return self[name] | |
|
2232 | except KeyError: | |
|
2233 | # Try to resolve as a local path or URI. | |
|
2234 | try: | |
|
2235 | # we pass the ui instance are warning might need to be issued | |
|
2236 | return path(ui, None, rawloc=name) | |
|
2237 | except ValueError: | |
|
2238 | raise error.RepoError(_(b'repository %s does not exist') % name) | |
|
2239 | ||
|
2240 | ||
|
2241 | _pathsuboptions = {} | |
|
2242 | ||
|
2243 | ||
|
2244 | def pathsuboption(option, attr): | |
|
2245 | """Decorator used to declare a path sub-option. | |
|
2246 | ||
|
2247 | Arguments are the sub-option name and the attribute it should set on | |
|
2248 | ``path`` instances. | |
|
2249 | ||
|
2250 | The decorated function will receive as arguments a ``ui`` instance, | |
|
2251 | ``path`` instance, and the string value of this option from the config. | |
|
2252 | The function should return the value that will be set on the ``path`` | |
|
2253 | instance. | |
|
2254 | ||
|
2255 | This decorator can be used to perform additional verification of | |
|
2256 | sub-options and to change the type of sub-options. | |
|
2257 | """ | |
|
2258 | ||
|
2259 | def register(func): | |
|
2260 | _pathsuboptions[option] = (attr, func) | |
|
2261 | return func | |
|
2262 | ||
|
2263 | return register | |
|
2264 | ||
|
2265 | ||
|
2266 | @pathsuboption(b'pushurl', b'pushloc') | |
|
2267 | def pushurlpathoption(ui, path, value): | |
|
2268 | u = util.url(value) | |
|
2269 | # Actually require a URL. | |
|
2270 | if not u.scheme: | |
|
2271 | ui.warn(_(b'(paths.%s:pushurl not a URL; ignoring)\n') % path.name) | |
|
2272 | return None | |
|
2273 | ||
|
2274 | # Don't support the #foo syntax in the push URL to declare branch to | |
|
2275 | # push. | |
|
2276 | if u.fragment: | |
|
2277 | ui.warn( | |
|
2278 | _( | |
|
2279 | b'("#fragment" in paths.%s:pushurl not supported; ' | |
|
2280 | b'ignoring)\n' | |
|
2281 | ) | |
|
2282 | % path.name | |
|
2283 | ) | |
|
2284 | u.fragment = None | |
|
2285 | ||
|
2286 | return bytes(u) | |
|
2287 | ||
|
2288 | ||
|
2289 | @pathsuboption(b'pushrev', b'pushrev') | |
|
2290 | def pushrevpathoption(ui, path, value): | |
|
2291 | return value | |
|
2292 | ||
|
2293 | ||
|
2294 | class path(object): | |
|
2295 | """Represents an individual path and its configuration.""" | |
|
2296 | ||
|
2297 | def __init__(self, ui, name, rawloc=None, suboptions=None): | |
|
2298 | """Construct a path from its config options. | |
|
2299 | ||
|
2300 | ``ui`` is the ``ui`` instance the path is coming from. | |
|
2301 | ``name`` is the symbolic name of the path. | |
|
2302 | ``rawloc`` is the raw location, as defined in the config. | |
|
2303 | ``pushloc`` is the raw locations pushes should be made to. | |
|
2304 | ||
|
2305 | If ``name`` is not defined, we require that the location be a) a local | |
|
2306 | filesystem path with a .hg directory or b) a URL. If not, | |
|
2307 | ``ValueError`` is raised. | |
|
2308 | """ | |
|
2309 | if not rawloc: | |
|
2310 | raise ValueError(b'rawloc must be defined') | |
|
2311 | ||
|
2312 | # Locations may define branches via syntax <base>#<branch>. | |
|
2313 | u = util.url(rawloc) | |
|
2314 | branch = None | |
|
2315 | if u.fragment: | |
|
2316 | branch = u.fragment | |
|
2317 | u.fragment = None | |
|
2318 | ||
|
2319 | self.url = u | |
|
2320 | # the url from the config/command line before dealing with `path://` | |
|
2321 | self.raw_url = u.copy() | |
|
2322 | self.branch = branch | |
|
2323 | ||
|
2324 | self.name = name | |
|
2325 | self.rawloc = rawloc | |
|
2326 | self.loc = b'%s' % u | |
|
2327 | ||
|
2328 | self._validate_path() | |
|
2329 | ||
|
2330 | _path, sub_opts = ui.configsuboptions(b'paths', b'*') | |
|
2331 | self._own_sub_opts = {} | |
|
2332 | if suboptions is not None: | |
|
2333 | self._own_sub_opts = suboptions.copy() | |
|
2334 | sub_opts.update(suboptions) | |
|
2335 | self._all_sub_opts = sub_opts.copy() | |
|
2336 | ||
|
2337 | self._apply_suboptions(ui, sub_opts) | |
|
2338 | ||
|
2339 | def chain_path(self, ui, paths): | |
|
2340 | if self.url.scheme == b'path': | |
|
2341 | assert self.url.path is None | |
|
2342 | try: | |
|
2343 | subpath = paths[self.url.host] | |
|
2344 | except KeyError: | |
|
2345 | m = _('cannot use `%s`, "%s" is not a known path') | |
|
2346 | m %= (self.rawloc, self.url.host) | |
|
2347 | raise error.Abort(m) | |
|
2348 | if subpath.raw_url.scheme == b'path': | |
|
2349 | m = _('cannot use `%s`, "%s" is also define as a `path://`') | |
|
2350 | m %= (self.rawloc, self.url.host) | |
|
2351 | raise error.Abort(m) | |
|
2352 | self.url = subpath.url | |
|
2353 | self.rawloc = subpath.rawloc | |
|
2354 | self.loc = subpath.loc | |
|
2355 | if self.branch is None: | |
|
2356 | self.branch = subpath.branch | |
|
2357 | else: | |
|
2358 | base = self.rawloc.rsplit(b'#', 1)[0] | |
|
2359 | self.rawloc = b'%s#%s' % (base, self.branch) | |
|
2360 | suboptions = subpath._all_sub_opts.copy() | |
|
2361 | suboptions.update(self._own_sub_opts) | |
|
2362 | self._apply_suboptions(ui, suboptions) | |
|
2363 | ||
|
2364 | def _validate_path(self): | |
|
2365 | # When given a raw location but not a symbolic name, validate the | |
|
2366 | # location is valid. | |
|
2367 | if ( | |
|
2368 | not self.name | |
|
2369 | and not self.url.scheme | |
|
2370 | and not self._isvalidlocalpath(self.loc) | |
|
2371 | ): | |
|
2372 | raise ValueError( | |
|
2373 | b'location is not a URL or path to a local ' | |
|
2374 | b'repo: %s' % self.rawloc | |
|
2375 | ) | |
|
2376 | ||
|
2377 | def _apply_suboptions(self, ui, sub_options): | |
|
2378 | # Now process the sub-options. If a sub-option is registered, its | |
|
2379 | # attribute will always be present. The value will be None if there | |
|
2380 | # was no valid sub-option. | |
|
2381 | for suboption, (attr, func) in pycompat.iteritems(_pathsuboptions): | |
|
2382 | if suboption not in sub_options: | |
|
2383 | setattr(self, attr, None) | |
|
2384 | continue | |
|
2385 | ||
|
2386 | value = func(ui, self, sub_options[suboption]) | |
|
2387 | setattr(self, attr, value) | |
|
2388 | ||
|
2389 | def _isvalidlocalpath(self, path): | |
|
2390 | """Returns True if the given path is a potentially valid repository. | |
|
2391 | This is its own function so that extensions can change the definition of | |
|
2392 | 'valid' in this case (like when pulling from a git repo into a hg | |
|
2393 | one).""" | |
|
2394 | try: | |
|
2395 | return os.path.isdir(os.path.join(path, b'.hg')) | |
|
2396 | # Python 2 may return TypeError. Python 3, ValueError. | |
|
2397 | except (TypeError, ValueError): | |
|
2398 | return False | |
|
2399 | ||
|
2400 | @property | |
|
2401 | def suboptions(self): | |
|
2402 | """Return sub-options and their values for this path. | |
|
2403 | ||
|
2404 | This is intended to be used for presentation purposes. | |
|
2405 | """ | |
|
2406 | d = {} | |
|
2407 | for subopt, (attr, _func) in pycompat.iteritems(_pathsuboptions): | |
|
2408 | value = getattr(self, attr) | |
|
2409 | if value is not None: | |
|
2410 | d[subopt] = value | |
|
2411 | return d | |
|
2412 | ||
|
2413 | ||
|
2414 | 2183 | # we instantiate one globally shared progress bar to avoid |
|
2415 | 2184 | # competing progress bars when multiple UI objects get created |
|
2416 | 2185 | _progresssingleton = None |
General Comments 0
You need to be logged in to leave comments.
Login now