repoview: extract a factory function of proxy class
authorYuya Nishihara <yuya@tcha.org>
Tue, 05 Dec 2017 21:50:33 +0900
changeset 40931 c752fbe228fb5fb5338b4b90efcfed7c2046ef7b
parent 40930 9ce4e01f58ee04d754c9d0dc6887492d2ce37c15
child 40932 d4ad9d695a9e453b2ccd622207d14bca80550457
push id615
push usergszorc@mozilla.com
push dateFri, 08 Dec 2017 00:11:22 +0000
repoview: extract a factory function of proxy class This makes sure that dynamically-created class objects are isolated from local binding of repo instances. The type cache is moved to module level as it isn't tied to each instance.
mercurial/localrepo.py
mercurial/repoview.py
mercurial/statichttprepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -497,19 +497,16 @@ class localrepository(object):
         # - new obsolescence marker,
         # - working directory parent change,
         # - bookmark changes
         self.filteredrevcache = {}
 
         # post-dirstate-status hooks
         self._postdsstatus = []
 
-        # Cache of types representing filtered repos.
-        self._filteredrepotypes = weakref.WeakKeyDictionary()
-
         # generic mapping between names and nodes
         self.names = namespaces.namespaces()
 
         # Key to signature value.
         self._sparsesignaturecache = {}
         # Signature to cached matcher instance.
         self._sparsematchercache = {}
 
@@ -675,30 +672,18 @@ class localrepository(object):
     def unfiltered(self):
         """Return unfiltered version of the repository
 
         Intended to be overwritten by filtered repo."""
         return self
 
     def filtered(self, name):
         """Return a filtered version of a repository"""
-        # Python <3.4 easily leaks types via __mro__. See
-        # https://bugs.python.org/issue17950. We cache dynamically
-        # created types so this method doesn't leak on every
-        # invocation.
-
-        key = self.unfiltered().__class__
-        if key not in self._filteredrepotypes:
-            # Build a new type with the repoview mixin and the base
-            # class of this repo.
-            class filteredrepo(repoview.repoview, key):
-                pass
-            self._filteredrepotypes[key] = filteredrepo
-
-        return self._filteredrepotypes[key](self, name)
+        cls = repoview.newtype(self.unfiltered().__class__)
+        return cls(self, name)
 
     @repofilecache('bookmarks', 'bookmarks.current')
     def _bookmarks(self):
         return bookmarks.bmstore(self)
 
     @property
     def _activebookmark(self):
         return self._bookmarks.active
--- a/mercurial/repoview.py
+++ b/mercurial/repoview.py
@@ -4,16 +4,17 @@
 #                Logilab SA        <contact@logilab.fr>
 #
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
 from __future__ import absolute_import
 
 import copy
+import weakref
 
 from .node import nullrev
 from . import (
     obsolete,
     phases,
     tags as tagsmod,
 )
 
@@ -235,8 +236,21 @@ class repoview(object):
     def __getattr__(self, attr):
         return getattr(self._unfilteredrepo, attr)
 
     def __setattr__(self, attr, value):
         return setattr(self._unfilteredrepo, attr, value)
 
     def __delattr__(self, attr):
         return delattr(self._unfilteredrepo, attr)
+
+# Python <3.4 easily leaks types via __mro__. See
+# https://bugs.python.org/issue17950. We cache dynamically created types
+# so they won't be leaked on every invocation of repo.filtered().
+_filteredrepotypes = weakref.WeakKeyDictionary()
+
+def newtype(base):
+    """Create a new type with the repoview mixin and the given base class"""
+    if base not in _filteredrepotypes:
+        class filteredrepo(repoview, base):
+            pass
+        _filteredrepotypes[base] = filteredrepo
+    return _filteredrepotypes[base]
--- a/mercurial/statichttprepo.py
+++ b/mercurial/statichttprepo.py
@@ -161,18 +161,16 @@ class statichttprepository(localrepo.loc
         self.changelog = changelog.changelog(self.svfs)
         self._tags = None
         self.nodetagscache = None
         self._branchcaches = {}
         self._revbranchcache = None
         self.encodepats = None
         self.decodepats = None
         self._transref = None
-        # Cache of types representing filtered repos.
-        self._filteredrepotypes = {}
 
     def _restrictcapabilities(self, caps):
         caps = super(statichttprepository, self)._restrictcapabilities(caps)
         return caps.difference(["pushkey"])
 
     def url(self):
         return self._url