Patchwork [2,of,3] repoview: extract a factory function of proxy class

login
register
mail settings
Submitter Yuya Nishihara
Date Dec. 5, 2017, 1:40 p.m.
Message ID <4c2ec6b9d65bd38a7ac9.1512481235@mimosa>
Download mbox | patch
Permalink /patch/25938/
State Accepted
Headers show

Comments

Yuya Nishihara - Dec. 5, 2017, 1:40 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1512478233 -32400
#      Tue Dec 05 21:50:33 2017 +0900
# Node ID 4c2ec6b9d65bd38a7ac9138241a42a2e60d0a605
# Parent  ac2f7bb76d318b3f32d83f4674d99a78bb3ea42b
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.

Patch

diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -502,9 +502,6 @@  class localrepository(object):
         # 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()
 
@@ -680,20 +677,8 @@  class localrepository(object):
 
     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):
diff --git a/mercurial/repoview.py b/mercurial/repoview.py
--- a/mercurial/repoview.py
+++ b/mercurial/repoview.py
@@ -9,6 +9,7 @@ 
 from __future__ import absolute_import
 
 import copy
+import weakref
 
 from .node import nullrev
 from . import (
@@ -240,3 +241,16 @@  class repoview(object):
 
     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]
diff --git a/mercurial/statichttprepo.py b/mercurial/statichttprepo.py
--- a/mercurial/statichttprepo.py
+++ b/mercurial/statichttprepo.py
@@ -166,8 +166,6 @@  class statichttprepository(localrepo.loc
         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)