D392: util: add base class for transactional context managers

martinvonz (Martin von Zweigbergk) phabricator at mercurial-scm.org
Tue Aug 15 02:14:49 EDT 2017


This revision was automatically updated to reflect the committed changes.
Closed by commit rHGbbbbd3c30bfc: util: add base class for transactional context managers (authored by martinvonz).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D392?vs=914&id=918

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

AFFECTED FILES
  mercurial/dirstateguard.py
  mercurial/exchange.py
  mercurial/transaction.py
  mercurial/util.py

CHANGE DETAILS

diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -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
diff --git a/mercurial/transaction.py b/mercurial/transaction.py
--- a/mercurial/transaction.py
+++ b/mercurial/transaction.py
@@ -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
 
diff --git a/mercurial/exchange.py b/mercurial/exchange.py
--- a/mercurial/exchange.py
+++ b/mercurial/exchange.py
@@ -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
diff --git a/mercurial/dirstateguard.py b/mercurial/dirstateguard.py
--- a/mercurial/dirstateguard.py
+++ b/mercurial/dirstateguard.py
@@ -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")



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


More information about the Mercurial-devel mailing list