changeset 33793:bbbbd3c30bfc

util: add base class for transactional context managers We have at least three types with a close() and a release() method where the close() method is supposed to be called on success and the release() method is supposed to be called last, whether successful or not. Two of them (transaction and dirstateguard) already have identical implementations of __enter__ and __exit__. Let's extract a base class for this, so we reuse the code and so the third type (transactionmanager) can also be used as a context manager. Differential Revision: https://phab.mercurial-scm.org/D392
author Martin von Zweigbergk <martinvonz@google.com>
date Fri, 28 Jul 2017 22:42:10 -0700
parents 5904511fc9f8
children 389f7b17ffb3
files mercurial/dirstateguard.py mercurial/exchange.py mercurial/transaction.py mercurial/util.py
diffstat 4 files changed, 30 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/dirstateguard.py	Mon Aug 14 16:26:36 2017 -0700
+++ b/mercurial/dirstateguard.py	Fri Jul 28 22:42:10 2017 -0700
@@ -11,9 +11,10 @@
 
 from . import (
     error,
+    util,
 )
 
-class dirstateguard(object):
+class dirstateguard(util.transactional):
     '''Restore dirstate at unexpected failure.
 
     At the construction, this class does:
@@ -43,16 +44,6 @@
             # ``release(tr, ....)``.
             self._abort()
 
-    def __enter__(self):
-        return self
-
-    def __exit__(self, exc_type, exc_val, exc_tb):
-        try:
-            if exc_type is None:
-                self.close()
-        finally:
-            self.release()
-
     def close(self):
         if not self._active: # already inactivated
             msg = (_("can't close already inactivated backup: %s")
--- a/mercurial/exchange.py	Mon Aug 14 16:26:36 2017 -0700
+++ b/mercurial/exchange.py	Fri Jul 28 22:42:10 2017 -0700
@@ -1168,7 +1168,7 @@
         # deprecated; talk to trmanager directly
         return self.trmanager.transaction()
 
-class transactionmanager(object):
+class transactionmanager(util.transactional):
     """An object to manage the life cycle of a transaction
 
     It creates the transaction on demand and calls the appropriate hooks when
--- a/mercurial/transaction.py	Mon Aug 14 16:26:36 2017 -0700
+++ b/mercurial/transaction.py	Fri Jul 28 22:42:10 2017 -0700
@@ -101,7 +101,7 @@
         # only pure backup file remains, it is sage to ignore any error
         pass
 
-class transaction(object):
+class transaction(util.transactional):
     def __init__(self, report, opener, vfsmap, journalname, undoname=None,
                  after=None, createmode=None, validator=None, releasefn=None,
                  checkambigfiles=None):
@@ -376,16 +376,6 @@
         if self.count > 0 and self.usages == 0:
             self._abort()
 
-    def __enter__(self):
-        return self
-
-    def __exit__(self, exc_type, exc_val, exc_tb):
-        try:
-            if exc_type is None:
-                self.close()
-        finally:
-            self.release()
-
     def running(self):
         return self.count > 0
 
--- a/mercurial/util.py	Mon Aug 14 16:26:36 2017 -0700
+++ b/mercurial/util.py	Fri Jul 28 22:42:10 2017 -0700
@@ -15,6 +15,7 @@
 
 from __future__ import absolute_import
 
+import abc
 import bz2
 import calendar
 import codecs
@@ -592,6 +593,31 @@
             for k, v in src:
                 self[k] = v
 
+class transactional(object):
+    """Base class for making a transactional type into a context manager."""
+    __metaclass__ = abc.ABCMeta
+
+    @abc.abstractmethod
+    def close(self):
+        """Successfully closes the transaction."""
+
+    @abc.abstractmethod
+    def release(self):
+        """Marks the end of the transaction.
+
+        If the transaction has not been closed, it will be aborted.
+        """
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        try:
+            if exc_type is None:
+                self.close()
+        finally:
+            self.release()
+
 @contextlib.contextmanager
 def acceptintervention(tr=None):
     """A context manager that closes the transaction on InterventionRequired