D3716: ui: add an unsafeoperation context manager that can block SIGINT
durin42 (Augie Fackler)
phabricator at mercurial-scm.org
Tue Jun 12 15:31:53 UTC 2018
durin42 created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.
REVISION SUMMARY
The blocking of SIGINT is not done by default, but my hope is that we
will one day. This was inspired by Facebook's "nointerrupt" extension,
which is a bit more heavy-handed than this (whole commands are treated
as unsafe to interrupt). A future patch will enable this for varying
bits of Mercurial that are performing unsafe operations.
.. api::
New context manager ``ui.unsafeoperation()`` to mark portions of a command
as potentially unsafe places to interrupt Mercurial with Control-C or
similar.
REPOSITORY
rHG Mercurial
REVISION DETAIL
https://phab.mercurial-scm.org/D3716
AFFECTED FILES
mercurial/configitems.py
mercurial/ui.py
CHANGE DETAILS
diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -224,6 +224,7 @@
self._colormode = None
self._terminfoparams = {}
self._styles = {}
+ self._oldsiginthandler = None
if src:
self.fout = src.fout
@@ -334,6 +335,39 @@
self._blockedtimes[key + '_blocked'] += \
(util.timer() - starttime) * 1000
+ @contextlib.contextmanager
+ def unsafeoperation(self):
+ """Mark an operation as unsafe.
+
+ Most operations on a repository are safe to interrupt, but a
+ few are risky (for example repair.strip). This context manager
+ lets you advise Mercurial that something risky is happening so
+ that control-C etc can be blocked if desired.
+ """
+ enabled = self.configbool('experimental', 'nointerrupt')
+ inter = self.interactive() or not self.configbool(
+ 'experimental', 'nointerrupt-interactiveonly')
+ if not (enabled and inter and self._oldsiginthandler is None):
+ # if nointerrupt support is turned off, the process isn't
+ # interactive, or we're already in an unsafeoperation
+ # block, do nothing.
+ yield
+ return
+
+ def disablesiginthandler(*args):
+ self.warn(self.config('experimental', 'nointerrupt-message') + '\n')
+ signal.signal(signal.SIGINT, self._oldsiginthandler)
+ self._oldsiginthandler = None
+
+ try:
+ self._oldsiginthandler = signal.getsignal(signal.SIGINT)
+ signal.signal(signal.SIGINT, disablesiginthandler)
+ yield
+ finally:
+ if self._oldsiginthandler is not None:
+ signal.signal(signal.SIGINT, self._oldsiginthandler)
+ self._oldsiginthandler = None
+
def formatter(self, topic, opts):
return formatter.formatter(self, self, topic, opts)
diff --git a/mercurial/configitems.py b/mercurial/configitems.py
--- a/mercurial/configitems.py
+++ b/mercurial/configitems.py
@@ -560,6 +560,17 @@
coreconfigitem('experimental', 'mergedriver',
default=None,
)
+coreconfigitem('experimental', 'nointerrupt', default=False)
+_nointmsg = """
+==========================
+Interrupting Mercurial may leave your repo in a bad state.
+If you really want to interrupt your current command, press
+CTRL-C again.
+==========================
+""".strip()
+coreconfigitem('experimental', 'nointerrupt-message', default=_nointmsg)
+coreconfigitem('experimental', 'nointerrupt-interactiveonly', default=True)
+
coreconfigitem('experimental', 'obsmarkers-exchange-debug',
default=False,
)
To: durin42, #hg-reviewers
Cc: mercurial-devel
More information about the Mercurial-devel
mailing list