Mercurial > public > mercurial-scm > hg-stable
diff mercurial/ui.py @ 46906:33524c46a092
urlutil: extract `path` related code into a new module
They are a lot of code related to url and path handling scattering into various
large module. To consolidate the code before doing more change (for defining
"multi-path"), we gather it together.
Differential Revision: https://phab.mercurial-scm.org/D10373
author | Pierre-Yves David <pierre-yves.david@octobus.net> |
---|---|
date | Sun, 11 Apr 2021 23:54:35 +0200 |
parents | 395cf404e76a |
children | ffd3e823a7e5 |
line wrap: on
line diff
--- a/mercurial/ui.py Sat Apr 10 15:30:32 2021 +0200 +++ b/mercurial/ui.py Sun Apr 11 23:54:35 2021 +0200 @@ -26,7 +26,6 @@ from .pycompat import ( getattr, open, - setattr, ) from . import ( @@ -48,6 +47,7 @@ procutil, resourceutil, stringutil, + urlutil, ) urlreq = util.urlreq @@ -1049,7 +1049,7 @@ @util.propertycache def paths(self): - return paths(self) + return urlutil.paths(self) def getpath(self, *args, **kwargs): """see paths.getpath for details @@ -2180,237 +2180,6 @@ return util._estimatememory() -class paths(dict): - """Represents a collection of paths and their configs. - - Data is initially derived from ui instances and the config files they have - loaded. - """ - - def __init__(self, ui): - dict.__init__(self) - - for name, loc in ui.configitems(b'paths', ignoresub=True): - # No location is the same as not existing. - if not loc: - continue - loc, sub_opts = ui.configsuboptions(b'paths', name) - self[name] = path(ui, name, rawloc=loc, suboptions=sub_opts) - - for name, p in sorted(self.items()): - p.chain_path(ui, self) - - def getpath(self, ui, name, default=None): - """Return a ``path`` from a string, falling back to default. - - ``name`` can be a named path or locations. Locations are filesystem - paths or URIs. - - Returns None if ``name`` is not a registered path, a URI, or a local - path to a repo. - """ - # Only fall back to default if no path was requested. - if name is None: - if not default: - default = () - elif not isinstance(default, (tuple, list)): - default = (default,) - for k in default: - try: - return self[k] - except KeyError: - continue - return None - - # Most likely empty string. - # This may need to raise in the future. - if not name: - return None - - try: - return self[name] - except KeyError: - # Try to resolve as a local path or URI. - try: - # we pass the ui instance are warning might need to be issued - return path(ui, None, rawloc=name) - except ValueError: - raise error.RepoError(_(b'repository %s does not exist') % name) - - -_pathsuboptions = {} - - -def pathsuboption(option, attr): - """Decorator used to declare a path sub-option. - - Arguments are the sub-option name and the attribute it should set on - ``path`` instances. - - The decorated function will receive as arguments a ``ui`` instance, - ``path`` instance, and the string value of this option from the config. - The function should return the value that will be set on the ``path`` - instance. - - This decorator can be used to perform additional verification of - sub-options and to change the type of sub-options. - """ - - def register(func): - _pathsuboptions[option] = (attr, func) - return func - - return register - - -@pathsuboption(b'pushurl', b'pushloc') -def pushurlpathoption(ui, path, value): - u = util.url(value) - # Actually require a URL. - if not u.scheme: - ui.warn(_(b'(paths.%s:pushurl not a URL; ignoring)\n') % path.name) - return None - - # Don't support the #foo syntax in the push URL to declare branch to - # push. - if u.fragment: - ui.warn( - _( - b'("#fragment" in paths.%s:pushurl not supported; ' - b'ignoring)\n' - ) - % path.name - ) - u.fragment = None - - return bytes(u) - - -@pathsuboption(b'pushrev', b'pushrev') -def pushrevpathoption(ui, path, value): - return value - - -class path(object): - """Represents an individual path and its configuration.""" - - def __init__(self, ui, name, rawloc=None, suboptions=None): - """Construct a path from its config options. - - ``ui`` is the ``ui`` instance the path is coming from. - ``name`` is the symbolic name of the path. - ``rawloc`` is the raw location, as defined in the config. - ``pushloc`` is the raw locations pushes should be made to. - - If ``name`` is not defined, we require that the location be a) a local - filesystem path with a .hg directory or b) a URL. If not, - ``ValueError`` is raised. - """ - if not rawloc: - raise ValueError(b'rawloc must be defined') - - # Locations may define branches via syntax <base>#<branch>. - u = util.url(rawloc) - branch = None - if u.fragment: - branch = u.fragment - u.fragment = None - - self.url = u - # the url from the config/command line before dealing with `path://` - self.raw_url = u.copy() - self.branch = branch - - self.name = name - self.rawloc = rawloc - self.loc = b'%s' % u - - self._validate_path() - - _path, sub_opts = ui.configsuboptions(b'paths', b'*') - self._own_sub_opts = {} - if suboptions is not None: - self._own_sub_opts = suboptions.copy() - sub_opts.update(suboptions) - self._all_sub_opts = sub_opts.copy() - - self._apply_suboptions(ui, sub_opts) - - def chain_path(self, ui, paths): - if self.url.scheme == b'path': - assert self.url.path is None - try: - subpath = paths[self.url.host] - except KeyError: - m = _('cannot use `%s`, "%s" is not a known path') - m %= (self.rawloc, self.url.host) - raise error.Abort(m) - if subpath.raw_url.scheme == b'path': - m = _('cannot use `%s`, "%s" is also define as a `path://`') - m %= (self.rawloc, self.url.host) - raise error.Abort(m) - self.url = subpath.url - self.rawloc = subpath.rawloc - self.loc = subpath.loc - if self.branch is None: - self.branch = subpath.branch - else: - base = self.rawloc.rsplit(b'#', 1)[0] - self.rawloc = b'%s#%s' % (base, self.branch) - suboptions = subpath._all_sub_opts.copy() - suboptions.update(self._own_sub_opts) - self._apply_suboptions(ui, suboptions) - - def _validate_path(self): - # When given a raw location but not a symbolic name, validate the - # location is valid. - if ( - not self.name - and not self.url.scheme - and not self._isvalidlocalpath(self.loc) - ): - raise ValueError( - b'location is not a URL or path to a local ' - b'repo: %s' % self.rawloc - ) - - def _apply_suboptions(self, ui, sub_options): - # Now process the sub-options. If a sub-option is registered, its - # attribute will always be present. The value will be None if there - # was no valid sub-option. - for suboption, (attr, func) in pycompat.iteritems(_pathsuboptions): - if suboption not in sub_options: - setattr(self, attr, None) - continue - - value = func(ui, self, sub_options[suboption]) - setattr(self, attr, value) - - def _isvalidlocalpath(self, path): - """Returns True if the given path is a potentially valid repository. - This is its own function so that extensions can change the definition of - 'valid' in this case (like when pulling from a git repo into a hg - one).""" - try: - return os.path.isdir(os.path.join(path, b'.hg')) - # Python 2 may return TypeError. Python 3, ValueError. - except (TypeError, ValueError): - return False - - @property - def suboptions(self): - """Return sub-options and their values for this path. - - This is intended to be used for presentation purposes. - """ - d = {} - for subopt, (attr, _func) in pycompat.iteritems(_pathsuboptions): - value = getattr(self, attr) - if value is not None: - d[subopt] = value - return d - - # we instantiate one globally shared progress bar to avoid # competing progress bars when multiple UI objects get created _progresssingleton = None