comparison mercurial/ui.py @ 38526:313a940d49a3

ui: add an uninterruptable context manager that can block SIGINT 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. It's intentional that the KeyboardInterrupt is raised as the context manager exits: during the span of the context manager interrupting Mercurial could lead to data loss, but typically those spans are fairly narrow, so we can let the unsafe block complete and then terminate hg (which will leave the repo in a consistent state, even if it's not the user's desired state). .. api:: New context manager ``ui.uninterruptable()`` to mark portions of a command as potentially unsafe places to interrupt Mercurial with Control-C or similar. Differential Revision: https://phab.mercurial-scm.org/D3716
author Augie Fackler <augie@google.com>
date Wed, 27 Jun 2018 10:47:14 -0400
parents bec1212eceaa
children 152f4822d210
comparison
equal deleted inserted replaced
38525:c153f440682f 38526:313a940d49a3
222 self.logblockedtimes = False 222 self.logblockedtimes = False
223 # color mode: see mercurial/color.py for possible value 223 # color mode: see mercurial/color.py for possible value
224 self._colormode = None 224 self._colormode = None
225 self._terminfoparams = {} 225 self._terminfoparams = {}
226 self._styles = {} 226 self._styles = {}
227 self._uninterruptible = False
227 228
228 if src: 229 if src:
229 self.fout = src.fout 230 self.fout = src.fout
230 self.ferr = src.ferr 231 self.ferr = src.ferr
231 self.fin = src.fin 232 self.fin = src.fin
331 try: 332 try:
332 yield 333 yield
333 finally: 334 finally:
334 self._blockedtimes[key + '_blocked'] += \ 335 self._blockedtimes[key + '_blocked'] += \
335 (util.timer() - starttime) * 1000 336 (util.timer() - starttime) * 1000
337
338 @contextlib.contextmanager
339 def uninterruptable(self):
340 """Mark an operation as unsafe.
341
342 Most operations on a repository are safe to interrupt, but a
343 few are risky (for example repair.strip). This context manager
344 lets you advise Mercurial that something risky is happening so
345 that control-C etc can be blocked if desired.
346 """
347 enabled = self.configbool('experimental', 'nointerrupt')
348 if (enabled and
349 self.configbool('experimental', 'nointerrupt-interactiveonly')):
350 enabled = self.interactive()
351 if self._uninterruptible or not enabled:
352 # if nointerrupt support is turned off, the process isn't
353 # interactive, or we're already in an uninterruptable
354 # block, do nothing.
355 yield
356 return
357 def warn():
358 self.warn(_("shutting down cleanly\n"))
359 self.warn(
360 _("press ^C again to terminate immediately (dangerous)\n"))
361 return True
362 with procutil.uninterruptable(warn):
363 try:
364 self._uninterruptible = True
365 yield
366 finally:
367 self._uninterruptible = False
336 368
337 def formatter(self, topic, opts): 369 def formatter(self, topic, opts):
338 return formatter.formatter(self, self, topic, opts) 370 return formatter.formatter(self, self, topic, opts)
339 371
340 def _trusted(self, fp, f): 372 def _trusted(self, fp, f):