From 727e9301210449c03822e7acf798c2abe84a4384 Mon Sep 17 00:00:00 2001 From: Joao Coelho Date: Sat, 9 Apr 2022 19:14:26 -0500 Subject: [PATCH 01/10] Update docs and convert to GitHub Actions (#3) --- .github/workflows/tests.yml | 26 ++++++++++++++++++++++++++ .travis.yml | 14 -------------- CHANGELOG.txt | 4 ++++ README.rst | 6 ++---- pp/setup.py | 4 ++-- setup.py | 4 ++-- tox.ini | 8 -------- 7 files changed, 36 insertions(+), 30 deletions(-) create mode 100644 .github/workflows/tests.yml delete mode 100644 .travis.yml delete mode 100644 tox.ini diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..32aa88f --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,26 @@ +name: tests + +on: + - pull_request + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.7', '3.8', 'pypy-3.7'] + name: Python ${{ matrix.python-version }} + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + architecture: x64 + - name: Install requirements + run: | + python -m pip install -U pip + python -m pip install -r test-requires.txt + - name: Run tests + run: | + nosetests diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index df6fc56..0000000 --- a/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: python - -python: - - 2.7 - - 3.6 - - 3.5 - - 3.4 - - pypy - -install: - - pip install -r test-requires.txt - -script: - - python test.py diff --git a/CHANGELOG.txt b/CHANGELOG.txt index dbc81f1..928d61e 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,7 @@ +0.5.0 (unreleased) + * Update some documentation to point to `pprintpp2` instead. + * Use GitHub Actions instead of Travis + 0.4.0 * Add IPython plugin (thanks @roee30; https://github.com/wolever/pprintpp/pull/20) diff --git a/README.rst b/README.rst index 15aaa71..b362cfd 100644 --- a/README.rst +++ b/README.rst @@ -1,10 +1,8 @@ ``pprint++``: a drop-in replacement for ``pprint`` that's actually pretty ========================================================================= -.. image:: https://travis-ci.org/wolever/pprintpp.svg?branch=master - :target: https://travis-ci.org/wolever/pprintpp - -Now with Python 3 support! +This is a continuation of [pprintpp](https://github.com/wolever/pprintpp), by [David Wolever](https://github.com/wolever), +which seems to have been discontinued by the original writer. Currently, under development still. Installation ------------ diff --git a/pp/setup.py b/pp/setup.py index 64b85d7..1ec8813 100644 --- a/pp/setup.py +++ b/pp/setup.py @@ -10,12 +10,12 @@ try: long_description = open("README.rst", "U").read() except IOError: - long_description = "See https://github.com/wolever/pprintpp" + long_description = "See https://github.com/joaonc/pprintpp2" setup( name="pp-ez", version="0.2.0", - url="https://github.com/wolever/pprintpp", + url="https://github.com/joaonc/pprintpp2", author="David Wolever", author_email="david@wolever.net", description="A short alias for the pprintpp or pprint module", diff --git a/setup.py b/setup.py index aaece26..feea295 100644 --- a/setup.py +++ b/setup.py @@ -10,12 +10,12 @@ try: long_description = open("README.rst", "U").read() except IOError: - long_description = "See https://github.com/wolever/pprintpp" + long_description = "See https://github.com/joaonc/pprintpp2" setup( name="pprintpp", version="0.4.0", - url="https://github.com/wolever/pprintpp", + url="https://github.com/joaonc/pprintpp2", author="David Wolever", author_email="david@wolever.net", description="A drop-in replacement for pprint that's actually pretty", diff --git a/tox.ini b/tox.ini deleted file mode 100644 index e194976..0000000 --- a/tox.ini +++ /dev/null @@ -1,8 +0,0 @@ -[tox] -envlist = py26,py27,py33,py35,pypy - -[testenv] -deps = - -rtest-requires.txt -commands = - python test.py From 9c65ae8e89ddb71d58153b1531dc969a5948d244 Mon Sep 17 00:00:00 2001 From: Joao Coelho Date: Sat, 9 Apr 2022 22:19:04 -0500 Subject: [PATCH 02/10] Move to using `pytest` from `nose`. (#5) --- .github/workflows/tests.yml | 2 +- .gitignore | 169 +++++++++++++++++++++--- CHANGELOG.txt | 3 +- test-requires.txt | 3 +- test.py | 255 ++++++++++++++++++------------------ 5 files changed, 280 insertions(+), 152 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 32aa88f..efb7fe8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,4 +23,4 @@ jobs: python -m pip install -r test-requires.txt - name: Run tests run: | - nosetests + pytest test.py diff --git a/.gitignore b/.gitignore index 541171f..d9005f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,21 +1,152 @@ -*.class -*.o -*.pyc -*.sqlite3 -*.sw[op] -*~ -.DS_Store -bin-debug/* -bin-release/* -bin/* -tags -*.beam -*.dump -env/ -.env/ -*egg-info* -misc/ -dist/ -Icon? +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ .tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 928d61e..8ec4a98 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,7 @@ 0.5.0 (unreleased) * Update some documentation to point to `pprintpp2` instead. - * Use GitHub Actions instead of Travis + * Use GitHub Actions instead of Travis. + * Use `pytest` instead of `nose` (thanks @pgajdos). 0.4.0 * Add IPython plugin (thanks @roee30; diff --git a/test-requires.txt b/test-requires.txt index 3621aa6..4f6bf64 100644 --- a/test-requires.txt +++ b/test-requires.txt @@ -1,2 +1 @@ -nose==1.3.0 -parameterized==0.6.1 +pytest==7.1.1 diff --git a/test.py b/test.py index 45a0075..ef5ddcc 100644 --- a/test.py +++ b/test.py @@ -1,46 +1,99 @@ from __future__ import print_function import sys -import ctypes -import textwrap - -from nose.tools import assert_equal -from parameterized import parameterized, param +from contextlib import redirect_stdout +import io +import pytest sys.path.append("pp/") import pp import pprintpp as p from pprintpp import Counter, defaultdict, OrderedDict -class PPrintppTestBase(object): - def assertStdout(self, expected, trim=True): - if trim: - expected = textwrap.dedent(expected.rstrip().lstrip("\n")) - # Assumes that nose's capture plugin is active - assert_equal(sys.stdout.getvalue().rstrip(), expected) - - -class TestPP(PPrintppTestBase): - def test_pp(self): +def test_pp(): + expected = "['hello', 'world']" + f = io.StringIO() + with redirect_stdout(f): pp(["hello", "world"]) - self.assertStdout("['hello', 'world']") + actual = f.getvalue().rstrip("\n") + assert actual == expected - def test_pp_pprint(self): +def test_pp_print(): + expected = "'stuff'" + f = io.StringIO() + with redirect_stdout(f): pp.pprint("stuff") - self.assertStdout("'stuff'") + actual = f.getvalue().rstrip("\n") + assert actual == expected - def test_fmt(self): +def test_fmt(): + expected = "'asdf'\n'stuff'" + f = io.StringIO() + with redirect_stdout(f): print(pp.pformat("asdf")) print(pp.fmt("stuff")) - self.assertStdout(""" - 'asdf' - 'stuff' - """) - - def test_module_like(self): - print(dir(pp)) - print(repr(pp)) - + actual = f.getvalue().rstrip("\n") + assert actual == expected + +def test_module_like(): + print(dir(pp)) + print(repr(pp)) + +uni_safe = "\xe9 \u6f02 \u0e4f \u2661" +uni_unsafe = "\u200a \u0302 \n" +slashed = lambda s: u"%s'%s'" %( + p.u_prefix, + s.encode("ascii", "backslashreplace").decode("ascii").replace("\n", "\\n") +) + +@pytest.mark.skip('fix') +@pytest.mark.parametrize("input,expected,encoding", [ + (uni_safe, "%s'%s'" %(p.u_prefix, uni_safe), "utf-8"), + (uni_unsafe, slashed(uni_unsafe), "utf-8"), + (uni_unsafe, slashed(uni_unsafe), "ascii"), + ("\U0002F9B2", slashed("\U0002F9B2"), "ascii") +]) +def test_unicode(input, expected, encoding): + stream = p.TextIO(encoding=encoding) + p.pprint(input, stream=stream) + assert stream.getvalue().rstrip("\n") == expected + +test_back_and_forth_data = [ + "'\\'\"'", + '"\'"', + "'\"'", + "frozenset(['a', 'b', 'c'])", + "set([None, 1, 'a'])", + "[]", + "[1]", + "{}", + "{1: 1}", + "set()", + "set([1])", + "frozenset()", + "frozenset([1])", + "()", + "(1, )", + "MyDict({})", + "MyDict({1: 1})", + "MyList([])", + "MyList([1])", + "MyTuple(())", + "MyTuple((1, ))", + "MySet()", + "MySet([1])", + "MyFrozenSet()", + "MyFrozenSet([1])", + "Counter()", + "Counter({1: 1})", + "OrderedDict()", + "OrderedDict([(1, 1), (5, 5), (2, 2)])", + "MyOrderedDict()", + "MyOrderedDict([(1, 1)])", + "MyCounter()", + "MyCounter({1: 1})", + "MyCounterWithRepr('dummy')", +] class MyDict(dict): pass @@ -70,104 +123,48 @@ class MyCounterWithRepr(p.Counter): def __repr__(self): return "MyCounterWithRepr('dummy')" -class TestPPrint(PPrintppTestBase): - uni_safe = u"\xe9 \u6f02 \u0e4f \u2661" - uni_unsafe = u"\u200a \u0302 \n" - slashed = lambda s: u"%s'%s'" %( - p.u_prefix, - s.encode("ascii", "backslashreplace").decode("ascii").replace("\n", "\\n") - ) - - @parameterized([ - param("safe", uni_safe, "%s'%s'" %(p.u_prefix, uni_safe)), - param("unsafe", uni_unsafe, slashed(uni_unsafe)), - param("encoding-aware", uni_safe, slashed(uni_safe), encoding="ascii"), - param("high-end-chars", u"\U0002F9B2", slashed(u"\U0002F9B2"), encoding="ascii"), - ]) - def test_unicode(self, name, input, expected, encoding="utf-8"): - stream = p.TextIO(encoding=encoding) - p.pprint(input, stream=stream) - assert_equal(stream.getvalue().rstrip("\n"), expected) - - @parameterized([ - param(u"'\\'\"'"), - param(u'"\'"'), - param(u"'\"'"), - param("frozenset(['a', 'b', 'c'])"), - param("set([None, 1, 'a'])"), - - param("[]"), - param("[1]"), - param("{}"), - param("{1: 1}"), - param("set()"), - param("set([1])"), - param("frozenset()"), - param("frozenset([1])"), - param("()"), - param("(1, )"), - - param("MyDict({})"), - param("MyDict({1: 1})"), - param("MyList([])"), - param("MyList([1])"), - param("MyTuple(())"), - param("MyTuple((1, ))"), - param("MySet()"), - param("MySet([1])"), - param("MyFrozenSet()"), - param("MyFrozenSet([1])"), - - ] + ([] if not p._test_has_collections else [ - param("Counter()"), - param("Counter({1: 1})"), - param("OrderedDict()"), - param("OrderedDict([(1, 1), (5, 5), (2, 2)])"), - param("MyOrderedDict()"), - param("MyOrderedDict([(1, 1)])"), - param("MyCounter()"), - param("MyCounter({1: 1})"), - param("MyCounterWithRepr('dummy')"), - ])) - def test_back_and_forth(self, expected): - input = eval(expected) - stream = p.TextIO() - p.pprint(input, stream=stream) - assert_equal(stream.getvalue().rstrip("\n"), expected) - - if p._test_has_collections: - @parameterized([ - param("defaultdict(%r, {})" %(int, ), defaultdict(int)), - param("defaultdict(%r, {1: 1})" %(int, ), defaultdict(int, [(1, 1)])), - param("MyDefaultDict(%r, {})" %(int, ), MyDefaultDict(int)), - param("MyDefaultDict(%r, {1: 1})" %(int, ), MyDefaultDict(int, [(1, 1)])), - ]) - def test_expected_input(self, expected, input): - stream = p.TextIO() - p.pprint(input, stream=stream) - assert_equal(stream.getvalue().rstrip("\n"), expected) - - def test_unhashable_repr(self): - # In Python 3, C extensions can define a __repr__ method which is an - # instance of `instancemethod`, which is unhashable. It turns out to be - # spectacularly difficult to create an `instancemethod` and attach it to - # a type without using C... so we'll simulate it using a more explicitly - # unhashable type. - # See also: http://stackoverflow.com/q/40876368/71522 - - class UnhashableCallable(object): - __hash__ = None - - def __call__(self): - return "some-repr" - - class MyCls(object): - __repr__ = UnhashableCallable() - - obj = MyCls() - assert_equal(p.pformat(obj), "some-repr") - - -if __name__ == "__main__": - import nose - nose.main() +@pytest.mark.skip('fix') +@pytest.mark.parametrize("expected", test_back_and_forth_data) +def test_back_and_forth(expected): + input = eval(expected) + stream = p.TextIO() + p.pprint(input, stream=stream) + assert stream.getvalue().rstrip("\n") == expected + +test_expected_input_data = [ + ("defaultdict(%r, {})" %(int, ), defaultdict(int)), + ("defaultdict(%r, {1: 1})" %(int, ), defaultdict(int, [(1, 1)])), + ("MyDefaultDict(%r, {})" %(int, ), MyDefaultDict(int)), + ("MyDefaultDict(%r, {1: 1})" %(int, ), MyDefaultDict(int, [(1, 1)])), +] + +@pytest.mark.skip('fix') +@pytest.mark.parametrize("expected,input", test_expected_input_data) +def test_expected_input(expected, input): + stream = p.TextIO() + p.pprint(input, stream=stream) + assert stream.getvalue().rstrip("\n") == expected + + +def test_unhashable_repr(): + # In Python 3, C extensions can define a __repr__ method which is an + # instance of `instancemethod`, which is unhashable. It turns out to be + # spectacularly difficult to create an `instancemethod` and attach it to + # a type without using C... so we'll simulate it using a more explicitly + # unhashable type. + # See also: http://stackoverflow.com/q/40876368/71522 + + class UnhashableCallable(object): + __hash__ = None + + def __call__(self): + return "some-repr" + + class MyCls(object): + __repr__ = UnhashableCallable() + + obj = MyCls() + assert p.pformat(obj) == "some-repr" + + + From 65906cfdca354612df4371fa48295d28ef774c35 Mon Sep 17 00:00:00 2001 From: Joao Coelho Date: Sat, 9 Apr 2022 22:56:37 -0500 Subject: [PATCH 03/10] Add `sort_dicts` option (#9) --- CHANGELOG.txt | 1 + pprintpp/__init__.py | 26 ++++++++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 8ec4a98..1145a8c 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -2,6 +2,7 @@ * Update some documentation to point to `pprintpp2` instead. * Use GitHub Actions instead of Travis. * Use `pytest` instead of `nose` (thanks @pgajdos). + * `sort_dict` option added (thanks @gergelyk). 0.4.0 * Add IPython plugin (thanks @roee30; diff --git a/pprintpp/__init__.py b/pprintpp/__init__.py index 7cb8b43..47d67d4 100644 --- a/pprintpp/__init__.py +++ b/pprintpp/__init__.py @@ -139,15 +139,18 @@ def getvalue(self): for i in range(255) ) -def pprint(object, stream=None, indent=4, width=80, depth=None): +def pprint(object, stream=None, indent=4, width=80, depth=None, + sort_dicts=True): """Pretty-print a Python object to a stream [default is sys.stdout].""" printer = PrettyPrinter( - stream=stream, indent=indent, width=width, depth=depth) + stream=stream, indent=indent, width=width, depth=depth, + sort_dicts=sort_dicts) printer.pprint(object) -def pformat(object, indent=4, width=80, depth=None): +def pformat(object, indent=4, width=80, depth=None, sort_dicts=True): """Format a Python object into a pretty-printed representation.""" - return PrettyPrinter(indent=indent, width=width, depth=depth).pformat(object) + return PrettyPrinter(indent=indent, width=width, depth=depth, + sort_dicts=sort_dicts).pformat(object) def saferepr(object): """Version of repr() which can handle recursive data structures.""" @@ -200,6 +203,7 @@ class PPrintState(object): level = 0 max_width = 80 max_depth = None + sort_dicts = True stream = None context = None write_constrain = None @@ -270,7 +274,7 @@ def _mk_open_close_empty_dict(type_tuples): return res class PrettyPrinter(object): - def __init__(self, indent=4, width=80, depth=None, stream=None): + def __init__(self, indent=4, width=80, depth=None, stream=None, sort_dicts=True): """Handle pretty printing operations onto a stream using a set of configured parameters. @@ -287,10 +291,16 @@ def __init__(self, indent=4, width=80, depth=None, stream=None): The desired output stream. If omitted (or false), the standard output stream available at construction will be used. + sort_dicts + If `True`, dictionaries will be formatted with their keys sorted, + otherwise they will display in the order as returned by `items` + method. + """ self.get_default_state = lambda: PPrintState( indent=int(indent), max_width=int(width), + sort_dicts = sort_dicts, stream=stream or sys.stdout, context={}, ) @@ -363,7 +373,11 @@ def _write_nested_real(self, object, state, typeish, oneline=False): first = True joiner = oneline and ", " or ",\n" + indent_str if typeish == "dict": - for k, v in _sorted(object.items()): + items = object.items() + if state.sort_dicts: + items = _sorted(items) + + for k, v in items: if first: first = False else: From ee008658a440f0fe8861c6708451d3265dd7f7a0 Mon Sep 17 00:00:00 2001 From: Joao Coelho Date: Sat, 9 Apr 2022 23:29:24 -0500 Subject: [PATCH 04/10] Drop support for older Python versions (#10) --- CHANGELOG.txt | 3 +- README.rst | 10 +++--- pp/README.rst | 3 +- pp/setup.py | 1 - pprintpp/__init__.py | 75 +++++++------------------------------------- pprintpp/safesort.py | 8 +---- setup.py | 1 - test.py | 5 ++- 8 files changed, 24 insertions(+), 82 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 1145a8c..cee1e82 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -2,7 +2,8 @@ * Update some documentation to point to `pprintpp2` instead. * Use GitHub Actions instead of Travis. * Use `pytest` instead of `nose` (thanks @pgajdos). - * `sort_dict` option added (thanks @gergelyk). + * Add `sort_dict` option (thanks @gergelyk). + * Remove support for older Python versions (3.7+ from now on). 0.4.0 * Add IPython plugin (thanks @roee30; diff --git a/README.rst b/README.rst index b362cfd..b5a476e 100644 --- a/README.rst +++ b/README.rst @@ -1,15 +1,17 @@ ``pprint++``: a drop-in replacement for ``pprint`` that's actually pretty ========================================================================= -This is a continuation of [pprintpp](https://github.com/wolever/pprintpp), by [David Wolever](https://github.com/wolever), -which seems to have been discontinued by the original writer. Currently, under development still. +This is a continuation of `pprintpp `_, +by `David Wolever `_, which seems to have been +discontinued by the original writer. Currently, under development still. + +Python 3.7 and above. Installation ------------ -``pprint++`` can be installed with Python 2 or Python 3 using ``pip`` or -``easy_install``:: +``pprint++`` can be installed using ``pip`` or ``easy_install``:: $ pip install pprintpp - OR - diff --git a/pp/README.rst b/pp/README.rst index 5dc130e..489778c 100644 --- a/pp/README.rst +++ b/pp/README.rst @@ -1,8 +1,7 @@ ``pp``: an alias for pprint++ ============================= -``pp`` can be installed with Python 2 or Python 3 using ``pip`` or -``easy_install``:: +``pp`` can be installed with using ``pip`` or ``easy_install``:: $ pip install pprintpp pp-ez - OR - diff --git a/pp/setup.py b/pp/setup.py index 1ec8813..416566f 100644 --- a/pp/setup.py +++ b/pp/setup.py @@ -31,7 +31,6 @@ Natural Language :: English Operating System :: OS Independent Programming Language :: Python - Programming Language :: Python :: 2 Programming Language :: Python :: 3 Topic :: Software Development Topic :: Utilities diff --git a/pprintpp/__init__.py b/pprintpp/__init__.py index 47d67d4..af2b43f 100644 --- a/pprintpp/__init__.py +++ b/pprintpp/__init__.py @@ -12,69 +12,18 @@ "PrettyPrinter", ] +from collections import OrderedDict, defaultdict, Counter +from .safesort import safesort -# -# Py2/Py3 compatibility stuff -# - -try: - from collections import OrderedDict, defaultdict, Counter - _test_has_collections = True -except ImportError: - # Python 2.6 doesn't have collections - class dummy_class(object): - __repr__ = object() - OrderedDict = defaultdict = Counter = dummy_class - _test_has_collections = False - - -PY3 = sys.version_info >= (3, 0, 0) -BytesType = bytes -TextType = str if PY3 else unicode -u_prefix = '' if PY3 else 'u' - - -if PY3: - # Import builins explicitly to keep Py2 static analyzers happy - import builtins - chr_to_ascii = lambda x: builtins.ascii(x)[1:-1] - unichr = chr - from .safesort import safesort - _iteritems = lambda x: x.items() -else: - chr_to_ascii = lambda x: repr(x)[2:-1] - safesort = sorted - _iteritems = lambda x: x.iteritems() - - -def _sorted_py2(iterable): - with warnings.catch_warnings(): - if getattr(sys, "py3kwarning", False): - warnings.filterwarnings("ignore", "comparing unequal types " - "not supported", DeprecationWarning) - return sorted(iterable) -def _sorted_py3(iterable): +chr_to_ascii = lambda x: ascii(x)[1:-1] + +def _sorted(iterable): try: return sorted(iterable) except TypeError: return safesort(iterable) -_sorted = PY3 and _sorted_py3 or _sorted_py3 - -if hasattr(TextType, 'isascii'): # Python>=3.7 - _isascii = TextType.isascii -else: - def _isascii(text): - try: - text.encode('ascii') - except UnicodeEncodeError: - return False - return True - -# -# End compatibility stuff -# class TextIO(io.TextIOWrapper): def __init__(self, encoding=None): @@ -135,7 +84,7 @@ def getvalue(self): } ascii_table = dict( - (unichr(i), chr_to_ascii(unichr(i))) + (chr(i), chr_to_ascii(chr(i))) for i in range(255) ) @@ -240,7 +189,7 @@ def write(self, data): if self.write_constrain < 0: raise self.WriteConstrained - if isinstance(data, BytesType): + if isinstance(data, bytes): data = data.decode("latin1") self.stream.write(data) nl_idx = data.rfind("\n") @@ -386,7 +335,7 @@ def _write_nested_real(self, object, state, typeish, oneline=False): state.write(": ") self._format(v, state) elif typeish == "odict": - for k, v in _iteritems(object): + for k, v in object.items(): if first: first = False else: @@ -463,12 +412,12 @@ def _format(self, object, state): write(closer) return - if r == BytesType.__repr__: + if r == bytes.__repr__: write(repr(object)) return - if r == TextType.__repr__: - if _isascii(object): # Optimalization + if r == str.__repr__: + if str.isascii(object): # Optimalization write(repr(object)) return if "'" in object and '"' not in object: @@ -480,7 +429,7 @@ def _format(self, object, state): qget = quotes.get ascii_table_get = ascii_table.get unicat_get = unicodedata.category - write(u_prefix + quote) + write(quote) for char in object: if ord(char) > 0x7F: cat = unicat_get(char) diff --git a/pprintpp/safesort.py b/pprintpp/safesort.py index a7631db..2f0c9c4 100644 --- a/pprintpp/safesort.py +++ b/pprintpp/safesort.py @@ -1,9 +1,6 @@ -import sys import textwrap import functools -PY3 = (sys.version_info >= (3, 0, 0)) - def memoized_property(f): @functools.wraps(f) def memoized_property_helper(self): @@ -38,9 +35,7 @@ def __init__(self, obj, key=None): @memoized_property def prefix(self): - if PY3: - return tuple(t.__name__ for t in type(self.obj).__mro__) - return type(self.obj).__mro__ + return tuple(t.__name__ for t in type(self.obj).__mro__) @memoized_property def safeobj(self): @@ -69,5 +64,4 @@ def __hash__(self): def safesort(input, key=None, reverse=False): """ Safely sort heterogeneous collections. """ - # TODO: support cmp= on Py 2.x? return sorted(input, key=lambda o: SafelySortable(o, key=key), reverse=reverse) diff --git a/setup.py b/setup.py index feea295..7bf4893 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,6 @@ Natural Language :: English Operating System :: OS Independent Programming Language :: Python - Programming Language :: Python :: 2 Programming Language :: Python :: 3 Topic :: Software Development Topic :: Utilities diff --git a/test.py b/test.py index ef5ddcc..4fb15bc 100644 --- a/test.py +++ b/test.py @@ -41,14 +41,13 @@ def test_module_like(): uni_safe = "\xe9 \u6f02 \u0e4f \u2661" uni_unsafe = "\u200a \u0302 \n" -slashed = lambda s: u"%s'%s'" %( - p.u_prefix, +slashed = lambda s: u"'%s'" %( s.encode("ascii", "backslashreplace").decode("ascii").replace("\n", "\\n") ) @pytest.mark.skip('fix') @pytest.mark.parametrize("input,expected,encoding", [ - (uni_safe, "%s'%s'" %(p.u_prefix, uni_safe), "utf-8"), + (uni_safe, "'%s'" % uni_safe, "utf-8"), (uni_unsafe, slashed(uni_unsafe), "utf-8"), (uni_unsafe, slashed(uni_unsafe), "ascii"), ("\U0002F9B2", slashed("\U0002F9B2"), "ascii") From 203356fb67e2d5a84860808c878e1bd89f221823 Mon Sep 17 00:00:00 2001 From: Joao Coelho Date: Sun, 10 Apr 2022 00:18:34 -0500 Subject: [PATCH 05/10] `isort` and `black` (#11) `isort` and `black` applied. --- .github/workflows/linting.yml | 21 ++ .github/workflows/tests.yml | 4 +- CHANGELOG.txt | 11 +- pp/pp.py | 2 +- pp/setup.py | 10 +- pprintpp/__init__.py | 215 +++++++++++---------- pprintpp/ipython.py | 7 +- pprintpp/safesort.py | 19 +- pyproject.toml | 23 +++ requirements-dev.txt | 2 + test-requires.txt => requirements-test.txt | 0 setup.py | 10 +- test.py => tests/test_pprintpp.py | 62 ++++-- 13 files changed, 237 insertions(+), 149 deletions(-) create mode 100644 .github/workflows/linting.yml create mode 100644 pyproject.toml create mode 100644 requirements-dev.txt rename test-requires.txt => requirements-test.txt (100%) rename test.py => tests/test_pprintpp.py (81%) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml new file mode 100644 index 0000000..29d31dd --- /dev/null +++ b/.github/workflows/linting.yml @@ -0,0 +1,21 @@ +name: linting +# Linting tools use `pyproject.toml` and `setup.cfg` for config. + +on: + - pull_request + +jobs: + linting: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: "3.7" + - name: Install requirements + run: pip install -r requirements-dev.txt + - name: isort + run: isort . + - name: black + run: black . diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index efb7fe8..1174605 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,7 +20,7 @@ jobs: - name: Install requirements run: | python -m pip install -U pip - python -m pip install -r test-requires.txt + python -m pip install -r requirements-test.txt - name: Run tests run: | - pytest test.py + python -m pytest . diff --git a/CHANGELOG.txt b/CHANGELOG.txt index cee1e82..88e3b32 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,9 +1,12 @@ 0.5.0 (unreleased) - * Update some documentation to point to `pprintpp2` instead. - * Use GitHub Actions instead of Travis. - * Use `pytest` instead of `nose` (thanks @pgajdos). - * Add `sort_dict` option (thanks @gergelyk). + * Update documentation to point to `pprintpp2` instead. * Remove support for older Python versions (3.7+ from now on). + * Add `sort_dict` option (thanks @gergelyk). + * Use `pytest` instead of `nose` (thanks @pgajdos). + * Use GitHub Actions instead of Travis. + * Linting (isort, black, etc.). + Wrapping at 100 lines from the previous 80. + * Other file and folder restructuring. 0.4.0 * Add IPython plugin (thanks @roee30; diff --git a/pp/pp.py b/pp/pp.py index df25941..76859da 100644 --- a/pp/pp.py +++ b/pp/pp.py @@ -31,7 +31,7 @@ def fmt(self, *args, **kwargs): return self.pformat(*args, **kwargs) def __repr__(self): - return "" %( + return "" % ( self.__name__, self.pprint_mod.__name__, ) diff --git a/pp/setup.py b/pp/setup.py index 416566f..ba26dcf 100644 --- a/pp/setup.py +++ b/pp/setup.py @@ -23,7 +23,9 @@ py_modules=["pp"], install_requires=[], license="BSD", - classifiers=[ x.strip() for x in """ + classifiers=[ + x.strip() + for x in """ Development Status :: 3 - Alpha Environment :: Console Intended Audience :: Developers @@ -34,5 +36,9 @@ Programming Language :: Python :: 3 Topic :: Software Development Topic :: Utilities - """.split("\n") if x.strip() ], + """.split( + "\n" + ) + if x.strip() + ], ) diff --git a/pprintpp/__init__.py b/pprintpp/__init__.py index af2b43f..af12554 100644 --- a/pprintpp/__init__.py +++ b/pprintpp/__init__.py @@ -1,23 +1,21 @@ from __future__ import print_function +import ast import io import os -import ast import sys -import warnings import unicodedata +import warnings +from collections import Counter, OrderedDict, defaultdict -__all__ = [ - "pprint", "pformat", "isreadable", "isrecursive", "saferepr", - "PrettyPrinter", -] - -from collections import OrderedDict, defaultdict, Counter from .safesort import safesort +__all__ = ["pprint", "pformat", "isreadable", "isrecursive", "saferepr", "PrettyPrinter"] + chr_to_ascii = lambda x: ascii(x)[1:-1] + def _sorted(iterable): try: return sorted(iterable) @@ -42,6 +40,7 @@ def getvalue(self): # ambiguous are repr'd, others will be printed. I made this table mostly by # hand, mostly guessing, so please file bugs. # Source: http://www.unicode.org/reports/tr44/#GC_Values_Table +# fmt: off unicode_printable_categories = { "Lu": 1, # Uppercase_Letter an uppercase letter "Ll": 1, # Lowercase_Letter a lowercase letter @@ -82,37 +81,41 @@ def getvalue(self): "Cn": 0, # Unassigned a reserved unassigned code point or a noncharacter "C": 0, # Other Cc | Cf | Cs | Co | Cn } +# fmt: on + +ascii_table = dict((chr(i), chr_to_ascii(chr(i))) for i in range(255)) -ascii_table = dict( - (chr(i), chr_to_ascii(chr(i))) - for i in range(255) -) -def pprint(object, stream=None, indent=4, width=80, depth=None, - sort_dicts=True): +def pprint(object, stream=None, indent=4, width=80, depth=None, sort_dicts=True): """Pretty-print a Python object to a stream [default is sys.stdout].""" printer = PrettyPrinter( - stream=stream, indent=indent, width=width, depth=depth, - sort_dicts=sort_dicts) + stream=stream, indent=indent, width=width, depth=depth, sort_dicts=sort_dicts + ) printer.pprint(object) + def pformat(object, indent=4, width=80, depth=None, sort_dicts=True): """Format a Python object into a pretty-printed representation.""" - return PrettyPrinter(indent=indent, width=width, depth=depth, - sort_dicts=sort_dicts).pformat(object) + return PrettyPrinter(indent=indent, width=width, depth=depth, sort_dicts=sort_dicts).pformat( + object + ) + def saferepr(object): """Version of repr() which can handle recursive data structures.""" return PrettyPrinter().pformat(object) + def isreadable(object): """Determine if saferepr(object) is readable by eval().""" return PrettyPrinter().isreadable(object) + def isrecursive(object): """Determine if object requires a recursive representation.""" return PrettyPrinter().isrecursive(object) + def console(argv=None): if argv is None: argv = sys.argv @@ -120,22 +123,24 @@ def console(argv=None): name = argv[0] if name.startswith("/"): name = os.path.basename(name) - print("Usage: %s" %(argv[0], )) + print("Usage: %s" % (argv[0],)) print() - print("Pipe Python literals into %s to pretty-print them" %(argv[0], )) + print("Pipe Python literals into %s to pretty-print them" % (argv[0],)) return 1 obj = ast.literal_eval(sys.stdin.read().strip()) pprint(obj) return 0 + def monkeypatch(mod=None, quiet=False): if "pprint" in sys.modules and not quiet: - warnings.warn("'pprint' has already been imported; monkeypatching " - "won't work everywhere.") + warnings.warn("'pprint' has already been imported; monkeypatching won't work everywhere.") import pprint + sys.modules["pprint_original"] = pprint sys.modules["pprint"] = mod or sys.modules["pprintpp"] + class PPrintSharedState(object): recursive = False readable = True @@ -201,30 +206,34 @@ def write(self, data): def get_indent_string(self): return (self.level * self.indent) * " " + def _mk_open_close_empty_dict(type_tuples): - """ Generates a dictionary mapping either ``cls.__repr__`` xor ``cls`` to - a tuple of ``(container_type, repr_open, repr_close, repr_empty)`` (see - ``PrettyPrinter._open_close_empty`` for examples). + """ + Generates a dictionary mapping either ``cls.__repr__`` xor ``cls`` to + a tuple of ``(container_type, repr_open, repr_close, repr_empty)`` (see + ``PrettyPrinter._open_close_empty`` for examples). - Using either ``cls.__repr__`` xor ``cls`` is important because some - types (specifically, ``set`` and ``frozenset`` on PyPy) share a - ``__repr__``. When we are determining how to repr an object, the type - is first checked, then if it's not found ``type.__repr__`` is checked. + Using either ``cls.__repr__`` xor ``cls`` is important because some + types (specifically, ``set`` and ``frozenset`` on PyPy) share a + ``__repr__``. When we are determining how to repr an object, the type + is first checked, then if it's not found ``type.__repr__`` is checked. - Note that ``__repr__`` is used so that trivial subclasses will behave - sensibly. """ + Note that ``__repr__`` is used so that trivial subclasses will behave + sensibly.""" res = {} for (cls, open_close_empty) in type_tuples: if cls.__repr__ in res: - res[cls] = (cls, ) + open_close_empty + res[cls] = (cls,) + open_close_empty else: - res[cls.__repr__] = (cls, ) + open_close_empty + res[cls.__repr__] = (cls,) + open_close_empty return res + class PrettyPrinter(object): def __init__(self, indent=4, width=80, depth=None, stream=None, sort_dicts=True): - """Handle pretty printing operations onto a stream using a set of + """ + Handle pretty printing operations onto a stream using a set of configured parameters. indent @@ -249,7 +258,7 @@ def __init__(self, indent=4, width=80, depth=None, stream=None, sort_dicts=True) self.get_default_state = lambda: PPrintState( indent=int(indent), max_width=int(width), - sort_dicts = sort_dicts, + sort_dicts=sort_dicts, stream=stream or sys.stdout, context={}, ) @@ -277,16 +286,18 @@ def isreadable(self, object): self._format(object, state) return state.s.readable and not state.s.recursive - _open_close_empty = _mk_open_close_empty_dict([ - (dict, ("dict", "{", "}", "{}")), - (list, ("list", "[", "]", "[]")), - (tuple, ("tuple", "(", ")", "()")), - (set, ("set", "__PP_TYPE__([", "])", "__PP_TYPE__()")), - (frozenset, ("set", "__PP_TYPE__([", "])", "__PP_TYPE__()")), - (Counter, ("dict", "__PP_TYPE__({", "})", "__PP_TYPE__()")), - (defaultdict, ("dict", None, "})", None)), - (OrderedDict, ("odict", "__PP_TYPE__([", "])", "__PP_TYPE__()")), - ]) + _open_close_empty = _mk_open_close_empty_dict( + [ + (dict, ("dict", "{", "}", "{}")), + (list, ("list", "[", "]", "[]")), + (tuple, ("tuple", "(", ")", "()")), + (set, ("set", "__PP_TYPE__([", "])", "__PP_TYPE__()")), + (frozenset, ("set", "__PP_TYPE__([", "])", "__PP_TYPE__()")), + (Counter, ("dict", "__PP_TYPE__({", "})", "__PP_TYPE__()")), + (defaultdict, ("dict", None, "})", None)), + (OrderedDict, ("odict", "__PP_TYPE__([", "])", "__PP_TYPE__()")), + ] + ) def _format_nested_objects(self, object, state, typeish=None): objid = id(object) @@ -297,12 +308,9 @@ def _format_nested_objects(self, object, state, typeish=None): # that it takes three characters to close the object (ex, `]),`) oneline_state = state.clone(clone_shared=True) oneline_state.stream = TextIO() - oneline_state.write_constrain = ( - state.max_width - state.s.cur_line_length - 3 - ) + oneline_state.write_constrain = state.max_width - state.s.cur_line_length - 3 try: - self._write_nested_real(object, oneline_state, typeish, - oneline=True) + self._write_nested_real(object, oneline_state, typeish, oneline=True) oneline_value = oneline_state.stream.getvalue() if "\n" in oneline_value: oneline_value = None @@ -375,10 +383,7 @@ def _format(self, object, state): # Note: see comments on _mk_open_close_empty_dict for the rational # behind looking up based first on type then on __repr__. try: - opener_closer_empty = ( - self._open_close_empty.get(typ) or - self._open_close_empty.get(r) - ) + opener_closer_empty = self._open_close_empty.get(typ) or self._open_close_empty.get(r) except TypeError: # This will happen if the type or the __repr__ is unhashable. # See: https://github.com/wolever/pprintpp/issues/18 @@ -391,11 +396,11 @@ def _format(self, object, state): opener = "__PP_TYPE__(" + opener closer = closer + ")" if empty is not None and "__PP_TYPE__" not in empty: - empty = "__PP_TYPE__(%s)" %(empty, ) + empty = "__PP_TYPE__(%s)" % (empty,) if r == defaultdict.__repr__: factory_repr = object.default_factory - opener = "__PP_TYPE__(%r, {" %(factory_repr, ) + opener = "__PP_TYPE__(%r, {" % (factory_repr,) empty = opener + closer length = len(object) @@ -439,26 +444,18 @@ def _format(self, object, state): continue except UnicodeEncodeError: pass - write( - qget(char) or - ascii_table_get(char) or - chr_to_ascii(char) - ) + write(qget(char) or ascii_table_get(char) or chr_to_ascii(char)) write(quote) return orepr = repr(object) orepr = orepr.replace("\n", "\n" + state.get_indent_string()) - state.s.readable = ( - state.s.readable and - not orepr.startswith("<") - ) + state.s.readable = state.s.readable and not orepr.startswith("<") write(orepr) return def _repr(self, object, context, level): - repr, readable, recursive = self.format(object, context.copy(), - self._depth, level) + repr, readable, recursive = self.format(object, context.copy(), self._depth, level) if not readable: self._readable = False if recursive: @@ -476,59 +473,63 @@ def format(self, object, context, maxlevels, level): def _recursion(self, object, state): state.s.recursive = True - return ("" - % (type(object).__name__, id(object))) + return "" % (type(object).__name__, id(object)) if __name__ == "__main__": try: import numpy as np except ImportError: + class np(object): @staticmethod def array(o): return o - somelist = [1,2,3] + somelist = [1, 2, 3] recursive = [] recursive.extend([recursive, recursive, recursive]) - pprint({ - "a": {"a": "b"}, - "b": [somelist, somelist], - "c": [ - (1, ), - (1,2,3), - ], - "ordereddict": OrderedDict([ - (1, 1), - (10, 10), - (2, 2), - (11, 11) - ]), - "counter": [ - Counter(), - Counter("asdfasdfasdf"), - ], - "dd": [ - defaultdict(int, {}), - defaultdict(int, {"foo": 42}), - ], - "frozenset": frozenset("abc"), - "np": [ - "hello", - #np.array([[1,2],[3,4]]), - "world", - ], - u"u": ["a", u"\u1234", "b"], - "recursive": recursive, - "z": { - "very very very long key stuff 1234": { - "much value": "very nest! " * 10, - u"unicode": u"4U!'\"", + pprint( + { + "a": {"a": "b"}, + "b": [somelist, somelist], + "c": [ + (1,), + (1, 2, 3), + ], + "ordereddict": OrderedDict( + [ + (1, 1), + (10, 10), + (2, 2), + (11, 11), + ] + ), + "counter": [ + Counter(), + Counter("asdfasdfasdf"), + ], + "dd": [ + defaultdict(int, {}), + defaultdict(int, {"foo": 42}), + ], + "frozenset": frozenset("abc"), + "np": [ + "hello", + # np.array([[1,2],[3,4]]), + "world", + ], + u"u": ["a", u"\u1234", "b"], + "recursive": recursive, + "z": { + "very very very long key stuff 1234": { + "much value": "very nest! " * 10, + u"unicode": u"4U!'\"", + }, + "aldksfj alskfj askfjas fkjasdlkf jasdlkf ajslfjas": ["asdf"] * 10, }, - "aldksfj alskfj askfjas fkjasdlkf jasdlkf ajslfjas": ["asdf"] * 10, - }, - }) + } + ) pprint(u"\xe9e\u0301") uni_safe = u"\xe9 \u6f02 \u0e4f \u2661" uni_unsafe = u"\u200a \u0301 \n" @@ -542,9 +543,11 @@ def array(o): def load_ipython_extension(ipython): from .ipython import load_ipython_extension + return load_ipython_extension(ipython) def unload_ipython_extension(ipython): from .ipython import unload_ipython_extension + return unload_ipython_extension(ipython) diff --git a/pprintpp/ipython.py b/pprintpp/ipython.py index 66f8477..fff3d77 100644 --- a/pprintpp/ipython.py +++ b/pprintpp/ipython.py @@ -9,12 +9,11 @@ https://stackoverflow.com/users/1530134/kupiakos """ import IPython -from traitlets.config import Configurable from traitlets import Int +from traitlets.config import Configurable from . import pformat - original_representation = IPython.lib.pretty.RepresentationPrinter DEFAULT_INDENTATION = 2 @@ -30,7 +29,8 @@ def unload_ipython_extension(ipython): IPython.lib.pretty.RepresentationPrinter = original_representation try: pprintpp = [ - configurable for configurable in ipython.configurables + configurable + for configurable in ipython.configurables if isinstance(configurable, PPrintPP) ][0] except IndexError: @@ -60,4 +60,5 @@ class PPrintPP(Configurable): """ PPrintPP configuration """ + indentation = Int(config=True) diff --git a/pprintpp/safesort.py b/pprintpp/safesort.py index 2f0c9c4..51aba99 100644 --- a/pprintpp/safesort.py +++ b/pprintpp/safesort.py @@ -1,5 +1,6 @@ -import textwrap import functools +import textwrap + def memoized_property(f): @functools.wraps(f) @@ -7,10 +8,13 @@ def memoized_property_helper(self): val = f(self) self.__dict__[f.__name__] = val return val + return property(memoized_property_helper) + def _build_safe_cmp_func(name, cmp, prefix=""): - code = textwrap.dedent("""\ + code = textwrap.dedent( + """\ def {name}(self, other): try: return {prefix}(self.obj {cmp} other.obj) @@ -21,17 +25,16 @@ def {name}(self, other): except TypeError: pass return {prefix}(self.verysafeobj {cmp} other.verysafeobj) - """).format(name=name, cmp=cmp, prefix=prefix) + """ + ).format(name=name, cmp=cmp, prefix=prefix) gs = ls = {} exec(code, gs, ls) return gs[name] + class SafelySortable(object): def __init__(self, obj, key=None): - self.obj = ( - obj if key is None else - key(obj) - ) + self.obj = obj if key is None else key(obj) @memoized_property def prefix(self): @@ -63,5 +66,5 @@ def __hash__(self): def safesort(input, key=None, reverse=False): - """ Safely sort heterogeneous collections. """ + """Safely sort heterogeneous collections.""" return sorted(input, key=lambda o: SafelySortable(o, key=key), reverse=reverse) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8d3ab34 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,23 @@ +[tool.bandit] + +[tool.black] +line-length = 100 +target-version = ['py37'] +skip-string-normalization = true + +[tool.isort] +known_first_party = 'pprintpp' +profile = 'black' +sections = 'FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER' + +[tool.pytest.ini_options] +testpaths = ['tests'] + +[tool.mypy] +python_version = '3.7' +warn_return_any = true +warn_unused_configs = true + +[[tool.mypy.overrides]] +module = ['invoke', 'pytest_check'] +ignore_missing_imports = true diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..d33adb1 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,2 @@ +black==22.3.0 +isort==5.10.1 diff --git a/test-requires.txt b/requirements-test.txt similarity index 100% rename from test-requires.txt rename to requirements-test.txt diff --git a/setup.py b/setup.py index 7bf4893..75dc54c 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,9 @@ }, install_requires=[], license="BSD", - classifiers=[ x.strip() for x in """ + classifiers=[ + x.strip() + for x in """ Development Status :: 3 - Alpha Environment :: Console Intended Audience :: Developers @@ -39,5 +41,9 @@ Programming Language :: Python :: 3 Topic :: Software Development Topic :: Utilities - """.split("\n") if x.strip() ], + """.split( + "\n" + ) + if x.strip() + ], ) diff --git a/test.py b/tests/test_pprintpp.py similarity index 81% rename from test.py rename to tests/test_pprintpp.py index 4fb15bc..68d53e9 100644 --- a/test.py +++ b/tests/test_pprintpp.py @@ -1,14 +1,16 @@ from __future__ import print_function +import io import sys from contextlib import redirect_stdout -import io + import pytest sys.path.append("pp/") import pp import pprintpp as p -from pprintpp import Counter, defaultdict, OrderedDict +from pprintpp import Counter, OrderedDict, defaultdict + def test_pp(): expected = "['hello', 'world']" @@ -18,6 +20,7 @@ def test_pp(): actual = f.getvalue().rstrip("\n") assert actual == expected + def test_pp_print(): expected = "'stuff'" f = io.StringIO() @@ -26,6 +29,7 @@ def test_pp_print(): actual = f.getvalue().rstrip("\n") assert actual == expected + def test_fmt(): expected = "'asdf'\n'stuff'" f = io.StringIO() @@ -35,28 +39,35 @@ def test_fmt(): actual = f.getvalue().rstrip("\n") assert actual == expected + def test_module_like(): - print(dir(pp)) - print(repr(pp)) + print(dir(pp)) + print(repr(pp)) + uni_safe = "\xe9 \u6f02 \u0e4f \u2661" uni_unsafe = "\u200a \u0302 \n" -slashed = lambda s: u"'%s'" %( +slashed = lambda s: u"'%s'" % ( s.encode("ascii", "backslashreplace").decode("ascii").replace("\n", "\\n") ) + @pytest.mark.skip('fix') -@pytest.mark.parametrize("input,expected,encoding", [ - (uni_safe, "'%s'" % uni_safe, "utf-8"), - (uni_unsafe, slashed(uni_unsafe), "utf-8"), - (uni_unsafe, slashed(uni_unsafe), "ascii"), - ("\U0002F9B2", slashed("\U0002F9B2"), "ascii") -]) +@pytest.mark.parametrize( + "input,expected,encoding", + [ + (uni_safe, "'%s'" % uni_safe, "utf-8"), + (uni_unsafe, slashed(uni_unsafe), "utf-8"), + (uni_unsafe, slashed(uni_unsafe), "ascii"), + ("\U0002F9B2", slashed("\U0002F9B2"), "ascii"), + ], +) def test_unicode(input, expected, encoding): stream = p.TextIO(encoding=encoding) p.pprint(input, stream=stream) assert stream.getvalue().rstrip("\n") == expected + test_back_and_forth_data = [ "'\\'\"'", '"\'"', @@ -94,34 +105,44 @@ def test_unicode(input, expected, encoding): "MyCounterWithRepr('dummy')", ] + class MyDict(dict): pass + class MyList(list): pass + class MyTuple(tuple): pass + class MySet(set): pass + class MyFrozenSet(frozenset): pass + class MyOrderedDict(p.OrderedDict): pass + class MyDefaultDict(p.defaultdict): pass + class MyCounter(p.Counter): pass + class MyCounterWithRepr(p.Counter): def __repr__(self): return "MyCounterWithRepr('dummy')" + @pytest.mark.skip('fix') @pytest.mark.parametrize("expected", test_back_and_forth_data) def test_back_and_forth(expected): @@ -130,15 +151,17 @@ def test_back_and_forth(expected): p.pprint(input, stream=stream) assert stream.getvalue().rstrip("\n") == expected -test_expected_input_data = [ - ("defaultdict(%r, {})" %(int, ), defaultdict(int)), - ("defaultdict(%r, {1: 1})" %(int, ), defaultdict(int, [(1, 1)])), - ("MyDefaultDict(%r, {})" %(int, ), MyDefaultDict(int)), - ("MyDefaultDict(%r, {1: 1})" %(int, ), MyDefaultDict(int, [(1, 1)])), -] @pytest.mark.skip('fix') -@pytest.mark.parametrize("expected,input", test_expected_input_data) +@pytest.mark.parametrize( + "expected,input", + [ + ("defaultdict(%r, {})" % (int,), defaultdict(int)), + ("defaultdict(%r, {1: 1})" % (int,), defaultdict(int, [(1, 1)])), + ("MyDefaultDict(%r, {})" % (int,), MyDefaultDict(int)), + ("MyDefaultDict(%r, {1: 1})" % (int,), MyDefaultDict(int, [(1, 1)])), + ], +) def test_expected_input(expected, input): stream = p.TextIO() p.pprint(input, stream=stream) @@ -164,6 +187,3 @@ class MyCls(object): obj = MyCls() assert p.pformat(obj) == "some-repr" - - - From 78c31b4ba6a17ffcf9558609dc4af63d93633699 Mon Sep 17 00:00:00 2001 From: Joao Coelho Date: Sun, 10 Apr 2022 09:24:04 -0500 Subject: [PATCH 06/10] `flake8` (#12) --- .github/workflows/linting.yml | 2 + CHANGELOG.txt | 2 +- pprintpp/__init__.py | 79 ++++++++++++++++++----------------- requirements-dev.txt | 1 + setup.cfg | 4 ++ tests/test_pprintpp.py | 13 +++--- 6 files changed, 55 insertions(+), 46 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 29d31dd..c731efe 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -19,3 +19,5 @@ jobs: run: isort . - name: black run: black . + - name: flake8 + run: flake8 . diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 88e3b32..b38c165 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -4,7 +4,7 @@ * Add `sort_dict` option (thanks @gergelyk). * Use `pytest` instead of `nose` (thanks @pgajdos). * Use GitHub Actions instead of Travis. - * Linting (isort, black, etc.). + * Linting: `isort`, `black`, `flake8`. Wrapping at 100 lines from the previous 80. * Other file and folder restructuring. diff --git a/pprintpp/__init__.py b/pprintpp/__init__.py index af12554..c9e078e 100644 --- a/pprintpp/__init__.py +++ b/pprintpp/__init__.py @@ -13,7 +13,8 @@ __all__ = ["pprint", "pformat", "isreadable", "isrecursive", "saferepr", "PrettyPrinter"] -chr_to_ascii = lambda x: ascii(x)[1:-1] +def chr_to_ascii(c): + return ascii(c)[1:-1] def _sorted(iterable): @@ -42,44 +43,44 @@ def getvalue(self): # Source: http://www.unicode.org/reports/tr44/#GC_Values_Table # fmt: off unicode_printable_categories = { - "Lu": 1, # Uppercase_Letter an uppercase letter - "Ll": 1, # Lowercase_Letter a lowercase letter - "Lt": 1, # Titlecase_Letter a digraphic character, with first part uppercase - "LC": 1, # Cased_Letter Lu | Ll | Lt - "Lm": 0, # Modifier_Letter a modifier letter - "Lo": 1, # Other_Letter other letters, including syllables and ideographs - "L": 1, # Letter Lu | Ll | Lt | Lm | Lo - "Mn": 0, # Nonspacing_Mark a nonspacing combining mark (zero advance width) - "Mc": 0, # Spacing_Mark a spacing combining mark (positive advance width) - "Me": 0, # Enclosing_Mark an enclosing combining mark - "M": 1, # Mark Mn | Mc | Me - "Nd": 1, # Decimal_Number a decimal digit - "Nl": 1, # Letter_Number a letterlike numeric character - "No": 1, # Other_Number a numeric character of other type - "N": 1, # Number Nd | Nl | No - "Pc": 1, # Connector_Punctuation a connecting punctuation mark, like a tie - "Pd": 1, # Dash_Punctuation a dash or hyphen punctuation mark - "Ps": 1, # Open_Punctuation an opening punctuation mark (of a pair) - "Pe": 1, # Close_Punctuation a closing punctuation mark (of a pair) - "Pi": 1, # Initial_Punctuation an initial quotation mark - "Pf": 1, # Final_Punctuation a final quotation mark - "Po": 1, # Other_Punctuation a punctuation mark of other type - "P": 1, # Punctuation Pc | Pd | Ps | Pe | Pi | Pf | Po - "Sm": 1, # Math_Symbol a symbol of mathematical use - "Sc": 1, # Currency_Symbol a currency sign - "Sk": 1, # Modifier_Symbol a non-letterlike modifier symbol - "So": 1, # Other_Symbol a symbol of other type - "S": 1, # Symbol Sm | Sc | Sk | So - "Zs": 0, # Space_Separator a space character (of various non-zero widths) - "Zl": 0, # Line_Separator U+2028 LINE SEPARATOR only - "Zp": 0, # Paragraph_Separator U+2029 PARAGRAPH SEPARATOR only - "Z": 1, # Separator Zs | Zl | Zp - "Cc": 0, # Control a C0 or C1 control code - "Cf": 0, # Format a format control character - "Cs": 0, # Surrogate a surrogate code point - "Co": 0, # Private_Use a private-use character - "Cn": 0, # Unassigned a reserved unassigned code point or a noncharacter - "C": 0, # Other Cc | Cf | Cs | Co | Cn + "Lu": 1, # Uppercase_Letter an uppercase letter + "Ll": 1, # Lowercase_Letter a lowercase letter + "Lt": 1, # Titlecase_Letter a digraphic character, with first part uppercase + "LC": 1, # Cased_Letter Lu | Ll | Lt + "Lm": 0, # Modifier_Letter a modifier letter + "Lo": 1, # Other_Letter other letters, including syllables and ideographs + "L": 1, # Letter Lu | Ll | Lt | Lm | Lo + "Mn": 0, # Nonspacing_Mark a nonspacing combining mark (zero advance width) + "Mc": 0, # Spacing_Mark a spacing combining mark (positive advance width) + "Me": 0, # Enclosing_Mark an enclosing combining mark + "M": 1, # Mark Mn | Mc | Me + "Nd": 1, # Decimal_Number a decimal digit + "Nl": 1, # Letter_Number a letterlike numeric character + "No": 1, # Other_Number a numeric character of other type + "N": 1, # Number Nd | Nl | No + "Pc": 1, # Connector_Punctuation a connecting punctuation mark, like a tie + "Pd": 1, # Dash_Punctuation a dash or hyphen punctuation mark + "Ps": 1, # Open_Punctuation an opening punctuation mark (of a pair) + "Pe": 1, # Close_Punctuation a closing punctuation mark (of a pair) + "Pi": 1, # Initial_Punctuation an initial quotation mark + "Pf": 1, # Final_Punctuation a final quotation mark + "Po": 1, # Other_Punctuation a punctuation mark of other type + "P": 1, # Punctuation Pc | Pd | Ps | Pe | Pi | Pf | Po + "Sm": 1, # Math_Symbol a symbol of mathematical use + "Sc": 1, # Currency_Symbol a currency sign + "Sk": 1, # Modifier_Symbol a non-letterlike modifier symbol + "So": 1, # Other_Symbol a symbol of other type + "S": 1, # Symbol Sm | Sc | Sk | So + "Zs": 0, # Space_Separator a space character (of various non-zero widths) + "Zl": 0, # Line_Separator U+2028 LINE SEPARATOR only + "Zp": 0, # Paragraph_Separator U+2029 PARAGRAPH SEPARATOR only + "Z": 1, # Separator Zs | Zl | Zp + "Cc": 0, # Control a C0 or C1 control code + "Cf": 0, # Format a format control character + "Cs": 0, # Surrogate a surrogate code point + "Co": 0, # Private_Use a private-use character + "Cn": 0, # Unassigned a reserved unassigned code point or a noncharacter + "C": 0, # Other Cc | Cf | Cs | Co | Cn } # fmt: on diff --git a/requirements-dev.txt b/requirements-dev.txt index d33adb1..ce1e046 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,2 +1,3 @@ black==22.3.0 +flake8==4.0.1 isort==5.10.1 diff --git a/setup.cfg b/setup.cfg index 2a9acf1..72b9ed6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,6 @@ [bdist_wheel] universal = 1 + +[flake8] +exclude = .git,.github,.venv,__pycache__ +max-line-length = 100 diff --git a/tests/test_pprintpp.py b/tests/test_pprintpp.py index 68d53e9..d0fd303 100644 --- a/tests/test_pprintpp.py +++ b/tests/test_pprintpp.py @@ -5,11 +5,11 @@ from contextlib import redirect_stdout import pytest +import pprintpp as p +from pprintpp import defaultdict sys.path.append("pp/") -import pp -import pprintpp as p -from pprintpp import Counter, OrderedDict, defaultdict +import pp # noqa: E402 module level import not at top of file def test_pp(): @@ -47,9 +47,10 @@ def test_module_like(): uni_safe = "\xe9 \u6f02 \u0e4f \u2661" uni_unsafe = "\u200a \u0302 \n" -slashed = lambda s: u"'%s'" % ( - s.encode("ascii", "backslashreplace").decode("ascii").replace("\n", "\\n") -) + + +def slashed(s): + return "'%s'" % s.encode("ascii", "backslashreplace").decode("ascii").replace("\n", "\\n") @pytest.mark.skip('fix') From c7676c1de719cd2f5c18abf42b40f549db0d93d7 Mon Sep 17 00:00:00 2001 From: Joao Coelho Date: Sun, 10 Apr 2022 12:59:06 -0500 Subject: [PATCH 07/10] `safety`, `bandit` and `mypy` (#13) --- .github/workflows/linting.yml | 6 ++++++ CHANGELOG.txt | 3 ++- pprintpp/__init__.py | 4 ++-- pyproject.toml | 4 +++- requirements-dev.txt | 5 +++++ 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index c731efe..ae621f6 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -21,3 +21,9 @@ jobs: run: black . - name: flake8 run: flake8 . + - name: mypy + run: mypy . + - name: bandit + run: bandit . + - name: safety + run: safety check diff --git a/CHANGELOG.txt b/CHANGELOG.txt index b38c165..dd149ad 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -4,8 +4,9 @@ * Add `sort_dict` option (thanks @gergelyk). * Use `pytest` instead of `nose` (thanks @pgajdos). * Use GitHub Actions instead of Travis. - * Linting: `isort`, `black`, `flake8`. + * Linting: `isort`, `black`, `flake8` and `mypy`. Wrapping at 100 lines from the previous 80. + * Security: `safety` and `bandit`. * Other file and folder restructuring. 0.4.0 diff --git a/pprintpp/__init__.py b/pprintpp/__init__.py index c9e078e..432bd27 100644 --- a/pprintpp/__init__.py +++ b/pprintpp/__init__.py @@ -482,13 +482,13 @@ def _recursion(self, object, state): import numpy as np except ImportError: - class np(object): + class np(object): # type: ignore @staticmethod def array(o): return o somelist = [1, 2, 3] - recursive = [] + recursive = [] # type: ignore # TODO: Add type hinting for mypy recursive.extend([recursive, recursive, recursive]) pprint( { diff --git a/pyproject.toml b/pyproject.toml index 8d3ab34..7aa7c75 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,4 +1,5 @@ [tool.bandit] +recursive = true # TODO: fix [tool.black] line-length = 100 @@ -17,7 +18,8 @@ testpaths = ['tests'] python_version = '3.7' warn_return_any = true warn_unused_configs = true +exclude = ['pp/setup.py'] [[tool.mypy.overrides]] -module = ['invoke', 'pytest_check'] +module = ['IPython', 'numpy', 'traitlets.*'] ignore_missing_imports = true diff --git a/requirements-dev.txt b/requirements-dev.txt index ce1e046..55716fe 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,8 @@ +-r requirements-test.txt +bandit # Always want latest black==22.3.0 flake8==4.0.1 isort==5.10.1 +mypy==0.942 +safety # Always want latest +types-setuptools == 57.4.12 From 3a17e6b56e093fcd0de620f6fcf5f125af6d4a53 Mon Sep 17 00:00:00 2001 From: David Wolever Date: Wed, 13 Apr 2022 22:07:44 -0400 Subject: [PATCH 08/10] Cleanups for merge back to main --- .github/workflows/tests.yml | 2 +- .python-version | 4 ++++ README.rst | 18 ++++++------------ pp/setup.py | 6 +++--- requirements-test.txt | 2 +- setup.py | 6 +++--- tests/test_pprintpp.py | 10 ++++------ tox.ini | 8 ++++++++ 8 files changed, 30 insertions(+), 26 deletions(-) create mode 100644 .python-version create mode 100644 tox.ini diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1174605..4ec498a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.7', '3.8', 'pypy-3.7'] + python-version: ['3.7', '3.8', '3.9', '3.10', 'pypy-3.7'] name: Python ${{ matrix.python-version }} steps: - uses: actions/checkout@v3 diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..68dd488 --- /dev/null +++ b/.python-version @@ -0,0 +1,4 @@ +3.7.12 +3.8.12 +3.9.10 +3.10.2 diff --git a/README.rst b/README.rst index b5a476e..cd3c1a1 100644 --- a/README.rst +++ b/README.rst @@ -1,12 +1,6 @@ ``pprint++``: a drop-in replacement for ``pprint`` that's actually pretty ========================================================================= -This is a continuation of `pprintpp `_, -by `David Wolever `_, which seems to have been -discontinued by the original writer. Currently, under development still. - -Python 3.7 and above. - Installation ------------ @@ -42,14 +36,14 @@ Usage 3. As an `ipython `_ extension:: In [1]: %load_ext pprintpp - + This will use pprintpp for ipython's output. - + To load this extension when ipython starts, put the previous line in your `startup file `_. - + You can change the indentation level like so:: - - In [2]: %config PPrintPP.indentation = 4 + + In [2]: %config PPrintPP.indentation = 4 4. To monkeypatch ``pprint``:: @@ -218,7 +212,7 @@ Without ``printpp``:: >>> pprint.pprint(["Hello", np.array([[1,2],[3,4]])]) ['Hello', array([[1, 2], [3, 4]])] - >>> tweet = {'coordinates': None, 'created_at': 'Mon Jun 27 19:32:19 +0000 2011', 'entities': {'hashtags': [], 'urls': [{'display_url': 'tumblr.com/xnr37hf0yz', 'expanded_url': 'http://tumblr.com/xnr37hf0yz', 'indices': [107, 126], 'url': 'http://t.co/cCIWIwg'}], 'user_mentions': []}, 'place': None, 'source': 'Tumblr', 'truncated': False, 'user': {'contributors_enabled': True, 'default_profile': False, 'entities': {'hashtags': [], 'urls': [], 'user_mentions': []}, 'favourites_count': 20, 'id_str': '6253282', 'profile_link_color': '0094C2'}} + >>> tweet = {'coordinates': None, 'created_at': 'Mon Jun 27 19:32:19 +0000 2011', 'entities': {'hashtags': [], 'urls': [{'display_url': 'tumblr.com/xnr37hf0yz', 'expanded_url': 'http://tumblr.com/xnr37hf0yz', 'indices': [107, 126], 'url': 'http://t.co/cCIWIwg'}], 'user_mentions': []}, 'place': None, 'source': 'Tumblr', 'truncated': False, 'user': {'contributors_enabled': True, 'default_profile': False, 'entities': {'hashtags': [], 'urls': [], 'user_mentions': []}, 'favourites_count': 20, 'id_str': '6253282', 'profile_link_color': '0094C2'}} >>> pprint.pprint(tweet) {'coordinates': None, 'created_at': 'Mon Jun 27 19:32:19 +0000 2011', diff --git a/pp/setup.py b/pp/setup.py index ba26dcf..95a3e46 100644 --- a/pp/setup.py +++ b/pp/setup.py @@ -10,12 +10,12 @@ try: long_description = open("README.rst", "U").read() except IOError: - long_description = "See https://github.com/joaonc/pprintpp2" + long_description = "See https://github.com/wolever/pprintpp" setup( name="pp-ez", - version="0.2.0", - url="https://github.com/joaonc/pprintpp2", + version="0.3.0", + url="https://github.com/wolever/pprintpp", author="David Wolever", author_email="david@wolever.net", description="A short alias for the pprintpp or pprint module", diff --git a/requirements-test.txt b/requirements-test.txt index 4f6bf64..49435c9 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1 +1 @@ -pytest==7.1.1 +pytest==7.1.1 \ No newline at end of file diff --git a/setup.py b/setup.py index 75dc54c..7a1c5d0 100644 --- a/setup.py +++ b/setup.py @@ -10,12 +10,12 @@ try: long_description = open("README.rst", "U").read() except IOError: - long_description = "See https://github.com/joaonc/pprintpp2" + long_description = "See https://github.com/wolever/pprintpp" setup( name="pprintpp", - version="0.4.0", - url="https://github.com/joaonc/pprintpp2", + version="0.5.0", + url="https://github.com/wolever/pprintpp", author="David Wolever", author_email="david@wolever.net", description="A drop-in replacement for pprint that's actually pretty", diff --git a/tests/test_pprintpp.py b/tests/test_pprintpp.py index d0fd303..48f3719 100644 --- a/tests/test_pprintpp.py +++ b/tests/test_pprintpp.py @@ -3,6 +3,7 @@ import io import sys from contextlib import redirect_stdout +from collections import Counter, defaultdict, OrderedDict import pytest import pprintpp as p @@ -53,7 +54,6 @@ def slashed(s): return "'%s'" % s.encode("ascii", "backslashreplace").decode("ascii").replace("\n", "\\n") -@pytest.mark.skip('fix') @pytest.mark.parametrize( "input,expected,encoding", [ @@ -127,15 +127,15 @@ class MyFrozenSet(frozenset): pass -class MyOrderedDict(p.OrderedDict): +class MyOrderedDict(OrderedDict): pass -class MyDefaultDict(p.defaultdict): +class MyDefaultDict(defaultdict): pass -class MyCounter(p.Counter): +class MyCounter(Counter): pass @@ -144,7 +144,6 @@ def __repr__(self): return "MyCounterWithRepr('dummy')" -@pytest.mark.skip('fix') @pytest.mark.parametrize("expected", test_back_and_forth_data) def test_back_and_forth(expected): input = eval(expected) @@ -153,7 +152,6 @@ def test_back_and_forth(expected): assert stream.getvalue().rstrip("\n") == expected -@pytest.mark.skip('fix') @pytest.mark.parametrize( "expected,input", [ diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..fac37d7 --- /dev/null +++ b/tox.ini @@ -0,0 +1,8 @@ +[tox] +envlist = py37,py38,py39,py310,pypy + +[testenv] +deps = + -rrequirements-test.txt +commands = + python -m pytest . \ No newline at end of file From 7104642d299d9ca61f7c054719cd7666840444bc Mon Sep 17 00:00:00 2001 From: David Wolever Date: Wed, 13 Apr 2022 22:37:48 -0400 Subject: [PATCH 09/10] Clean up formatting --- CHANGELOG.txt | 4 --- pp/setup.py | 21 ++++++------ pprintpp/__init__.py | 76 +++++++++++++++++++++--------------------- pprintpp/safesort.py | 6 ++-- pyproject.toml | 4 +++ setup.py | 21 ++++++------ tests/test_pprintpp.py | 1 - 7 files changed, 65 insertions(+), 68 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index dd149ad..2406b51 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,13 +1,9 @@ 0.5.0 (unreleased) - * Update documentation to point to `pprintpp2` instead. * Remove support for older Python versions (3.7+ from now on). * Add `sort_dict` option (thanks @gergelyk). * Use `pytest` instead of `nose` (thanks @pgajdos). * Use GitHub Actions instead of Travis. - * Linting: `isort`, `black`, `flake8` and `mypy`. - Wrapping at 100 lines from the previous 80. * Security: `safety` and `bandit`. - * Other file and folder restructuring. 0.4.0 * Add IPython plugin (thanks @roee30; diff --git a/pp/setup.py b/pp/setup.py index 95a3e46..06e5fb8 100644 --- a/pp/setup.py +++ b/pp/setup.py @@ -26,17 +26,16 @@ classifiers=[ x.strip() for x in """ - Development Status :: 3 - Alpha - Environment :: Console - Intended Audience :: Developers - License :: OSI Approved :: BSD License - Natural Language :: English - Operating System :: OS Independent - Programming Language :: Python - Programming Language :: Python :: 3 - Topic :: Software Development - Topic :: Utilities - """.split( + Environment :: Console + Intended Audience :: Developers + License :: OSI Approved :: BSD License + Natural Language :: English + Operating System :: OS Independent + Programming Language :: Python + Programming Language :: Python :: 3 + Topic :: Software Development + Topic :: Utilities + """.split( "\n" ) if x.strip() diff --git a/pprintpp/__init__.py b/pprintpp/__init__.py index 432bd27..c1647a7 100644 --- a/pprintpp/__init__.py +++ b/pprintpp/__init__.py @@ -43,44 +43,44 @@ def getvalue(self): # Source: http://www.unicode.org/reports/tr44/#GC_Values_Table # fmt: off unicode_printable_categories = { - "Lu": 1, # Uppercase_Letter an uppercase letter - "Ll": 1, # Lowercase_Letter a lowercase letter - "Lt": 1, # Titlecase_Letter a digraphic character, with first part uppercase - "LC": 1, # Cased_Letter Lu | Ll | Lt - "Lm": 0, # Modifier_Letter a modifier letter - "Lo": 1, # Other_Letter other letters, including syllables and ideographs - "L": 1, # Letter Lu | Ll | Lt | Lm | Lo - "Mn": 0, # Nonspacing_Mark a nonspacing combining mark (zero advance width) - "Mc": 0, # Spacing_Mark a spacing combining mark (positive advance width) - "Me": 0, # Enclosing_Mark an enclosing combining mark - "M": 1, # Mark Mn | Mc | Me - "Nd": 1, # Decimal_Number a decimal digit - "Nl": 1, # Letter_Number a letterlike numeric character - "No": 1, # Other_Number a numeric character of other type - "N": 1, # Number Nd | Nl | No - "Pc": 1, # Connector_Punctuation a connecting punctuation mark, like a tie - "Pd": 1, # Dash_Punctuation a dash or hyphen punctuation mark - "Ps": 1, # Open_Punctuation an opening punctuation mark (of a pair) - "Pe": 1, # Close_Punctuation a closing punctuation mark (of a pair) - "Pi": 1, # Initial_Punctuation an initial quotation mark - "Pf": 1, # Final_Punctuation a final quotation mark - "Po": 1, # Other_Punctuation a punctuation mark of other type - "P": 1, # Punctuation Pc | Pd | Ps | Pe | Pi | Pf | Po - "Sm": 1, # Math_Symbol a symbol of mathematical use - "Sc": 1, # Currency_Symbol a currency sign - "Sk": 1, # Modifier_Symbol a non-letterlike modifier symbol - "So": 1, # Other_Symbol a symbol of other type - "S": 1, # Symbol Sm | Sc | Sk | So - "Zs": 0, # Space_Separator a space character (of various non-zero widths) - "Zl": 0, # Line_Separator U+2028 LINE SEPARATOR only - "Zp": 0, # Paragraph_Separator U+2029 PARAGRAPH SEPARATOR only - "Z": 1, # Separator Zs | Zl | Zp - "Cc": 0, # Control a C0 or C1 control code - "Cf": 0, # Format a format control character - "Cs": 0, # Surrogate a surrogate code point - "Co": 0, # Private_Use a private-use character - "Cn": 0, # Unassigned a reserved unassigned code point or a noncharacter - "C": 0, # Other Cc | Cf | Cs | Co | Cn + "Lu": 1, # Uppercase_Letter an uppercase letter + "Ll": 1, # Lowercase_Letter a lowercase letter + "Lt": 1, # Titlecase_Letter a digraphic character, with first part uppercase + "LC": 1, # Cased_Letter Lu | Ll | Lt + "Lm": 0, # Modifier_Letter a modifier letter + "Lo": 1, # Other_Letter other letters, including syllables and ideographs + "L": 1, # Letter Lu | Ll | Lt | Lm | Lo + "Mn": 0, # Nonspacing_Mark a nonspacing combining mark (zero advance width) + "Mc": 0, # Spacing_Mark a spacing combining mark (positive advance width) + "Me": 0, # Enclosing_Mark an enclosing combining mark + "M": 1, # Mark Mn | Mc | Me + "Nd": 1, # Decimal_Number a decimal digit + "Nl": 1, # Letter_Number a letterlike numeric character + "No": 1, # Other_Number a numeric character of other type + "N": 1, # Number Nd | Nl | No + "Pc": 1, # Connector_Punctuation a connecting punctuation mark, like a tie + "Pd": 1, # Dash_Punctuation a dash or hyphen punctuation mark + "Ps": 1, # Open_Punctuation an opening punctuation mark (of a pair) + "Pe": 1, # Close_Punctuation a closing punctuation mark (of a pair) + "Pi": 1, # Initial_Punctuation an initial quotation mark + "Pf": 1, # Final_Punctuation a final quotation mark + "Po": 1, # Other_Punctuation a punctuation mark of other type + "P": 1, # Punctuation Pc | Pd | Ps | Pe | Pi | Pf | Po + "Sm": 1, # Math_Symbol a symbol of mathematical use + "Sc": 1, # Currency_Symbol a currency sign + "Sk": 1, # Modifier_Symbol a non-letterlike modifier symbol + "So": 1, # Other_Symbol a symbol of other type + "S": 1, # Symbol Sm | Sc | Sk | So + "Zs": 0, # Space_Separator a space character (of various non-zero widths) + "Zl": 0, # Line_Separator U+2028 LINE SEPARATOR only + "Zp": 0, # Paragraph_Separator U+2029 PARAGRAPH SEPARATOR only + "Z": 1, # Separator Zs | Zl | Zp + "Cc": 0, # Control a C0 or C1 control code + "Cf": 0, # Format a format control character + "Cs": 0, # Surrogate a surrogate code point + "Co": 0, # Private_Use a private-use character + "Cn": 0, # Unassigned a reserved unassigned code point or a noncharacter + "C": 0, # Other Cc | Cf | Cs | Co | Cn } # fmt: on diff --git a/pprintpp/safesort.py b/pprintpp/safesort.py index 51aba99..a5ddd5e 100644 --- a/pprintpp/safesort.py +++ b/pprintpp/safesort.py @@ -14,7 +14,7 @@ def memoized_property_helper(self): def _build_safe_cmp_func(name, cmp, prefix=""): code = textwrap.dedent( - """\ + f"""\ def {name}(self, other): try: return {prefix}(self.obj {cmp} other.obj) @@ -25,8 +25,8 @@ def {name}(self, other): except TypeError: pass return {prefix}(self.verysafeobj {cmp} other.verysafeobj) - """ - ).format(name=name, cmp=cmp, prefix=prefix) + """ + ) gs = ls = {} exec(code, gs, ls) return gs[name] diff --git a/pyproject.toml b/pyproject.toml index 7aa7c75..bce0600 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,10 @@ sections = 'FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER' [tool.pytest.ini_options] testpaths = ['tests'] +[tool.flake8] +max-line-length = 120 +disable = ["E261"] + [tool.mypy] python_version = '3.7' warn_return_any = true diff --git a/setup.py b/setup.py index 7a1c5d0..8761ea0 100644 --- a/setup.py +++ b/setup.py @@ -31,17 +31,16 @@ classifiers=[ x.strip() for x in """ - Development Status :: 3 - Alpha - Environment :: Console - Intended Audience :: Developers - License :: OSI Approved :: BSD License - Natural Language :: English - Operating System :: OS Independent - Programming Language :: Python - Programming Language :: Python :: 3 - Topic :: Software Development - Topic :: Utilities - """.split( + Environment :: Console + Intended Audience :: Developers + License :: OSI Approved :: BSD License + Natural Language :: English + Operating System :: OS Independent + Programming Language :: Python + Programming Language :: Python :: 3 + Topic :: Software Development + Topic :: Utilities + """.split( "\n" ) if x.strip() diff --git a/tests/test_pprintpp.py b/tests/test_pprintpp.py index 48f3719..3f346b3 100644 --- a/tests/test_pprintpp.py +++ b/tests/test_pprintpp.py @@ -7,7 +7,6 @@ import pytest import pprintpp as p -from pprintpp import defaultdict sys.path.append("pp/") import pp # noqa: E402 module level import not at top of file From 873163731f6908cc37ccef0a908917559cb82ae7 Mon Sep 17 00:00:00 2001 From: David Wolever Date: Wed, 13 Apr 2022 23:22:27 -0400 Subject: [PATCH 10/10] Fix flake8 --- .flake8 | 0 pyproject.toml | 4 ---- setup.cfg | 3 ++- 3 files changed, 2 insertions(+), 5 deletions(-) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml index bce0600..7aa7c75 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,10 +14,6 @@ sections = 'FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER' [tool.pytest.ini_options] testpaths = ['tests'] -[tool.flake8] -max-line-length = 120 -disable = ["E261"] - [tool.mypy] python_version = '3.7' warn_return_any = true diff --git a/setup.cfg b/setup.cfg index 72b9ed6..a672378 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,5 +2,6 @@ universal = 1 [flake8] -exclude = .git,.github,.venv,__pycache__ +exclude = .git,.github,.venv,__pycache__,env/ +ignore = E261,E241 max-line-length = 100