Rev 4863: BZR_COLUMNS can override terminal_width() in http://bazaar.launchpad.net/~vila/bzr/integration

Vincent Ladeuil v.ladeuil+lp at free.fr
Fri Dec 4 10:38:21 GMT 2009


At http://bazaar.launchpad.net/~vila/bzr/integration

------------------------------------------------------------
revno: 4863 [merge]
revision-id: v.ladeuil+lp at free.fr-20091204103804-ezdgsf6iucib6v8x
parent: pqm at pqm.ubuntu.com-20091204080756-orgsvo74a1jk13rs
parent: v.ladeuil+lp at free.fr-20091204103655-r00e0gz8z3lbni0r
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: integration
timestamp: Fri 2009-12-04 11:38:04 +0100
message:
  BZR_COLUMNS can override terminal_width()
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/help.py                 help.py-20050505025907-4dd7a6d63912f894
  bzrlib/help_topics/__init__.py help_topics.py-20060920210027-rnim90q9e0bwxvy4-1
  bzrlib/log.py                  log.py-20050505065812-c40ce11702fe5fb1
  bzrlib/osutils.py              osutils.py-20050309040759-eeaff12fbf77ac86
  bzrlib/status.py               status.py-20050505062338-431bfa63ec9b19e6
  bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
  bzrlib/tests/blackbox/test_ignore.py test_ignore.py-20060703063225-4tm8dc2pa7wwg2t3-1
  bzrlib/tests/blackbox/test_too_much.py blackbox.py-20050620052131-a7370d756399f615
  bzrlib/tests/test_osutils.py   test_osutils.py-20051201224856-e48ee24c12182989
  bzrlib/ui/text.py              text.py-20051130153916-2e438cffc8afc478
  bzrlib/win32utils.py           win32console.py-20051021033308-123c6c929d04973d
