Skip to content

Commit dfe23fd

Browse files
committed
starting tests for prettier
1 parent 68b1700 commit dfe23fd

3 files changed

Lines changed: 156 additions & 22 deletions

File tree

devtools/prettier.py

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1+
import collections
12
import io
23
import textwrap
3-
import collections
4-
from typing import Any
4+
from typing import Any, Generator, Union
55

6+
try:
7+
from pygments import highlight
8+
from pygments.lexers import PythonLexer
9+
from pygments.formatters import Terminal256Formatter
10+
except ImportError: # pragma: no cover
11+
pyg_lexer = pyg_formatter = None
12+
else:
13+
pyg_lexer, pyg_formatter = PythonLexer(), Terminal256Formatter(style='vim')
614

715
PARENTHESES_LOOKUP = [
816
(list, '[', ']'),
@@ -12,43 +20,53 @@
1220

1321
class PrettyFormat:
1422
def __init__(self,
23+
colorize=False,
1524
indent_step=4,
1625
indent_char=' ',
1726
repr_strings=False,
27+
simple_cuttoff=10,
28+
max_width=120,
1829
yield_from_generators=True):
1930
# TODO colours
31+
self._colorize = colorize
2032
self._indent_step = indent_step
2133
self._c = indent_char
2234
self._repr_strings = repr_strings
2335
self._repr_generators = not yield_from_generators
36+
self._simple_cuttoff = simple_cuttoff
37+
self._max_width = max_width
2438
self._type_lookup = [
2539
(dict, self._format_dict),
2640
((tuple, list, set), self._format_list_like),
2741
(str, self._format_str),
42+
(bytes, self._format_bytes),
2843
(collections.Generator, self._format_generators),
2944
]
3045

3146
def __call__(self, value: Any, *, indent_current: int=0):
3247
self._stream = io.StringIO()
3348
self._format(value, indent_current=indent_current, indent_first=True)
34-
return self._stream.getvalue()
49+
s = self._stream.getvalue()
50+
if self._colorize and pyg_lexer:
51+
s = highlight(s, lexer=pyg_lexer, formatter=pyg_formatter)
52+
return s
3553

3654
def _format(self, value: Any, indent_current: int, indent_first: bool):
3755
if indent_first:
3856
self._stream.write(indent_current * self._c)
3957

40-
indent_new = indent_current + self._indent_step
41-
for t, func in self._type_lookup:
42-
if isinstance(value, t):
43-
func(value, indent_current, indent_new)
44-
return
45-
46-
value_s = repr(value)
47-
if '\n' in value_s:
48-
value_s = textwrap.indent(value_s, indent_new * self._c).lstrip(' ')
49-
self._stream.write(value_s)
50-
51-
def _format_dict(self, value, indent_current, indent_new):
58+
value_repr = repr(value)
59+
if len(value_repr) <= self._simple_cuttoff and not isinstance(value, collections.Generator):
60+
self._stream.write(value_repr)
61+
else:
62+
indent_new = indent_current + self._indent_step
63+
for t, func in self._type_lookup:
64+
if isinstance(value, t):
65+
func(value, value_repr, indent_current, indent_new)
66+
return
67+
self._format_raw(value, value_repr, indent_current, indent_new)
68+
69+
def _format_dict(self, value: dict, value_repr: str, indent_current: int, indent_new: int):
5270
self._stream.write('{\n')
5371
for k, v in value.items():
5472
self._format(k, indent_new, True)
@@ -57,7 +75,7 @@ def _format_dict(self, value, indent_current, indent_new):
5775
self._stream.write(',\n')
5876
self._stream.write(indent_current * self._c + '}')
5977

60-
def _format_list_like(self, value, indent_current, indent_new):
78+
def _format_list_like(self, value: Union[list, tuple, set], value_repr: str, indent_current: int, indent_new: int):
6179
open_, close_ = '(', ')'
6280
for t, *oc in PARENTHESES_LOOKUP:
6381
if isinstance(value, t):
@@ -70,9 +88,9 @@ def _format_list_like(self, value, indent_current, indent_new):
7088
self._stream.write(',\n')
7189
self._stream.write(indent_current * self._c + close_)
7290

73-
def _format_str(self, value, indent_current, indent_new):
91+
def _format_str(self, value: str, value_repr: str, indent_current: int, indent_new: int):
7492
if self._repr_strings:
75-
self._stream.write(repr(value))
93+
self._stream.write(value_repr)
7694
else:
7795
lines = value.splitlines(True)
7896
if len(lines) > 1:
@@ -82,21 +100,51 @@ def _format_str(self, value, indent_current, indent_new):
82100
self._stream.write(prefix + repr(line) + '\n')
83101
self._stream.write(indent_current * self._c + ')')
84102
else:
85-
self._stream.write(repr(value))
103+
self._stream.write(value_repr)
104+
105+
def _format_bytes(self, value: bytes, value_repr: str, indent_current: int, indent_new: int):
106+
wrap = self._max_width - indent_new - 3
107+
if len(value) < wrap:
108+
self._stream.write(value_repr)
109+
else:
110+
self._stream.write('(\n')
111+
prefix = indent_new * self._c
112+
start, end = 0, wrap
113+
while start < len(value):
114+
line = value[start:end]
115+
self._stream.write(prefix + repr(line) + '\n')
116+
start = end
117+
end += wrap
118+
self._stream.write(indent_current * self._c + ')')
86119

87-
def _format_generators(self, value, indent_current, indent_new):
120+
def _format_generators(self, value: Generator, value_repr: str, indent_current: int, indent_new: int):
88121
if self._repr_generators:
89-
self._stream.write(repr(value))
122+
self._stream.write(value_repr)
90123
else:
91124
self._stream.write('(\n')
92125
for v in value:
93126
self._format(v, indent_new, True)
94127
self._stream.write(',\n')
95128
self._stream.write(indent_current * self._c + ')')
96129

130+
def _format_raw(self, value: Any, value_repr: str, indent_current: int, indent_new: int):
131+
lines = value_repr.splitlines(True)
132+
if len(lines) > 1 or (len(value_repr) + indent_current) >= self._max_width:
133+
self._stream.write('(\n')
134+
wrap_at = self._max_width - indent_new
135+
prefix = indent_new * self._c
136+
for line in lines:
137+
sub_lines = textwrap.wrap(line, wrap_at)
138+
for sline in sub_lines:
139+
self._stream.write(prefix + sline + '\n')
140+
self._stream.write(indent_current * self._c + ')')
141+
else:
142+
self._stream.write(value_repr)
143+
97144

98145
pformat = PrettyFormat()
146+
_ppformat = PrettyFormat(colorize=True)
99147

100148

101149
def pprint(s):
102-
print(pformat(s), flush=True)
150+
print(_ppformat(s), flush=True)

tests/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ pytest-isort==0.1.0
1212
pytest-mock==1.6.0
1313
pytest-sugar==0.8.0
1414
pytest-toolbox==0.2
15+
numpy # pyup: ignore

tests/test_prettier.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import string
2+
3+
import numpy
4+
5+
from devtools.prettier import PrettyFormat, pformat
6+
7+
8+
def test_dict():
9+
v = pformat({1: 2, 3: 4})
10+
print(v)
11+
assert v == (
12+
'{\n'
13+
' 1: 2,\n'
14+
' 3: 4,\n'
15+
'}')
16+
17+
18+
def test_list():
19+
v = pformat(list(range(6)))
20+
assert v == (
21+
'[\n'
22+
' 0,\n'
23+
' 1,\n'
24+
' 2,\n'
25+
' 3,\n'
26+
' 4,\n'
27+
' 5,\n'
28+
']')
29+
30+
31+
def test_set():
32+
v = pformat(set(range(6)))
33+
assert v == (
34+
'{\n'
35+
' 0,\n'
36+
' 1,\n'
37+
' 2,\n'
38+
' 3,\n'
39+
' 4,\n'
40+
' 5,\n'
41+
'}')
42+
43+
44+
def test_generator():
45+
v = pformat((i for i in range(3)))
46+
assert v == (
47+
'(\n'
48+
' 0,\n'
49+
' 1,\n'
50+
' 2,\n'
51+
')')
52+
53+
54+
def test_str():
55+
pformat_ = PrettyFormat(max_width=12)
56+
v = pformat_(string.ascii_lowercase + '\n' + string.digits)
57+
assert v == (
58+
"(\n"
59+
" 'abcdefghijklmnopqrstuvwxyz\\n'\n"
60+
" '0123456789'\n"
61+
")")
62+
63+
64+
def test_bytes():
65+
pformat_ = PrettyFormat(max_width=12)
66+
v = pformat_(string.ascii_lowercase.encode())
67+
assert v == (
68+
"(\n"
69+
" b'abcde'\n"
70+
" b'fghij'\n"
71+
" b'klmno'\n"
72+
" b'pqrst'\n"
73+
" b'uvwxy'\n"
74+
" b'z'\n"
75+
")")
76+
77+
78+
def test_indent_numpy():
79+
v = pformat({'numpy test': numpy.array(range(20))})
80+
assert v == ("{\n"
81+
" 'numpy test': (\n"
82+
" array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,\n"
83+
" 17, 18, 19])\n"
84+
" ),\n"
85+
"}")

0 commit comments

Comments
 (0)