From e51f5c8ad1e8e2120e8d782b27322d8b07c252d5 Mon Sep 17 00:00:00 2001 From: "Augusto W. Andreoli" Date: Tue, 9 Mar 2021 11:13:49 +0100 Subject: [PATCH 1/5] refactor: move some configs from setup.cfg to tox.ini --- docs/ideas/ini.toml | 2 +- setup.cfg | 116 ------------------------------------ src/nitpick/plugins/ini.py | 7 ++- src/nitpick/schemas.py | 6 +- src/nitpick/style.py | 4 +- tasks.py | 2 +- tests/test_config.py | 7 ++- tests/test_ini.py | 53 +++++++++-------- tests/test_pre_commit.py | 8 +-- tests/test_style.py | 48 +++++++-------- tox.ini | 117 +++++++++++++++++++++++++++++++++++++ 11 files changed, 186 insertions(+), 184 deletions(-) create mode 100644 tox.ini diff --git a/docs/ideas/ini.toml b/docs/ideas/ini.toml index 1888cd9b..c81d935c 100644 --- a/docs/ideas/ini.toml +++ b/docs/ideas/ini.toml @@ -48,7 +48,7 @@ inline-quotes = "{{ nit.present(str, choices=['double', 'single']) }}" skip__separators = "," known_first_party__separators = "," -["setup.cfg"."tool:pytest"] +["tox.ini".pytest] # https://github.com/andreoliwa/nitpick/issues/271 addopts__separators = " " norecursedirs__separators = " " diff --git a/setup.cfg b/setup.cfg index 3c6a6cfc..7cb74ffc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,121 +27,5 @@ warn_no_return = True warn_redundant_casts = True warn_unused_ignores = True -[tool:pytest] -addopts = -v -norecursedirs = .* build dist CVS _darcs {arch} *.egg venv var docs -# https://docs.pytest.org/en/stable/reference.html#confval-testpaths -testpaths = src tests - -[tox:tox] -# https://tox.readthedocs.io/en/latest/config.html -isolated_build = True -envlist = clean,lint,py39,py38,py37,py36,docs,report - -[gh-actions] -# https://github.com/ymyzk/tox-gh-actions -python = - 3.9: py39 - 3.8: py38, clean, lint, docs, report - 3.7: py37 - 3.6: py36 - -[testenv] -description = Run tests with pytest and coverage -extras = test -depends = - {py39,py38,py37,py36}: clean - report: py39,py38,py37,py36 -setenv = - PY_IGNORE_IMPORTMISMATCH = 1 -commands = - python -m pip --version - # https://pytest-cov.readthedocs.io/en/latest/config.html#caveats - # https://docs.pytest.org/en/stable/skipping.html - python -m pytest --cov-config=setup.cfg --cov --cov-append --cov-report=term-missing --doctest-modules -s -rxXs {posargs:-vv} - -[testenv:clean] -description = Erase data for the coverage report before running tests -platform = linux|darwin -skip_install = true -deps = coverage -commands = coverage erase - -[testenv:lint] -description = Lint all files with pre-commit -basepython = python3.8 -platform = linux|darwin -# pylint needs both these extras: -extras = - # Install pylint itself - lint - # For pylint to inspect tests - test -deps = - pip>=19.2 - safety -# Run nitpick and pylint with tox, because local repos don't seem to work well with https://pre-commit.ci/ -commands = - # Run Nitpick locally on itself - flake8 --select=NIP - pylint src/ - safety check - -[testenv:report] -description = Coverage report -platform = linux|darwin -skip_install = true -deps = coverage -commands = - coverage report - coverage html - -[testenv:docs] -description = Build the HTML docs using Sphinx (sphinx-build, API docs, link checks) -basepython = python3.8 -platform = linux|darwin -extras = doc -commands = - python3 docs/generate_rst.py - sphinx-apidoc --force --follow-links --module-first --separate --implicit-namespaces --ext-autodoc --ext-doctest --ext-intersphinx --ext-todo --ext-coverage --ext-imgmath --ext-mathjax --ext-ifconfig --ext-viewcode --ext-githubpages --output-dir docs/source src/nitpick/ - # To stop failing when a page is unreachable, add a hyphen at the start of the line: - # https://tox.readthedocs.io/en/latest/example/basic.html#ignoring-exit-code - # Some errors during link check have to be ignored. - # E.g.: when a new TOML style is added, its link will be broken until a new release is published. - - sphinx-build --color -j auto -b linkcheck docs "{toxworkdir}/docs_out" - # Use these options to debug Sphinx: -nWT --keep-going -vvv - sphinx-build --color -j auto -d "{toxworkdir}/docs_doctree" -b html docs "{toxworkdir}/docs_out" {posargs} - -[coverage:run] -# https://coverage.readthedocs.io/en/latest/config.html#run -branch = true -parallel = true -source = src/ -omit = - tests/* - .tox/* -# This config is needed by https://github.com/marketplace/actions/coveralls-python#usage -relative_files = True - -[coverage:report] -# https://coverage.readthedocs.io/en/latest/config.html#report -show_missing = true -precision = 2 -skip_covered = true -skip_empty = true -sort = Cover - -# https://coverage.readthedocs.io/en/latest/excluding.html#advanced-exclusion -exclude_lines = - pragma: no cover - def __repr__ - if self.debug: - if settings.DEBUG - if TYPE_CHECKING: - raise AssertionError - raise NotImplementedError - if 0: - if __name__ == .__main__.: - [bandit] exclude = tests/* diff --git a/src/nitpick/plugins/ini.py b/src/nitpick/plugins/ini.py index e0c18a30..5796e993 100644 --- a/src/nitpick/plugins/ini.py +++ b/src/nitpick/plugins/ini.py @@ -37,6 +37,7 @@ class IniPlugin(NitpickPlugin): - `setup.cfg `_ - `.editorconfig `_ + - `tox.ini `_ Style examples enforcing values on INI files: :ref:`flake8 configuration `. """ @@ -112,7 +113,7 @@ def remove_top_section(multiline_text: str) -> str: return "\n".join(line for line in multiline_text.splitlines() if TOP_SECTION not in line) def get_missing_output(self) -> str: - """Get a missing output string example from the missing sections in setup.cfg.""" + """Get a missing output string example from the missing sections in an INI file.""" missing = self.missing_sections if not missing: return "" @@ -130,7 +131,7 @@ def get_missing_output(self) -> str: # TODO: convert the contents to dict (with IniConfig().sections?) and mimic other plugins doing dict diffs def enforce_rules(self) -> Iterator[Fuss]: - """Enforce rules on missing sections and missing key/value pairs in setup.cfg.""" + """Enforce rules on missing sections and missing key/value pairs in an INI file.""" try: yield from self._read_file() except Error: @@ -218,7 +219,7 @@ def enforce_comma_separated_values(self, section, key, raw_actual: Any, raw_expe def compare_different_keys(self, section, key, raw_actual: Any, raw_expected: Any) -> Iterator[Fuss]: """Compare different keys, with special treatment when they are lists or numeric.""" if isinstance(raw_actual, (int, float, bool)) or isinstance(raw_expected, (int, float, bool)): - # A boolean "True" or "true" has the same effect on setup.cfg. + # A boolean "True" or "true" has the same effect on ConfigParser files. actual = str(raw_actual).lower() expected = str(raw_expected).lower() else: diff --git a/src/nitpick/schemas.py b/src/nitpick/schemas.py index 939eb54b..197c5558 100644 --- a/src/nitpick/schemas.py +++ b/src/nitpick/schemas.py @@ -6,7 +6,7 @@ from sortedcontainers import SortedDict from nitpick import fields -from nitpick.constants import READ_THE_DOCS_URL +from nitpick.constants import READ_THE_DOCS_URL, SETUP_CFG from nitpick.generic import flatten @@ -44,7 +44,7 @@ class NitpickStylesSectionSchema(BaseNitpickSchema): class IniSchema(BaseNitpickSchema): - """Validation schema for setup.cfg.""" + """Validation schema for INI files.""" error_messages = {"unknown": help_message("Unknown configuration", "nitpick_section.html#comma-separated-values")} @@ -59,7 +59,7 @@ class NitpickFilesSectionSchema(BaseNitpickSchema): absent = fields.Dict(fields.NonEmptyString, fields.String()) present = fields.Dict(fields.NonEmptyString, fields.String()) # TODO: load this schema dynamically, then add this next field setup_cfg - setup_cfg = fields.Nested(IniSchema, data_key="setup.cfg") + setup_cfg = fields.Nested(IniSchema, data_key=SETUP_CFG) class NitpickSectionSchema(BaseNitpickSchema): diff --git a/src/nitpick/style.py b/src/nitpick/style.py index 681c9dc3..200189e5 100644 --- a/src/nitpick/style.py +++ b/src/nitpick/style.py @@ -294,7 +294,7 @@ def file_field_pair(filename: str, base_file_class: Type[NitpickPlugin]) -> Dict if base_file_class.validation_schema: field = fields.Nested(base_file_class.validation_schema, **kwargs) else: - # For default files (pyproject.toml, setup.cfg...), there is no strict schema; + # For some files (e.g.: pyproject.toml, INI files), there is no strict schema; # it can be anything they allow. # It's out of Nitpick's scope to validate those files. field = fields.Dict(fields.String, **kwargs) @@ -321,7 +321,7 @@ def rebuild_dynamic_schema(self, data: JsonDict = None) -> None: if data is None: # Data is empty; so this is the first time the dynamic class is being rebuilt. # Loop on classes with predetermined names, and add fields for them on the dynamic validation schema. - # E.g.: setup.cfg, pre-commit, pyproject.toml: files whose names we already know at this point. + # E.g.: pre-commit, pyproject.toml: files whose names we already know at this point. for subclass in fixed_name_classes: new_files_found.update(self.file_field_pair(subclass.filename, subclass)) else: diff --git a/tasks.py b/tasks.py index fd0e5231..370f1908 100644 --- a/tasks.py +++ b/tasks.py @@ -19,7 +19,7 @@ class ToxCommands: def __init__(self) -> None: self._parser = ConfigParser() - self._parser.read("setup.cfg") + self._parser.read("tox.ini") def list_commands(self, section: str) -> Iterator[str]: """List all commands in a section.""" diff --git a/tests/test_config.py b/tests/test_config.py index cd3a433a..f8767b2f 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,6 +1,7 @@ """Config tests.""" import pytest +from nitpick.constants import PYPROJECT_TOML, SETUP_CFG from nitpick.core import Nitpick from tests.helpers import ProjectMock @@ -83,10 +84,10 @@ def test_django_project_structure(tmp_path): ).touch_file( "my_django_project/manage.py" ).style( - """ - ["pyproject.toml".tool.black] + f""" + ["{PYPROJECT_TOML}".tool.black] lines = 100 - ["setup.cfg".flake8] + ["{SETUP_CFG}".flake8] some = "thing" """ ).api_check_then_apply() diff --git a/tests/test_ini.py b/tests/test_ini.py index 3ee856bc..c85c0bc3 100644 --- a/tests/test_ini.py +++ b/tests/test_ini.py @@ -1,4 +1,4 @@ -"""setup.cfg tests.""" +"""Test INI files.""" from configparser import ParsingError from unittest import mock @@ -75,11 +75,11 @@ def test_default_style_is_applied(project_with_default_style): def test_comma_separated_keys_on_style_file(tmp_path): """Comma separated keys on the style file.""" ProjectMock(tmp_path).style( - """ - [nitpick.files."setup.cfg"] + f""" + [nitpick.files."{SETUP_CFG}"] comma_separated_values = ["food.eat", "food.drink"] - ["setup.cfg".food] + ["{SETUP_CFG}".food] eat = "salt,ham,eggs" drink = "water,bier,wine" """ @@ -416,14 +416,14 @@ def test_missing_different_values_editorconfig_with_root(tmp_path): def test_invalid_configuration_comma_separated_values(tmp_path): """Test an invalid configuration for comma_separated_values.""" ProjectMock(tmp_path).style( - """ - ["setup.cfg".flake8] + f""" + ["{SETUP_CFG}".flake8] max-line-length = 85 max-complexity = 12 ignore = "D100,D101,D102,D103,D104,D105,D106,D107,D202,E203,W503" select = "E241,C,E,F,W,B,B9" - [nitpick.files."setup.cfg"] + [nitpick.files."{SETUP_CFG}"] comma_separated_values = ["flake8.ignore", "flake8.exclude"] """ ).api_check().assert_violations( @@ -446,8 +446,8 @@ def test_invalid_configuration_comma_separated_values(tmp_path): def test_invalid_section_dot_fields(tmp_path): """Test invalid section/field pairs.""" ProjectMock(tmp_path).style( - """ - [nitpick.files."setup.cfg"] + f""" + [nitpick.files."{SETUP_CFG}"] comma_separated_values = ["no_dot", "multiple.dots.here", ".filed_only", "section_only."] """ ).setup_cfg("").api_check().assert_violations( @@ -456,11 +456,11 @@ def test_invalid_section_dot_fields(tmp_path): "nitpick-style.toml", 1, " has an incorrect style. Invalid config:", - """ - nitpick.files."setup.cfg".comma_separated_values.0: Dot is missing. Use . - nitpick.files."setup.cfg".comma_separated_values.1: There's more than one dot. Use . - nitpick.files."setup.cfg".comma_separated_values.2: Empty section name. Use . - nitpick.files."setup.cfg".comma_separated_values.3: Empty field name. Use . + f""" + nitpick.files."{SETUP_CFG}".comma_separated_values.0: Dot is missing. Use . + nitpick.files."{SETUP_CFG}".comma_separated_values.1: There's more than one dot. Use . + nitpick.files."{SETUP_CFG}".comma_separated_values.2: Empty section name. Use . + nitpick.files."{SETUP_CFG}".comma_separated_values.3: Empty field name. Use . """, ) ) @@ -469,13 +469,13 @@ def test_invalid_section_dot_fields(tmp_path): def test_invalid_sections_comma_separated_values(tmp_path): """Test invalid sections on comma_separated_values.""" ProjectMock(tmp_path).style( - """ - ["setup.cfg".flake8] + f""" + ["{SETUP_CFG}".flake8] ignore = "W503,E203,FI58,PT003,C408" exclude = "venv*,**/migrations/" per-file-ignores = "tests/**.py:FI18,setup.py:FI18" - [nitpick.files."setup.cfg"] + [nitpick.files."{SETUP_CFG}"] comma_separated_values = ["flake8.ignore", "flake8.exclude", "falek8.per-file-ignores", "aaa.invalid-section"] """ ).setup_cfg( @@ -510,8 +510,8 @@ def test_multiline_comment(tmp_path): Another valid line """ ProjectMock(tmp_path).style( - """ - ["setup.cfg".flake8] + f""" + ["{SETUP_CFG}".flake8] new = "value" """ ).setup_cfg(original_file).api_apply().assert_violations( @@ -542,8 +542,8 @@ def test_duplicated_option(tmp_path): """ project = ProjectMock(tmp_path) project.style( - """ - ["setup.cfg".abc] + f""" + ["{SETUP_CFG}".abc] hard = "as a rock" """ ).setup_cfg(original_file).api_apply().assert_violations( @@ -561,7 +561,7 @@ def test_duplicated_option(tmp_path): @mock.patch.object(ConfigUpdater, "update_file") def test_simulate_parsing_error_when_saving(update_file, tmp_path): - """Simulate a parsing error when saving setup.cfg.""" + """Simulate a parsing error when saving an INI file.""" update_file.side_effect = ParsingError(source="simulating a captured error") original_file = """ @@ -569,8 +569,8 @@ def test_simulate_parsing_error_when_saving(update_file, tmp_path): existing = value """ ProjectMock(tmp_path).style( - """ - ["setup.cfg".flake8] + f""" + ["{SETUP_CFG}".flake8] new = "value" """ ).setup_cfg(original_file).api_apply().assert_violations( @@ -619,8 +619,7 @@ def test_generic_ini_with_missing_header(tmp_path): ": parsing error (MissingSectionHeaderError): File contains no section headers.\n" f"file: {project.path_for('generic.ini')!r}, line: 1\n" "'this_key_is_invalid = for a generic .ini (it should always have a section)\\n'", - ), + ) ).assert_file_contents( - "generic.ini", - expected_generic_ini, + "generic.ini", expected_generic_ini ) diff --git a/tests/test_pre_commit.py b/tests/test_pre_commit.py index 83e441e2..9bd19386 100644 --- a/tests/test_pre_commit.py +++ b/tests/test_pre_commit.py @@ -4,7 +4,7 @@ import pytest from testfixtures import compare -from nitpick.constants import PRE_COMMIT_CONFIG_YAML +from nitpick.constants import PRE_COMMIT_CONFIG_YAML, SETUP_CFG from nitpick.plugins.pre_commit import PreCommitHook from nitpick.violations import Fuss from tests.helpers import NBSP, ProjectMock @@ -249,12 +249,12 @@ def test_style_missing_hooks_in_repo(tmp_path): def test_style_missing_id_in_hook(tmp_path): """Test style file is missing id in hook.""" ProjectMock(tmp_path).style( - ''' + f''' [[".pre-commit-config.yaml".repos]] repo = "another" hooks = """ - name: isort - entry: isort -sp setup.cfg + entry: isort -sp {SETUP_CFG} """ ''' ).pre_commit( @@ -272,7 +272,7 @@ def test_style_missing_id_in_hook(tmp_path): ": style file is missing 'id' in hook:", f""" {NBSP*4}name: isort - {NBSP*4}entry: isort -sp setup.cfg + {NBSP*4}entry: isort -sp {SETUP_CFG} """, ) ) diff --git a/tests/test_style.py b/tests/test_style.py index 61cd40e1..e814a44b 100644 --- a/tests/test_style.py +++ b/tests/test_style.py @@ -8,7 +8,7 @@ import pytest import responses -from nitpick.constants import DOT_SLASH, PYPROJECT_TOML, READ_THE_DOCS_URL, TOML_EXTENSION +from nitpick.constants import DOT_SLASH, PYPROJECT_TOML, READ_THE_DOCS_URL, SETUP_CFG, TOML_EXTENSION from nitpick.violations import Fuss from tests.helpers import SUGGESTION_BEGIN, SUGGESTION_END, XFAIL_ON_WINDOWS, ProjectMock, assert_conditions @@ -21,23 +21,23 @@ def test_multiple_styles_overriding_values(offline, tmp_path): """Test multiple style files with precedence (the latest ones overrides the previous ones).""" ProjectMock(tmp_path).named_style( "isort1", - """ - ["setup.cfg".isort] + f""" + ["{SETUP_CFG}".isort] line_length = 80 known_first_party = "tests" xxx = "aaa" """, ).named_style( "styles/isort2", - """ - ["setup.cfg".isort] + f""" + ["{SETUP_CFG}".isort] line_length = 120 xxx = "yyy" """, ).named_style( "flake8", - """ - ["setup.cfg".flake8] + f""" + ["{SETUP_CFG}".flake8] inline-quotes = "double" something = 123 """, @@ -71,7 +71,7 @@ def test_multiple_styles_overriding_values(offline, tmp_path): """ ).assert_errors_contain( f""" - NIP321 File setup.cfg was not found. Create it with this content:{SUGGESTION_BEGIN} + NIP321 File {SETUP_CFG} was not found. Create it with this content:{SUGGESTION_BEGIN} [flake8] inline-quotes = double something = 123 @@ -82,9 +82,9 @@ def test_multiple_styles_overriding_values(offline, tmp_path): xxx = yyy{SUGGESTION_END} """ ).cli_ls( - """ - pyproject.toml - setup.cfg + f""" + {PYPROJECT_TOML} + {SETUP_CFG} """ ) @@ -94,29 +94,29 @@ def test_include_styles_overriding_values(offline, tmp_path): """One style file can include another (also recursively). Ignore styles that were already included.""" ProjectMock(tmp_path).named_style( "isort1", - """ + f""" [nitpick.styles] include = "styles/isort2.toml" - ["setup.cfg".isort] + ["{SETUP_CFG}".isort] line_length = 80 known_first_party = "tests" xxx = "aaa" """, ).named_style( "styles/isort2", - """ + f""" [nitpick.styles] include = ["styles/isort2.toml", "flake8.toml"] - ["setup.cfg".isort] + ["{SETUP_CFG}".isort] line_length = 120 xxx = "yyy" """, ).named_style( "flake8", - """ + f""" [nitpick.styles] include = ["black.toml"] - ["setup.cfg".flake8] + ["{SETUP_CFG}".flake8] inline-quotes = "double" something = 123 """, @@ -143,7 +143,7 @@ def test_include_styles_overriding_values(offline, tmp_path): """ ).assert_errors_contain( f""" - NIP321 File setup.cfg was not found. Create it with this content:{SUGGESTION_BEGIN} + NIP321 File {SETUP_CFG} was not found. Create it with this content:{SUGGESTION_BEGIN} [flake8] inline-quotes = double something = 123 @@ -495,8 +495,8 @@ def test_merge_styles_into_single_file(offline, tmp_path): """Merge all styles into a single TOML file on the cache dir. Also test merging lists (pre-commit repos).""" ProjectMock(tmp_path).load_styles("black", "isort").named_style( "isort_overrides", - """ - ["setup.cfg".isort] + f""" + ["{SETUP_CFG}".isort] another_key = "some value" multi_line_output = 6 """, @@ -508,11 +508,11 @@ def test_merge_styles_into_single_file(offline, tmp_path): ).flake8( offline=offline ).assert_merged_style( - ''' + f''' ["pyproject.toml".tool.black] line-length = 120 - ["setup.cfg".isort] + ["{SETUP_CFG}".isort] line_length = 120 skip = ".tox,build" known_first_party = "tests" @@ -578,8 +578,8 @@ def test_invalid_tool_nitpick_on_pyproject_toml(offline, tmp_path): def test_invalid_toml(tmp_path): """Invalid TOML should emit a NIP warning, not raise TomlDecodeError.""" ProjectMock(tmp_path).style( - """ - ["setup.cfg".flake8] + f""" + ["{SETUP_CFG}".flake8] ignore = D100,D104,D202,E203,W503 """ ).api_check_then_apply( diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..e1255318 --- /dev/null +++ b/tox.ini @@ -0,0 +1,117 @@ +[tox] +# https://tox.readthedocs.io/en/latest/config.html +isolated_build = True +envlist = clean,lint,py39,py38,py37,py36,docs,report + +[gh-actions] +# https://github.com/ymyzk/tox-gh-actions +python = + 3.9: py39 + 3.8: py38, clean, lint, docs, report + 3.7: py37 + 3.6: py36 + +[testenv] +description = Run tests with pytest and coverage +extras = test +depends = + {py39,py38,py37,py36}: clean + report: py39,py38,py37,py36 +setenv = + PY_IGNORE_IMPORTMISMATCH = 1 +commands = + python -m pip --version + # https://pytest-cov.readthedocs.io/en/latest/config.html#caveats + # https://docs.pytest.org/en/stable/skipping.html + python -m pytest --cov-config=tox.ini --cov --cov-append --cov-report=term-missing --doctest-modules -s -rxXs {posargs:-vv} + +[testenv:clean] +description = Erase data for the coverage report before running tests +platform = linux|darwin +skip_install = true +deps = coverage +commands = coverage erase + +[testenv:lint] +description = Lint all files with pre-commit +basepython = python3.8 +platform = linux|darwin +# pylint needs both these extras: +extras = + # Install pylint itself + lint + # For pylint to inspect tests + test +deps = + pip>=19.2 + safety +# Run nitpick and pylint with tox, because local repos don't seem to work well with https://pre-commit.ci/ +commands = + # Run Nitpick locally on itself + flake8 --select=NIP + pylint src/ + safety check + +[testenv:report] +description = Coverage report +platform = linux|darwin +skip_install = true +deps = coverage +commands = + coverage report + coverage html + +[testenv:docs] +description = Build the HTML docs using Sphinx (sphinx-build, API docs, link checks) +basepython = python3.8 +platform = linux|darwin +extras = doc +commands = + python3 docs/generate_rst.py + sphinx-apidoc --force --follow-links --module-first --separate --implicit-namespaces --ext-autodoc --ext-doctest --ext-intersphinx --ext-todo --ext-coverage --ext-imgmath --ext-mathjax --ext-ifconfig --ext-viewcode --ext-githubpages --output-dir docs/source src/nitpick/ + # To stop failing when a page is unreachable, add a hyphen at the start of the line: + # https://tox.readthedocs.io/en/latest/example/basic.html#ignoring-exit-code + # Some errors during link check have to be ignored. + # E.g.: when a new TOML style is added, its link will be broken until a new release is published. + - sphinx-build --color -j auto -b linkcheck docs "{toxworkdir}/docs_out" + # Use these options to debug Sphinx: -nWT --keep-going -vvv + sphinx-build --color -j auto -d "{toxworkdir}/docs_doctree" -b html docs "{toxworkdir}/docs_out" {posargs} + +[pytest] +# https://docs.pytest.org/en/stable/customize.html#tox-ini +addopts = -v +norecursedirs = .* build dist CVS _darcs {arch} *.egg venv var docs +# https://docs.pytest.org/en/stable/reference.html#confval-testpaths +testpaths = src tests + +[coverage:run] +# https://coverage.readthedocs.io/en/latest/config.html#run +branch = true +parallel = true +source = src/ +omit = + tests/* + .tox/* + */pypoetry/virtualenvs/* +# This config is needed by https://github.com/marketplace/actions/coveralls-python#usage +relative_files = True + +[coverage:report] +# https://coverage.readthedocs.io/en/latest/config.html#report +show_missing = true +precision = 2 +skip_covered = true +skip_empty = true +sort = Cover + +# https://coverage.readthedocs.io/en/latest/excluding.html#advanced-exclusion +exclude_lines = + pragma: no cover + def __repr__ + if self.debug: + if settings.DEBUG + if TYPE_CHECKING: + raise AssertionError + raise NotImplementedError + if 0: + if __name__ == .__main__.: From 5b2355abc075aab7a4ec49d6b79365c3cd7bbefb Mon Sep 17 00:00:00 2001 From: "Augusto W. Andreoli" Date: Tue, 9 Mar 2021 11:16:40 +0100 Subject: [PATCH 2/5] build: fix version on .bumpversion.cfg --- .bumpversion.cfg | 2 +- package.json | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 94d058f6..9516c2b4 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.24.0 +current_version = 0.24.1 commit = False tag = False diff --git a/package.json b/package.json index 7a29d91b..128e1f6f 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ [ "@semantic-release/exec", { - "prepareCmd": "bumpversion --allow-dirty --no-commit --no-tag --new-version ${nextRelease.version} patch && pre-commit run --all-files end-of-file-fixer || true && rm -rf dist/ && poetry build && twine upload --verbose --disable-progress-bar --skip-existing --password $TWINE_TEST_PASSWORD -r testpypi dist/*", + "prepareCmd": "bumpversion --allow-dirty --no-commit --no-tag --new-version ${nextRelease.version} patch && pre-commit run --all-files || true && rm -rf dist/ && poetry build && twine upload --verbose --disable-progress-bar --skip-existing --password $TWINE_TEST_PASSWORD -r testpypi dist/*", "publishCmd": "twine upload --verbose --disable-progress-bar --skip-existing dist/*" } ], @@ -27,7 +27,7 @@ "@semantic-release/git", { "assets": [ - "setup.cfg", + ".bumpversion.cfg", "src/nitpick/__init__.py", "pyproject.toml", "package.json", @@ -55,9 +55,9 @@ "cli", "flake8", "style", - "pre-commit", - "pyproject.toml", - "setup.cfg", + "yaml", + "toml", + "ini", "json", "deps", "deps-dev", From 772cdb3995dfe2ba6c642354969c4d7d12f6e225 Mon Sep 17 00:00:00 2001 From: "Augusto W. Andreoli" Date: Tue, 9 Mar 2021 11:31:28 +0100 Subject: [PATCH 3/5] style: enforce configs on tox.ini --- nitpick-style.toml | 1 + styles/tox.toml | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 styles/tox.toml diff --git a/nitpick-style.toml b/nitpick-style.toml index 77d87318..3b8d734f 100644 --- a/nitpick-style.toml +++ b/nitpick-style.toml @@ -19,6 +19,7 @@ include = [ "styles/isort", "styles/mypy", "styles/pytest", + "styles/tox", "styles/package-json", "styles/editorconfig", ] diff --git a/styles/tox.toml b/styles/tox.toml new file mode 100644 index 00000000..04a92eee --- /dev/null +++ b/styles/tox.toml @@ -0,0 +1,28 @@ +["tox.ini".tox] +# https://tox.readthedocs.io/en/latest/config.html +isolated_build = true + +["tox.ini".testenv] +description = "Run tests with pytest and coverage" +extras = "test" + +["tox.ini"."coverage:run"] +# https://coverage.readthedocs.io/en/latest/config.html#run +branch = true +parallel = true +source = "src/" +# TODO: deal with multiline INI values in https://github.com/andreoliwa/nitpick/issues/271 +#omit = """tests/* +#.tox/* +#*/pypoetry/virtualenvs/* +#""" +# This config is needed by https://github.com/marketplace/actions/coveralls-python#usage +relative_files = true + +["tox.ini"."coverage:report"] +# https://coverage.readthedocs.io/en/latest/config.html#report +show_missing = true +precision = 2 +skip_covered = true +skip_empty = true +sort = "Cover" From e40c205975ca28a07a0622f64a7d474313d26049 Mon Sep 17 00:00:00 2001 From: "Augusto W. Andreoli" Date: Tue, 9 Mar 2021 11:52:23 +0100 Subject: [PATCH 4/5] docs: add tox to the examples, check links after other commands --- docs/examples.rst | 38 ++++++++++++++++++++++++++++++++++++++ docs/generate_rst.py | 1 + docs/plugins.rst | 1 + docs/targets.rst | 23 ++++++++++++----------- src/nitpick/plugins/ini.py | 2 +- tasks.py | 5 +++-- tox.ini | 7 +++++-- 7 files changed, 61 insertions(+), 16 deletions(-) diff --git a/docs/examples.rst b/docs/examples.rst index aa1c4237..2b30aa17 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -412,3 +412,41 @@ Contents of `styles/python39.toml `_: + +.. code-block:: toml + + ["tox.ini".tox] + # https://tox.readthedocs.io/en/latest/config.html + isolated_build = true + + ["tox.ini".testenv] + description = "Run tests with pytest and coverage" + extras = "test" + + ["tox.ini"."coverage:run"] + # https://coverage.readthedocs.io/en/latest/config.html#run + branch = true + parallel = true + source = "src/" + # TODO: deal with multiline INI values in https://github.com/andreoliwa/nitpick/issues/271 + #omit = """tests/* + #.tox/* + #*/pypoetry/virtualenvs/* + #""" + # This config is needed by https://github.com/marketplace/actions/coveralls-python#usage + relative_files = true + + ["tox.ini"."coverage:report"] + # https://coverage.readthedocs.io/en/latest/config.html#report + show_missing = true + precision = 2 + skip_covered = true + skip_empty = true + sort = "Cover" diff --git a/docs/generate_rst.py b/docs/generate_rst.py index 5893459e..edb33889 100644 --- a/docs/generate_rst.py +++ b/docs/generate_rst.py @@ -46,6 +46,7 @@ "python37.toml": "Python 3.7", "python38.toml": "Python 3.8", "python39.toml": "Python 3.9", + "tox.toml": "tox_", } ) CLI_MAPPING = [ diff --git a/docs/plugins.rst b/docs/plugins.rst index 22e027a4..2f503df5 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -45,6 +45,7 @@ Examples of ``.ini`` files handled by this plugin: - `setup.cfg `_ - `.editorconfig `_ +- `tox.ini `_ Style examples enforcing values on INI files: :ref:`flake8 configuration `. diff --git a/docs/targets.rst b/docs/targets.rst index d3c2d9eb..b28f5f58 100644 --- a/docs/targets.rst +++ b/docs/targets.rst @@ -8,20 +8,21 @@ .. _Bash: https://www.gnu.org/software/bash/ .. _black: https://github.com/psf/black .. _commitlint: https://commitlint.js.org/ -.. _Django: https://www.djangoproject.com +.. _Django: https://github.com/django/django .. _EditorConfig: https://editorconfig.org -.. _flake8: https://gitlab.com/pycqa/flake8/ +.. _flake8: https://gitlab.com/pycqa/flake8 .. _Flask CLI: https://flask.palletsprojects.com/en/1.1.x/cli/ .. _Invoke: https://github.com/pyinvoke/invoke -.. _IPython: https://ipython.org -.. _isort: https://github.com/PyCQA/isort/ -.. _mypy: https://github.com/python/mypy/ -.. _Nitpick: https://github.com/andreoliwa/nitpick/ +.. _IPython: https://github.com/ipython/ipython +.. _isort: https://github.com/PyCQA/isort +.. _mypy: https://github.com/python/mypy +.. _Nitpick: https://github.com/andreoliwa/nitpick .. _package.json: https://docs.npmjs.com/files/package.json -.. _Pipenv: https://github.com/pypa/pipenv/ +.. _Pipenv: https://github.com/pypa/pipenv .. _pipx: https://github.com/pipxproject/pipx -.. _Poetry: https://github.com/python-poetry/poetry/ -.. _pre-commit: https://pre-commit.com/ -.. _Pylint: https://www.pylint.org -.. _pytest: https://pytest.org/ +.. _Poetry: https://github.com/python-poetry/poetry +.. _pre-commit: https://github.com/pre-commit/pre-commit +.. _Pylint: https://github.com/PyCQA/pylint +.. _pytest: https://github.com/pytest-dev/pytest .. _TOML: https://github.com/toml-lang/toml +.. _tox: https://github.com/tox-dev/tox diff --git a/src/nitpick/plugins/ini.py b/src/nitpick/plugins/ini.py index 5796e993..74148777 100644 --- a/src/nitpick/plugins/ini.py +++ b/src/nitpick/plugins/ini.py @@ -37,7 +37,7 @@ class IniPlugin(NitpickPlugin): - `setup.cfg `_ - `.editorconfig `_ - - `tox.ini `_ + - `tox.ini `_ Style examples enforcing values on INI files: :ref:`flake8 configuration `. """ diff --git a/tasks.py b/tasks.py index 370f1908..e26a898c 100644 --- a/tasks.py +++ b/tasks.py @@ -155,12 +155,13 @@ def doc(c, full=False, recreate=False, links=False, open=False, debug=False): c.run(f"poetry run {tox.api}") if debug: c.run("poetry run sphinx-apidoc --help") - if links: - c.run(f"poetry run {tox.check_links}", warn=True) debug_options = "-nWT --keep-going -vvv" if debug else "" c.run(f"poetry run {tox.html_docs} {debug_options}") + if links: + c.run(f"poetry run {tox.check_links}", warn=True) + if open: c.run(f"open {DOCS_BUILD_PATH}/docs_out/index.html") diff --git a/tox.ini b/tox.ini index e1255318..2759bb47 100644 --- a/tox.ini +++ b/tox.ini @@ -69,13 +69,16 @@ extras = doc commands = python3 docs/generate_rst.py sphinx-apidoc --force --follow-links --module-first --separate --implicit-namespaces --ext-autodoc --ext-doctest --ext-intersphinx --ext-todo --ext-coverage --ext-imgmath --ext-mathjax --ext-ifconfig --ext-viewcode --ext-githubpages --output-dir docs/source src/nitpick/ + + # Use these options to debug Sphinx: -nWT --keep-going -vvv + sphinx-build --color -j auto -d "{toxworkdir}/docs_doctree" -b html docs "{toxworkdir}/docs_out" {posargs} + + # Run link checks after building the docs # To stop failing when a page is unreachable, add a hyphen at the start of the line: # https://tox.readthedocs.io/en/latest/example/basic.html#ignoring-exit-code # Some errors during link check have to be ignored. # E.g.: when a new TOML style is added, its link will be broken until a new release is published. - sphinx-build --color -j auto -b linkcheck docs "{toxworkdir}/docs_out" - # Use these options to debug Sphinx: -nWT --keep-going -vvv - sphinx-build --color -j auto -d "{toxworkdir}/docs_doctree" -b html docs "{toxworkdir}/docs_out" {posargs} [pytest] # https://docs.pytest.org/en/stable/customize.html#tox-ini From 610f83294983d22dc0369d6f7922b7cf2e039c9f Mon Sep 17 00:00:00 2001 From: "Augusto W. Andreoli" Date: Tue, 9 Mar 2021 16:27:00 +0100 Subject: [PATCH 5/5] test: tox.ini is applied with the default style --- src/nitpick/constants.py | 1 + tests/test_generic.py | 3 ++- tests/test_ini.py | 30 +++++++++++++++++++++++++++--- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/nitpick/constants.py b/src/nitpick/constants.py index 2348b250..808e776b 100644 --- a/src/nitpick/constants.py +++ b/src/nitpick/constants.py @@ -21,6 +21,7 @@ MANAGE_PY = "manage.py" PRE_COMMIT_CONFIG_YAML = ".pre-commit-config.yaml" EDITOR_CONFIG = ".editorconfig" +TOX_INI = "tox.ini" SINGLE_QUOTE = "'" DOUBLE_QUOTE = '"' diff --git a/tests/test_generic.py b/tests/test_generic.py index 750addbd..e90558e5 100644 --- a/tests/test_generic.py +++ b/tests/test_generic.py @@ -6,6 +6,7 @@ from testfixtures import compare +from nitpick.constants import TOX_INI from nitpick.generic import MergeDict, flatten, get_subclasses, relative_to_current_dir from tests.helpers import assert_conditions @@ -93,7 +94,7 @@ def test_relative_to_current_dir(home, cwd): Path(project_dir): "", f"{home_dir}{sep}another": f"{home_dir}{sep}another", Path(f"{home_dir}{sep}bla{sep}bla"): f"{home_dir}{sep}bla{sep}bla", - f"{project_dir}{sep}tox.ini": "tox.ini", + f"{project_dir}{sep}{TOX_INI}": TOX_INI, Path(f"{project_dir}{sep}apps{sep}manage.py"): f"apps{sep}manage.py", f"{home_dir}{sep}another{sep}one{sep}bites.py": f"{home_dir}{sep}another{sep}one{sep}bites.py", Path(f"{home_dir}{sep}bla{sep}bla.txt"): f"{home_dir}{sep}bla{sep}bla.txt", diff --git a/tests/test_ini.py b/tests/test_ini.py index c85c0bc3..c711a1ca 100644 --- a/tests/test_ini.py +++ b/tests/test_ini.py @@ -4,7 +4,7 @@ from configupdater import ConfigUpdater -from nitpick.constants import EDITOR_CONFIG, SETUP_CFG +from nitpick.constants import EDITOR_CONFIG, SETUP_CFG, TOX_INI from nitpick.plugins.ini import IniPlugin, Violations from nitpick.violations import Fuss, SharedViolations from tests.helpers import XFAIL_ON_WINDOWS, ProjectMock @@ -65,11 +65,35 @@ def test_default_style_is_applied(project_with_default_style): [Makefile] indent_style = tab """ + expected_tox_ini = """ + [coverage:report] + precision = 2 + show_missing = True + skip_covered = True + skip_empty = True + sort = Cover + + [coverage:run] + branch = True + parallel = True + relative_files = True + source = src/ + + [testenv] + description = Run tests with pytest and coverage + extras = test + + [tox] + isolated_build = True + """ project_with_default_style.api_check_then_apply( Fuss(True, SETUP_CFG, 321, " was not found. Create it with this content:", expected_setup_cfg), Fuss(True, EDITOR_CONFIG, 321, " was not found. Create it with this content:", expected_editor_config), - partial_names=[SETUP_CFG, EDITOR_CONFIG], - ).assert_file_contents(SETUP_CFG, expected_setup_cfg) + Fuss(True, TOX_INI, 321, " was not found. Create it with this content:", expected_tox_ini), + partial_names=[SETUP_CFG, EDITOR_CONFIG, TOX_INI], + ).assert_file_contents( + SETUP_CFG, expected_setup_cfg, EDITOR_CONFIG, expected_editor_config, TOX_INI, expected_tox_ini + ) def test_comma_separated_keys_on_style_file(tmp_path):