comparison mercurial/ui.py @ 30992:61b4122019d3

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.
author Augie Fackler <augie@google.com>
date Wed, 15 Feb 2017 17:47:51 -0500
parents 60b5db2ab586
children 3ed6e43998df
comparison
equal deleted inserted replaced
30991:3844b3299a53 30992:61b4122019d3
5 # This software may be used and distributed according to the terms of the 5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version. 6 # GNU General Public License version 2 or any later version.
7 7
8 from __future__ import absolute_import 8 from __future__ import absolute_import
9 9
10 import atexit
10 import collections 11 import collections
11 import contextlib 12 import contextlib
12 import errno 13 import errno
13 import getpass 14 import getpass
14 import inspect 15 import inspect
15 import os 16 import os
16 import re 17 import re
18 import signal
17 import socket 19 import socket
20 import subprocess
18 import sys 21 import sys
19 import tempfile 22 import tempfile
20 import traceback 23 import traceback
21 24
22 from .i18n import _ 25 from .i18n import _
113 return self._get_mgr().add_password(*args, **kwargs) 116 return self._get_mgr().add_password(*args, **kwargs)
114 117
115 def find_user_password(self, *args, **kwargs): 118 def find_user_password(self, *args, **kwargs):
116 return self._get_mgr().find_user_password(*args, **kwargs) 119 return self._get_mgr().find_user_password(*args, **kwargs)
117 120
121 def _catchterm(*args):
122 raise error.SignalInterrupt
118 123
119 class ui(object): 124 class ui(object):
120 def __init__(self, src=None): 125 def __init__(self, src=None):
121 """Create a fresh new ui object if no src given 126 """Create a fresh new ui object if no src given
122 127
147 152
148 if src: 153 if src:
149 self.fout = src.fout 154 self.fout = src.fout
150 self.ferr = src.ferr 155 self.ferr = src.ferr
151 self.fin = src.fin 156 self.fin = src.fin
157 self.pageractive = src.pageractive
152 158
153 self._tcfg = src._tcfg.copy() 159 self._tcfg = src._tcfg.copy()
154 self._ucfg = src._ucfg.copy() 160 self._ucfg = src._ucfg.copy()
155 self._ocfg = src._ocfg.copy() 161 self._ocfg = src._ocfg.copy()
156 self._trustusers = src._trustusers.copy() 162 self._trustusers = src._trustusers.copy()
164 self._blockedtimes = src._blockedtimes 170 self._blockedtimes = src._blockedtimes
165 else: 171 else:
166 self.fout = util.stdout 172 self.fout = util.stdout
167 self.ferr = util.stderr 173 self.ferr = util.stderr
168 self.fin = util.stdin 174 self.fin = util.stdin
175 self.pageractive = False
169 176
170 # shared read-only environment 177 # shared read-only environment
171 self.environ = encoding.environ 178 self.environ = encoding.environ
172 179
173 self.httppasswordmgrdb = httppasswordmgrdbproxy() 180 self.httppasswordmgrdb = httppasswordmgrdbproxy()
822 def _isatty(self, fh): 829 def _isatty(self, fh):
823 if self.configbool('ui', 'nontty', False): 830 if self.configbool('ui', 'nontty', False):
824 return False 831 return False
825 return util.isatty(fh) 832 return util.isatty(fh)
826 833
834 def pager(self, command):
835 """Start a pager for subsequent command output.
836
837 Commands which produce a long stream of output should call
838 this function to activate the user's preferred pagination
839 mechanism (which may be no pager). Calling this function
840 precludes any future use of interactive functionality, such as
841 prompting the user or activating curses.
842
843 Args:
844 command: The full, non-aliased name of the command. That is, "log"
845 not "history, "summary" not "summ", etc.
846 """
847 if (self.pageractive
848 # TODO: if we want to allow HGPLAINEXCEPT=pager,
849 # formatted() will need some adjustment.
850 or not self.formatted()
851 or self.plain()
852 # TODO: expose debugger-enabled on the UI object
853 or '--debugger' in sys.argv):
854 # We only want to paginate if the ui appears to be
855 # interactive, the user didn't say HGPLAIN or
856 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
857 return
858
859 # TODO: add a "system defaults" config section so this default
860 # of more(1) can be easily replaced with a global
861 # configuration file. For example, on OS X the sane default is
862 # less(1), not more(1), and on debian it's
863 # sensible-pager(1). We should probably also give the system
864 # default editor command similar treatment.
865 envpager = encoding.environ.get('PAGER', 'more')
866 pagercmd = self.config('pager', 'pager', envpager)
867 self.pageractive = True
868 # Preserve the formatted-ness of the UI. This is important
869 # because we mess with stdout, which might confuse
870 # auto-detection of things being formatted.
871 self.setconfig('ui', 'formatted', self.formatted(), 'pager')
872 self.setconfig('ui', 'interactive', False, 'pager')
873 if util.safehasattr(signal, "SIGPIPE"):
874 signal.signal(signal.SIGPIPE, _catchterm)
875 self._runpager(pagercmd)
876
877 def _runpager(self, command):
878 """Actually start the pager and set up file descriptors.
879
880 This is separate in part so that extensions (like chg) can
881 override how a pager is invoked.
882 """
883 pager = subprocess.Popen(command, shell=True, bufsize=-1,
884 close_fds=util.closefds, stdin=subprocess.PIPE,
885 stdout=util.stdout, stderr=util.stderr)
886
887 # back up original file descriptors
888 stdoutfd = os.dup(util.stdout.fileno())
889 stderrfd = os.dup(util.stderr.fileno())
890
891 os.dup2(pager.stdin.fileno(), util.stdout.fileno())
892 if self._isatty(util.stderr):
893 os.dup2(pager.stdin.fileno(), util.stderr.fileno())
894
895 @atexit.register
896 def killpager():
897 if util.safehasattr(signal, "SIGINT"):
898 signal.signal(signal.SIGINT, signal.SIG_IGN)
899 # restore original fds, closing pager.stdin copies in the process
900 os.dup2(stdoutfd, util.stdout.fileno())
901 os.dup2(stderrfd, util.stderr.fileno())
902 pager.stdin.close()
903 pager.wait()
904
827 def interface(self, feature): 905 def interface(self, feature):
828 """what interface to use for interactive console features? 906 """what interface to use for interactive console features?
829 907
830 The interface is controlled by the value of `ui.interface` but also by 908 The interface is controlled by the value of `ui.interface` but also by
831 the value of feature-specific configuration. For example: 909 the value of feature-specific configuration. For example: