From 98261fa32a6161f33e7503c5c09427f4f24da8fb Mon Sep 17 00:00:00 2001 From: Wahid Shaikh <119995897+0x00zer0day@users.noreply.github.com> Date: Wed, 11 Oct 2023 01:23:31 +0530 Subject: [PATCH] Support `NO_COLOR` env var for `wemake` formatter (#2750) --- CHANGELOG.md | 7 + docs/pages/usage/formatter.rst | 9 + .../snapshots/snap_test_formatter_output.py | 663 +++++++++++++++++- tests/test_formatter/test_formatter_output.py | 27 +- wemake_python_styleguide/formatter.py | 49 +- 5 files changed, 725 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1e4b06c7..6d80686c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,12 +16,19 @@ Semantic versioning in our case means: But, in the future we might change the configuration names/logic, change the client facing API, change code conventions significantly, etc. + ## WIP +### Features + +- `wemake` output formatter now respects `NO_COLOR=1` option + to disable text highlighting. See https://no-color.org + ### Bugfixes - Fix `ForbiddenInlineIgnoreViolation` config parsing. #2590 + ## 0.18.0 ### Features diff --git a/docs/pages/usage/formatter.rst b/docs/pages/usage/formatter.rst index 34ac90daa..5ca2331cc 100644 --- a/docs/pages/usage/formatter.rst +++ b/docs/pages/usage/formatter.rst @@ -114,3 +114,12 @@ You can also show links to the documentation pages of violations: In modern terminals, you can click them to open the respective docs page. We do not include ``show-violation-links`` in our default configuration. + + +.. rubric:: Disabling colors and text highlight + +Set ``NO_COLOR=1`` environment variable +to completely disable all text highligt and colors +in ``wemake`` formatter. + +See https://no-color.org for more information about ``NO_COLOR``. diff --git a/tests/test_formatter/snapshots/snap_test_formatter_output.py b/tests/test_formatter/snapshots/snap_test_formatter_output.py index e4b0280d9..7da3afe2a 100644 --- a/tests/test_formatter/snapshots/snap_test_formatter_output.py +++ b/tests/test_formatter/snapshots/snap_test_formatter_output.py @@ -7,7 +7,7 @@ snapshots = Snapshot() -snapshots['test_formatter[cli_options0-regular] formatter_regular'] = ''' +snapshots['test_formatter[default_colors-cli_options0-regular] formatter_regular_None'] = ''' \x1b[4m\x1b[1m./tests/fixtures/formatter/formatter1.py\x1b[0m\x1b[0m 1:1 WPS111 Found too short name: s < 2 1:7 WPS110 Found wrong variable name: handle @@ -24,7 +24,7 @@ https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ ''' -snapshots['test_formatter[cli_options1-regular_statistic] formatter_regular_statistic'] = ''' +snapshots['test_formatter[default_colors-cli_options1-regular_statistic] formatter_regular_statistic_None'] = ''' \x1b[4m\x1b[1m./tests/fixtures/formatter/formatter1.py\x1b[0m\x1b[0m 1:1 WPS111 Found too short name: s < 2 1:7 WPS110 Found wrong variable name: handle @@ -66,7 +66,7 @@ https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ ''' -snapshots['test_formatter[cli_options2-with_source] formatter_with_source'] = ''' +snapshots['test_formatter[default_colors-cli_options2-with_source] formatter_with_source_None'] = ''' \x1b[4m\x1b[1m./tests/fixtures/formatter/formatter1.py\x1b[0m\x1b[0m 1:1 WPS111 Found too short name: s < 2 @@ -107,7 +107,7 @@ https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ ''' -snapshots['test_formatter[cli_options3-with_links] formatter_with_links'] = ''' +snapshots['test_formatter[default_colors-cli_options3-with_links] formatter_with_links_None'] = ''' \x1b[4m\x1b[1m./tests/fixtures/formatter/formatter1.py\x1b[0m\x1b[0m 1:1 WPS111 Found too short name: s < 2 -> https://pyflak.es/WPS111 @@ -132,7 +132,7 @@ https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ ''' -snapshots['test_formatter[cli_options4-with_source_statistic] formatter_with_source_statistic'] = ''' +snapshots['test_formatter[default_colors-cli_options4-with_source_statistic] formatter_with_source_statistic_None'] = ''' \x1b[4m\x1b[1m./tests/fixtures/formatter/formatter1.py\x1b[0m\x1b[0m 1:1 WPS111 Found too short name: s < 2 @@ -198,7 +198,7 @@ https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ ''' -snapshots['test_formatter[cli_options5-with_source_links] formatter_with_source_links'] = ''' +snapshots['test_formatter[default_colors-cli_options5-with_source_links] formatter_with_source_links_None'] = ''' \x1b[4m\x1b[1m./tests/fixtures/formatter/formatter1.py\x1b[0m\x1b[0m 1:1 WPS111 Found too short name: s < 2 @@ -247,7 +247,7 @@ https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ ''' -snapshots['test_formatter[cli_options6-statistic_with_source] formatter_statistic_with_source'] = ''' +snapshots['test_formatter[default_colors-cli_options6-statistic_with_source] formatter_statistic_with_source_None'] = ''' \x1b[4m\x1b[1m./tests/fixtures/formatter/formatter1.py\x1b[0m\x1b[0m 1:1 WPS111 Found too short name: s < 2 @@ -313,29 +313,664 @@ https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ ''' -snapshots['test_formatter_correct[cli_options0-regular] formatter_correct_regular'] = '' +snapshots['test_formatter[with_colors-cli_options0-regular] formatter_regular_False'] = ''' +\x1b[4m\x1b[1m./tests/fixtures/formatter/formatter1.py\x1b[0m\x1b[0m + 1:1 WPS111 Found too short name: s < 2 + 1:7 WPS110 Found wrong variable name: handle + 2:21 WPS432 Found magic number: 200 + 2:21 WPS303 Found underscored number: 2_00 + +\x1b[4m\x1b[1m./tests/fixtures/formatter/formatter2.py\x1b[0m\x1b[0m + 1:1 WPS110 Found wrong variable name: data + 1:10 WPS110 Found wrong variable name: param + 2:12 WPS437 Found protected attribute usage: _protected + 2:31 WPS303 Found underscored number: 10_00 + +Full list of violations and explanations: +https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ +''' + +snapshots['test_formatter[with_colors-cli_options1-regular_statistic] formatter_regular_statistic_False'] = ''' +\x1b[4m\x1b[1m./tests/fixtures/formatter/formatter1.py\x1b[0m\x1b[0m + 1:1 WPS111 Found too short name: s < 2 + 1:7 WPS110 Found wrong variable name: handle + 2:21 WPS432 Found magic number: 200 + 2:21 WPS303 Found underscored number: 2_00 + +\x1b[4m\x1b[1m./tests/fixtures/formatter/formatter2.py\x1b[0m\x1b[0m + 1:1 WPS110 Found wrong variable name: data + 1:10 WPS110 Found wrong variable name: param + 2:12 WPS437 Found protected attribute usage: _protected + 2:31 WPS303 Found underscored number: 10_00 + +\x1b[1mWPS110\x1b[0m: Found wrong variable name: handle + 1 ./tests/fixtures/formatter/formatter1.py + 2 ./tests/fixtures/formatter/formatter2.py +\x1b[4mTotal: 3\x1b[0m + +\x1b[1mWPS111\x1b[0m: Found too short name: s < 2 + 1 ./tests/fixtures/formatter/formatter1.py +\x1b[4mTotal: 1\x1b[0m + +\x1b[1mWPS303\x1b[0m: Found underscored number: 2_00 + 1 ./tests/fixtures/formatter/formatter1.py + 1 ./tests/fixtures/formatter/formatter2.py +\x1b[4mTotal: 2\x1b[0m + +\x1b[1mWPS432\x1b[0m: Found magic number: 200 + 1 ./tests/fixtures/formatter/formatter1.py +\x1b[4mTotal: 1\x1b[0m + +\x1b[1mWPS437\x1b[0m: Found protected attribute usage: _protected + 1 ./tests/fixtures/formatter/formatter2.py +\x1b[4mTotal: 1\x1b[0m + + +\x1b[4m\x1b[1mAll errors: 8\x1b[0m\x1b[0m + +Full list of violations and explanations: +https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ +''' + +snapshots['test_formatter[with_colors-cli_options2-with_source] formatter_with_source_False'] = ''' +\x1b[4m\x1b[1m./tests/fixtures/formatter/formatter1.py\x1b[0m\x1b[0m + + 1:1 WPS111 Found too short name: s < 2 + \x1b[34mdef\x1b[39;49;00m \x1b[32ms\x1b[39;49;00m(handle: \x1b[36mint\x1b[39;49;00m) -> \x1b[36mint\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m + ^ + + 1:7 WPS110 Found wrong variable name: handle + \x1b[34mdef\x1b[39;49;00m \x1b[32ms\x1b[39;49;00m(handle: \x1b[36mint\x1b[39;49;00m) -> \x1b[36mint\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m + ^ + + 2:21 WPS432 Found magic number: 200 + \x1b[34mreturn\x1b[39;49;00m handle + \x1b[34m2_00\x1b[39;49;00m\x1b[37m\x1b[39;49;00m + ^ + + 2:21 WPS303 Found underscored number: 2_00 + \x1b[34mreturn\x1b[39;49;00m handle + \x1b[34m2_00\x1b[39;49;00m\x1b[37m\x1b[39;49;00m + ^ + +\x1b[4m\x1b[1m./tests/fixtures/formatter/formatter2.py\x1b[0m\x1b[0m + + 1:1 WPS110 Found wrong variable name: data + \x1b[34mdef\x1b[39;49;00m \x1b[32mdata\x1b[39;49;00m(param) -> \x1b[36mint\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m + ^ + + 1:10 WPS110 Found wrong variable name: param + \x1b[34mdef\x1b[39;49;00m \x1b[32mdata\x1b[39;49;00m(param) -> \x1b[36mint\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m + ^ + + 2:12 WPS437 Found protected attribute usage: _protected + \x1b[34mreturn\x1b[39;49;00m param._protected + \x1b[34m10_00\x1b[39;49;00m\x1b[37m\x1b[39;49;00m + ^ + + 2:31 WPS303 Found underscored number: 10_00 + \x1b[34mreturn\x1b[39;49;00m param._protected + \x1b[34m10_00\x1b[39;49;00m\x1b[37m\x1b[39;49;00m + ^ + +Full list of violations and explanations: +https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ +''' + +snapshots['test_formatter[with_colors-cli_options3-with_links] formatter_with_links_False'] = ''' +\x1b[4m\x1b[1m./tests/fixtures/formatter/formatter1.py\x1b[0m\x1b[0m + 1:1 WPS111 Found too short name: s < 2 + -> https://pyflak.es/WPS111 + 1:7 WPS110 Found wrong variable name: handle + -> https://pyflak.es/WPS110 + 2:21 WPS432 Found magic number: 200 + -> https://pyflak.es/WPS432 + 2:21 WPS303 Found underscored number: 2_00 + -> https://pyflak.es/WPS303 + +\x1b[4m\x1b[1m./tests/fixtures/formatter/formatter2.py\x1b[0m\x1b[0m + 1:1 WPS110 Found wrong variable name: data + -> https://pyflak.es/WPS110 + 1:10 WPS110 Found wrong variable name: param + -> https://pyflak.es/WPS110 + 2:12 WPS437 Found protected attribute usage: _protected + -> https://pyflak.es/WPS437 + 2:31 WPS303 Found underscored number: 10_00 + -> https://pyflak.es/WPS303 + +Full list of violations and explanations: +https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ +''' + +snapshots['test_formatter[with_colors-cli_options4-with_source_statistic] formatter_with_source_statistic_False'] = ''' +\x1b[4m\x1b[1m./tests/fixtures/formatter/formatter1.py\x1b[0m\x1b[0m + + 1:1 WPS111 Found too short name: s < 2 + \x1b[34mdef\x1b[39;49;00m \x1b[32ms\x1b[39;49;00m(handle: \x1b[36mint\x1b[39;49;00m) -> \x1b[36mint\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m + ^ + + 1:7 WPS110 Found wrong variable name: handle + \x1b[34mdef\x1b[39;49;00m \x1b[32ms\x1b[39;49;00m(handle: \x1b[36mint\x1b[39;49;00m) -> \x1b[36mint\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m + ^ + + 2:21 WPS432 Found magic number: 200 + \x1b[34mreturn\x1b[39;49;00m handle + \x1b[34m2_00\x1b[39;49;00m\x1b[37m\x1b[39;49;00m + ^ + + 2:21 WPS303 Found underscored number: 2_00 + \x1b[34mreturn\x1b[39;49;00m handle + \x1b[34m2_00\x1b[39;49;00m\x1b[37m\x1b[39;49;00m + ^ + +\x1b[4m\x1b[1m./tests/fixtures/formatter/formatter2.py\x1b[0m\x1b[0m + + 1:1 WPS110 Found wrong variable name: data + \x1b[34mdef\x1b[39;49;00m \x1b[32mdata\x1b[39;49;00m(param) -> \x1b[36mint\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m + ^ + + 1:10 WPS110 Found wrong variable name: param + \x1b[34mdef\x1b[39;49;00m \x1b[32mdata\x1b[39;49;00m(param) -> \x1b[36mint\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m + ^ + + 2:12 WPS437 Found protected attribute usage: _protected + \x1b[34mreturn\x1b[39;49;00m param._protected + \x1b[34m10_00\x1b[39;49;00m\x1b[37m\x1b[39;49;00m + ^ + + 2:31 WPS303 Found underscored number: 10_00 + \x1b[34mreturn\x1b[39;49;00m param._protected + \x1b[34m10_00\x1b[39;49;00m\x1b[37m\x1b[39;49;00m + ^ + +\x1b[1mWPS110\x1b[0m: Found wrong variable name: handle + 1 ./tests/fixtures/formatter/formatter1.py + 2 ./tests/fixtures/formatter/formatter2.py +\x1b[4mTotal: 3\x1b[0m + +\x1b[1mWPS111\x1b[0m: Found too short name: s < 2 + 1 ./tests/fixtures/formatter/formatter1.py +\x1b[4mTotal: 1\x1b[0m + +\x1b[1mWPS303\x1b[0m: Found underscored number: 2_00 + 1 ./tests/fixtures/formatter/formatter1.py + 1 ./tests/fixtures/formatter/formatter2.py +\x1b[4mTotal: 2\x1b[0m + +\x1b[1mWPS432\x1b[0m: Found magic number: 200 + 1 ./tests/fixtures/formatter/formatter1.py +\x1b[4mTotal: 1\x1b[0m + +\x1b[1mWPS437\x1b[0m: Found protected attribute usage: _protected + 1 ./tests/fixtures/formatter/formatter2.py +\x1b[4mTotal: 1\x1b[0m + + +\x1b[4m\x1b[1mAll errors: 8\x1b[0m\x1b[0m + +Full list of violations and explanations: +https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ +''' + +snapshots['test_formatter[with_colors-cli_options5-with_source_links] formatter_with_source_links_False'] = ''' +\x1b[4m\x1b[1m./tests/fixtures/formatter/formatter1.py\x1b[0m\x1b[0m + + 1:1 WPS111 Found too short name: s < 2 + -> https://pyflak.es/WPS111 + \x1b[34mdef\x1b[39;49;00m \x1b[32ms\x1b[39;49;00m(handle: \x1b[36mint\x1b[39;49;00m) -> \x1b[36mint\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m + ^ + + 1:7 WPS110 Found wrong variable name: handle + -> https://pyflak.es/WPS110 + \x1b[34mdef\x1b[39;49;00m \x1b[32ms\x1b[39;49;00m(handle: \x1b[36mint\x1b[39;49;00m) -> \x1b[36mint\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m + ^ + + 2:21 WPS432 Found magic number: 200 + -> https://pyflak.es/WPS432 + \x1b[34mreturn\x1b[39;49;00m handle + \x1b[34m2_00\x1b[39;49;00m\x1b[37m\x1b[39;49;00m + ^ + + 2:21 WPS303 Found underscored number: 2_00 + -> https://pyflak.es/WPS303 + \x1b[34mreturn\x1b[39;49;00m handle + \x1b[34m2_00\x1b[39;49;00m\x1b[37m\x1b[39;49;00m + ^ + +\x1b[4m\x1b[1m./tests/fixtures/formatter/formatter2.py\x1b[0m\x1b[0m + + 1:1 WPS110 Found wrong variable name: data + -> https://pyflak.es/WPS110 + \x1b[34mdef\x1b[39;49;00m \x1b[32mdata\x1b[39;49;00m(param) -> \x1b[36mint\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m + ^ + + 1:10 WPS110 Found wrong variable name: param + -> https://pyflak.es/WPS110 + \x1b[34mdef\x1b[39;49;00m \x1b[32mdata\x1b[39;49;00m(param) -> \x1b[36mint\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m + ^ + + 2:12 WPS437 Found protected attribute usage: _protected + -> https://pyflak.es/WPS437 + \x1b[34mreturn\x1b[39;49;00m param._protected + \x1b[34m10_00\x1b[39;49;00m\x1b[37m\x1b[39;49;00m + ^ + + 2:31 WPS303 Found underscored number: 10_00 + -> https://pyflak.es/WPS303 + \x1b[34mreturn\x1b[39;49;00m param._protected + \x1b[34m10_00\x1b[39;49;00m\x1b[37m\x1b[39;49;00m + ^ + +Full list of violations and explanations: +https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ +''' + +snapshots['test_formatter[with_colors-cli_options6-statistic_with_source] formatter_statistic_with_source_False'] = ''' +\x1b[4m\x1b[1m./tests/fixtures/formatter/formatter1.py\x1b[0m\x1b[0m + + 1:1 WPS111 Found too short name: s < 2 + \x1b[34mdef\x1b[39;49;00m \x1b[32ms\x1b[39;49;00m(handle: \x1b[36mint\x1b[39;49;00m) -> \x1b[36mint\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m + ^ + + 1:7 WPS110 Found wrong variable name: handle + \x1b[34mdef\x1b[39;49;00m \x1b[32ms\x1b[39;49;00m(handle: \x1b[36mint\x1b[39;49;00m) -> \x1b[36mint\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m + ^ + + 2:21 WPS432 Found magic number: 200 + \x1b[34mreturn\x1b[39;49;00m handle + \x1b[34m2_00\x1b[39;49;00m\x1b[37m\x1b[39;49;00m + ^ + + 2:21 WPS303 Found underscored number: 2_00 + \x1b[34mreturn\x1b[39;49;00m handle + \x1b[34m2_00\x1b[39;49;00m\x1b[37m\x1b[39;49;00m + ^ + +\x1b[4m\x1b[1m./tests/fixtures/formatter/formatter2.py\x1b[0m\x1b[0m + + 1:1 WPS110 Found wrong variable name: data + \x1b[34mdef\x1b[39;49;00m \x1b[32mdata\x1b[39;49;00m(param) -> \x1b[36mint\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m + ^ + + 1:10 WPS110 Found wrong variable name: param + \x1b[34mdef\x1b[39;49;00m \x1b[32mdata\x1b[39;49;00m(param) -> \x1b[36mint\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m + ^ + + 2:12 WPS437 Found protected attribute usage: _protected + \x1b[34mreturn\x1b[39;49;00m param._protected + \x1b[34m10_00\x1b[39;49;00m\x1b[37m\x1b[39;49;00m + ^ + + 2:31 WPS303 Found underscored number: 10_00 + \x1b[34mreturn\x1b[39;49;00m param._protected + \x1b[34m10_00\x1b[39;49;00m\x1b[37m\x1b[39;49;00m + ^ + +\x1b[1mWPS110\x1b[0m: Found wrong variable name: handle + 1 ./tests/fixtures/formatter/formatter1.py + 2 ./tests/fixtures/formatter/formatter2.py +\x1b[4mTotal: 3\x1b[0m + +\x1b[1mWPS111\x1b[0m: Found too short name: s < 2 + 1 ./tests/fixtures/formatter/formatter1.py +\x1b[4mTotal: 1\x1b[0m + +\x1b[1mWPS303\x1b[0m: Found underscored number: 2_00 + 1 ./tests/fixtures/formatter/formatter1.py + 1 ./tests/fixtures/formatter/formatter2.py +\x1b[4mTotal: 2\x1b[0m + +\x1b[1mWPS432\x1b[0m: Found magic number: 200 + 1 ./tests/fixtures/formatter/formatter1.py +\x1b[4mTotal: 1\x1b[0m + +\x1b[1mWPS437\x1b[0m: Found protected attribute usage: _protected + 1 ./tests/fixtures/formatter/formatter2.py +\x1b[4mTotal: 1\x1b[0m + + +\x1b[4m\x1b[1mAll errors: 8\x1b[0m\x1b[0m + +Full list of violations and explanations: +https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ +''' + +snapshots['test_formatter[without_colors-cli_options0-regular] formatter_regular_True'] = ''' +./tests/fixtures/formatter/formatter1.py + 1:1 WPS111 Found too short name: s < 2 + 1:7 WPS110 Found wrong variable name: handle + 2:21 WPS432 Found magic number: 200 + 2:21 WPS303 Found underscored number: 2_00 + +./tests/fixtures/formatter/formatter2.py + 1:1 WPS110 Found wrong variable name: data + 1:10 WPS110 Found wrong variable name: param + 2:12 WPS437 Found protected attribute usage: _protected + 2:31 WPS303 Found underscored number: 10_00 + +Full list of violations and explanations: +https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ +''' + +snapshots['test_formatter[without_colors-cli_options1-regular_statistic] formatter_regular_statistic_True'] = ''' +./tests/fixtures/formatter/formatter1.py + 1:1 WPS111 Found too short name: s < 2 + 1:7 WPS110 Found wrong variable name: handle + 2:21 WPS432 Found magic number: 200 + 2:21 WPS303 Found underscored number: 2_00 + +./tests/fixtures/formatter/formatter2.py + 1:1 WPS110 Found wrong variable name: data + 1:10 WPS110 Found wrong variable name: param + 2:12 WPS437 Found protected attribute usage: _protected + 2:31 WPS303 Found underscored number: 10_00 + +WPS110: Found wrong variable name: handle + 1 ./tests/fixtures/formatter/formatter1.py + 2 ./tests/fixtures/formatter/formatter2.py +Total: 3 + +WPS111: Found too short name: s < 2 + 1 ./tests/fixtures/formatter/formatter1.py +Total: 1 + +WPS303: Found underscored number: 2_00 + 1 ./tests/fixtures/formatter/formatter1.py + 1 ./tests/fixtures/formatter/formatter2.py +Total: 2 + +WPS432: Found magic number: 200 + 1 ./tests/fixtures/formatter/formatter1.py +Total: 1 + +WPS437: Found protected attribute usage: _protected + 1 ./tests/fixtures/formatter/formatter2.py +Total: 1 + + +All errors: 8 + +Full list of violations and explanations: +https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ +''' + +snapshots['test_formatter[without_colors-cli_options2-with_source] formatter_with_source_True'] = ''' +./tests/fixtures/formatter/formatter1.py + + 1:1 WPS111 Found too short name: s < 2 + def s(handle: int) -> int: + ^ + + 1:7 WPS110 Found wrong variable name: handle + def s(handle: int) -> int: + ^ + + 2:21 WPS432 Found magic number: 200 + return handle + 2_00 + ^ + + 2:21 WPS303 Found underscored number: 2_00 + return handle + 2_00 + ^ + +./tests/fixtures/formatter/formatter2.py + + 1:1 WPS110 Found wrong variable name: data + def data(param) -> int: + ^ + + 1:10 WPS110 Found wrong variable name: param + def data(param) -> int: + ^ + + 2:12 WPS437 Found protected attribute usage: _protected + return param._protected + 10_00 + ^ + + 2:31 WPS303 Found underscored number: 10_00 + return param._protected + 10_00 + ^ + +Full list of violations and explanations: +https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ +''' + +snapshots['test_formatter[without_colors-cli_options3-with_links] formatter_with_links_True'] = ''' +./tests/fixtures/formatter/formatter1.py + 1:1 WPS111 Found too short name: s < 2 + -> https://pyflak.es/WPS111 + 1:7 WPS110 Found wrong variable name: handle + -> https://pyflak.es/WPS110 + 2:21 WPS432 Found magic number: 200 + -> https://pyflak.es/WPS432 + 2:21 WPS303 Found underscored number: 2_00 + -> https://pyflak.es/WPS303 + +./tests/fixtures/formatter/formatter2.py + 1:1 WPS110 Found wrong variable name: data + -> https://pyflak.es/WPS110 + 1:10 WPS110 Found wrong variable name: param + -> https://pyflak.es/WPS110 + 2:12 WPS437 Found protected attribute usage: _protected + -> https://pyflak.es/WPS437 + 2:31 WPS303 Found underscored number: 10_00 + -> https://pyflak.es/WPS303 + +Full list of violations and explanations: +https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ +''' + +snapshots['test_formatter[without_colors-cli_options4-with_source_statistic] formatter_with_source_statistic_True'] = ''' +./tests/fixtures/formatter/formatter1.py + + 1:1 WPS111 Found too short name: s < 2 + def s(handle: int) -> int: + ^ + + 1:7 WPS110 Found wrong variable name: handle + def s(handle: int) -> int: + ^ + + 2:21 WPS432 Found magic number: 200 + return handle + 2_00 + ^ + + 2:21 WPS303 Found underscored number: 2_00 + return handle + 2_00 + ^ + +./tests/fixtures/formatter/formatter2.py + + 1:1 WPS110 Found wrong variable name: data + def data(param) -> int: + ^ + + 1:10 WPS110 Found wrong variable name: param + def data(param) -> int: + ^ + + 2:12 WPS437 Found protected attribute usage: _protected + return param._protected + 10_00 + ^ + + 2:31 WPS303 Found underscored number: 10_00 + return param._protected + 10_00 + ^ + +WPS110: Found wrong variable name: handle + 1 ./tests/fixtures/formatter/formatter1.py + 2 ./tests/fixtures/formatter/formatter2.py +Total: 3 + +WPS111: Found too short name: s < 2 + 1 ./tests/fixtures/formatter/formatter1.py +Total: 1 + +WPS303: Found underscored number: 2_00 + 1 ./tests/fixtures/formatter/formatter1.py + 1 ./tests/fixtures/formatter/formatter2.py +Total: 2 + +WPS432: Found magic number: 200 + 1 ./tests/fixtures/formatter/formatter1.py +Total: 1 + +WPS437: Found protected attribute usage: _protected + 1 ./tests/fixtures/formatter/formatter2.py +Total: 1 + + +All errors: 8 + +Full list of violations and explanations: +https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ +''' + +snapshots['test_formatter[without_colors-cli_options5-with_source_links] formatter_with_source_links_True'] = ''' +./tests/fixtures/formatter/formatter1.py + + 1:1 WPS111 Found too short name: s < 2 + -> https://pyflak.es/WPS111 + def s(handle: int) -> int: + ^ + + 1:7 WPS110 Found wrong variable name: handle + -> https://pyflak.es/WPS110 + def s(handle: int) -> int: + ^ + + 2:21 WPS432 Found magic number: 200 + -> https://pyflak.es/WPS432 + return handle + 2_00 + ^ + + 2:21 WPS303 Found underscored number: 2_00 + -> https://pyflak.es/WPS303 + return handle + 2_00 + ^ + +./tests/fixtures/formatter/formatter2.py + + 1:1 WPS110 Found wrong variable name: data + -> https://pyflak.es/WPS110 + def data(param) -> int: + ^ + + 1:10 WPS110 Found wrong variable name: param + -> https://pyflak.es/WPS110 + def data(param) -> int: + ^ + + 2:12 WPS437 Found protected attribute usage: _protected + -> https://pyflak.es/WPS437 + return param._protected + 10_00 + ^ + + 2:31 WPS303 Found underscored number: 10_00 + -> https://pyflak.es/WPS303 + return param._protected + 10_00 + ^ + +Full list of violations and explanations: +https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ +''' + +snapshots['test_formatter[without_colors-cli_options6-statistic_with_source] formatter_statistic_with_source_True'] = ''' +./tests/fixtures/formatter/formatter1.py + + 1:1 WPS111 Found too short name: s < 2 + def s(handle: int) -> int: + ^ + + 1:7 WPS110 Found wrong variable name: handle + def s(handle: int) -> int: + ^ + + 2:21 WPS432 Found magic number: 200 + return handle + 2_00 + ^ + + 2:21 WPS303 Found underscored number: 2_00 + return handle + 2_00 + ^ + +./tests/fixtures/formatter/formatter2.py + + 1:1 WPS110 Found wrong variable name: data + def data(param) -> int: + ^ + + 1:10 WPS110 Found wrong variable name: param + def data(param) -> int: + ^ + + 2:12 WPS437 Found protected attribute usage: _protected + return param._protected + 10_00 + ^ + + 2:31 WPS303 Found underscored number: 10_00 + return param._protected + 10_00 + ^ -snapshots['test_formatter_correct[cli_options1-regular_statistic] formatter_correct_regular_statistic'] = ''' +WPS110: Found wrong variable name: handle + 1 ./tests/fixtures/formatter/formatter1.py + 2 ./tests/fixtures/formatter/formatter2.py +Total: 3 + +WPS111: Found too short name: s < 2 + 1 ./tests/fixtures/formatter/formatter1.py +Total: 1 + +WPS303: Found underscored number: 2_00 + 1 ./tests/fixtures/formatter/formatter1.py + 1 ./tests/fixtures/formatter/formatter2.py +Total: 2 + +WPS432: Found magic number: 200 + 1 ./tests/fixtures/formatter/formatter1.py +Total: 1 + +WPS437: Found protected attribute usage: _protected + 1 ./tests/fixtures/formatter/formatter2.py +Total: 1 + + +All errors: 8 + +Full list of violations and explanations: +https://wemake-python-styleguide.rtfd.io/en/xx.xx/pages/usage/violations/ +''' + +snapshots['test_formatter_correct[with_colors-cli_options0-regular] formatter_correct_regular_False'] = '' + +snapshots['test_formatter_correct[with_colors-cli_options1-regular_statistic] formatter_correct_regular_statistic_False'] = ''' \x1b[4m\x1b[1mAll errors: 0\x1b[0m\x1b[0m ''' -snapshots['test_formatter_correct[cli_options2-with_source] formatter_correct_with_source'] = '' +snapshots['test_formatter_correct[with_colors-cli_options2-with_source] formatter_correct_with_source_False'] = '' -snapshots['test_formatter_correct[cli_options3-with_links] formatter_correct_with_links'] = '' +snapshots['test_formatter_correct[with_colors-cli_options3-with_links] formatter_correct_with_links_False'] = '' -snapshots['test_formatter_correct[cli_options4-with_source_statistic] formatter_correct_with_source_statistic'] = ''' +snapshots['test_formatter_correct[with_colors-cli_options4-with_source_statistic] formatter_correct_with_source_statistic_False'] = ''' \x1b[4m\x1b[1mAll errors: 0\x1b[0m\x1b[0m ''' -snapshots['test_formatter_correct[cli_options5-with_source_links] formatter_correct_with_source_links'] = '' +snapshots['test_formatter_correct[with_colors-cli_options5-with_source_links] formatter_correct_with_source_links_False'] = '' -snapshots['test_formatter_correct[cli_options6-statistic_with_source] formatter_correct_statistic_with_source'] = ''' +snapshots['test_formatter_correct[with_colors-cli_options6-statistic_with_source] formatter_correct_statistic_with_source_False'] = ''' \x1b[4m\x1b[1mAll errors: 0\x1b[0m\x1b[0m ''' +snapshots['test_formatter_correct[without_colors-cli_options0-regular] formatter_correct_regular_True'] = '' + +snapshots['test_formatter_correct[without_colors-cli_options1-regular_statistic] formatter_correct_regular_statistic_True'] = ''' + +All errors: 0 +''' + +snapshots['test_formatter_correct[without_colors-cli_options2-with_source] formatter_correct_with_source_True'] = '' + +snapshots['test_formatter_correct[without_colors-cli_options3-with_links] formatter_correct_with_links_True'] = '' + +snapshots['test_formatter_correct[without_colors-cli_options4-with_source_statistic] formatter_correct_with_source_statistic_True'] = ''' + +All errors: 0 +''' + +snapshots['test_formatter_correct[without_colors-cli_options5-with_source_links] formatter_correct_with_source_links_True'] = '' + +snapshots['test_formatter_correct[without_colors-cli_options6-statistic_with_source] formatter_correct_statistic_with_source_True'] = ''' + +All errors: 0 +''' + snapshots['test_ipynb formatter_ipynb'] = ''' \x1b[4m\x1b[1mtests/fixtures/notebook.ipynb\x1b[0m\x1b[0m 3:1 DAR101 Missing parameter(s) in Docstring: - good_name diff --git a/tests/test_formatter/test_formatter_output.py b/tests/test_formatter/test_formatter_output.py index afd41446e..d05928ef3 100644 --- a/tests/test_formatter/test_formatter_output.py +++ b/tests/test_formatter/test_formatter_output.py @@ -50,7 +50,12 @@ def _safe_output(output: str) -> str: (['--show-source', '--show-violation-links'], 'with_source_links'), (['--statistic', '--show-source'], 'statistic_with_source'), ]) -def test_formatter(snapshot, cli_options, output): +@pytest.mark.parametrize( + 'no_color', + [True, False, None], + ids=['without_colors', 'with_colors', 'default_colors'], +) +def test_formatter(snapshot, cli_options, output, no_color): """ End-to-End test to that formatting works well. @@ -59,6 +64,10 @@ def test_formatter(snapshot, cli_options, output): """ filename1 = './tests/fixtures/formatter/formatter1.py' filename2 = './tests/fixtures/formatter/formatter2.py' + if no_color is None: + env = os.environ + else: + env = {**os.environ, 'NO_COLOR': '1' if no_color else '0'} process = subprocess.Popen( [ @@ -77,12 +86,13 @@ def test_formatter(snapshot, cli_options, output): stderr=subprocess.PIPE, universal_newlines=True, encoding='utf8', + env=env, ) stdout, _ = process.communicate() snapshot.assert_match( _safe_output(stdout), - 'formatter_{0}'.format(output), + 'formatter_{0}_{1}'.format(output, no_color), ) @@ -95,7 +105,12 @@ def test_formatter(snapshot, cli_options, output): (['--show-source', '--show-violation-links'], 'with_source_links'), (['--statistic', '--show-source'], 'statistic_with_source'), ]) -def test_formatter_correct(snapshot, cli_options, output): +@pytest.mark.parametrize( + 'no_color', + [True, False], + ids=['without_colors', 'with_colors'], +) +def test_formatter_correct(snapshot, cli_options, output, no_color): """All correct code should not raise any violations and no output.""" filename = './tests/fixtures/formatter/correct.py' @@ -115,12 +130,14 @@ def test_formatter_correct(snapshot, cli_options, output): stderr=subprocess.PIPE, universal_newlines=True, encoding='utf8', + env={**os.environ, 'NO_COLOR': '1' if no_color else '0'}, ) - stdout, _ = process.communicate() + stdout, stderr = process.communicate() + assert process.returncode == 0, (stdout, stderr) snapshot.assert_match( _safe_output(stdout), - 'formatter_correct_{0}'.format(output), + 'formatter_correct_{0}_{1}'.format(output, no_color), ) diff --git a/wemake_python_styleguide/formatter.py b/wemake_python_styleguide/formatter.py index a2f81b2b1..06dd563ed 100644 --- a/wemake_python_styleguide/formatter.py +++ b/wemake_python_styleguide/formatter.py @@ -26,7 +26,8 @@ """ from collections import defaultdict -from typing import ClassVar, DefaultDict, List +from os import environ +from typing import ClassVar, DefaultDict, Final, List from flake8.formatting.base import BaseFormatter from flake8.statistics import Statistics @@ -34,19 +35,20 @@ from pygments import highlight from pygments.formatters import TerminalFormatter from pygments.lexers import PythonLexer -from typing_extensions import Final from wemake_python_styleguide.version import pkg_version #: That url is generated and hosted by Sphinx. -DOCS_URL_TEMPLATE: Final = ( +_DOCS_URL_TEMPLATE: Final = ( 'https://wemake-python-styleguide.rtfd.io/en/{0}/pages/usage/violations/' ) #: This url points to the specific violation page. -SHORTLINK_TEMPLATE: Final = ( - 'https://pyflak.es/{0}' -) +_SHORTLINK_TEMPLATE: Final = 'https://pyflak.es/{0}' + +#: Option to disable any code highlight and text output format. +#: See https://no-color.org +_NO_COLOR: Final = environ.get('NO_COLOR', '0') == '1' class WemakeFormatter(BaseFormatter): # noqa: WPS214 @@ -64,7 +66,7 @@ class WemakeFormatter(BaseFormatter): # noqa: WPS214 """ - _doc_url: ClassVar[str] = DOCS_URL_TEMPLATE.format(pkg_version) + _doc_url: ClassVar[str] = _DOCS_URL_TEMPLATE.format(pkg_version) # API: @@ -160,7 +162,7 @@ def _show_link(self, error: Violation) -> str: return ' {spacing}-> {link}'.format( spacing=' ' * 9, - link=SHORTLINK_TEMPLATE.format(error.code), + link=_SHORTLINK_TEMPLATE.format(error.code), ) def _print_header(self, filename: str) -> None: @@ -200,36 +202,61 @@ def _should_show_source(self, error: Violation) -> bool: # Formatting text: -def _bold(text: str) -> str: +def _bold(text: str, *, no_color: bool = _NO_COLOR) -> str: r""" Returns bold formatted text. >>> _bold('Hello!') '\x1b[1mHello!\x1b[0m' + Returns non-formatted text if environment variable ``NO_COLOR=1``. + + >>> _bold('Hello!', no_color=True) + 'Hello!' + """ + if no_color: + return text return '\033[1m{0}\033[0m'.format(text) -def _underline(text: str) -> str: +def _underline(text: str, *, no_color: bool = _NO_COLOR) -> str: r""" Returns underlined formatted text. >>> _underline('Hello!') '\x1b[4mHello!\x1b[0m' + Returns non-formatted text if environment variable ``NO_COLOR=1``. + + >>> _underline('Hello!', no_color=True) + 'Hello!' + """ + if no_color: + return text return '\033[4m{0}\033[0m'.format(text) -def _highlight(source: str, lexer, formatter) -> str: +def _highlight( + source: str, + lexer: PythonLexer, + formatter: TerminalFormatter, + *, + no_color: bool = _NO_COLOR, +) -> str: """ Highlights source code. Might fail. + Returns non-formatted text if environment variable ``NO_COLOR=1``. + See also: https://github.com/wemake-services/wemake-python-styleguide/issues/794 + https://no-color.org """ + if no_color: + return source try: return highlight(source, lexer, formatter) except Exception: # pragma: no cover