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") 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/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/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