pax_global_header00006660000000000000000000000064140644460770014526gustar00rootroot0000000000000052 comment=fb919b77069f52ef6597b0ac74ea0939873d8298 icecream-2.1.1/000077500000000000000000000000001406444607700132775ustar00rootroot00000000000000icecream-2.1.1/.gitignore000066400000000000000000000001011406444607700152570ustar00rootroot00000000000000*~ .#* \#* .tox dist/ .eggs/ build/ *.pyc *.pyo *.egg *.egg-info icecream-2.1.1/.travis.yml000066400000000000000000000007171406444607700154150ustar00rootroot00000000000000language: python sudo: false matrix: include: - python: 2.7 env: TOXENV=py27 - python: 3.5 env: TOXENV=py35 - python: 3.6 env: TOXENV=py36 - python: 3.7 env: TOXENV=py37 - python: 3.8 env: TOXENV=py38 - python: 3.9 env: TOXENV=py39 - python: pypy env: TOXENV=pypy - python: pypy3 env: TOXENV=pypy3 install: travis_retry pip install tox script: tox notifications: email: false icecream-2.1.1/LICENSE.txt000066400000000000000000000020361406444607700151230ustar00rootroot00000000000000Copyright 2018 Ansgar Grunseid Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.icecream-2.1.1/MANIFEST.in000066400000000000000000000000361406444607700150340ustar00rootroot00000000000000include LICENSE.txt README.md icecream-2.1.1/README.md000066400000000000000000000173411406444607700145640ustar00rootroot00000000000000

icecream

