Browse Source

Merge "Enable color support based on tty and config"

tags/1.0.0
Jenkins 4 years ago
parent
commit
0e37367426
4 changed files with 131 additions and 26 deletions
  1. 14
    0
      git-review.1
  2. 43
    17
      git_review/cmd.py
  3. 72
    7
      git_review/tests/test_unit.py
  4. 2
    2
      git_review/tests/utils.py

+ 14
- 0
git-review.1 View File

@@ -221,6 +221,20 @@ in the
221 221
 .Pa .gitreview
222 222
 file.
223 223
 .El
224
+.Bl -tag
225
+.It color.review
226
+Whether to use ANSI escape sequences to add color to the output displayed by
227
+this command. Default value is determined by color.ui.
228
+.Bl -tag
229
+.It auto or true
230
+If you want output to use color when written to the terminal (default with Git
231
+1.8.4 and newer).
232
+.It always
233
+If you want all output to use color
234
+.It never or false
235
+If you wish not to use color for any output. (default with Git older than 1.8.4)
236
+.El
237
+.El
224 238
 .Sh FILES
225 239
 To use
226 240
 .Nm

+ 43
- 17
git_review/cmd.py View File

@@ -60,6 +60,7 @@ DEFAULTS = dict(scheme='ssh', hostname=False, port=None, project=False,
60 60
 
61 61
 _branch_name = None
62 62
 _has_color = None
63
+_use_color = None
63 64
 _orig_head = None
64 65
 
65 66
 
@@ -529,6 +530,29 @@ def query_reviews_over_ssh(remote_url, change=None, current_patch_set=True,
529 530
     return changes
530 531
 
531 532
 
533
+def set_color_output(color="auto"):
534
+    global _use_color
535
+    if check_color_support():
536
+        if color == "auto":
537
+            check_use_color_output()
538
+        else:
539
+            _use_color = color == "always"
540
+
541
+
542
+def check_use_color_output():
543
+    global _use_color
544
+    if _use_color is None:
545
+        if check_color_support():
546
+            # we can support color, now check if we should use it
547
+            stdout = "true" if sys.stdout.isatty() else "false"
548
+            test_command = "git config --get-colorbool color.review " + stdout
549
+            color = run_command(test_command)
550
+            _use_color = color == "true"
551
+        else:
552
+            _use_color = False
553
+    return _use_color
554
+
555
+
532 556
 def check_color_support():
533 557
     global _has_color
534 558
     if _has_color is None:
@@ -688,23 +712,10 @@ def get_branch_name(target_branch):
688 712
 
689 713
 
690 714
 def assert_one_change(remote, branch, yes, have_hook):
691
-    has_color = check_color_support()
692
-    if has_color:
693
-        color = git_config_get_value("color", "ui")
694
-        if color is None:
695
-            color = "auto"
696
-        else:
697
-            color = color.lower()
698
-        if (color == "" or color == "true"):
699
-            color = "auto"
700
-        elif color == "false":
701
-            color = "never"
702
-        elif color == "auto":
703
-            # Python is not a tty, we have to force colors
704
-            color = "always"
705
-        use_color = "--color=%s" % color
715
+    if check_use_color_output():
716
+        use_color = "--color=always"
706 717
     else:
707
-        use_color = ""
718
+        use_color = "--color=never"
708 719
     cmd = ("git log %s --decorate --oneline HEAD --not --remotes=%s" % (
709 720
            use_color, remote))
710 721
     (status, output) = run_command_status(cmd)
@@ -809,7 +820,7 @@ def list_reviews(remote):
809 820
 
810 821
     REVIEW_FIELDS = ('number', 'branch', 'subject')
811 822
     FIELDS = range(len(REVIEW_FIELDS))
812
-    if check_color_support():
823
+    if check_use_color_output():
813 824
         review_field_color = (colors.yellow, colors.green, "")
814 825
         color_reset = colors.reset
815 826
     else:
@@ -1153,6 +1164,18 @@ def _main():
1153 1164
     parser.add_argument("--no-custom-script", dest="custom_script",
1154 1165
                         action="store_false", default=True,
1155 1166
                         help="Do not run custom scripts.")
1167
+    parser.add_argument("--color", dest="color", metavar="<when>",
1168
+                        nargs="?", choices=["always", "never", "auto"],
1169
+                        help="Show color output. --color (without [<when>]) "
1170
+                             "is the same as --color=always. <when> can be "
1171
+                             "one of %(choices)s. Behaviour can also be "
1172
+                             "controlled by the color.ui and color.review "
1173
+                             "configuration settings.")
1174
+    parser.add_argument("--no-color", dest="color", action="store_const",
1175
+                        const="never",
1176
+                        help="Turn off colored output. Can be used to "
1177
+                             "override configuration options. Same as "
1178
+                             "setting --color=never.")
1156 1179
     parser.add_argument("--license", dest="license", action="store_true",
1157 1180
                         help="Print the license and exit")
1158 1181
     parser.add_argument("--version", action="version",
@@ -1197,6 +1220,9 @@ def _main():
1197 1220
     check_remote(branch, remote, config['scheme'],
1198 1221
                  config['hostname'], config['port'], config['project'])
1199 1222
 
1223
+    if options.color:
1224
+        set_color_output(options.color)
1225
+
1200 1226
     if options.changeidentifier:
1201 1227
         if options.compare:
1202 1228
             compare_review(options.changeidentifier,

+ 72
- 7
git_review/tests/test_unit.py View File

@@ -15,18 +15,23 @@
15 15
 # See the License for the specific language governing permissions and
16 16
 # limitations under the License.
17 17
 
18
-# Use of io.StringIO in python =< 2.7 requires all strings handled to be
19
-# unicode. See if StringIO.StringIO is available first
20
-try:
21
-    import StringIO as io
22
-except ImportError:
23
-    import io
18
+import functools
19
+import os
24 20
 import textwrap
25 21
 
22
+import fixtures
26 23
 import mock
27 24
 import testtools
28 25
 
29 26
 from git_review import cmd
27
+from git_review.tests import utils
28
+
29
+# Use of io.StringIO in python =< 2.7 requires all strings handled to be
30
+# unicode. See if StringIO.StringIO is available first
31
+try:
32
+    import StringIO as io
33
+except ImportError:
34
+    import io
30 35
 
31 36
 
32 37
 class ConfigTestCase(testtools.TestCase):
@@ -50,7 +55,7 @@ class ConfigTestCase(testtools.TestCase):
50 55
         self.assertFalse(exists_mock.called)
51 56
 
52 57
 
53
-class GitReviewConsole(testtools.TestCase):
58
+class GitReviewConsole(testtools.TestCase, fixtures.TestWithFixtures):
54 59
     """Class for testing the console output of git-review."""
55 60
 
56 61
     reviews = [
@@ -70,6 +75,29 @@ class GitReviewConsole(testtools.TestCase):
70 75
                        'max width is short enough'
71 76
         }]
72 77
 
78
+    def setUp(self):
79
+        super(GitReviewConsole, self).setUp()
80
+        # ensure all tests get a separate git dir to work in to avoid
81
+        # local git config from interfering
82
+        self.tempdir = self.useFixture(fixtures.TempDir())
83
+        self._run_git = functools.partial(utils.run_git,
84
+                                          chdir=self.tempdir.path)
85
+
86
+        self.run_cmd_patcher = mock.patch('git_review.cmd.run_command_status')
87
+        run_cmd_partial = functools.partial(
88
+            cmd.run_command_status, GIT_WORK_TREE=self.tempdir.path,
89
+            GIT_DIR=os.path.join(self.tempdir.path, '.git'))
90
+        self.run_cmd_mock = self.run_cmd_patcher.start()
91
+        self.run_cmd_mock.side_effect = run_cmd_partial
92
+
93
+        self._run_git('init')
94
+        self._run_git('commit', '--allow-empty', '-m "initial commit"')
95
+        self._run_git('commit', '--allow-empty', '-m "2nd commit"')
96
+
97
+    def tearDown(self):
98
+        self.run_cmd_patcher.stop()
99
+        super(GitReviewConsole, self).tearDown()
100
+
73 101
     @mock.patch('git_review.cmd.query_reviews')
74 102
     @mock.patch('git_review.cmd.get_remote_url', mock.MagicMock)
75 103
     @mock.patch('git_review.cmd._has_color', False)
@@ -87,3 +115,40 @@ class GitReviewConsole(testtools.TestCase):
87 115
                 self.assertEqual(line.isspace(), False,
88 116
                                  "Extra blank lines appearing between reviews"
89 117
                                  "in console output")
118
+
119
+    @mock.patch('git_review.cmd._use_color', None)
120
+    def test_color_output_disabled(self):
121
+        """Test disabling of colour output color.ui defaults to enabled
122
+        """
123
+
124
+        # git versions < 1.8.4 default to 'color.ui' being false
125
+        # so must be set to auto to correctly test
126
+        self._run_git("config", "color.ui", "auto")
127
+
128
+        self._run_git("config", "color.review", "never")
129
+        self.assertFalse(cmd.check_use_color_output(),
130
+                         "Failed to detect color output disabled")
131
+
132
+    @mock.patch('git_review.cmd._use_color', None)
133
+    def test_color_output_forced(self):
134
+        """Test force enable of colour output when color.ui
135
+        is defaulted to false
136
+        """
137
+
138
+        self._run_git("config", "color.ui", "never")
139
+
140
+        self._run_git("config", "color.review", "always")
141
+        self.assertTrue(cmd.check_use_color_output(),
142
+                        "Failed to detect color output forcefully "
143
+                        "enabled")
144
+
145
+    @mock.patch('git_review.cmd._use_color', None)
146
+    def test_color_output_fallback(self):
147
+        """Test fallback to using color.ui when color.review is not
148
+        set
149
+        """
150
+
151
+        self._run_git("config", "color.ui", "always")
152
+        self.assertTrue(cmd.check_use_color_output(),
153
+                        "Failed to use fallback to color.ui when "
154
+                        "color.review not present")

+ 2
- 2
git_review/tests/utils.py View File

@@ -54,9 +54,9 @@ def run_cmd(*args, **kwargs):
54 54
     return out.strip()
55 55
 
56 56
 
57
-def run_git(command, *args):
57
+def run_git(command, *args, **kwargs):
58 58
     """Run git command with the specified args."""
59
-    return run_cmd("git", command, *args)
59
+    return run_cmd("git", command, *args, **kwargs)
60 60
 
61 61
 
62 62
 def write_to_file(path, content):

Loading…
Cancel
Save