Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions icecream/icecream.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import ast
import inspect
import pprint
import shutil
import sys
from datetime import datetime
from contextlib import contextmanager
Expand Down Expand Up @@ -154,15 +155,38 @@ def format_pair(prefix, arg, value):
return '\n'.join(lines)


def argumentToString(obj):
s = DEFAULT_ARG_TO_STRING_FUNCTION(obj)
def argumentToString(obj, width=DEFAULT_LINE_WRAP_WIDTH):
s = DEFAULT_ARG_TO_STRING_FUNCTION(obj, width=width)
s = s.replace('\\n', '\n') # Preserve string newlines in output.
return s


def detect_terminal_width(prefix, default=DEFAULT_LINE_WRAP_WIDTH):
""" Returns the number of columns that this terminal can handle. """
width = default
try:
if hasattr(shutil, "get_terminal_size"):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to have near the top of the file:

try:
    from shutil import get_terminal_size
except ImportError:
    try:
        from backports.shutil_get_terminal_size import get_terminal_size
    except ImportError:
        def get_terminal_size():
            # get COLUMNS environment variable

icecream should still work if backports.shutil_get_terminal_size isn't installed, e.g. if the user chooses to uninstall it even after installing it automatically.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do it that way then 👍

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Although in my terminal, env['COLUMNS'] isn't set, so I'm not sure which environments/OSes do set that variable)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know anything about this either, but I imagine it's a convention so that humans can easily set COLUMNS=50 when running a program and expect that many parts of the program will automatically respect that.

width = shutil.get_terminal_size().columns
else: # Python 2.x doesn't support get_terminal_size
from backports.shutil_get_terminal_size import get_terminal_size
width = get_terminal_size().columns
except OSError: # Not in TTY
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the shutil version doesn't raise OSError any more. But maybe the whole thing should be wrapped in except Exception just in case something weird happens.

Also you should pass the default width as a fallback to get_terminal_size. Otherwise it's going to use its own default fallback (80 columns) when the other methods fail.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do on both counts.

pass
return width - len(prefix)


def supports_param(fn, param="width"):
""" Returns True if the function supports that parameter. """
try:
from inspect import signature
return param in signature(fn).parameters
except ImportError: # Python 2.x
from inspect import getargspec
return param in getargspec(fn).args


class IceCreamDebugger:
_pairDelimiter = ', ' # Used by the tests in tests/.
lineWrapWidth = DEFAULT_LINE_WRAP_WIDTH
contextDelimiter = DEFAULT_CONTEXT_DELIMITER

def __init__(self, prefix=DEFAULT_PREFIX,
Expand All @@ -173,6 +197,8 @@ def __init__(self, prefix=DEFAULT_PREFIX,
self.includeContext = includeContext
self.outputFunction = outputFunction
self.argToStringFunction = argToStringFunction
self.passWidthParam = supports_param(self.argToStringFunction)
self.lineWrapWidth = detect_terminal_width(self.prefix, DEFAULT_LINE_WRAP_WIDTH)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still doesn't account for the width of argPrefix, or changing the prefix with configureOutput.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, will fix.


def __call__(self, *args):
if self.enabled:
Expand Down Expand Up @@ -232,7 +258,8 @@ def _constructArgumentOutput(self, prefix, context, pairs):
def argPrefix(arg):
return '%s: ' % arg

pairs = [(arg, self.argToStringFunction(val)) for arg, val in pairs]
kwargs = {"width": self.lineWrapWidth} if self.passWidthParam else {}
pairs = [(arg, self.argToStringFunction(val, **kwargs)) for arg, val in pairs]
# For cleaner output, if <arg> is a literal, eg 3, "string", b'bytes',
# etc, only output the value, not the argument and the value, as the
# argument and the value will be identical or nigh identical. Ex: with
Expand Down Expand Up @@ -325,6 +352,7 @@ def configureOutput(self, prefix=_absent, outputFunction=_absent,

if argToStringFunction is not _absent:
self.argToStringFunction = argToStringFunction
self.passWidthParam = supports_param(self.argToStringFunction)

if includeContext is not _absent:
self.includeContext = includeContext
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def run_tests(self):
'pygments>=2.2.0',
'executing>=0.3.1',
'asttokens>=2.0.1',
'backports.shutil-get-terminal-size==1.0.0',
Comment thread
dawngerpony marked this conversation as resolved.
Outdated
],
cmdclass={
'test': RunTests,
Expand Down
37 changes: 37 additions & 0 deletions tests/test_icecream.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#
# License: MIT
#
import textwrap

import sys
import unittest
Expand All @@ -32,6 +33,10 @@
c = 3


def isPython2():
return int(sys.version[0]) == 2
Comment thread
dawngerpony marked this conversation as resolved.
Outdated


def noop(*args, **kwargs):
return

Expand Down Expand Up @@ -182,6 +187,7 @@ def parseOutputIntoPairs(out, err, assertNumLines,
class TestIceCream(unittest.TestCase):
def setUp(self):
ic._pairDelimiter = TEST_PAIR_DELIMITER
ic.lineWrapWidth = icecream.DEFAULT_LINE_WRAP_WIDTH

def testWithoutArgs(self):
with disableColoring(), captureStandardStreams() as (out, err):
Expand Down Expand Up @@ -464,6 +470,7 @@ def testSingleTupleArgument(self):
self.assertEqual(pair, ('(a, b)', '(1, 2)'))

def testMultilineContainerArgs(self):
ic.lineWrapWidth = icecream.DEFAULT_LINE_WRAP_WIDTH
Comment thread
dawngerpony marked this conversation as resolved.
Outdated
with disableColoring(), captureStandardStreams() as (out, err):
ic((a,
b))
Expand Down Expand Up @@ -518,3 +525,33 @@ def testColoring(self):
ic({1: 'str'}) # Output should be colored with ANSI control codes.

assert hasAnsiEscapeCodes(err.getvalue())

def testStringWithLineLengthOfTen(self):
Comment thread
dawngerpony marked this conversation as resolved.
Outdated
""" Test a string with a short line wrap width. """
ic.lineWrapWidth = 10
s = "123456789 1234567890"
with disableColoring(), captureStandardStreams() as (out, err):
ic(s)
actual = err.getvalue().strip()
if isPython2():
self.assertEqual(actual, "ic| s: '123456789 1234567890'")
else:
expected = textwrap.dedent("""
ic| s: ('123456789 '
'1234567890')
""").strip()
self.assertEqual(actual, expected)
Comment thread
dawngerpony marked this conversation as resolved.
Outdated

def testListWithLineLengthOfTen(self):
""" Test a list with a short line wrap width. """
ic.lineWrapWidth = 10
lst = ["1", "2", "3", "4"]
with disableColoring(), captureStandardStreams() as (out, err):
ic(lst)
actual = err.getvalue().strip()
expected = textwrap.dedent("""
ic| lst: ['1',
'2',
'3',
'4']""").strip()
self.assertEqual(actual, expected)