-------------- next part --------------
=== modified file 'NEWS'
--- a/NEWS	2009-12-04 08:07:56 +0000
+++ b/NEWS	2009-12-04 10:38:04 +0000
@@ -26,6 +26,13 @@
 * ``bzr commit`` now has a ``--commit-time`` option.
   (Alexander Sack, #459276)
 
+* The ``BZR_COLUMNS`` envrionment variable can be set to force bzr to
+  respect a given terminal width. This can be useful when output is
+  redirected or in obscure cases where the default value is not
+  appropriate. Pagers can use it to get a better control of the line
+  lengths. 
+  (Vincent Ladeuil)
+
 Bug Fixes
 *********
 
@@ -45,6 +52,12 @@
 * Lots of bugfixes for the test suite on Windows. We should once again
   have a test suite with no failures on Windows. (John Arbash Meinel)
 
+* ``osutils.terminal_width()`` obeys the BZR_COLUMNS envrionment
+  variable but returns None if the terminal is not a tty (when output is
+  redirected for example). Also fixes its usage under OSes that doesn't
+  provide termios.TIOCGWINSZ.  
+  (Joke de Buhr, Vincent Ladeuil, #353370, #62539)
+
 * Terminate ssh subprocesses when no references to them remain, fixing
   subprocess and file descriptor leaks.  (Andrew Bennetts, #426662)
   

=== modified file 'bzrlib/help.py'
--- a/bzrlib/help.py	2009-06-19 09:06:56 +0000
+++ b/bzrlib/help.py	2009-12-02 15:24:34 +0000
@@ -78,7 +78,11 @@
     shown_commands = [(n, o) for n, o in commands if o.hidden == hidden]
     max_name = max(len(n) for n, o in shown_commands)
     indent = ' ' * (max_name + 1)
-    width = osutils.terminal_width() - 1
+    width = osutils.terminal_width()
+    if width is None:
+        width = osutils.default_terminal_width
+    # we need one extra space for terminals that wrap on last char
+    width = width - 1
 
     for cmd_name, cmd_object in sorted(shown_commands):
         plugin_name = cmd_object.plugin_name()

=== modified file 'bzrlib/help_topics/__init__.py'
--- a/bzrlib/help_topics/__init__.py	2009-12-02 23:11:43 +0000
+++ b/bzrlib/help_topics/__init__.py	2009-12-04 10:38:04 +0000
@@ -585,6 +585,7 @@
 BZR_SSH          Path to SSH client, or one of paramiko, openssh, sshcorp, plink.
 BZR_LOG          Location of .bzr.log (use '/dev/null' to suppress log).
 BZR_LOG (Win32)  Location of .bzr.log (use 'NUL' to suppress log).
+BZR_COLUMNS      Override implicit terminal width.
 BZR_CONCURRENCY  Number of processes that can be run concurrently (selftest).
 ================ =================================================================
 """

=== modified file 'bzrlib/log.py'
--- a/bzrlib/log.py	2009-11-30 12:24:55 +0000
+++ b/bzrlib/log.py	2009-12-02 16:21:42 +0000
@@ -1573,12 +1573,16 @@
 
     def __init__(self, *args, **kwargs):
         super(LineLogFormatter, self).__init__(*args, **kwargs)
-        self._max_chars = terminal_width() - 1
+        width = terminal_width()
+        if width is not None:
+            # we need one extra space for terminals that wrap on last char
+            width = width - 1
+        self._max_chars = width
 
     def truncate(self, str, max_len):
-        if len(str) <= max_len:
+        if max_len is None or len(str) <= max_len:
             return str
-        return str[:max_len-3]+'...'
+        return str[:max_len-3] + '...'
 
     def date_string(self, rev):
         return format_date(rev.timestamp, rev.timezone or 0,

=== modified file 'bzrlib/osutils.py'
--- a/bzrlib/osutils.py	2009-12-02 07:56:16 +0000
+++ b/bzrlib/osutils.py	2009-12-04 10:36:55 +0000
@@ -1330,25 +1330,51 @@
     normalized_filename = _inaccessible_normalized_filename
 
 
+default_terminal_width = 80
+"""The default terminal width for ttys.
+
+This is defined so that higher levels can share a common fallback value when
+terminal_width() returns None.
+"""
+
+
 def terminal_width():
-    """Return estimated terminal width."""
+    """Return terminal width.
+
+    None is returned if the width can't established precisely.
+    """
+
+    # If BZR_COLUMNS is set, take it, user is always right
+    try:
+        return int(os.environ['BZR_COLUMNS'])
+    except (KeyError, ValueError):
+        pass
+
+    # If COLUMNS is set, take it, the terminal knows better
+    try:
+        return int(os.environ['COLUMNS'])
+    except (KeyError, ValueError):
+        pass
+
+    isatty = getattr(sys.stdout, 'isatty', None)
+    if  isatty is None or not isatty():
+        # Don't guess, setting BZR_COLUMNS is the recommended way to override.
+        return None
+
     if sys.platform == 'win32':
-        return win32utils.get_console_size()[0]
-    width = 0
+        return win32utils.get_console_size(defaultx=None)[0]
+
     try:
         import struct, fcntl, termios
         s = struct.pack('HHHH', 0, 0, 0, 0)
         x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
         width = struct.unpack('HHHH', x)[1]
-    except IOError:
-        pass
-    if width <= 0:
-        try:
-            width = int(os.environ['COLUMNS'])
-        except:
-            pass
-    if width <= 0:
-        width = 80
+    except (IOError, AttributeError):
+        return None
+
+    if width <= 0:
+        # Consider invalid values as meaning no width
+        return None
 
     return width
 

=== modified file 'bzrlib/status.py'
--- a/bzrlib/status.py	2009-08-06 05:07:12 +0000
+++ b/bzrlib/status.py	2009-12-02 15:24:34 +0000
@@ -197,8 +197,10 @@
     if len(parents) < 2:
         return
 
-    # we need one extra space for terminals that wrap on last char
-    term_width = osutils.terminal_width() - 1
+    term_width = osutils.terminal_width()
+    if term_width is not None:
+        # we need one extra space for terminals that wrap on last char
+        term_width = term_width - 1
     if short:
         first_prefix = 'P   '
         sub_prefix = 'P.   '
@@ -206,6 +208,14 @@
         first_prefix = '  '
         sub_prefix = '    '
 
+    def show_log_message(rev, prefix):
+        if term_width is None:
+            width = term_width
+        else:
+            width = term_width - len(prefix)
+        log_message = log_formatter.log_string(None, rev, width, prefix=prefix)
+        to_file.write(log_message + '\n')
+
     pending = parents[1:]
     branch = new.branch
     last_revision = parents[0]
@@ -213,7 +223,8 @@
         if verbose:
             to_file.write('pending merges:\n')
         else:
-            to_file.write('pending merge tips: (use -v to see all merge revisions)\n')
+            to_file.write('pending merge tips:'
+                          ' (use -v to see all merge revisions)\n')
     graph = branch.repository.get_graph()
     other_revisions = [last_revision]
     log_formatter = log.LineLogFormatter(to_file)
@@ -227,9 +238,7 @@
             continue
 
         # Log the merge, as it gets a slightly different formatting
-        log_message = log_formatter.log_string(None, rev,
-                        term_width - len(first_prefix))
-        to_file.write(first_prefix + log_message + '\n')
+        show_log_message(rev, first_prefix)
         if not verbose:
             continue
 
@@ -267,10 +276,7 @@
             if rev is None:
                 to_file.write(sub_prefix + '(ghost) ' + sub_merge + '\n')
                 continue
-            log_message = log_formatter.log_string(None,
-                            revisions[sub_merge],
-                            term_width - len(sub_prefix))
-            to_file.write(sub_prefix + log_message + '\n')
+            show_log_message(revisions[sub_merge], sub_prefix)
 
 
 def _filter_nonexistent(orig_paths, old_tree, new_tree):

=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2009-12-03 18:10:26 +0000
+++ b/bzrlib/tests/__init__.py	2009-12-04 10:38:04 +0000
@@ -533,11 +533,15 @@
     def report_test_start(self, test):
         self.count += 1
         name = self._shortened_test_description(test)
-        # width needs space for 6 char status, plus 1 for slash, plus an
-        # 11-char time string, plus a trailing blank
-        # when NUMBERED_DIRS: plus 5 chars on test number, plus 1 char on space
-        self.stream.write(self._ellipsize_to_right(name,
-                          osutils.terminal_width()-18))
+        width = osutils.terminal_width()
+        if width is not None:
+            # width needs space for 6 char status, plus 1 for slash, plus an
+            # 11-char time string, plus a trailing blank
+            # when NUMBERED_DIRS: plus 5 chars on test number, plus 1 char on
+            # space
+            self.stream.write(self._ellipsize_to_right(name, width-18))
+        else:
+            self.stream.write(name)
         self.stream.flush()
 
     def _error_summary(self, err):
@@ -1532,6 +1536,7 @@
             'TERM': 'dumb',
             'LINES': '25',
             'COLUMNS': '80',
+            'BZR_COLUMNS': '80',
             # SSH Agent
             'SSH_AUTH_SOCK': None,
             # Proxies

=== modified file 'bzrlib/tests/blackbox/test_ignore.py'
--- a/bzrlib/tests/blackbox/test_ignore.py	2009-03-23 14:59:43 +0000
+++ b/bzrlib/tests/blackbox/test_ignore.py	2009-10-23 10:41:15 +0000
@@ -32,7 +32,6 @@
 from bzrlib.errors import BzrCommandError
 from bzrlib.osutils import (
     pathjoin,
-    terminal_width,
     )
 from bzrlib.tests.test_sftp_transport import TestCaseWithSFTPServer
 from bzrlib.tests.blackbox import ExternalBase

=== modified file 'bzrlib/tests/blackbox/test_too_much.py'
--- a/bzrlib/tests/blackbox/test_too_much.py	2009-09-17 11:54:41 +0000
+++ b/bzrlib/tests/blackbox/test_too_much.py	2009-12-02 15:24:34 +0000
@@ -44,11 +44,6 @@
     )
 from bzrlib.branch import Branch
 from bzrlib.errors import BzrCommandError
-from bzrlib.osutils import (
-    has_symlinks,
-    pathjoin,
-    terminal_width,
-    )
 from bzrlib.tests.http_utils import TestCaseWithWebserver
 from bzrlib.tests.test_sftp_transport import TestCaseWithSFTPServer
 from bzrlib.tests.blackbox import ExternalBase
@@ -87,7 +82,7 @@
         os.rmdir('revertdir')
         self.run_bzr('revert')
 
-        if has_symlinks():
+        if osutils.has_symlinks():
             os.symlink('/unlikely/to/exist', 'symlink')
             self.run_bzr('add symlink')
             self.run_bzr('commit -m f')
@@ -374,7 +369,7 @@
         self.run_bzr('init')
 
         self.assertIsSameRealPath(self.run_bzr('root')[0].rstrip(),
-                                  pathjoin(self.test_dir, 'branch1'))
+                                  osutils.pathjoin(self.test_dir, 'branch1'))
 
         progress("status of new file")
 
@@ -443,9 +438,10 @@
 
         log_out = self.run_bzr('log --line')[0]
         # determine the widest line we want
-        max_width = terminal_width() - 1
-        for line in log_out.splitlines():
-            self.assert_(len(line) <= max_width, len(line))
+        max_width = osutils.terminal_width()
+        if max_width is not None:
+            for line in log_out.splitlines():
+                self.assert_(len(line) <= max_width - 1, len(line))
         self.assert_("this is my new commit and" not in log_out)
         self.assert_("this is my new commit" in log_out)
 
@@ -462,7 +458,7 @@
 
         self.run_bzr('info')
 
-        if has_symlinks():
+        if osutils.has_symlinks():
             progress("symlinks")
             mkdir('symlinks')
             chdir('symlinks')

=== modified file 'bzrlib/tests/test_osutils.py'
--- a/bzrlib/tests/test_osutils.py	2009-12-02 07:56:16 +0000
+++ b/bzrlib/tests/test_osutils.py	2009-12-02 16:21:42 +0000
@@ -23,6 +23,7 @@
 import socket
 import stat
 import sys
+import termios
 import time
 
 from bzrlib import (
@@ -1922,3 +1923,63 @@
             r"bzr: warning: some compiled extensions could not be loaded; "
             "see <https://answers\.launchpad\.net/bzr/\+faq/703>\n"
             )
+
+
+class TestTerminalWidth(tests.TestCase):
+
+    def test_default_values(self):
+        self.assertEquals(80, osutils.default_terminal_width)
+
+    def test_defaults_to_BZR_COLUMNS(self):
+        # BZR_COLUMNS is set by the test framework
+        self.assertEquals('80', os.environ['BZR_COLUMNS'])
+        os.environ['BZR_COLUMNS'] = '12'
+        self.assertEquals(12, osutils.terminal_width())
+
+    def test_falls_back_to_COLUMNS(self):
+        del os.environ['BZR_COLUMNS']
+        os.environ['COLUMNS'] = '42'
+        self.assertEquals(42, osutils.terminal_width())
+
+    def test_tty_default_without_columns(self):
+        del os.environ['BZR_COLUMNS']
+        del os.environ['COLUMNS']
+        orig_stdout = sys.stdout
+        def restore():
+            sys.stdout = orig_stdout
+        self.addCleanup(restore)
+
+        class I_am_a_tty(object):
+            def isatty(self):
+                return True
+
+        sys.stdout = I_am_a_tty()
+        self.assertEquals(None, osutils.terminal_width())
+
+    def test_non_tty_default_without_columns(self):
+        del os.environ['BZR_COLUMNS']
+        del os.environ['COLUMNS']
+        orig_stdout = sys.stdout
+        def restore():
+            sys.stdout = orig_stdout
+        self.addCleanup(restore)
+        sys.stdout = None
+        self.assertEquals(None, osutils.terminal_width())
+
+    def test_TIOCGWINSZ(self):
+        # bug 63539 is about a termios without TIOCGWINSZ attribute
+        exist = True
+        try:
+            orig = termios.TIOCGWINSZ
+        except AttributeError:
+            exist = False
+
+        def restore():
+            if exist:
+                termios.TIOCGWINSZ = orig
+        self.addCleanup(restore)
+
+        del termios.TIOCGWINSZ
+        del os.environ['BZR_COLUMNS']
+        del os.environ['COLUMNS']
+        self.assertEquals(None, osutils.terminal_width())

=== modified file 'bzrlib/ui/text.py'
--- a/bzrlib/ui/text.py	2009-09-23 06:29:46 +0000
+++ b/bzrlib/ui/text.py	2009-12-02 15:24:34 +0000
@@ -234,8 +234,10 @@
 
     def _show_line(self, s):
         # sys.stderr.write("progress %r\n" % s)
-        n = self._width - 1
-        self._term_file.write('\r%-*.*s\r' % (n, n, s))
+        if self._width is not None:
+            n = self._width - 1
+            s = '%-*.*s' % (n, n, s)
+        self._term_file.write('\r' + s + '\r')
 
     def clear(self):
         if self._have_output:

=== modified file 'bzrlib/win32utils.py'
--- a/bzrlib/win32utils.py	2009-11-20 16:42:28 +0000
+++ b/bzrlib/win32utils.py	2009-12-02 16:21:42 +0000
@@ -182,7 +182,7 @@
         return (defaultx, defaulty)
 
     # To avoid problem with redirecting output via pipe
-    # need to use stderr instead of stdout
+    # we need to use stderr instead of stdout
     h = ctypes.windll.kernel32.GetStdHandle(WIN32_STDERR_HANDLE)
     csbi = ctypes.create_string_buffer(22)
     res = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)



More information about the bazaar-commits mailing list