Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/ui.py @ 38528: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
38527:c153f440682f | 38528: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): |