Skip to content

Commit 63e8ea1

Browse files
committed
Resolves issues with CSV printing on Python 2
Add lots of unit tests to ensure we don't regress. Signed-off-by: Stephen Finucane <stephen@that.guru> Fixes: f3974aa ("utils: Wrap CSV in quotes")
1 parent f3974aa commit 63e8ea1

File tree

3 files changed

+67
-15
lines changed

3 files changed

+67
-15
lines changed

git_pw/utils.py

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,10 @@
1111
import sys
1212

1313
import click
14+
import six
1415
from tabulate import tabulate
1516

1617

17-
if sys.version_info < (3, 0):
18-
from io import BytesIO # noqa
19-
else:
20-
from io import StringIO # noqa
21-
22-
if sys.version_info < (3, 0):
23-
_text = unicode # noqa
24-
else:
25-
_text = str # noqa
26-
27-
2818
def trim(string, length=70): # type: (str, int) -> str
2919
"""Trim a string to the given length."""
3020
return (string[:length - 1] + '...') if len(string) > length else string
@@ -70,11 +60,13 @@ def _tabulate(output, headers, fmt):
7060
elif fmt == 'simple':
7161
return tabulate(output, headers, tablefmt='simple')
7262
elif fmt == 'csv':
73-
result = StringIO()
74-
writer = csv.writer(result, quoting=csv.QUOTE_ALL)
75-
writer.writerow(headers)
63+
result = six.StringIO()
64+
writer = csv.writer(
65+
result, quoting=csv.QUOTE_ALL, lineterminator=os.linesep)
66+
writer.writerow([six.ensure_str(h) for h in headers])
7667
for item in output:
77-
writer.writerow(_text(x if x is not None else '') for x in item)
68+
writer.writerow([
69+
six.ensure_str(x if x is not None else '') for x in item])
7870
return result.getvalue()
7971

8072
print('pw.format must be one of: table, simple, csv')

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ click>=6.0,<8.0
22
requests>2.0,<3.0
33
tabulate>=0.8
44
arrow>=0.10
5+
six>=1.12

tests/test_utils.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
# -*- coding: utf-8 -*-
2+
13
"""Unit tests for ``git_pw/utils.py``."""
24

35
import subprocess
6+
import textwrap
47
import os
58

69
import mock
@@ -78,3 +81,59 @@ def test_echo_via_pager_env_default(mock_inner, mock_tabulate, mock_config):
7881
mock_config.assert_called_once_with('core.parser')
7982
mock_tabulate.assert_called_once_with('test', ('foo',), None)
8083
mock_inner.assert_called_once_with('less', mock_tabulate.return_value)
84+
85+
86+
def _test_tabulate(fmt):
87+
output = [(b'foo', 'bar', u'baz', '😀', None)]
88+
headers = ('col1', 'colb', 'colIII', 'colX', 'colY')
89+
90+
result = utils._tabulate(output, headers, fmt)
91+
92+
return output, headers, result
93+
94+
95+
@mock.patch.object(utils, 'tabulate')
96+
def test_tabulate_table(mock_tabulate):
97+
output, headers, result = _test_tabulate('table')
98+
99+
mock_tabulate.assert_called_once_with(output, headers, tablefmt='psql')
100+
assert result == mock_tabulate.return_value
101+
102+
103+
@mock.patch.object(utils, 'tabulate')
104+
def test_tabulate_simple(mock_tabulate):
105+
output, headers, result = _test_tabulate('simple')
106+
107+
mock_tabulate.assert_called_once_with(output, headers, tablefmt='simple')
108+
assert result == mock_tabulate.return_value
109+
110+
111+
@mock.patch.object(utils, 'tabulate')
112+
def test_tabulate_csv(mock_tabulate):
113+
output, headers, result = _test_tabulate('csv')
114+
115+
mock_tabulate.assert_not_called()
116+
assert result == textwrap.dedent("""\
117+
"col1","colb","colIII","colX","colY"
118+
"foo","bar","baz","😀",""
119+
""")
120+
121+
122+
@mock.patch.object(utils, 'git_config', return_value='simple')
123+
@mock.patch.object(utils, 'tabulate')
124+
def test_tabulate_git_config(mock_tabulate, mock_git_config):
125+
output, headers, result = _test_tabulate(None)
126+
127+
mock_git_config.assert_called_once_with('pw.format')
128+
mock_tabulate.assert_called_once_with(output, headers, tablefmt='simple')
129+
assert result == mock_tabulate.return_value
130+
131+
132+
@mock.patch.object(utils, 'git_config', return_value='')
133+
@mock.patch.object(utils, 'tabulate')
134+
def test_tabulate_default(mock_tabulate, mock_git_config):
135+
output, headers, result = _test_tabulate(None)
136+
137+
mock_git_config.assert_called_once_with('pw.format')
138+
mock_tabulate.assert_called_once_with(output, headers, tablefmt='psql')
139+
assert result == mock_tabulate.return_value

0 commit comments

Comments
 (0)