D4642: localrepo: iteratively derive local repository type

indygreg (Gregory Szorc) phabricator at mercurial-scm.org
Sun Sep 23 05:39:02 EDT 2018


This revision was automatically updated to reflect the committed changes.
Closed by commit rHGe4e881572382: localrepo: iteratively derive local repository type (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D4642?vs=11176&id=11268

REVISION DETAIL
  https://phab.mercurial-scm.org/D4642

AFFECTED FILES
  mercurial/localrepo.py
  mercurial/repository.py
  mercurial/statichttprepo.py
  tests/test-check-interfaces.py

CHANGE DETAILS

diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py
--- a/tests/test-check-interfaces.py
+++ b/tests/test-check-interfaces.py
@@ -142,8 +142,10 @@
     ziverify.verifyClass(repository.ipeerbase, unionrepo.unionpeer)
     checkzobject(unionrepo.unionpeer(dummyrepo()))
 
-    ziverify.verifyClass(repository.completelocalrepository,
+    ziverify.verifyClass(repository.ilocalrepositorymain,
                          localrepo.localrepository)
+    ziverify.verifyClass(repository.ilocalrepositoryfilestorage,
+                         localrepo.revlogfilestorage)
     repo = localrepo.makelocalrepository(ui, rootdir)
     checkzobject(repo)
 
diff --git a/mercurial/statichttprepo.py b/mercurial/statichttprepo.py
--- a/mercurial/statichttprepo.py
+++ b/mercurial/statichttprepo.py
@@ -134,7 +134,8 @@
     def canpush(self):
         return False
 
-class statichttprepository(localrepo.localrepository):
+class statichttprepository(localrepo.localrepository,
+                           localrepo.revlogfilestorage):
     supported = localrepo.localrepository._basesupported
 
     def __init__(self, ui, path):
diff --git a/mercurial/repository.py b/mercurial/repository.py
--- a/mercurial/repository.py
+++ b/mercurial/repository.py
@@ -1222,8 +1222,21 @@
         Raises ``error.LookupError`` if the node is not known.
         """
 
-class completelocalrepository(interfaceutil.Interface):
-    """Monolithic interface for local repositories.
+class ilocalrepositoryfilestorage(interfaceutil.Interface):
+    """Local repository sub-interface providing access to tracked file storage.
+
+    This interface defines how a repository accesses storage for a single
+    tracked file path.
+    """
+
+    def file(f):
+        """Obtain a filelog for a tracked path.
+
+        The returned type conforms to the ``ifilestorage`` interface.
+        """
+
+class ilocalrepositorymain(interfaceutil.Interface):
+    """Main interface for local repositories.
 
     This currently captures the reality of things - not how things should be.
     """
@@ -1439,12 +1452,6 @@
     def wjoin(f, *insidef):
         """Calls self.vfs.reljoin(self.root, f, *insidef)"""
 
-    def file(f):
-        """Obtain a filelog for a tracked path.
-
-        The returned type conforms to the ``ifilestorage`` interface.
-        """
-
     def setparents(p1, p2):
         """Set the parent nodes of the working directory."""
 
@@ -1572,3 +1579,7 @@
 
     def savecommitmessage(text):
         pass
+
+class completelocalrepository(ilocalrepositorymain,
+                              ilocalrepositoryfilestorage):
+    """Complete interface for a local repository."""
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -380,11 +380,32 @@
     """Create a local repository object.
 
     Given arguments needed to construct a local repository, this function
-    derives a type suitable for representing that repository and returns an
-    instance of it.
+    performs various early repository loading functionality (such as
+    reading the ``.hg/requires`` and ``.hg/hgrc`` files), validates that
+    the repository can be opened, derives a type suitable for representing
+    that repository, and returns an instance of it.
 
     The returned object conforms to the ``repository.completelocalrepository``
     interface.
+
+    The repository type is derived by calling a series of factory functions
+    for each aspect/interface of the final repository. These are defined by
+    ``REPO_INTERFACES``.
+
+    Each factory function is called to produce a type implementing a specific
+    interface. The cumulative list of returned types will be combined into a
+    new type and that type will be instantiated to represent the local
+    repository.
+
+    The factory functions each receive various state that may be consulted
+    as part of deriving a type.
+
+    Extensions should wrap these factory functions to customize repository type
+    creation. Note that an extension's wrapped function may be called even if
+    that extension is not loaded for the repo being constructed. Extensions
+    should check if their ``__name__`` appears in the
+    ``extensionmodulenames`` set passed to the factory function and no-op if
+    not.
     """
     ui = baseui.copy()
     # Prevent copying repo configuration.
@@ -430,6 +451,9 @@
     else:
         extensions.loadall(ui)
 
+    # Set of module names of extensions loaded for this repository.
+    extensionmodulenames = {m.__name__ for n, m in extensions.extensions(ui)}
+
     supportedrequirements = gathersupportedrequirements(ui)
 
     # We first validate the requirements are known.
@@ -490,7 +514,46 @@
     cachevfs = vfsmod.vfs(cachepath, cacheaudited=True)
     cachevfs.createmode = store.createmode
 
-    return localrepository(
+    # Now resolve the type for the repository object. We do this by repeatedly
+    # calling a factory function to produces types for specific aspects of the
+    # repo's operation. The aggregate returned types are used as base classes
+    # for a dynamically-derived type, which will represent our new repository.
+
+    bases = []
+    extrastate = {}
+
+    for iface, fn in REPO_INTERFACES:
+        # We pass all potentially useful state to give extensions tons of
+        # flexibility.
+        typ = fn(ui=ui,
+                 intents=intents,
+                 requirements=requirements,
+                 wdirvfs=wdirvfs,
+                 hgvfs=hgvfs,
+                 store=store,
+                 storevfs=storevfs,
+                 storeoptions=storevfs.options,
+                 cachevfs=cachevfs,
+                 extensionmodulenames=extensionmodulenames,
+                 extrastate=extrastate,
+                 baseclasses=bases)
+
+        if not isinstance(typ, type):
+            raise error.ProgrammingError('unable to construct type for %s' %
+                                         iface)
+
+        bases.append(typ)
+
+    # type() allows you to use characters in type names that wouldn't be
+    # recognized as Python symbols in source code. We abuse that to add
+    # rich information about our constructed repo.
+    name = pycompat.sysstr(b'derivedrepo:%s<%s>' % (
+        wdirvfs.base,
+        b','.join(sorted(requirements))))
+
+    cls = type(name, tuple(bases), {})
+
+    return cls(
         baseui=baseui,
         ui=ui,
         origroot=path,
@@ -666,8 +729,47 @@
 
     return options
 
- at interfaceutil.implementer(repository.completelocalrepository)
+def makemain(**kwargs):
+    """Produce a type conforming to ``ilocalrepositorymain``."""
+    return localrepository
+
+ at interfaceutil.implementer(repository.ilocalrepositoryfilestorage)
+class revlogfilestorage(object):
+    """File storage when using revlogs."""
+
+    def file(self, path):
+        if path[0] == b'/':
+            path = path[1:]
+
+        return filelog.filelog(self.svfs, path)
+
+def makefilestorage(requirements, **kwargs):
+    """Produce a type conforming to ``ilocalrepositoryfilestorage``."""
+    return revlogfilestorage
+
+# List of repository interfaces and factory functions for them. Each
+# will be called in order during ``makelocalrepository()`` to iteratively
+# derive the final type for a local repository instance.
+REPO_INTERFACES = [
+    (repository.ilocalrepositorymain, makemain),
+    (repository.ilocalrepositoryfilestorage, makefilestorage),
+]
+
+ at interfaceutil.implementer(repository.ilocalrepositorymain)
 class localrepository(object):
+    """Main class for representing local repositories.
+
+    All local repositories are instances of this class.
+
+    Constructed on its own, instances of this class are not usable as
+    repository objects. To obtain a usable repository object, call
+    ``hg.repository()``, ``localrepo.instance()``, or
+    ``localrepo.makelocalrepository()``. The latter is the lowest-level.
+    ``instance()`` adds support for creating new repositories.
+    ``hg.repository()`` adds more extension integration, including calling
+    ``reposetup()``. Generally speaking, ``hg.repository()`` should be
+    used.
+    """
 
     # obsolete experimental requirements:
     #  - manifestv2: An experimental new manifest format that allowed
@@ -1325,11 +1427,6 @@
     def wjoin(self, f, *insidef):
         return self.vfs.reljoin(self.root, f, *insidef)
 
-    def file(self, f):
-        if f[0] == '/':
-            f = f[1:]
-        return filelog.filelog(self.svfs, f)
-
     def setparents(self, p1, p2=nullid):
         with self.dirstate.parentchange():
             copies = self.dirstate.setparents(p1, p2)



To: indygreg, #hg-reviewers
Cc: yuja, mercurial-devel


More information about the Mercurial-devel mailing list