[PATCH RFC] pager: migrate to core

Bryan O'Sullivan bos at serpentine.com
Tue Nov 24 18:31:14 UTC 2015


# HG changeset patch
# User Bryan O'Sullivan <bos at serpentine.com>
# Date 1448389126 28800
#      Tue Nov 24 10:18:46 2015 -0800
# Node ID f63938a2292eaec78637835221916794d6f60994
# Parent  f668eef04abc3de94f41b527daa0bb7a0cf76f56
pager: migrate to core

We need to retain an extension module for a long time (possibly
indefinitely), for two reasons:

- Deleting the extension entirely will break the configs of existing
  users of the extension.

- Some out-of-tree extensions "know" that hgext.pager.attended is
  a variable they can modify.

diff -r f668eef04abc -r f63938a2292e hgext/pager.py
--- a/hgext/pager.py	Fri Nov 20 16:54:55 2015 -0500
+++ b/hgext/pager.py	Tue Nov 24 10:18:46 2015 -0800
@@ -12,143 +12,9 @@
 #
 # Run "hg help pager" to get info on configuration.
 
-'''browse command output with an external pager
+'''now part of core mercurial, extension for compatibility only'''
 
-To set the pager that should be used, set the application variable::
+from mercurial import pager
 
-  [pager]
-  pager = less -FRX
-
-If no pager is set, the pager extensions uses the environment variable
-$PAGER. If neither pager.pager, nor $PAGER is set, no pager is used.
-
-You can disable the pager for certain commands by adding them to the
-pager.ignore list::
-
-  [pager]
-  ignore = version, help, update
-
-You can also enable the pager only for certain commands using
-pager.attend. Below is the default list of commands to be paged::
-
-  [pager]
-  attend = annotate, cat, diff, export, glog, log, qdiff
-
-Setting pager.attend to an empty value will cause all commands to be
-paged.
-
-If pager.attend is present, pager.ignore will be ignored.
-
-Lastly, you can enable and disable paging for individual commands with
-the attend-<command> option. This setting takes precedence over
-existing attend and ignore options and defaults::
-
-  [pager]
-  attend-cat = false
-
-To ignore global commands like :hg:`version` or :hg:`help`, you have
-to specify them in your user configuration file.
-
-The --pager=... option can also be used to control when the pager is
-used. Use a boolean value like yes, no, on, off, or use auto for
-normal behavior.
-
-'''
-
-import atexit, sys, os, signal, subprocess
-from mercurial import commands, dispatch, util, extensions, cmdutil
-from mercurial.i18n import _
-
-# Note for extension authors: ONLY specify testedwith = 'internal' for
-# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
-# be specifying the version(s) of Mercurial they are tested with, or
-# leave the attribute unspecified.
-testedwith = 'internal'
-
-def _runpager(ui, p):
-    pager = subprocess.Popen(p, shell=True, bufsize=-1,
-                             close_fds=util.closefds, stdin=subprocess.PIPE,
-                             stdout=sys.stdout, stderr=sys.stderr)
-
-    # back up original file objects and descriptors
-    olduifout = ui.fout
-    oldstdout = sys.stdout
-    stdoutfd = os.dup(sys.stdout.fileno())
-    stderrfd = os.dup(sys.stderr.fileno())
-
-    # create new line-buffered stdout so that output can show up immediately
-    ui.fout = sys.stdout = newstdout = os.fdopen(sys.stdout.fileno(), 'wb', 1)
-    os.dup2(pager.stdin.fileno(), sys.stdout.fileno())
-    if ui._isatty(sys.stderr):
-        os.dup2(pager.stdin.fileno(), sys.stderr.fileno())
-
-    @atexit.register
-    def killpager():
-        if util.safehasattr(signal, "SIGINT"):
-            signal.signal(signal.SIGINT, signal.SIG_IGN)
-        pager.stdin.close()
-        ui.fout = olduifout
-        sys.stdout = oldstdout
-        # close new stdout while it's associated with pager; otherwise stdout
-        # fd would be closed when newstdout is deleted
-        newstdout.close()
-        # restore original fds: stdout is open again
-        os.dup2(stdoutfd, sys.stdout.fileno())
-        os.dup2(stderrfd, sys.stderr.fileno())
-        pager.wait()
-
-def uisetup(ui):
-    if '--debugger' in sys.argv or not ui.formatted():
-        return
-
-    def pagecmd(orig, ui, options, cmd, cmdfunc):
-        p = ui.config("pager", "pager", os.environ.get("PAGER"))
-        usepager = False
-        always = util.parsebool(options['pager'])
-        auto = options['pager'] == 'auto'
-
-        if not p:
-            pass
-        elif always:
-            usepager = True
-        elif not auto:
-            usepager = False
-        else:
-            attend = ui.configlist('pager', 'attend', attended)
-            ignore = ui.configlist('pager', 'ignore')
-            cmds, _ = cmdutil.findcmd(cmd, commands.table)
-
-            for cmd in cmds:
-                var = 'attend-%s' % cmd
-                if ui.config('pager', var):
-                    usepager = ui.configbool('pager', var)
-                    break
-                if (cmd in attend or
-                     (cmd not in ignore and not attend)):
-                    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, signal.SIG_DFL)
-            _runpager(ui, p)
-        return orig(ui, options, cmd, cmdfunc)
-
-    # Wrap dispatch._runcommand after color is loaded so color can see
-    # ui.pageractive. Otherwise, if we loaded first, color's wrapped
-    # dispatch._runcommand would run without having access to ui.pageractive.
-    def afterloaded(loaded):
-        extensions.wrapfunction(dispatch, '_runcommand', pagecmd)
-    extensions.afterloaded('color', afterloaded)
-
-def extsetup(ui):
-    commands.globalopts.append(
-        ('', 'pager', 'auto',
-         _("when to paginate (boolean, always, auto, or never)"),
-         _('TYPE')))
-
-attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff']
+# needed by some out-of-tree extensions
+attended = pager.attended
diff -r f668eef04abc -r f63938a2292e mercurial/commands.py
--- a/mercurial/commands.py	Fri Nov 20 16:54:55 2015 -0500
+++ b/mercurial/commands.py	Tue Nov 24 10:18:46 2015 -0800
@@ -74,6 +74,8 @@ globalopts = [
     ('', 'version', None, _('output version information and exit')),
     ('h', 'help', None, _('display help and exit')),
     ('', 'hidden', False, _('consider hidden changesets')),
+    ('', 'pager', 'auto',
+     _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
 ]
 
 dryrunopts = [('n', 'dry-run', None,
diff -r f668eef04abc -r f63938a2292e mercurial/dispatch.py
--- a/mercurial/dispatch.py	Fri Nov 20 16:54:55 2015 -0500
+++ b/mercurial/dispatch.py	Tue Nov 24 10:18:46 2015 -0800
@@ -33,6 +33,7 @@ from . import (
     fancyopts,
     hg,
     hook,
+    pager,
     ui as uimod,
     util,
 )
@@ -779,6 +780,7 @@ def _dispatch(req):
     if shellaliasfn:
         return shellaliasfn()
 
+    pager.uisetup(lui)
     # Configure extensions in phases: uisetup, extsetup, cmdtable, and
     # reposetup. Programs like TortoiseHg will call _dispatch several
     # times so we keep track of configured extensions in _loaded.
diff -r f668eef04abc -r f63938a2292e mercurial/help.py
--- a/mercurial/help.py	Fri Nov 20 16:54:55 2015 -0500
+++ b/mercurial/help.py	Tue Nov 24 10:18:46 2015 -0800
@@ -172,6 +172,7 @@ helptable = sorted([
     (["glossary"], _("Glossary"), loaddoc('glossary')),
     (["hgignore", "ignore"], _("Syntax for Mercurial Ignore Files"),
      loaddoc('hgignore')),
+    (["pager"], _("Using an External Pager"), loaddoc('pager')),
     (["phases"], _("Working with Phases"), loaddoc('phases')),
     (['scripting'], _('Using Mercurial from scripts and automation'),
      loaddoc('scripting')),
diff -r f668eef04abc -r f63938a2292e mercurial/help/pager.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/help/pager.txt	Tue Nov 24 10:18:46 2015 -0800
@@ -0,0 +1,38 @@
+To set the pager that should be used, set the application variable::
+
+  [pager]
+  pager = less -FRX
+
+If no pager is set, the pager extensions uses the environment variable
+$PAGER. If neither pager.pager, nor $PAGER is set, no pager is used.
+
+You can disable the pager for certain commands by adding them to the
+pager.ignore list::
+
+  [pager]
+  ignore = version, help, update
+
+You can also enable the pager only for certain commands using
+pager.attend. Below is the default list of commands to be paged::
+
+  [pager]
+  attend = annotate, cat, diff, export, glog, log, qdiff
+
+Setting pager.attend to an empty value will cause all commands to be
+paged.
+
+If pager.attend is present, pager.ignore will be ignored.
+
+Lastly, you can enable and disable paging for individual commands with
+the attend-<command> option. This setting takes precedence over
+existing attend and ignore options and defaults::
+
+  [pager]
+  attend-cat = false
+
+To ignore global commands like :hg:`version` or :hg:`help`, you have
+to specify them in your user configuration file.
+
+The --pager=... option can also be used to control when the pager is
+used. Use a boolean value like yes, no, on, off, or use auto for
+normal behavior.
diff -r f668eef04abc -r f63938a2292e mercurial/pager.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/pager.py	Tue Nov 24 10:18:46 2015 -0800
@@ -0,0 +1,92 @@
+# pager.py - display output using a pager
+#
+# Copyright 2008 David Soria Parra <dsp at php.net>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+import atexit, sys, os, signal, subprocess
+from . import commands, dispatch, util, extensions, cmdutil
+from .i18n import _
+
+def _runpager(ui, p):
+    pager = subprocess.Popen(p, shell=True, bufsize=-1,
+                             close_fds=util.closefds, stdin=subprocess.PIPE,
+                             stdout=sys.stdout, stderr=sys.stderr)
+
+    # back up original file objects and descriptors
+    olduifout = ui.fout
+    oldstdout = sys.stdout
+    stdoutfd = os.dup(sys.stdout.fileno())
+    stderrfd = os.dup(sys.stderr.fileno())
+
+    # create new line-buffered stdout so that output can show up immediately
+    ui.fout = sys.stdout = newstdout = os.fdopen(sys.stdout.fileno(), 'wb', 1)
+    os.dup2(pager.stdin.fileno(), sys.stdout.fileno())
+    if ui._isatty(sys.stderr):
+        os.dup2(pager.stdin.fileno(), sys.stderr.fileno())
+
+    @atexit.register
+    def killpager():
+        if util.safehasattr(signal, "SIGINT"):
+            signal.signal(signal.SIGINT, signal.SIG_IGN)
+        pager.stdin.close()
+        ui.fout = olduifout
+        sys.stdout = oldstdout
+        # close new stdout while it's associated with pager; otherwise stdout
+        # fd would be closed when newstdout is deleted
+        newstdout.close()
+        # restore original fds: stdout is open again
+        os.dup2(stdoutfd, sys.stdout.fileno())
+        os.dup2(stderrfd, sys.stderr.fileno())
+        pager.wait()
+
+def uisetup(ui):
+    if '--debugger' in sys.argv or not ui.formatted():
+        return
+
+    def pagecmd(orig, ui, options, cmd, cmdfunc):
+        p = ui.config("pager", "pager", os.environ.get("PAGER"))
+        usepager = False
+        always = util.parsebool(options['pager'])
+        auto = options['pager'] == 'auto'
+
+        if not p:
+            pass
+        elif always:
+            usepager = True
+        elif not auto:
+            usepager = False
+        else:
+            attend = ui.configlist('pager', 'attend', attended)
+            ignore = ui.configlist('pager', 'ignore')
+            cmds, _ = cmdutil.findcmd(cmd, commands.table)
+
+            for cmd in cmds:
+                var = 'attend-%s' % cmd
+                if ui.config('pager', var):
+                    usepager = ui.configbool('pager', var)
+                    break
+                if (cmd in attend or
+                     (cmd not in ignore and not attend)):
+                    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, signal.SIG_DFL)
+            _runpager(ui, p)
+        return orig(ui, options, cmd, cmdfunc)
+
+    # Wrap dispatch._runcommand after color is loaded so color can see
+    # ui.pageractive. Otherwise, if we loaded first, color's wrapped
+    # dispatch._runcommand would run without having access to ui.pageractive.
+    def aftercolor(loaded):
+        extensions.wrapfunction(dispatch, '_runcommand', pagecmd)
+    extensions.afterloaded('color', aftercolor)
+
+attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff']


More information about the Mercurial-devel mailing list