### IceCream — Never use print() to debug again Do you ever use `print()` or `log()` to debug your code? Of course you do. IceCream, or `ic` for short, makes print debugging a little sweeter. `ic()` is like `print()`, but better: 1. It prints both expressions/variable names and their values. 2. It's 40% faster to type. 3. Data structures are pretty printed. 4. Output is syntax highlighted. 5. It optionally includes program context: filename, line number, and parent function. IceCream is well tested, [permissively licensed](LICENSE.txt), and supports Python 2, Python 3, PyPy2, and PyPy3. ### Inspect Variables Have you ever printed variables or expressions to debug your program? If you've ever typed something like ```python print(foo('123')) ``` or the more thorough ```python print("foo('123')", foo('123')) ``` then `ic()` is here to help. With arguments, `ic()` inspects itself and prints both its own arguments and the values of those arguments. ```python from icecream import ic def foo(i): return i + 333 ic(foo(123)) ``` Prints ``` ic| foo(123): 456 ``` Similarly, ```python d = {'key': {1: 'one'}} ic(d['key'][1]) class klass(): attr = 'yep' ic(klass.attr) ``` Prints ``` ic| d['key'][1]: 'one' ic| klass.attr: 'yep' ``` Just give `ic()` a variable or expression and you're done. Easy. ### Inspect Execution Have you ever used `print()` to determine which parts of your program are executed, and in which order they're executed? For example, if you've ever added print statements to debug code like ```python def foo(): print(0) first() if expression: print(1) second() else: print(2) third() ``` then `ic()` helps here, too. Without arguments, `ic()` inspects itself and prints the calling filename, line number, and parent function. ```python from icecream import ic def foo(): ic() first() if expression: ic() second() else: ic() third() ``` Prints ``` ic| example.py:4 in foo() ic| example.py:11 in foo() ``` Just call `ic()` and you're done. Simple. ### Return Value `ic()` returns its argument(s), so `ic()` can easily be inserted into pre-existing code. ```pycon >>> a = 6 >>> def half(i): >>> return i / 2 >>> b = half(ic(a)) ic| a: 6 >>> ic(b) ic| b: 3 ``` ### Miscellaneous `ic.format(*args)` is like `ic()` but the output is returned as a string instead of written to stderr. ```pycon >>> from icecream import ic >>> s = 'sup' >>> out = ic.format(s) >>> print(out) ic| s: 'sup' ``` Additionally, `ic()`'s output can be entirely disabled, and later re-enabled, with `ic.disable()` and `ic.enable()` respectively. ```python from icecream import ic ic(1) ic.disable() ic(2) ic.enable() ic(3) ``` Prints ``` ic| 1: 1 ic| 3: 3 ``` `ic()` continues to return its arguments when disabled, of course; no existing code with `ic()` breaks. ### Import Tricks To make `ic()` available in every file without needing to be imported in every file, you can `install()` it. For example, in a root `A.py`: ```python #!/usr/bin/env python3 # -*- coding: utf-8 -*- from icecream import install install() from B import foo foo() ``` and then in `B.py`, which is imported by `A.py`, just call `ic()`: ```python # -*- coding: utf-8 -*- def foo(): x = 3 ic(x) ``` `install()` adds `ic()` to the [builtins](https://docs.python.org/3.8/library/builtins.html) module, which is shared amongst all files imported by the interpreter. Similarly, `ic()` can later be `uninstall()`ed, too. `ic()` can also be imported in a manner that fails gracefully if IceCream isn't installed, like in production environments (i.e. not development). To that end, this fallback import snippet may prove useful: ```python try: from icecream import ic except ImportError: # Graceful fallback if IceCream isn't installed. ic = lambda *a: None if not a else (a[0] if len(a) == 1 else a) # noqa ``` ### Configuration `ic.configureOutput(prefix, outputFunction, argToStringFunction, includeContext)` can be used to adopt a custom output prefix (the default is `ic| `), change the output function (default is to write to stderr), customize how arguments are serialized to strings, and/or include the `ic()` call's context (filename, line number, and parent function) in `ic()` output with arguments. ```pycon >>> from icecream import ic >>> ic.configureOutput(prefix='hello -> ') >>> ic('world') hello -> 'world' ``` `prefix` can optionally be a function, too. ```pycon >>> import time >>> from icecream import ic >>> >>> def unixTimestamp(): >>> return '%i |> ' % int(time.time()) >>> >>> ic.configureOutput(prefix=unixTimestamp) >>> ic('world') 1519185860 |> 'world': 'world' ``` `outputFunction`, if provided, is called with `ic()`'s output instead of that output being written to stderr (the default). ```pycon >>> import logging >>> from icecream import ic >>> >>> def warn(s): >>> logging.warning(s) >>> >>> ic.configureOutput(outputFunction=warn) >>> ic('eep') WARNING:root:ic| 'eep': 'eep' ``` `argToStringFunction`, if provided, is called with argument values to be serialized to displayable strings. The default is PrettyPrint's [pprint.pformat()](https://docs.python.org/3/library/pprint.html#pprint.pformat), but this can be changed to, for example, handle non-standard datatypes in a custom fashion. ```pycon >>> from icecream import ic >>> >>> def toString(obj): >>> if isinstance(obj, str): >>> return '[!string %r with length %i!]' % (obj, len(obj)) >>> return repr(obj) >>> >>> ic.configureOutput(argToStringFunction=toString) >>> ic(7, 'hello') ic| 7: 7, 'hello': [!string 'hello' with length 5!] ``` `includeContext`, if provided and True, adds the `ic()` call's filename, line number, and parent function to `ic()`'s output. ```pycon >>> from icecream import ic >>> ic.configureOutput(includeContext=True) >>> >>> def foo(): >>> ic('str') >>> foo() ic| example.py:12 in foo()- 'str': 'str' ``` `includeContext` is False by default. ### Installation Installing IceCream with pip is easy. ``` $ pip install icecream ``` ### Related Python libraries `ic()` uses [**`executing`**](https://github.com/alexmojaki/executing) by [**@alexmojaki**](https://github.com/alexmojaki) to reliably locate `ic()` calls in Python source. It's magic. ### IceCream in Other Languages Delicious IceCream should be enjoyed in every language. - Dart: [icecream](https://github.com/HallerPatrick/icecream) - Rust: [icecream-rs](https://github.com/ericchang00/icecream-rs) - Node.js: [node-icecream](https://github.com/jmerle/node-icecream) - C++: [IceCream-Cpp](https://github.com/renatoGarcia/icecream-cpp) - C99: [icecream-c](https://github.com/chunqian/icecream-c) - PHP: [icecream-php](https://github.com/ntzm/icecream-php) - Go: [icecream-go](https://github.com/WAY29/icecream-go) - Ruby: [Ricecream](https://github.com/nodai2hITC/ricecream) - Java: [icecream-java](https://github.com/Akshay-Thakare/icecream-java) - R: [icecream](https://github.com/lewinfox/icecream) - Lua: [icecream-lua](https://github.com/wlingze/icecream-lua) If you'd like a similar `ic()` function in your favorite language, please open a pull request! IceCream's goal is to sweeten print debugging with a handy-dandy `ic()` function in every language. icecream-2.1.1/changelog.txt000066400000000000000000000053641406444607700157770ustar00rootroot00000000000000================================================================================ v2.1.1 ================================================================================ Added: Support for Python 3.9. Changed: Use timestamps in the local timezone instead of less helpful UTC timestamps. ================================================================================ v2.1.0 ================================================================================ Added: install() and uninstall() functions that add or remove ic() from the builtins module. Changed: Switch to ast.literal_eval() to determine if an argument and value are the same, and thus only the value should be output. Huge thank you to Ed Cardinal and Alex Hall. ================================================================================ v2.0.0 ================================================================================ Added: Support for Python 3.8. Removed: Support for Python 3.4. Changed: Switched core AST parsing engine to Alex Hall's executing (https://github.com/alexmojaki/executing). Huge thank you to Alex Hall. Changed: Whitespace in arguments is no longer collapsed. Indentation in multiline arguments is now preserved. ================================================================================ v1.5.0 ================================================================================ Fixed: Support multiline container arguments. e.g. ic([a, b]) Fixed: Include LICENSE.txt in source distributions. Changed: Collapse argument whitespace, e.g. ic([ a, b ]) -> ic| [a, b]. ================================================================================ v1.4.0 ================================================================================ Added: Colorize output with pygments. Added: Test Python style with pycodestyle. Fixed: Parse and print tuple arguments correctly, e.g. ic((a, b)). Fixed: Fail gracefully when the underlying source code changes during execution. Changed: Print values (e.g. 1, 'foo', etc) by themselves, nonredundantly. For example, ic(3) now prints 'ic| 3' instead of 'ic| 3: 3'. ================================================================================ v1.3.1 ================================================================================ Removed: Support for Python 3.3, which reached EOL on 2017-09-29. Fixed: ic() invocations that fail to find or access source code (e.g. eval(), exec(), python -i, etc) now print an error message instead of throwing an IOError (Python 2) or OSError (Python 3). ================================================================================ v1.3 ================================================================================ First release. This changelog wasn't maintained prior to v1.3. icecream-2.1.1/icecream/000077500000000000000000000000001406444607700150475ustar00rootroot00000000000000icecream-2.1.1/icecream/__init__.py000066400000000000000000000007731406444607700171670ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # IceCream - Never use print() to debug again # # Ansgar Grunseid # grunseid.com # grunseid@gmail.com # # License: MIT # from os.path import dirname, join as pjoin from .icecream import * # noqa from .builtins import install, uninstall # Import all variables in __version__.py without explicit imports. meta = {} with open(pjoin(dirname(__file__), '__version__.py')) as f: exec(f.read(), meta) globals().update(dict((k, v) for k, v in meta.items() if k not in globals())) icecream-2.1.1/icecream/__version__.py000066400000000000000000000007451406444607700177100ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # IceCream - Never use print() to debug again # # Ansgar Grunseid # grunseid.com # grunseid@gmail.com # # License: MIT # __title__ = 'icecream' __license__ = 'MIT' __version__ = '2.1.1' __author__ = 'Ansgar Grunseid' __contact__ = 'grunseid@gmail.com' __url__ = 'https://github.com/gruns/icecream' __description__ = ( 'Never use print() to debug again; inspect variables, expressions, and ' 'program execution with a single, simple function call.') icecream-2.1.1/icecream/builtins.py000066400000000000000000000006021406444607700172500ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # IceCream - Never use print() to debug again # # Ansgar Grunseid # grunseid.com # grunseid@gmail.com # # License: MIT # import icecream try: builtins = __import__('__builtin__') except ImportError: builtins = __import__('builtins') def install(ic='ic'): setattr(builtins, ic, icecream.ic) def uninstall(ic='ic'): delattr(builtins, ic) icecream-2.1.1/icecream/coloring.py000066400000000000000000000070471406444607700172450ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # IceCream - Never use print() to debug again # # Ansgar Grunseid # grunseid.com # grunseid@gmail.com # # License: MIT # from pygments.style import Style from pygments.token import ( Text, Name, Error, Other, String, Number, Keyword, Generic, Literal, Comment, Operator, Whitespace, Punctuation) # Solarized: https://ethanschoonover.com/solarized/ class SolarizedDark(Style): BASE03 = '#002b36' # noqa BASE02 = '#073642' # noqa BASE01 = '#586e75' # noqa BASE00 = '#657b83' # noqa BASE0 = '#839496' # noqa BASE1 = '#93a1a1' # noqa BASE2 = '#eee8d5' # noqa BASE3 = '#fdf6e3' # noqa YELLOW = '#b58900' # noqa ORANGE = '#cb4b16' # noqa RED = '#dc322f' # noqa MAGENTA = '#d33682' # noqa VIOLET = '#6c71c4' # noqa BLUE = '#268bd2' # noqa CYAN = '#2aa198' # noqa GREEN = '#859900' # noqa styles = { Text: BASE0, Whitespace: BASE03, Error: RED, Other: BASE0, Name: BASE1, Name.Attribute: BASE0, Name.Builtin: BLUE, Name.Builtin.Pseudo: BLUE, Name.Class: BLUE, Name.Constant: YELLOW, Name.Decorator: ORANGE, Name.Entity: ORANGE, Name.Exception: ORANGE, Name.Function: BLUE, Name.Property: BLUE, Name.Label: BASE0, Name.Namespace: YELLOW, Name.Other: BASE0, Name.Tag: GREEN, Name.Variable: ORANGE, Name.Variable.Class: BLUE, Name.Variable.Global: BLUE, Name.Variable.Instance: BLUE, String: CYAN, String.Backtick: CYAN, String.Char: CYAN, String.Doc: CYAN, String.Double: CYAN, String.Escape: ORANGE, String.Heredoc: CYAN, String.Interpol: ORANGE, String.Other: CYAN, String.Regex: CYAN, String.Single: CYAN, String.Symbol: CYAN, Number: CYAN, Number.Float: CYAN, Number.Hex: CYAN, Number.Integer: CYAN, Number.Integer.Long: CYAN, Number.Oct: CYAN, Keyword: GREEN, Keyword.Constant: GREEN, Keyword.Declaration: GREEN, Keyword.Namespace: ORANGE, Keyword.Pseudo: ORANGE, Keyword.Reserved: GREEN, Keyword.Type: GREEN, Generic: BASE0, Generic.Deleted: BASE0, Generic.Emph: BASE0, Generic.Error: BASE0, Generic.Heading: BASE0, Generic.Inserted: BASE0, Generic.Output: BASE0, Generic.Prompt: BASE0, Generic.Strong: BASE0, Generic.Subheading: BASE0, Generic.Traceback: BASE0, Literal: BASE0, Literal.Date: BASE0, Comment: BASE01, Comment.Multiline: BASE01, Comment.Preproc: BASE01, Comment.Single: BASE01, Comment.Special: BASE01, Operator: BASE0, Operator.Word: GREEN, Punctuation: BASE0, } icecream-2.1.1/icecream/icecream.py000066400000000000000000000233421406444607700171750ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # # IceCream - Never use print() to debug again # # Ansgar Grunseid # grunseid.com # grunseid@gmail.com # # License: MIT # from __future__ import print_function import ast import inspect import pprint import sys from datetime import datetime from contextlib import contextmanager from os.path import basename from textwrap import dedent import colorama import executing from pygments import highlight # See https://gist.github.com/XVilka/8346728 for color support in various # terminals and thus whether to use Terminal256Formatter or # TerminalTrueColorFormatter. from pygments.formatters import Terminal256Formatter from pygments.lexers import PythonLexer as PyLexer, Python3Lexer as Py3Lexer from .coloring import SolarizedDark PYTHON2 = (sys.version_info[0] == 2) _absent = object() def bindStaticVariable(name, value): def decorator(fn): setattr(fn, name, value) return fn return decorator @bindStaticVariable('formatter', Terminal256Formatter(style=SolarizedDark)) @bindStaticVariable( 'lexer', PyLexer(ensurenl=False) if PYTHON2 else Py3Lexer(ensurenl=False)) def colorize(s): self = colorize return highlight(s, self.lexer, self.formatter) @contextmanager def supportTerminalColorsInWindows(): # Filter and replace ANSI escape sequences on Windows with equivalent Win32 # API calls. This code does nothing on non-Windows systems. colorama.init() yield colorama.deinit() def stderrPrint(*args): print(*args, file=sys.stderr) def isLiteral(s): try: ast.literal_eval(s) except Exception: return False return True def colorizedStderrPrint(s): colored = colorize(s) with supportTerminalColorsInWindows(): stderrPrint(colored) DEFAULT_PREFIX = 'ic| ' DEFAULT_LINE_WRAP_WIDTH = 70 # Characters. DEFAULT_CONTEXT_DELIMITER = '- ' DEFAULT_OUTPUT_FUNCTION = colorizedStderrPrint DEFAULT_ARG_TO_STRING_FUNCTION = pprint.pformat class NoSourceAvailableError(OSError): """ Raised when icecream fails to find or access source code that's required to parse and analyze. This can happen, for example, when - ic() is invoked inside a REPL or interactive shell, e.g. from the command line (CLI) or with python -i. - The source code is mangled and/or packaged, e.g. with a project freezer like PyInstaller. - The underlying source code changed during execution. See https://stackoverflow.com/a/33175832. """ infoMessage = ( 'Failed to access the underlying source code for analysis. Was ic() ' 'invoked in a REPL (e.g. from the command line), a frozen application ' '(e.g. packaged with PyInstaller), or did the underlying source code ' 'change during execution?') def callOrValue(obj): return obj() if callable(obj) else obj class Source(executing.Source): def get_text_with_indentation(self, node): result = self.asttokens().get_text(node) if '\n' in result: result = ' ' * node.first_token.start[1] + result result = dedent(result) result = result.strip() return result def prefixLinesAfterFirst(prefix, s): lines = s.splitlines(True) for i in range(1, len(lines)): lines[i] = prefix + lines[i] return ''.join(lines) def indented_lines(prefix, string): lines = string.splitlines() return [prefix + lines[0]] + [ ' ' * len(prefix) + line for line in lines[1:] ] def format_pair(prefix, arg, value): arg_lines = indented_lines(prefix, arg) value_prefix = arg_lines[-1] + ': ' looksLikeAString = value[0] + value[-1] in ["''", '""'] if looksLikeAString: # Align the start of multiline strings. value = prefixLinesAfterFirst(' ', value) value_lines = indented_lines(value_prefix, value) lines = arg_lines[:-1] + value_lines return '\n'.join(lines) def argumentToString(obj): s = DEFAULT_ARG_TO_STRING_FUNCTION(obj) s = s.replace('\\n', '\n') # Preserve string newlines in output. return s class IceCreamDebugger: _pairDelimiter = ', ' # Used by the tests in tests/. lineWrapWidth = DEFAULT_LINE_WRAP_WIDTH contextDelimiter = DEFAULT_CONTEXT_DELIMITER def __init__(self, prefix=DEFAULT_PREFIX, outputFunction=DEFAULT_OUTPUT_FUNCTION, argToStringFunction=argumentToString, includeContext=False): self.enabled = True self.prefix = prefix self.includeContext = includeContext self.outputFunction = outputFunction self.argToStringFunction = argToStringFunction def __call__(self, *args): if self.enabled: callFrame = inspect.currentframe().f_back try: out = self._format(callFrame, *args) except NoSourceAvailableError as err: prefix = callOrValue(self.prefix) out = prefix + 'Error: ' + err.infoMessage self.outputFunction(out) if not args: # E.g. ic(). passthrough = None elif len(args) == 1: # E.g. ic(1). passthrough = args[0] else: # E.g. ic(1, 2, 3). passthrough = args return passthrough def format(self, *args): callFrame = inspect.currentframe().f_back out = self._format(callFrame, *args) return out def _format(self, callFrame, *args): prefix = callOrValue(self.prefix) callNode = Source.executing(callFrame).node if callNode is None: raise NoSourceAvailableError() context = self._formatContext(callFrame, callNode) if not args: time = self._formatTime() out = prefix + context + time else: if not self.includeContext: context = '' out = self._formatArgs( callFrame, callNode, prefix, context, args) return out def _formatArgs(self, callFrame, callNode, prefix, context, args): source = Source.for_frame(callFrame) sanitizedArgStrs = [ source.get_text_with_indentation(arg) for arg in callNode.args] pairs = list(zip(sanitizedArgStrs, args)) out = self._constructArgumentOutput(prefix, context, pairs) return out def _constructArgumentOutput(self, prefix, context, pairs): def argPrefix(arg): return '%s: ' % arg pairs = [(arg, self.argToStringFunction(val)) 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 # ic("hello"), just output # # ic| 'hello', # # instead of # # ic| "hello": 'hello'. # pairStrs = [ val if isLiteral(arg) else (argPrefix(arg) + val) for arg, val in pairs] allArgsOnOneLine = self._pairDelimiter.join(pairStrs) multilineArgs = len(allArgsOnOneLine.splitlines()) > 1 contextDelimiter = self.contextDelimiter if context else '' allPairs = prefix + context + contextDelimiter + allArgsOnOneLine firstLineTooLong = len(allPairs.splitlines()[0]) > self.lineWrapWidth if multilineArgs or firstLineTooLong: # ic| foo.py:11 in foo() # multilineStr: 'line1 # line2' # # ic| foo.py:11 in foo() # a: 11111111111111111111 # b: 22222222222222222222 if context: lines = [prefix + context] + [ format_pair(len(prefix) * ' ', arg, value) for arg, value in pairs ] # ic| multilineStr: 'line1 # line2' # # ic| a: 11111111111111111111 # b: 22222222222222222222 else: arg_lines = [ format_pair('', arg, value) for arg, value in pairs ] lines = indented_lines(prefix, '\n'.join(arg_lines)) # ic| foo.py:11 in foo()- a: 1, b: 2 # ic| a: 1, b: 2, c: 3 else: lines = [prefix + context + contextDelimiter + allArgsOnOneLine] return '\n'.join(lines) def _formatContext(self, callFrame, callNode): filename, lineNumber, parentFunction = self._getContext( callFrame, callNode) if parentFunction != '': parentFunction = '%s()' % parentFunction context = '%s:%s in %s' % (filename, lineNumber, parentFunction) return context def _formatTime(self): now = datetime.now() formatted = now.strftime('%H:%M:%S.%f')[:-3] return ' at %s' % formatted def _getContext(self, callFrame, callNode): lineNumber = callNode.lineno frameInfo = inspect.getframeinfo(callFrame) parentFunction = frameInfo.function filename = basename(frameInfo.filename) return filename, lineNumber, parentFunction def enable(self): self.enabled = True def disable(self): self.enabled = False def configureOutput(self, prefix=_absent, outputFunction=_absent, argToStringFunction=_absent, includeContext=_absent): if prefix is not _absent: self.prefix = prefix if outputFunction is not _absent: self.outputFunction = outputFunction if argToStringFunction is not _absent: self.argToStringFunction = argToStringFunction if includeContext is not _absent: self.includeContext = includeContext ic = IceCreamDebugger() icecream-2.1.1/logo.svg000066400000000000000000000310011406444607700147530ustar00rootroot00000000000000 image/svg+xml icecream-2.1.1/setup.cfg000066400000000000000000000001031406444607700151120ustar00rootroot00000000000000[bdist_wheel] universal = 1 [metadata] license_file = LICENSE.txt icecream-2.1.1/setup.py000066400000000000000000000062341406444607700150160ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # # IceCream - Never use print() to debug again # # Ansgar Grunseid # grunseid.com # grunseid@gmail.com # # License: MIT # import os import sys from os.path import dirname, join as pjoin from setuptools import setup, find_packages, Command from setuptools.command.test import test as TestCommand meta = {} with open(pjoin('icecream', '__version__.py')) as f: exec(f.read(), meta) class Publish(Command): """Publish to PyPI with twine.""" user_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): os.system('python setup.py sdist bdist_wheel') sdist = 'dist/icecream-%s.tar.gz' % meta['__version__'] wheel = 'dist/icecream-%s-py2.py3-none-any.whl' % meta['__version__'] rc = os.system('twine upload "%s" "%s"' % (sdist, wheel)) sys.exit(rc) class RunTests(TestCommand): """ Run the unit tests. By default, `python setup.py test` fails if tests/ isn't a Python module (that is, if the tests/ directory doesn't contain an __init__.py file). But the tests/ directory shouldn't contain an __init__.py file and tests/ shouldn't be a Python module. See http://doc.pytest.org/en/latest/goodpractices.html Running the unit tests manually here enables `python setup.py test` without tests/ being a Python module. """ def run_tests(self): from unittest import TestLoader, TextTestRunner tests_dir = pjoin(dirname(__file__), 'tests') suite = TestLoader().discover(tests_dir) result = TextTestRunner().run(suite) sys.exit(0 if result.wasSuccessful() else -1) setup( name=meta['__title__'], license=meta['__license__'], version=meta['__version__'], author=meta['__author__'], author_email=meta['__contact__'], url=meta['__url__'], description=meta['__description__'], long_description=( 'Information and documentation can be found at ' 'https://github.com/gruns/icecream.'), platforms=['any'], packages=find_packages(), include_package_data=True, classifiers=[ 'License :: OSI Approved :: MIT License', 'Natural Language :: English', 'Intended Audience :: Developers', 'Topic :: Software Development :: Libraries', 'Development Status :: 4 - Beta', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: Implementation :: PyPy', 'Programming Language :: Python :: Implementation :: CPython', ], tests_require=[], install_requires=[ 'colorama>=0.3.9', 'pygments>=2.2.0', 'executing>=0.3.1', 'asttokens>=2.0.1', ], cmdclass={ 'test': RunTests, 'publish': Publish, }, ) icecream-2.1.1/tests/000077500000000000000000000000001406444607700144415ustar00rootroot00000000000000icecream-2.1.1/tests/install_test_import.py000066400000000000000000000002661406444607700211160ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # IceCream - Never use print() to debug again # # Ansgar Grunseid # grunseid.com # grunseid@gmail.com # # License: MIT # def runMe(): x = 3 ic(x) icecream-2.1.1/tests/test_icecream.py000066400000000000000000000413411406444607700176250ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # IceCream - Never use print() to debug again # # Ansgar Grunseid # grunseid.com # grunseid@gmail.com # # License: MIT # import sys import unittest try: # Python 2.x. from StringIO import StringIO except ImportError: # Python 3.x. from io import StringIO from contextlib import contextmanager from os.path import basename, splitext import icecream from icecream import ic, stderrPrint, NoSourceAvailableError TEST_PAIR_DELIMITER = '| ' MYFILENAME = basename(__file__) a = 1 b = 2 c = 3 def noop(*args, **kwargs): return def hasAnsiEscapeCodes(s): # Oversimplified, but ¯\_(ツ)_/¯. TODO(grun): Test with regex. return '\x1b[' in s class FakeTeletypeBuffer(StringIO): """ Extend StringIO to act like a TTY so ANSI control codes aren't stripped when wrapped with colorama's wrap_stream(). """ def isatty(self): return True @contextmanager def disableColoring(): originalOutputFunction = ic.outputFunction ic.configureOutput(outputFunction=stderrPrint) yield ic.configureOutput(outputFunction=originalOutputFunction) @contextmanager def configureIcecreamOutput(prefix=None, outputFunction=None, argToStringFunction=None, includeContext=None): oldPrefix = ic.prefix oldOutputFunction = ic.outputFunction oldArgToStringFunction = ic.argToStringFunction oldIncludeContext = ic.includeContext if prefix: ic.configureOutput(prefix=prefix) if outputFunction: ic.configureOutput(outputFunction=outputFunction) if argToStringFunction: ic.configureOutput(argToStringFunction=argToStringFunction) if includeContext: ic.configureOutput(includeContext=includeContext) yield ic.configureOutput( oldPrefix, oldOutputFunction, oldArgToStringFunction, oldIncludeContext) @contextmanager def captureStandardStreams(): realStdout = sys.stdout realStderr = sys.stderr newStdout = FakeTeletypeBuffer() newStderr = FakeTeletypeBuffer() try: sys.stdout = newStdout sys.stderr = newStderr yield newStdout, newStderr finally: sys.stdout = realStdout sys.stderr = realStderr def stripPrefix(line): if line.startswith(ic.prefix): line = line.strip()[len(ic.prefix):] return line def lineIsContextAndTime(line): line = stripPrefix(line) # ic| f.py:33 in foo() at 08:08:51.389 context, time = line.split(' at ') return ( lineIsContext(context) and len(time.split(':')) == 3 and len(time.split('.')) == 2) def lineIsContext(line): line = stripPrefix(line) # ic| f.py:33 in foo() sourceLocation, function = line.split(' in ') # f.py:33 in foo() filename, lineNumber = sourceLocation.split(':') # f.py:33 name, ext = splitext(filename) return ( int(lineNumber) > 0 and ext in ['.py', '.pyc', '.pyo'] and name == splitext(MYFILENAME)[0] and (function == '' or function.endswith('()'))) def lineAfterContext(line, prefix): if line.startswith(prefix): line = line[len(prefix):] toks = line.split(' in ', 1) if len(toks) == 2: rest = toks[1].split(' ') line = ' '.join(rest[1:]) return line def parseOutputIntoPairs(out, err, assertNumLines, prefix=icecream.DEFAULT_PREFIX): if isinstance(out, StringIO): out = out.getvalue() if isinstance(err, StringIO): err = err.getvalue() assert not out lines = err.splitlines() if assertNumLines: assert len(lines) == assertNumLines linePairs = [] for line in lines: line = lineAfterContext(line, prefix) if not line: linePairs.append([]) continue pairStrs = line.split(TEST_PAIR_DELIMITER) pairs = [tuple(s.split(':', 1)) for s in pairStrs] # Indented line of a multiline value. if len(pairs[0]) == 1 and line.startswith(' '): arg, value = linePairs[-1][-1] looksLikeAString = value[0] in ["'", '"'] prefix = (arg + ': ') + (' ' if looksLikeAString else '') dedented = line[len(ic.prefix) + len(prefix):] linePairs[-1][-1] = (arg, value + '\n' + dedented) else: items = [ (p[0].strip(), None) if len(p) == 1 # A value, like ic(3). else (p[0].strip(), p[1].strip()) # A variable, like ic(a). for p in pairs] linePairs.append(items) return linePairs class TestIceCream(unittest.TestCase): def setUp(self): ic._pairDelimiter = TEST_PAIR_DELIMITER def testWithoutArgs(self): with disableColoring(), captureStandardStreams() as (out, err): ic() assert lineIsContextAndTime(err.getvalue()) def testAsArgument(self): with disableColoring(), captureStandardStreams() as (out, err): noop(ic(a), ic(b)) pairs = parseOutputIntoPairs(out, err, 2) assert pairs[0][0] == ('a', '1') and pairs[1][0] == ('b', '2') with disableColoring(), captureStandardStreams() as (out, err): dic = {1: ic(a)} # noqa lst = [ic(b), ic()] # noqa pairs = parseOutputIntoPairs(out, err, 3) assert pairs[0][0] == ('a', '1') assert pairs[1][0] == ('b', '2') assert lineIsContextAndTime(err.getvalue().splitlines()[-1]) def testSingleArgument(self): with disableColoring(), captureStandardStreams() as (out, err): ic(a) assert parseOutputIntoPairs(out, err, 1)[0][0] == ('a', '1') def testMultipleArguments(self): with disableColoring(), captureStandardStreams() as (out, err): ic(a, b) pairs = parseOutputIntoPairs(out, err, 1)[0] assert pairs == [('a', '1'), ('b', '2')] def testNestedMultiline(self): with disableColoring(), captureStandardStreams() as (out, err): ic( ) assert lineIsContextAndTime(err.getvalue()) with disableColoring(), captureStandardStreams() as (out, err): ic(a, 'foo') pairs = parseOutputIntoPairs(out, err, 1)[0] assert pairs == [('a', '1'), ("'foo'", None)] with disableColoring(), captureStandardStreams() as (out, err): noop(noop(noop({1: ic( noop())}))) assert parseOutputIntoPairs(out, err, 1)[0][0] == ('noop()', 'None') def testExpressionArguments(self): class klass(): attr = 'yep' d = {'d': {1: 'one'}, 'k': klass} with disableColoring(), captureStandardStreams() as (out, err): ic(d['d'][1]) pair = parseOutputIntoPairs(out, err, 1)[0][0] assert pair == ("d['d'][1]", "'one'") with disableColoring(), captureStandardStreams() as (out, err): ic(d['k'].attr) pair = parseOutputIntoPairs(out, err, 1)[0][0] assert pair == ("d['k'].attr", "'yep'") def testMultipleCallsOnSameLine(self): with disableColoring(), captureStandardStreams() as (out, err): ic(a); ic(b, c) # noqa pairs = parseOutputIntoPairs(out, err, 2) assert pairs[0][0] == ('a', '1') assert pairs[1] == [('b', '2'), ('c', '3')] def testCallSurroundedByExpressions(self): with disableColoring(), captureStandardStreams() as (out, err): noop(); ic(a); noop() # noqa assert parseOutputIntoPairs(out, err, 1)[0][0] == ('a', '1') def testComments(self): with disableColoring(), captureStandardStreams() as (out, err): """Comment."""; ic(); # Comment. # noqa assert lineIsContextAndTime(err.getvalue()) def testMethodArguments(self): class Foo: def foo(self): return 'foo' f = Foo() with disableColoring(), captureStandardStreams() as (out, err): ic(f.foo()) assert parseOutputIntoPairs(out, err, 1)[0][0] == ('f.foo()', "'foo'") def testComplicated(self): with disableColoring(), captureStandardStreams() as (out, err): noop(); ic(); noop(); ic(a, # noqa b, noop.__class__.__name__, # noqa noop ()); noop() # noqa pairs = parseOutputIntoPairs(out, err, 2) assert lineIsContextAndTime(err.getvalue().splitlines()[0]) assert pairs[1] == [ ('a', '1'), ('b', '2'), ('noop.__class__.__name__', "'function'"), ('noop ()', 'None')] def testReturnValue(self): with disableColoring(), captureStandardStreams() as (out, err): assert ic() is None assert ic(1) == 1 assert ic(1, 2, 3) == (1, 2, 3) def testDifferentName(self): from icecream import ic as foo with disableColoring(), captureStandardStreams() as (out, err): foo() assert lineIsContextAndTime(err.getvalue()) newname = foo with disableColoring(), captureStandardStreams() as (out, err): newname(a) pair = parseOutputIntoPairs(out, err, 1)[0][0] assert pair == ('a', '1') def testPrefixConfiguration(self): prefix = 'lolsup ' with configureIcecreamOutput(prefix, stderrPrint): with disableColoring(), captureStandardStreams() as (out, err): ic(a) pair = parseOutputIntoPairs(out, err, 1, prefix=prefix)[0][0] assert pair == ('a', '1') def prefixFunction(): return 'lolsup ' with configureIcecreamOutput(prefix=prefixFunction): with disableColoring(), captureStandardStreams() as (out, err): ic(b) pair = parseOutputIntoPairs(out, err, 1, prefix=prefixFunction())[0][0] assert pair == ('b', '2') def testOutputFunction(self): lst = [] def appendTo(s): lst.append(s) with configureIcecreamOutput(ic.prefix, appendTo): with captureStandardStreams() as (out, err): ic(a) assert not out.getvalue() and not err.getvalue() with configureIcecreamOutput(outputFunction=appendTo): with captureStandardStreams() as (out, err): ic(b) assert not out.getvalue() and not err.getvalue() pairs = parseOutputIntoPairs(out, '\n'.join(lst), 2) assert pairs == [[('a', '1')], [('b', '2')]] def testEnableDisable(self): with disableColoring(), captureStandardStreams() as (out, err): assert ic(a) == 1 assert ic.enabled ic.disable() assert not ic.enabled assert ic(b) == 2 ic.enable() assert ic.enabled assert ic(c) == 3 pairs = parseOutputIntoPairs(out, err, 2) assert pairs == [[('a', '1')], [('c', '3')]] def testArgToStringFunction(self): def hello(obj): return 'zwei' with configureIcecreamOutput(argToStringFunction=hello): with disableColoring(), captureStandardStreams() as (out, err): eins = 'ein' ic(eins) pair = parseOutputIntoPairs(out, err, 1)[0][0] assert pair == ('eins', 'zwei') def testSingleArgumentLongLineNotWrapped(self): # A single long line with one argument is not line wrapped. longStr = '*' * (ic.lineWrapWidth + 1) with disableColoring(), captureStandardStreams() as (out, err): ic(longStr) pair = parseOutputIntoPairs(out, err, 1)[0][0] assert len(err.getvalue()) > ic.lineWrapWidth assert pair == ('longStr', ic.argToStringFunction(longStr)) def testMultipleArgumentsLongLineWrapped(self): # A single long line with multiple variables is line wrapped. val = '*' * int(ic.lineWrapWidth / 4) valStr = ic.argToStringFunction(val) v1 = v2 = v3 = v4 = val with disableColoring(), captureStandardStreams() as (out, err): ic(v1, v2, v3, v4) pairs = parseOutputIntoPairs(out, err, 4) assert pairs == [[(k, valStr)] for k in ['v1', 'v2', 'v3', 'v4']] lines = err.getvalue().splitlines() assert ( lines[0].startswith(ic.prefix) and lines[1].startswith(' ' * len(ic.prefix)) and lines[2].startswith(' ' * len(ic.prefix)) and lines[3].startswith(' ' * len(ic.prefix))) def testMultilineValueWrapped(self): # Multiline values are line wrapped. multilineStr = 'line1\nline2' with disableColoring(), captureStandardStreams() as (out, err): ic(multilineStr) pair = parseOutputIntoPairs(out, err, 2)[0][0] assert pair == ('multilineStr', ic.argToStringFunction(multilineStr)) def testIncludeContextSingleLine(self): i = 3 with configureIcecreamOutput(includeContext=True): with disableColoring(), captureStandardStreams() as (out, err): ic(i) pair = parseOutputIntoPairs(out, err, 1)[0][0] assert pair == ('i', '3') def testValues(self): with disableColoring(), captureStandardStreams() as (out, err): # Test both 'asdf' and "asdf"; see # https://github.com/gruns/icecream/issues/53. ic(3, 'asdf', "asdf") pairs = parseOutputIntoPairs(out, err, 1) assert pairs == [[('3', None), ("'asdf'", None), ("'asdf'", None)]] def testIncludeContextMultiLine(self): multilineStr = 'line1\nline2' with configureIcecreamOutput(includeContext=True): with disableColoring(), captureStandardStreams() as (out, err): ic(multilineStr) firstLine = err.getvalue().splitlines()[0] assert lineIsContext(firstLine) pair = parseOutputIntoPairs(out, err, 3)[1][0] assert pair == ('multilineStr', ic.argToStringFunction(multilineStr)) def testFormat(self): with disableColoring(), captureStandardStreams() as (out, err): """comment"""; noop(); ic( # noqa 'sup'); noop() # noqa """comment"""; noop(); s = ic.format( # noqa 'sup'); noop() # noqa assert s == err.getvalue().rstrip() def testMultilineInvocationWithComments(self): with disableColoring(), captureStandardStreams() as (out, err): ic( # Comment. a, # Comment. # Comment. b, # Comment. ) # Comment. pairs = parseOutputIntoPairs(out, err, 1)[0] assert pairs == [('a', '1'), ('b', '2')] def testNoSourceAvailable(self): with disableColoring(), captureStandardStreams() as (out, err): eval('ic()') assert NoSourceAvailableError.infoMessage in err.getvalue() def testSingleTupleArgument(self): with disableColoring(), captureStandardStreams() as (out, err): ic((a, b)) pair = parseOutputIntoPairs(out, err, 1)[0][0] self.assertEqual(pair, ('(a, b)', '(1, 2)')) def testMultilineContainerArgs(self): with disableColoring(), captureStandardStreams() as (out, err): ic((a, b)) ic([a, b]) ic((a, b), [list(range(15)), list(range(15))]) self.assertEqual(err.getvalue().strip(), """ ic| (a, b): (1, 2) ic| [a, b]: [1, 2] ic| (a, b): (1, 2) [list(range(15)), list(range(15))]: [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]] """.strip()) with disableColoring(), captureStandardStreams() as (out, err): with configureIcecreamOutput(includeContext=True): ic((a, b), [list(range(15)), list(range(15))]) lines = err.getvalue().strip().splitlines() self.assertRegexpMatches( lines[0], r'ic\| test_icecream.py:\d+ in testMultilineContainerArgs\(\)', ) self.assertEqual('\n'.join(lines[1:]), """\ (a, b): (1, 2) [list(range(15)), list(range(15))]: [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]]""") def testMultipleTupleArguments(self): with disableColoring(), captureStandardStreams() as (out, err): ic((a, b), (b, a), a, b) pair = parseOutputIntoPairs(out, err, 1)[0] self.assertEqual(pair, [ ('(a, b)', '(1, 2)'), ('(b, a)', '(2, 1)'), ('a', '1'), ('b', '2')]) def testColoring(self): with captureStandardStreams() as (out, err): ic({1: 'str'}) # Output should be colored with ANSI control codes. assert hasAnsiEscapeCodes(err.getvalue()) icecream-2.1.1/tests/test_install.py000066400000000000000000000016001406444607700175150ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # IceCream - Never use print() to debug again # # Ansgar Grunseid # grunseid.com # grunseid@gmail.com # # License: MIT # import unittest import icecream from test_icecream import ( disableColoring, captureStandardStreams, parseOutputIntoPairs) from install_test_import import runMe class TestIceCreamInstall(unittest.TestCase): def testInstall(self): icecream.install() with disableColoring(), captureStandardStreams() as (out, err): runMe() assert parseOutputIntoPairs(out, err, 1)[0][0] == ('x', '3') icecream.uninstall() # Clean up builtins. def testUninstall(self): try: icecream.uninstall() except AttributeError: # Already uninstalled. pass # NameError: global name 'ic' is not defined. with self.assertRaises(NameError): runMe() icecream-2.1.1/tox.ini000066400000000000000000000001751406444607700146150ustar00rootroot00000000000000[tox] envlist = py27, py35, py36, py37, py38, py39, pypy, pypy3 [testenv] deps = nose commands = nosetests --exe []