From e191743730f85031d84932cf21d98de44feea326 Mon Sep 17 00:00:00 2001 From: Dawn James Date: Sun, 21 Nov 2021 15:46:31 +0000 Subject: [PATCH 01/12] Initial version of auto-detection of terminal width. --- icecream/icecream.py | 38 ++++++++++++++++++++++++++++++++++---- tests/test_icecream.py | 1 + 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/icecream/icecream.py b/icecream/icecream.py index 5a7ae4c..0602d84 100644 --- a/icecream/icecream.py +++ b/icecream/icecream.py @@ -16,6 +16,7 @@ import ast import inspect import pprint +import os import sys from datetime import datetime from contextlib import contextmanager @@ -87,6 +88,7 @@ def colorizedStderrPrint(s): DEFAULT_CONTEXT_DELIMITER = '- ' DEFAULT_OUTPUT_FUNCTION = colorizedStderrPrint DEFAULT_ARG_TO_STRING_FUNCTION = pprint.pformat +COLUMN_OVERFLOW = 4 # Line length appears to overflow by 4 characters. class NoSourceAvailableError(OSError): @@ -154,15 +156,40 @@ def format_pair(prefix, arg, value): return '\n'.join(lines) -def argumentToString(obj): - s = DEFAULT_ARG_TO_STRING_FUNCTION(obj) +# def argumentToString(obj, width): +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 columns(): + """ Returns the number of columns that this terminal can handle. """ + width = DEFAULT_LINE_WRAP_WIDTH + try: + # TODO come up with a more elegant solution than subtracting + # a seemingly random number of characters. + width = os.get_terminal_size().columns - COLUMN_OVERFLOW + except OSError: # Not in TTY + pass + except AttributeError: # Python 2.x + width = os.environ.get('COLUMNS', DEFAULT_LINE_WRAP_WIDTH) + return width + + +def has_width_param(fn): + """ Returns True if a function has a 'width' parameter. """ + try: + from inspect import signature + return "width" in signature(fn).parameters + except ImportError: # Python 2.x + from inspect import getargspec + return "width" in getargspec(fn).args + + class IceCreamDebugger: _pairDelimiter = ', ' # Used by the tests in tests/. - lineWrapWidth = DEFAULT_LINE_WRAP_WIDTH + lineWrapWidth = columns() contextDelimiter = DEFAULT_CONTEXT_DELIMITER def __init__(self, prefix=DEFAULT_PREFIX, @@ -232,7 +259,10 @@ def _constructArgumentOutput(self, prefix, context, pairs): def argPrefix(arg): return '%s: ' % arg - pairs = [(arg, self.argToStringFunction(val)) for arg, val in pairs] + kwargs = {} + if has_width_param(self.argToStringFunction): + kwargs["width"] = self.lineWrapWidth + pairs = [(arg, self.argToStringFunction(val, **kwargs)) for arg, val in pairs] # For cleaner output, if 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 diff --git a/tests/test_icecream.py b/tests/test_icecream.py index 8c695ee..520c1ff 100644 --- a/tests/test_icecream.py +++ b/tests/test_icecream.py @@ -464,6 +464,7 @@ def testSingleTupleArgument(self): self.assertEqual(pair, ('(a, b)', '(1, 2)')) def testMultilineContainerArgs(self): + ic.lineWrapWidth = icecream.DEFAULT_LINE_WRAP_WIDTH with disableColoring(), captureStandardStreams() as (out, err): ic((a, b)) From 42f96c78d5272f503499f6c45cb7dd1a4a2bdf4b Mon Sep 17 00:00:00 2001 From: Dawn James Date: Wed, 24 Nov 2021 08:16:41 +0000 Subject: [PATCH 02/12] Move parameter detection out of __call__ in case it causes a performance slowdown. --- icecream/icecream.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/icecream/icecream.py b/icecream/icecream.py index 0602d84..e6ff3bc 100644 --- a/icecream/icecream.py +++ b/icecream/icecream.py @@ -177,14 +177,14 @@ def columns(): return width -def has_width_param(fn): - """ Returns True if a function has a 'width' parameter. """ +def supports_param(fn, param="width"): + """ Returns True if the function supports that parameter. """ try: from inspect import signature - return "width" in signature(fn).parameters + return param in signature(fn).parameters except ImportError: # Python 2.x from inspect import getargspec - return "width" in getargspec(fn).args + return param in getargspec(fn).args class IceCreamDebugger: @@ -200,6 +200,7 @@ def __init__(self, prefix=DEFAULT_PREFIX, self.includeContext = includeContext self.outputFunction = outputFunction self.argToStringFunction = argToStringFunction + self.passWidthParam = supports_param(self.argToStringFunction) def __call__(self, *args): if self.enabled: @@ -259,9 +260,7 @@ def _constructArgumentOutput(self, prefix, context, pairs): def argPrefix(arg): return '%s: ' % arg - kwargs = {} - if has_width_param(self.argToStringFunction): - kwargs["width"] = self.lineWrapWidth + kwargs = {"width": self.lineWrapWidth} if self.passWidthParam else {} pairs = [(arg, self.argToStringFunction(val, **kwargs)) for arg, val in pairs] # For cleaner output, if is a literal, eg 3, "string", b'bytes', # etc, only output the value, not the argument and the value, as the @@ -355,6 +354,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 From 7e3256ba5fb6d26b1fd5fb5fe8c55625e7a093e1 Mon Sep 17 00:00:00 2001 From: Dawn James Date: Thu, 25 Nov 2021 12:57:32 +0000 Subject: [PATCH 03/12] Add a test (skipped in 2.x) --- tests/test_icecream.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_icecream.py b/tests/test_icecream.py index 520c1ff..9e1673b 100644 --- a/tests/test_icecream.py +++ b/tests/test_icecream.py @@ -519,3 +519,13 @@ def testColoring(self): ic({1: 'str'}) # Output should be colored with ANSI control codes. assert hasAnsiEscapeCodes(err.getvalue()) + + @unittest.skipIf(int(sys.version[0]) == 2, "pprint doesn't wrap long strings in Python 2.7") + def testLineLengthTen(self): + """ Test a specific line wrap width. """ + ic.lineWrapWidth = 10 + s = "123456789 1234567890" + with disableColoring(), captureStandardStreams() as (out, err): + ic(s) + pair = parseOutputIntoPairs(out, err, 2)[0] + self.assertEqual(pair, [('s', "('123456789 '\n '1234567890')")]) From d47c5f76a2aaed5c23f17cbda9bfd9b27c66aa3a Mon Sep 17 00:00:00 2001 From: Dawn James Date: Thu, 25 Nov 2021 13:00:59 +0000 Subject: [PATCH 04/12] Remove redundant commented-out line. --- icecream/icecream.py | 1 - 1 file changed, 1 deletion(-) diff --git a/icecream/icecream.py b/icecream/icecream.py index e6ff3bc..fa0bc51 100644 --- a/icecream/icecream.py +++ b/icecream/icecream.py @@ -156,7 +156,6 @@ def format_pair(prefix, arg, value): return '\n'.join(lines) -# def argumentToString(obj, width): 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. From 8f3658f1c607eec1caff8953e1f231bc7c5395b8 Mon Sep 17 00:00:00 2001 From: Dawn James Date: Sat, 27 Nov 2021 08:46:51 +0000 Subject: [PATCH 05/12] Refactor implementation; add another test. --- icecream/icecream.py | 21 ++++++++++----------- setup.py | 1 + tests/test_icecream.py | 36 +++++++++++++++++++++++++++++++----- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/icecream/icecream.py b/icecream/icecream.py index fa0bc51..e158556 100644 --- a/icecream/icecream.py +++ b/icecream/icecream.py @@ -16,7 +16,7 @@ import ast import inspect import pprint -import os +import shutil import sys from datetime import datetime from contextlib import contextmanager @@ -88,7 +88,6 @@ def colorizedStderrPrint(s): DEFAULT_CONTEXT_DELIMITER = '- ' DEFAULT_OUTPUT_FUNCTION = colorizedStderrPrint DEFAULT_ARG_TO_STRING_FUNCTION = pprint.pformat -COLUMN_OVERFLOW = 4 # Line length appears to overflow by 4 characters. class NoSourceAvailableError(OSError): @@ -162,18 +161,18 @@ def argumentToString(obj, width=DEFAULT_LINE_WRAP_WIDTH): return s -def columns(): +def detect_terminal_width(prefix, default=DEFAULT_LINE_WRAP_WIDTH): """ Returns the number of columns that this terminal can handle. """ - width = DEFAULT_LINE_WRAP_WIDTH + width = default try: - # TODO come up with a more elegant solution than subtracting - # a seemingly random number of characters. - width = os.get_terminal_size().columns - COLUMN_OVERFLOW + if hasattr(shutil, "get_terminal_size"): + 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 pass - except AttributeError: # Python 2.x - width = os.environ.get('COLUMNS', DEFAULT_LINE_WRAP_WIDTH) - return width + return width - len(prefix) def supports_param(fn, param="width"): @@ -188,7 +187,6 @@ def supports_param(fn, param="width"): class IceCreamDebugger: _pairDelimiter = ', ' # Used by the tests in tests/. - lineWrapWidth = columns() contextDelimiter = DEFAULT_CONTEXT_DELIMITER def __init__(self, prefix=DEFAULT_PREFIX, @@ -200,6 +198,7 @@ def __init__(self, prefix=DEFAULT_PREFIX, self.outputFunction = outputFunction self.argToStringFunction = argToStringFunction self.passWidthParam = supports_param(self.argToStringFunction) + self.lineWrapWidth = detect_terminal_width(self.prefix, DEFAULT_LINE_WRAP_WIDTH) def __call__(self, *args): if self.enabled: diff --git a/setup.py b/setup.py index df3d31d..96da87e 100644 --- a/setup.py +++ b/setup.py @@ -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', ], cmdclass={ 'test': RunTests, diff --git a/tests/test_icecream.py b/tests/test_icecream.py index 9e1673b..32311be 100644 --- a/tests/test_icecream.py +++ b/tests/test_icecream.py @@ -9,6 +9,7 @@ # # License: MIT # +import textwrap import sys import unittest @@ -32,6 +33,10 @@ c = 3 +def isPython2(): + return int(sys.version[0]) == 2 + + def noop(*args, **kwargs): return @@ -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): @@ -520,12 +526,32 @@ def testColoring(self): assert hasAnsiEscapeCodes(err.getvalue()) - @unittest.skipIf(int(sys.version[0]) == 2, "pprint doesn't wrap long strings in Python 2.7") - def testLineLengthTen(self): - """ Test a specific line wrap width. """ + def testStringWithLineLengthOfTen(self): + """ Test a string with a short line wrap width. """ ic.lineWrapWidth = 10 s = "123456789 1234567890" with disableColoring(), captureStandardStreams() as (out, err): ic(s) - pair = parseOutputIntoPairs(out, err, 2)[0] - self.assertEqual(pair, [('s', "('123456789 '\n '1234567890')")]) + 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) + + 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) From e26249fba372bf0cb987cca8538ddda73cdff69e Mon Sep 17 00:00:00 2001 From: Dawn James Date: Sat, 27 Nov 2021 15:37:20 +0000 Subject: [PATCH 06/12] Update tests/test_icecream.py Co-authored-by: Alex Hall --- tests/test_icecream.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_icecream.py b/tests/test_icecream.py index 32311be..e2fd106 100644 --- a/tests/test_icecream.py +++ b/tests/test_icecream.py @@ -534,13 +534,13 @@ def testStringWithLineLengthOfTen(self): ic(s) actual = err.getvalue().strip() if isPython2(): - self.assertEqual(actual, "ic| s: '123456789 1234567890'") + expected = "ic| s: '123456789 1234567890'" else: expected = textwrap.dedent(""" ic| s: ('123456789 ' '1234567890') """).strip() - self.assertEqual(actual, expected) + self.assertEqual(actual, expected) def testListWithLineLengthOfTen(self): """ Test a list with a short line wrap width. """ From 661e58ee4492c3eebe0f42169cc5b9d4e09b2ca1 Mon Sep 17 00:00:00 2001 From: Dawn James Date: Sat, 27 Nov 2021 15:37:43 +0000 Subject: [PATCH 07/12] Update tests/test_icecream.py Co-authored-by: Alex Hall --- tests/test_icecream.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_icecream.py b/tests/test_icecream.py index e2fd106..b550309 100644 --- a/tests/test_icecream.py +++ b/tests/test_icecream.py @@ -34,7 +34,7 @@ def isPython2(): - return int(sys.version[0]) == 2 + return sys.version_info[0] == 2 def noop(*args, **kwargs): From 78b0ae97ebf82bd1e2b381ef48ee9914d353e688 Mon Sep 17 00:00:00 2001 From: Dawn James Date: Sat, 27 Nov 2021 15:56:52 +0000 Subject: [PATCH 08/12] Implement most of the latest code review suggestions. --- icecream/icecream.py | 23 ++++++++++++++++------- setup.py | 2 +- tests/test_icecream.py | 17 +++++------------ 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/icecream/icecream.py b/icecream/icecream.py index e158556..d617aaa 100644 --- a/icecream/icecream.py +++ b/icecream/icecream.py @@ -15,8 +15,8 @@ import ast import inspect +import os import pprint -import shutil import sys from datetime import datetime from contextlib import contextmanager @@ -34,6 +34,15 @@ from .coloring import SolarizedDark +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(): + return os.environ['COLUMNS'] + PYTHON2 = (sys.version_info[0] == 2) @@ -165,13 +174,13 @@ 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"): - 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 + # We need to pass a terminal height in the tuple so we pass the default + # of 25 lines but it's not used for anything. + width = get_terminal_size((default, 25)).columns + except Exception: # Not in TTY or something else went wrong pass + # TODO account for argPrefix() + # TODO make sure we support configureOutput() return width - len(prefix) diff --git a/setup.py b/setup.py index 96da87e..38b6d02 100644 --- a/setup.py +++ b/setup.py @@ -103,7 +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', + 'backports.shutil-get-terminal-size==1.0.0; python_version < "3.3.0"', ], cmdclass={ 'test': RunTests, diff --git a/tests/test_icecream.py b/tests/test_icecream.py index b550309..f6ade5f 100644 --- a/tests/test_icecream.py +++ b/tests/test_icecream.py @@ -33,10 +33,6 @@ c = 3 -def isPython2(): - return sys.version_info[0] == 2 - - def noop(*args, **kwargs): return @@ -470,7 +466,6 @@ def testSingleTupleArgument(self): self.assertEqual(pair, ('(a, b)', '(1, 2)')) def testMultilineContainerArgs(self): - ic.lineWrapWidth = icecream.DEFAULT_LINE_WRAP_WIDTH with disableColoring(), captureStandardStreams() as (out, err): ic((a, b)) @@ -526,32 +521,30 @@ def testColoring(self): assert hasAnsiEscapeCodes(err.getvalue()) - def testStringWithLineLengthOfTen(self): + def testStringWithShortLineWrapWidth(self): """ 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(): + if icecream.PYTHON2: expected = "ic| s: '123456789 1234567890'" else: expected = textwrap.dedent(""" ic| s: ('123456789 ' '1234567890') """).strip() - self.assertEqual(actual, expected) + self.assertEqual(err.getvalue().strip(), expected) - def testListWithLineLengthOfTen(self): + def testListWithShortLineWrapWidth(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) + self.assertEqual(err.getvalue().strip(), expected) From c10907d619632b472c79bb9e3cca92bc3f327f62 Mon Sep 17 00:00:00 2001 From: Dawn James Date: Sat, 4 Dec 2021 19:14:07 +0000 Subject: [PATCH 09/12] Update icecream/icecream.py Co-authored-by: Alex Hall --- icecream/icecream.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/icecream/icecream.py b/icecream/icecream.py index d617aaa..f43012e 100644 --- a/icecream/icecream.py +++ b/icecream/icecream.py @@ -172,13 +172,12 @@ def argumentToString(obj, width=DEFAULT_LINE_WRAP_WIDTH): def detect_terminal_width(prefix, default=DEFAULT_LINE_WRAP_WIDTH): """ Returns the number of columns that this terminal can handle. """ - width = default try: # We need to pass a terminal height in the tuple so we pass the default # of 25 lines but it's not used for anything. width = get_terminal_size((default, 25)).columns except Exception: # Not in TTY or something else went wrong - pass + width = default # TODO account for argPrefix() # TODO make sure we support configureOutput() return width - len(prefix) From 2236996150d0040f3d82a83a0c21bda06ac1fc5d Mon Sep 17 00:00:00 2001 From: Dawn James Date: Tue, 7 Dec 2021 20:16:02 +0000 Subject: [PATCH 10/12] Support for configureOutput(). --- icecream/icecream.py | 23 ++++++++++--- tests/test_icecream.py | 74 +++++++++++++++++++++++++++++++++++------- 2 files changed, 80 insertions(+), 17 deletions(-) diff --git a/icecream/icecream.py b/icecream/icecream.py index f43012e..4ba7c18 100644 --- a/icecream/icecream.py +++ b/icecream/icecream.py @@ -93,6 +93,7 @@ def colorizedStderrPrint(s): DEFAULT_PREFIX = 'ic| ' +DEFAULT_TERMINAL_WIDTH = 80 DEFAULT_LINE_WRAP_WIDTH = 70 # Characters. DEFAULT_CONTEXT_DELIMITER = '- ' DEFAULT_OUTPUT_FUNCTION = colorizedStderrPrint @@ -170,7 +171,7 @@ def argumentToString(obj, width=DEFAULT_LINE_WRAP_WIDTH): return s -def detect_terminal_width(prefix, default=DEFAULT_LINE_WRAP_WIDTH): +def detect_terminal_width(default=DEFAULT_TERMINAL_WIDTH): """ Returns the number of columns that this terminal can handle. """ try: # We need to pass a terminal height in the tuple so we pass the default @@ -179,8 +180,7 @@ def detect_terminal_width(prefix, default=DEFAULT_LINE_WRAP_WIDTH): except Exception: # Not in TTY or something else went wrong width = default # TODO account for argPrefix() - # TODO make sure we support configureOutput() - return width - len(prefix) + return width def supports_param(fn, param="width"): @@ -196,6 +196,8 @@ def supports_param(fn, param="width"): class IceCreamDebugger: _pairDelimiter = ', ' # Used by the tests in tests/. contextDelimiter = DEFAULT_CONTEXT_DELIMITER + terminalWidth = DEFAULT_TERMINAL_WIDTH + lineWrapWidth = DEFAULT_LINE_WRAP_WIDTH def __init__(self, prefix=DEFAULT_PREFIX, outputFunction=DEFAULT_OUTPUT_FUNCTION, @@ -206,7 +208,7 @@ def __init__(self, prefix=DEFAULT_PREFIX, self.outputFunction = outputFunction self.argToStringFunction = argToStringFunction self.passWidthParam = supports_param(self.argToStringFunction) - self.lineWrapWidth = detect_terminal_width(self.prefix, DEFAULT_LINE_WRAP_WIDTH) + self._setLineWrapWidth() def __call__(self, *args): if self.enabled: @@ -227,6 +229,12 @@ def __call__(self, *args): return passthrough + def _setLineWrapWidth(self, terminal_width=None): + prefix_length = len(self.prefix()) if callable(self.prefix) else len(self.prefix) + width = terminal_width if terminal_width else detect_terminal_width(DEFAULT_LINE_WRAP_WIDTH) + self.terminalWidth = width + self.lineWrapWidth = width - prefix_length + def format(self, *args): callFrame = inspect.currentframe().f_back out = self._format(callFrame, *args) @@ -351,10 +359,15 @@ def disable(self): self.enabled = False def configureOutput(self, prefix=_absent, outputFunction=_absent, - argToStringFunction=_absent, includeContext=_absent): + argToStringFunction=_absent, includeContext=_absent, + terminalWidth=_absent): if prefix is not _absent: self.prefix = prefix + if prefix is not _absent or terminalWidth is not _absent: + new_terminal_width = terminalWidth if terminalWidth is not _absent else None + self._setLineWrapWidth(new_terminal_width) + if outputFunction is not _absent: self.outputFunction = outputFunction diff --git a/tests/test_icecream.py b/tests/test_icecream.py index f6ade5f..8a718ac 100644 --- a/tests/test_icecream.py +++ b/tests/test_icecream.py @@ -23,11 +23,9 @@ import icecream from icecream import ic, stderrPrint, NoSourceAvailableError - TEST_PAIR_DELIMITER = '| ' MYFILENAME = basename(__file__) - a = 1 b = 2 c = 3 @@ -62,11 +60,13 @@ def disableColoring(): @contextmanager def configureIcecreamOutput(prefix=None, outputFunction=None, - argToStringFunction=None, includeContext=None): + argToStringFunction=None, includeContext=None, terminalWidth=None): + oldPrefix = ic.prefix oldOutputFunction = ic.outputFunction oldArgToStringFunction = ic.argToStringFunction oldIncludeContext = ic.includeContext + oldTerminalWidth = ic.terminalWidth if prefix: ic.configureOutput(prefix=prefix) @@ -76,12 +76,14 @@ def configureIcecreamOutput(prefix=None, outputFunction=None, ic.configureOutput(argToStringFunction=argToStringFunction) if includeContext: ic.configureOutput(includeContext=includeContext) + if terminalWidth: + ic.configureOutput(terminalWidth=terminalWidth) yield ic.configureOutput( oldPrefix, oldOutputFunction, oldArgToStringFunction, - oldIncludeContext) + oldIncludeContext, oldTerminalWidth) @contextmanager @@ -183,7 +185,8 @@ def parseOutputIntoPairs(out, err, assertNumLines, class TestIceCream(unittest.TestCase): def setUp(self): ic._pairDelimiter = TEST_PAIR_DELIMITER - ic.lineWrapWidth = icecream.DEFAULT_LINE_WRAP_WIDTH + ic.configureOutput(prefix=icecream.DEFAULT_PREFIX, + terminalWidth=icecream.DEFAULT_TERMINAL_WIDTH) def testWithoutArgs(self): with disableColoring(), captureStandardStreams() as (out, err): @@ -538,13 +541,60 @@ def testStringWithShortLineWrapWidth(self): def testListWithShortLineWrapWidth(self): """ Test a list with a short line wrap width. """ - ic.lineWrapWidth = 10 - lst = ["1", "2", "3", "4"] + ic._setLineWrapWidth(10) + lst = ["1 2 3 4 5", "2", "3", "4"] with disableColoring(), captureStandardStreams() as (out, err): ic(lst) - expected = textwrap.dedent(""" - ic| lst: ['1', - '2', - '3', - '4']""").strip() + if icecream.PYTHON2: + expected = textwrap.dedent(""" + ic| lst: ['1 2 3 4 5', + '2', + '3', + '4']""").strip() + else: + expected = textwrap.dedent(""" + ic| lst: ['1 ' + '2 ' + '3 ' + '4 ' + '5', + '2', + '3', + '4']""").strip() self.assertEqual(err.getvalue().strip(), expected) + + def testLiteralWithShortLineWrapWidth(self): + """ Test a literal with a short line wrap width. """ + ic.lineWrapWidth = 10 + with disableColoring(), captureStandardStreams() as (out, err): + ic("banana banana") + if icecream.PYTHON2: + expected = 'ic| "banana banana": \'banana banana\'' + else: + expected = textwrap.dedent(""" + ic| "banana banana": ('banana ' + 'banana')""").strip() + actual = err.getvalue().strip() + self.assertEqual(expected, actual) + + def testConfigureOutput(self): + """ Test that line width is adjusted after running configureOutput() + with a new prefix. ic.lineWrapWidth will start at 70 then adjust + to 60, so a string that didn't wrap before should wrap now. + """ + s = "a 70 character string a 70 character string a 70 character string a 70" + with disableColoring(), captureStandardStreams() as (out, err): + ic(s) + self.assertEqual("ic| s: '%s'" % s, err.getvalue().strip()) + with configureIcecreamOutput(prefix="10prefix| ", + outputFunction=stderrPrint, + terminalWidth=icecream.DEFAULT_TERMINAL_WIDTH): + with disableColoring(), captureStandardStreams() as (out, err): + ic(s) + if icecream.PYTHON2: + expected = "10prefix| s: '%s'" % s + else: + expected = textwrap.dedent(""" + 10prefix| s: ('a 70 character string a 70 character string a 70 character string ' + 'a 70')""").strip() + self.assertEqual(expected, err.getvalue().strip()) From 9490fa3fd89ee3a7230ba697203629cbcc83a302 Mon Sep 17 00:00:00 2001 From: Dawn James Date: Tue, 7 Dec 2021 20:54:31 +0000 Subject: [PATCH 11/12] Default to not detecting terminal width. --- icecream/icecream.py | 14 ++++++++++---- tests/test_icecream.py | 25 +++++++++++++++++++------ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/icecream/icecream.py b/icecream/icecream.py index 4ba7c18..bef135e 100644 --- a/icecream/icecream.py +++ b/icecream/icecream.py @@ -201,14 +201,15 @@ class IceCreamDebugger: def __init__(self, prefix=DEFAULT_PREFIX, outputFunction=DEFAULT_OUTPUT_FUNCTION, - argToStringFunction=argumentToString, includeContext=False): + argToStringFunction=argumentToString, includeContext=False, + detectTerminalWidth=False): self.enabled = True self.prefix = prefix self.includeContext = includeContext self.outputFunction = outputFunction self.argToStringFunction = argToStringFunction self.passWidthParam = supports_param(self.argToStringFunction) - self._setLineWrapWidth() + self._setLineWrapWidth(detectTerminalWidth=detectTerminalWidth) def __call__(self, *args): if self.enabled: @@ -229,9 +230,14 @@ def __call__(self, *args): return passthrough - def _setLineWrapWidth(self, terminal_width=None): + def _setLineWrapWidth(self, detectTerminalWidth=False, terminalWidth=None): prefix_length = len(self.prefix()) if callable(self.prefix) else len(self.prefix) - width = terminal_width if terminal_width else detect_terminal_width(DEFAULT_LINE_WRAP_WIDTH) + if terminalWidth: + width = terminalWidth + elif detectTerminalWidth is True: + width = detect_terminal_width(DEFAULT_LINE_WRAP_WIDTH) + else: + width = DEFAULT_TERMINAL_WIDTH self.terminalWidth = width self.lineWrapWidth = width - prefix_length diff --git a/tests/test_icecream.py b/tests/test_icecream.py index 8a718ac..e30d090 100644 --- a/tests/test_icecream.py +++ b/tests/test_icecream.py @@ -9,6 +9,7 @@ # # License: MIT # +import os import textwrap import sys @@ -86,6 +87,18 @@ def configureIcecreamOutput(prefix=None, outputFunction=None, oldIncludeContext, oldTerminalWidth) +@contextmanager +def detectTerminalWidth(terminal_width=icecream.DEFAULT_TERMINAL_WIDTH): + width = str(terminal_width) + old_terminal_width = os.getenv('COLUMNS', width) + try: + os.environ['COLUMNS'] = width + yield ic._setLineWrapWidth(detectTerminalWidth=True) + finally: + os.environ['COLUMNS'] = old_terminal_width + ic._setLineWrapWidth(detectTerminalWidth=False, terminalWidth=int(old_terminal_width)) + + @contextmanager def captureStandardStreams(): realStdout = sys.stdout @@ -526,7 +539,7 @@ def testColoring(self): def testStringWithShortLineWrapWidth(self): """ Test a string with a short line wrap width. """ - ic.lineWrapWidth = 10 + ic._setLineWrapWidth(terminalWidth=10) s = "123456789 1234567890" with disableColoring(), captureStandardStreams() as (out, err): ic(s) @@ -541,7 +554,7 @@ def testStringWithShortLineWrapWidth(self): def testListWithShortLineWrapWidth(self): """ Test a list with a short line wrap width. """ - ic._setLineWrapWidth(10) + ic._setLineWrapWidth(terminalWidth=10) lst = ["1 2 3 4 5", "2", "3", "4"] with disableColoring(), captureStandardStreams() as (out, err): ic(lst) @@ -563,11 +576,11 @@ def testListWithShortLineWrapWidth(self): '4']""").strip() self.assertEqual(err.getvalue().strip(), expected) - def testLiteralWithShortLineWrapWidth(self): + def testLiteralWithShortTerminalWidth(self): """ Test a literal with a short line wrap width. """ - ic.lineWrapWidth = 10 - with disableColoring(), captureStandardStreams() as (out, err): - ic("banana banana") + with detectTerminalWidth(10): + with disableColoring(), captureStandardStreams() as (out, err): + ic("banana banana") if icecream.PYTHON2: expected = 'ic| "banana banana": \'banana banana\'' else: From 4a8ab877e6f6e49d1546188929f4f80fc1072a85 Mon Sep 17 00:00:00 2001 From: Dawn James Date: Sat, 11 Dec 2021 17:07:37 +0000 Subject: [PATCH 12/12] Update icecream/icecream.py Co-authored-by: Alex Hall --- icecream/icecream.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icecream/icecream.py b/icecream/icecream.py index bef135e..a87648d 100644 --- a/icecream/icecream.py +++ b/icecream/icecream.py @@ -235,7 +235,7 @@ def _setLineWrapWidth(self, detectTerminalWidth=False, terminalWidth=None): if terminalWidth: width = terminalWidth elif detectTerminalWidth is True: - width = detect_terminal_width(DEFAULT_LINE_WRAP_WIDTH) + width = detect_terminal_width(DEFAULT_TERMINAL_WIDTH) else: width = DEFAULT_TERMINAL_WIDTH self.terminalWidth = width