[PATCH 1 of 9 pager] pager: move pager-initiating code into core
Simon Farnsworth
simonfar at fb.com
Thu Feb 16 05:21:01 EST 2017
On 16/02/2017 02:12, Augie Fackler wrote:
> # HG changeset patch
> # User Augie Fackler <augie at google.com>
> # Date 1487198871 18000
> # Wed Feb 15 17:47:51 2017 -0500
> # Node ID 675643abfdb6adbdfd8bddfbc263701c9caca539
> # Parent e5363cb96233861fc99f7e9b85d7884d3121558c
> pager: move pager-initiating code into core
>
> No functionality change.
>
> A previous version of this API had a category argument on
> ui.pager(). As I migrated the commands in core, I couldn't come up
> with good enough consistency in any categorization scheme so I just
> scrapped the whole idea. It may be worth revisiting in the future.
>
> diff --git a/hgext/pager.py b/hgext/pager.py
> --- a/hgext/pager.py
> +++ b/hgext/pager.py
> @@ -60,19 +60,11 @@ you can use --pager=<value>::
> '''
> from __future__ import absolute_import
>
> -import atexit
> -import os
> -import signal
> -import subprocess
> -import sys
> -
> from mercurial.i18n import _
> from mercurial import (
> cmdutil,
> commands,
> dispatch,
> - encoding,
> - error,
> extensions,
> util,
> )
> @@ -83,48 +75,14 @@ from mercurial import (
> # leave the attribute unspecified.
> testedwith = 'ships-with-hg-core'
>
> -def _runpager(ui, p):
> - pager = subprocess.Popen(p, shell=True, bufsize=-1,
> - close_fds=util.closefds, stdin=subprocess.PIPE,
> - stdout=util.stdout, stderr=util.stderr)
> -
> - # back up original file descriptors
> - stdoutfd = os.dup(util.stdout.fileno())
> - stderrfd = os.dup(util.stderr.fileno())
> -
> - os.dup2(pager.stdin.fileno(), util.stdout.fileno())
> - if ui._isatty(util.stderr):
> - os.dup2(pager.stdin.fileno(), util.stderr.fileno())
> -
> - @atexit.register
> - def killpager():
> - if util.safehasattr(signal, "SIGINT"):
> - signal.signal(signal.SIGINT, signal.SIG_IGN)
> - # restore original fds, closing pager.stdin copies in the process
> - os.dup2(stdoutfd, util.stdout.fileno())
> - os.dup2(stderrfd, util.stderr.fileno())
> - pager.stdin.close()
> - pager.wait()
> -
> -def catchterm(*args):
> - raise error.SignalInterrupt
> -
This little helper...
> def uisetup(ui):
> - class pagerui(ui.__class__):
> - def _runpager(self, pagercmd):
> - _runpager(self, pagercmd)
> -
> - ui.__class__ = pagerui
>
> def pagecmd(orig, ui, options, cmd, cmdfunc):
> - p = ui.config("pager", "pager", encoding.environ.get("PAGER"))
> usepager = False
> always = util.parsebool(options['pager'])
> auto = options['pager'] == 'auto'
>
> - if not p or '--debugger' in sys.argv or not ui.formatted():
> - pass
> - elif always:
> + if always:
> usepager = True
> elif not auto:
> usepager = False
> @@ -143,14 +101,8 @@ def uisetup(ui):
> usepager = True
> break
>
> - setattr(ui, 'pageractive', usepager)
> -
> if usepager:
> - ui.setconfig('ui', 'formatted', ui.formatted(), 'pager')
> - ui.setconfig('ui', 'interactive', False, 'pager')
> - if util.safehasattr(signal, "SIGPIPE"):
> - signal.signal(signal.SIGPIPE, catchterm)
..and this change to signal handling are important. Without them, if you
do `hg log -r 'all()'` on a large repository, then quit the pager,
Mercurial will continue to crunch away in the background, generating
stdout that goes to the (now-deceased) pager.
> - ui._runpager(p)
> + ui.pager('extension-via-attend-' + cmd)
> return orig(ui, options, cmd, cmdfunc)
>
> # Wrap dispatch._runcommand after color is loaded so color can see
> diff --git a/mercurial/ui.py b/mercurial/ui.py
> --- a/mercurial/ui.py
> +++ b/mercurial/ui.py
> @@ -7,13 +7,16 @@
>
> from __future__ import absolute_import
>
> +import atexit
> import contextlib
> import errno
> import getpass
> import inspect
> import os
> import re
> +import signal
> import socket
> +import subprocess
> import sys
> import tempfile
> import traceback
> @@ -143,6 +146,7 @@ class ui(object):
> self.fout = src.fout
> self.ferr = src.ferr
> self.fin = src.fin
> + self.pageractive = src.pageractive
>
> self._tcfg = src._tcfg.copy()
> self._ucfg = src._ucfg.copy()
> @@ -159,6 +163,7 @@ class ui(object):
> self.fout = util.stdout
> self.ferr = util.stderr
> self.fin = util.stdin
> + self.pageractive = False
>
> # shared read-only environment
> self.environ = encoding.environ
> @@ -792,6 +797,75 @@ class ui(object):
> return False
> return util.isatty(fh)
>
> + def pager(self, command):
> + """Start a pager for subsequent command output.
> +
> + Commands which produce a long stream of output should call
> + this function to activate the user's preferred pagination
> + mechanism (which may be no pager). Calling this function
> + precludes any future use of interactive functionality, such as
> + prompting the user or activating curses.
> +
> + Args:
> + command: The full, non-aliased name of the command. That is, "log"
> + not "history, "summary" not "summ", etc.
> + """
> + if (self.pageractive
> + # TODO: if we want to allow HGPLAINEXCEPT=pager,
> + # formatted() will need some adjustment.
> + or not self.formatted()
> + or self.plain()
> + # TODO: expose debugger-enabled on the UI object
> + or '--debugger' in sys.argv):
> + # We only want to paginate if the ui appears to be
> + # interactive, the user didn't say HGPLAIN or
> + # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
> + return
> +
> + # TODO: add a "system defaults" config section so this default
> + # of more(1) can be easily replaced with a global
> + # configuration file. For example, on OS X the sane default is
> + # less(1), not more(1), and on debian it's
> + # sensible-pager(1). We should probably also give the system
> + # default editor command similar treatment.
> + envpager = encoding.environ.get('PAGER', 'more')
> + pagercmd = self.config('pager', 'pager', envpager)
> + self.pageractive = True
> + # Preserve the formatted-ness of the UI. This is important
> + # because we mess with stdout, which might confuse
> + # auto-detection of things being formatted.
> + self.setconfig('ui', 'formatted', self.formatted(), 'pager')
> + self.setconfig('ui', 'interactive', False, 'pager')
The equivalent signal handling change belongs here.
> + self._runpager(pagercmd)
> +
> + def _runpager(self, command):
> + """Actually start the pager and set up file descriptors.
> +
> + This is separate in part so that extensions (like chg) can
> + override how a pager is invoked.
> + """
> + pager = subprocess.Popen(command, shell=True, bufsize=-1,
> + close_fds=util.closefds, stdin=subprocess.PIPE,
> + stdout=util.stdout, stderr=util.stderr)
> +
> + # back up original file descriptors
> + stdoutfd = os.dup(util.stdout.fileno())
> + stderrfd = os.dup(util.stderr.fileno())
> +
> + os.dup2(pager.stdin.fileno(), util.stdout.fileno())
> + if self._isatty(util.stderr):
> + os.dup2(pager.stdin.fileno(), util.stderr.fileno())
> +
> + @atexit.register
> + def killpager():
> + if util.safehasattr(signal, "SIGINT"):
> + signal.signal(signal.SIGINT, signal.SIG_IGN)
> + # restore original fds, closing pager.stdin copies in the process
> + os.dup2(stdoutfd, util.stdout.fileno())
> + os.dup2(stderrfd, util.stderr.fileno())
> + pager.stdin.close()
> + pager.wait()
> +
> def interface(self, feature):
> """what interface to use for interactive console features?
>
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at mercurial-scm.org
> https://urldefense.proofpoint.com/v2/url?u=https-3A__www.mercurial-2Dscm.org_mailman_listinfo_mercurial-2Ddevel&d=DwIGaQ&c=5VD0RTtNlTh3ycd41b3MUw&r=mEgSWILcY4c4W3zjApBQLA&m=ZiiKlZHpcE6FnMZhBU6-nzVLClXOgHOARg0HpwT43yU&s=7H11BAlHtU6CGYWE4DAym4-8Q81xpiKmHHJ2JBpLKko&e=
>
--
Simon Farnsworth
More information about the Mercurial-devel
mailing list