Skip to content

Commit ca9b0f5

Browse files
committed
highlighting, uprev, readme
1 parent ba5cfdf commit ca9b0f5

File tree

8 files changed

+75
-33
lines changed

8 files changed

+75
-33
lines changed

README.rst

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ Install
1010

1111
Just::
1212

13-
pip install devtools
13+
pip install devtools[pygments]
14+
15+
(``pygments`` is not required but if it's available output will be highlighted and easier to read.)
1416

1517
Usage
1618
-----
@@ -24,7 +26,28 @@ Usage
2426
2527
Outputs::
2628

27-
test.py:4 <module>: whatever = [1, 2, 3] (list)
29+
test.py:4 <module>:
30+
whatever: [1, 2, 3] (list)
31+
32+
33+
That's only the tip of the iceberg, for example `demo.py </demo.py>`_:
34+
35+
.. code:: python
36+
37+
import numpy as np
38+
39+
data = {
40+
'foo': np.array(range(20)),
41+
'bar': {'apple', 'banana', 'carrot', 'grapefruit'},
42+
'spam': [{'a': i, 'b': (i for i in range(3))} for i in range(3)],
43+
'sentence': 'this is just a boring sentence.\n' * 4
44+
}
45+
46+
debug(data)
47+
48+
outputs:
49+
50+
.. image:: ./demo.py.png
2851

2952
Usage without Import
3053
--------------------

demo.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import numpy as np
2+
3+
data = {
4+
'foo': np.array(range(20)),
5+
'bar': {'apple', 'banana', 'carrot', 'grapefruit'},
6+
'spam': [{'a': i, 'b': (i for i in range(3))} for i in range(3)],
7+
'sentence': 'this is just a boring sentence.\n' * 4
8+
}
9+
10+
debug(data)

demo.py.png

39.3 KB
Loading

devtools/debug.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,20 @@
1212

1313
__all__ = ['Debug', 'debug']
1414
CWD = Path('.').resolve()
15-
pformat = PrettyFormat()
1615

1716

1817
def env_true(var_name, alt='FALSE'):
1918
return os.getenv(var_name, alt).upper() in {'1', 'TRUE'}
2019

2120

21+
pformat = PrettyFormat(
22+
indent_step=int(os.getenv('PY_DEVTOOLS_INDENT', 4)),
23+
simple_cutoff=int(os.getenv('PY_DEVTOOLS_SIMPLE_CUTOFF', 10)),
24+
width=int(os.getenv('PY_DEVTOOLS_SIMPLE_TERM_WIDTH', 80)),
25+
yield_from_generators=env_true('PY_DEVTOOLS_YIELD_FROM_GEN', 'TRUE'),
26+
)
27+
28+
2229
class DebugArgument:
2330
__slots__ = 'value', 'name', 'extra'
2431

@@ -30,17 +37,17 @@ def __init__(self, value, *, name=None, **extra):
3037
self.extra.append(('len', len(value)))
3138
self.extra += [(k, v) for k, v in extra.items() if v is not None]
3239

33-
def str(self, colours=False) -> str:
40+
def str(self, colour=False, highlight=False) -> str:
3441
s = ''
3542
if self.name:
36-
s = sformat(self.name, sformat.blue, apply=colours) + ': '
37-
s += pformat(self.value, indent=2)
43+
s = sformat(self.name, sformat.blue, apply=colour) + ': '
44+
s += pformat(self.value, indent=2, highlight=highlight)
3845
suffix = (
3946
' ({0.value.__class__.__name__}) {1}'
4047
.format(self, ' '.join('{}={}'.format(k, v) for k, v in self.extra))
4148
.rstrip(' ') # trailing space if extra is empty
4249
)
43-
s += sformat(suffix, sformat.dim, apply=colours)
50+
s += sformat(suffix, sformat.dim, apply=colour)
4451
return s
4552

4653
def __str__(self) -> str:
@@ -60,16 +67,16 @@ def __init__(self, *, filename: str, lineno: int, frame: str, arguments: List[De
6067
self.frame = frame
6168
self.arguments = arguments
6269

63-
def str(self, colours=False) -> str:
64-
if colours:
70+
def str(self, colour=False, highlight=False) -> str:
71+
if colour:
6572
prefix = '{}:{} {}\n '.format(
6673
sformat(self.filename, sformat.magenta),
6774
sformat(self.lineno, sformat.green),
6875
sformat(self.frame, sformat.green, sformat.italic)
6976
)
7077
else:
7178
prefix = '{0.filename}:{0.lineno} {0.frame}\n '.format(self)
72-
return prefix + '\n '.join(a.str(colours) for a in self.arguments)
79+
return prefix + '\n '.join(a.str(colour, highlight) for a in self.arguments)
7380

7481
def __str__(self) -> str:
7582
return self.str()
@@ -89,10 +96,12 @@ class Debug:
8996

9097
def __init__(self, *,
9198
warnings: Optional[bool]=None,
92-
colours: Optional[bool]=None,
99+
colour: Optional[bool]=None,
100+
highlight: Optional[bool]=None,
93101
frame_context_length: int=50):
94102
self._warnings = self._env_bool(warnings, 'PY_DEVTOOLS_WARNINGS')
95-
self._colours = self._env_bool(colours, 'PY_DEVTOOLS_COLOURS')
103+
self._colour = self._env_bool(colour, 'PY_DEVTOOLS_COLOUR')
104+
self._highlight = self._env_bool(highlight, 'PY_DEVTOOLS_HIGHLIGHT')
96105
# 50 lines should be enough to make sure we always get the entire function definition
97106
self._frame_context_length = frame_context_length
98107

@@ -106,7 +115,7 @@ def _env_bool(cls, value, env_name, env_default='TRUE'):
106115
def __call__(self, *args, file_=None, flush_=True, **kwargs) -> None:
107116
d_out = self._process(args, kwargs, r'debug *\(')
108117
colours_possible = isatty(file_)
109-
s = d_out.str(self._colours and colours_possible)
118+
s = d_out.str(self._colour and colours_possible, self._highlight and colours_possible)
110119
print(s, file=file_, flush=flush_)
111120

112121
def format(self, *args, **kwargs) -> DebugOutput:

devtools/prettier.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from .ansi import isatty
77

88
try:
9-
from pygments import highlight
9+
import pygments
1010
from pygments.lexers import PythonLexer
1111
from pygments.formatters import Terminal256Formatter
1212
except ImportError: # pragma: no cover
@@ -23,20 +23,18 @@
2323

2424
class PrettyFormat:
2525
def __init__(self,
26-
colours=False,
2726
indent_step=4,
2827
indent_char=' ',
2928
repr_strings=False,
3029
simple_cutoff=10,
31-
max_width=120,
30+
width=120,
3231
yield_from_generators=True):
33-
self._colours = colours
3432
self._indent_step = indent_step
3533
self._c = indent_char
3634
self._repr_strings = repr_strings
3735
self._repr_generators = not yield_from_generators
3836
self._simple_cutoff = simple_cutoff
39-
self._max_width = max_width
37+
self._width = width
4038
self._type_lookup = [
4139
(dict, self._format_dict),
4240
((tuple, list, set), self._format_list_like),
@@ -45,12 +43,13 @@ def __init__(self,
4543
(collections.Generator, self._format_generators),
4644
]
4745

48-
def __call__(self, value: Any, *, indent: int=0, indent_first: bool=False):
46+
def __call__(self, value: Any, *, indent: int=0, indent_first: bool=False, highlight: bool=False):
4947
self._stream = io.StringIO()
5048
self._format(value, indent_current=indent, indent_first=indent_first)
5149
s = self._stream.getvalue()
52-
if self._colours and pyg_lexer:
53-
s = highlight(s, lexer=pyg_lexer, formatter=pyg_formatter)
50+
if highlight and pyg_lexer:
51+
# apparently highlight adds a trailing new line we don't want
52+
s = pygments.highlight(s, lexer=pyg_lexer, formatter=pyg_formatter).rstrip('\n')
5453
return s
5554

5655
def _format(self, value: Any, indent_current: int, indent_first: bool):
@@ -105,7 +104,7 @@ def _format_str(self, value: str, value_repr: str, indent_current: int, indent_n
105104
self._stream.write(value_repr)
106105

107106
def _format_bytes(self, value: bytes, value_repr: str, indent_current: int, indent_new: int):
108-
wrap = self._max_width - indent_new - 3
107+
wrap = self._width - indent_new - 3
109108
if len(value) < wrap:
110109
self._stream.write(value_repr)
111110
else:
@@ -131,9 +130,9 @@ def _format_generators(self, value: Generator, value_repr: str, indent_current:
131130

132131
def _format_raw(self, value: Any, value_repr: str, indent_current: int, indent_new: int):
133132
lines = value_repr.splitlines(True)
134-
if len(lines) > 1 or (len(value_repr) + indent_current) >= self._max_width:
133+
if len(lines) > 1 or (len(value_repr) + indent_current) >= self._width:
135134
self._stream.write('(\n')
136-
wrap_at = self._max_width - indent_new
135+
wrap_at = self._width - indent_new
137136
prefix = indent_new * self._c
138137
for line in lines:
139138
sub_lines = textwrap.wrap(line, wrap_at)
@@ -145,8 +144,7 @@ def _format_raw(self, value: Any, value_repr: str, indent_current: int, indent_n
145144

146145

147146
pformat = PrettyFormat()
148-
_ppformat = PrettyFormat(colours=isatty())
149147

150148

151-
def pprint(s):
152-
print(_ppformat(s), flush=True)
149+
def pprint(s, file=None):
150+
print(pformat(s, highlight=isatty(file)), file=file, flush=True)

devtools/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
__all__ = ['VERSION']
44

5-
VERSION = StrictVersion('0.0.2')
5+
VERSION = StrictVersion('0.1.0')

setup.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,8 @@
3636
license='MIT',
3737
packages=['devtools'],
3838
python_requires='>=3.5',
39+
extras_require={
40+
'pygments': ['Pygments>=2.2.0'],
41+
},
3942
zip_safe=True,
4043
)

tests/test_prettier.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,10 @@ def test_print(capsys):
2828

2929

3030
def test_colours():
31-
pformat_ = PrettyFormat(colours=True)
32-
v = pformat_({1: 2, 3: 4})
31+
v = pformat({1: 2, 3: 4}, highlight=True)
3332
assert v.startswith('\x1b'), repr(v)
3433
v2 = strip_ansi(v)
35-
assert v2.rstrip('\n') == pformat({1: 2, 3: 4}), repr(v2)
34+
assert v2 == pformat({1: 2, 3: 4}), repr(v2)
3635

3736

3837
def test_list():
@@ -89,7 +88,7 @@ def test_generator_no_yield():
8988

9089

9190
def test_str():
92-
pformat_ = PrettyFormat(max_width=12)
91+
pformat_ = PrettyFormat(width=12)
9392
v = pformat_(string.ascii_lowercase + '\n' + string.digits)
9493
assert v == (
9594
"(\n"
@@ -105,7 +104,7 @@ def test_str_repr():
105104

106105

107106
def test_bytes():
108-
pformat_ = PrettyFormat(max_width=12)
107+
pformat_ = PrettyFormat(width=12)
109108
v = pformat_(string.ascii_lowercase.encode())
110109
assert v == """(
111110
b'abcde'

0 commit comments

Comments
 (0)