localrepo: validate supported requirements in makelocalrepository()
authorGregory Szorc <gregory.szorc@gmail.com>
Wed, 12 Sep 2018 14:54:17 -0700
changeset 39693 5b8e9b2060ef
parent 39692 6a3162ed881d
child 39694 6192980553b4
localrepo: validate supported requirements in makelocalrepository() This should be a glorified code move. I did take the opportunity to refactor things. We now have a separate function for gathering requirements and one for validating them. I also mode cosmetic changes to the code, such as not using abbreviations and using a set instead of list to model missing requirements. Differential Revision: https://phab.mercurial-scm.org/D4569
mercurial/localrepo.py
--- a/mercurial/localrepo.py	Wed Sep 12 14:45:52 2018 -0700
+++ b/mercurial/localrepo.py	Wed Sep 12 14:54:17 2018 -0700
@@ -430,6 +430,12 @@
     else:
         extensions.loadall(ui)
 
+    supportedrequirements = gathersupportedrequirements(ui)
+    ensurerequirementsrecognized(requirements, supportedrequirements)
+
+    # At this point, we know we should be capable of opening the repository.
+    # Now get on with doing that.
+
     return localrepository(
         baseui=baseui,
         ui=ui,
@@ -437,8 +443,57 @@
         wdirvfs=wdirvfs,
         hgvfs=hgvfs,
         requirements=requirements,
+        supportedrequirements=supportedrequirements,
         intents=intents)
 
+def gathersupportedrequirements(ui):
+    """Determine the complete set of recognized requirements."""
+    # Start with all requirements supported by this file.
+    supported = set(localrepository._basesupported)
+
+    # Execute ``featuresetupfuncs`` entries if they belong to an extension
+    # relevant to this ui instance.
+    modules = {m.__name__ for n, m in extensions.extensions(ui)}
+
+    for fn in featuresetupfuncs:
+        if fn.__module__ in modules:
+            fn(ui, supported)
+
+    # Add derived requirements from registered compression engines.
+    for name in util.compengines:
+        engine = util.compengines[name]
+        if engine.revlogheader():
+            supported.add(b'exp-compression-%s' % name)
+
+    return supported
+
+def ensurerequirementsrecognized(requirements, supported):
+    """Validate that a set of local requirements is recognized.
+
+    Receives a set of requirements. Raises an ``error.RepoError`` if there
+    exists any requirement in that set that currently loaded code doesn't
+    recognize.
+
+    Returns a set of supported requirements.
+    """
+    missing = set()
+
+    for requirement in requirements:
+        if requirement in supported:
+            continue
+
+        if not requirement or not requirement[0:1].isalnum():
+            raise error.RequirementError(_(b'.hg/requires file is corrupt'))
+
+        missing.add(requirement)
+
+    if missing:
+        raise error.RequirementError(
+            _(b'repository requires features unknown to this Mercurial: %s') %
+            b' '.join(sorted(missing)),
+            hint=_(b'see https://mercurial-scm.org/wiki/MissingRequirement '
+                   b'for more information'))
+
 @interfaceutil.implementer(repository.completelocalrepository)
 class localrepository(object):
 
@@ -490,7 +545,7 @@
     }
 
     def __init__(self, baseui, ui, origroot, wdirvfs, hgvfs, requirements,
-                 intents=None):
+                 supportedrequirements, intents=None):
         """Create a new local repository instance.
 
         Most callers should use ``hg.repository()``, ``localrepo.instance()``,
@@ -517,6 +572,10 @@
         requirements
            ``set`` of bytestrings representing repository opening requirements.
 
+        supportedrequirements
+           ``set`` of bytestrings representing repository requirements that we
+           know how to open. May be a supetset of ``requirements``.
+
         intents
            ``set`` of system strings indicating what this repo will be used
            for.
@@ -530,6 +589,8 @@
         # vfs rooted at .hg/. Used to access most non-store paths.
         self.vfs = hgvfs
         self.path = hgvfs.base
+        self.requirements = requirements
+        self.supported = supportedrequirements
 
         self.filtername = None
         # svfs: usually rooted at .hg/store, used to access repository history
@@ -545,41 +606,8 @@
         # This list it to be filled by extension during repo setup
         self._phasedefaults = []
 
-        if featuresetupfuncs:
-            self.supported = set(self._basesupported) # use private copy
-            extmods = set(m.__name__ for n, m
-                          in extensions.extensions(self.ui))
-            for setupfunc in featuresetupfuncs:
-                if setupfunc.__module__ in extmods:
-                    setupfunc(self.ui, self.supported)
-        else:
-            self.supported = self._basesupported
         color.setup(self.ui)
 
-        # Add compression engines.
-        for name in util.compengines:
-            engine = util.compengines[name]
-            if engine.revlogheader():
-                self.supported.add('exp-compression-%s' % name)
-
-        # Validate that all seen repository requirements are supported.
-        missingrequirements = []
-        for r in requirements:
-            if r not in self.supported:
-                if not r or not r[0:1].isalnum():
-                    raise error.RequirementError(
-                        _(".hg/requires file is corrupt"))
-                missingrequirements.append(r)
-        missingrequirements.sort()
-        if missingrequirements:
-            raise error.RequirementError(
-                _("repository requires features unknown to this Mercurial: %s")
-                % " ".join(missingrequirements),
-                hint=_("see https://mercurial-scm.org/wiki/MissingRequirement"
-                       " for more information"))
-
-        self.requirements = requirements
-
         cachepath = self.vfs.join('cache')
         self.sharedpath = self.path
         try: