From 5e4d8e3dac1d35a8d47a80d7b630637b229e472a Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 23 Mar 2020 23:56:39 +0300 Subject: [PATCH 1/7] Adds --baseline --- .gitignore | 3 + poetry.lock | 17 +- pyproject.toml | 1 + setup.cfg | 1 + tests/conftest.py | 1 + .../conftest.py => plugins/files.py} | 18 +- tests/plugins/tokenize_parser.py | 3 +- .../test_baseline/test_baseline_command.py | 173 ++++++++++++++++++ tests/test_plugins.py | 4 - .../test_comments/test_shebang.py | 36 +++- wemake_python_styleguide/checker.py | 3 + wemake_python_styleguide/compat/packaging.py | 2 - wemake_python_styleguide/logic/baseline.py | 120 ++++++++++++ wemake_python_styleguide/logic/system.py | 2 - wemake_python_styleguide/options/config.py | 11 ++ wemake_python_styleguide/options/defaults.py | 3 + .../options/validation.py | 1 + wemake_python_styleguide/patches/__init__.py | 0 wemake_python_styleguide/patches/baseline.py | 135 ++++++++++++++ wemake_python_styleguide/types.py | 4 + .../violations/complexity.py | 7 +- 21 files changed, 513 insertions(+), 32 deletions(-) rename tests/{test_visitors/test_tokenize/test_comments/conftest.py => plugins/files.py} (59%) create mode 100644 tests/test_pathes/test_baseline/test_baseline_command.py create mode 100644 wemake_python_styleguide/logic/baseline.py create mode 100644 wemake_python_styleguide/patches/__init__.py create mode 100644 wemake_python_styleguide/patches/baseline.py diff --git a/.gitignore b/.gitignore index 167e8d932..3a0dd8a09 100644 --- a/.gitignore +++ b/.gitignore @@ -203,3 +203,6 @@ fabric.properties # Used for testing: ex.py setup.py + +# Usually you don't need to gitignore this file, but we use it for testing: +/.flake8-baseline.json diff --git a/poetry.lock b/poetry.lock index 3d8a66f89..38d5e0835 100644 --- a/poetry.lock +++ b/poetry.lock @@ -369,6 +369,17 @@ version = "19.8.0" attrs = "*" flake8 = ">=3.0.0" +[[package]] +category = "dev" +description = "Adds coding magic comment checks to flake8" +name = "flake8-coding" +optional = false +python-versions = "*" +version = "1.3.2" + +[package.dependencies] +flake8 = "*" + [[package]] category = "main" description = "Flake8 lint for trailing commas." @@ -1624,7 +1635,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["jaraco.itertools", "func-timeout"] [metadata] -content-hash = "25d04c702b28cfc663b887aa1764709f30a07753b9677e986254830ff4277957" +content-hash = "9fe876aede33595424a1c277ed5b5dc79cc765f11635e70fe423e47229b236dc" python-versions = "^3.6" [metadata.files] @@ -1792,6 +1803,10 @@ flake8-bugbear = [ {file = "flake8-bugbear-19.8.0.tar.gz", hash = "sha256:d8c466ea79d5020cb20bf9f11cf349026e09517a42264f313d3f6fddb83e0571"}, {file = "flake8_bugbear-19.8.0-py35.py36.py37-none-any.whl", hash = "sha256:ded4d282778969b5ab5530ceba7aa1a9f1b86fa7618fc96a19a1d512331640f8"}, ] +flake8-coding = [ + {file = "flake8-coding-1.3.2.tar.gz", hash = "sha256:b8f4d5157a8f74670e6cfea732c3d9f4291a4e994c8701d2c55f787c6e6cb741"}, + {file = "flake8_coding-1.3.2-py2.py3-none-any.whl", hash = "sha256:79704112c44d09d4ab6c8965e76a20c3f7073d52146db60303bce777d9612260"}, +] flake8-commas = [ {file = "flake8-commas-2.0.0.tar.gz", hash = "sha256:d3005899466f51380387df7151fb59afec666a0f4f4a2c6a8995b975de0f44b7"}, {file = "flake8_commas-2.0.0-py2.py3-none-any.whl", hash = "sha256:ee2141a3495ef9789a3894ed8802d03eff1eaaf98ce6d8653a7c573ef101935e"}, diff --git a/pyproject.toml b/pyproject.toml index f3fe75e8b..15573b453 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -106,3 +106,4 @@ ipdb = "^0.12.3" ipython = "^7.10" astboom = "^0.4.2" tokelor = "^0.1.4" +flake8-coding = "^1.3.2" diff --git a/setup.cfg b/setup.cfg index bda17d33b..bf239fe8d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,6 +21,7 @@ max-line-length = 80 # Self settings: max-imports = 16 +no-accept-encodings = True # Excluding some directories: exclude = diff --git a/tests/conftest.py b/tests/conftest.py index bcc138889..4cf497a48 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,6 +6,7 @@ from wemake_python_styleguide.options.config import Configuration pytest_plugins = [ + 'plugins.files', 'plugins.violations', 'plugins.ast_tree', 'plugins.tokenize_parser', diff --git a/tests/test_visitors/test_tokenize/test_comments/conftest.py b/tests/plugins/files.py similarity index 59% rename from tests/test_visitors/test_tokenize/test_comments/conftest.py rename to tests/plugins/files.py index 0a90449c7..f84b2a7f9 100644 --- a/tests/test_visitors/test_tokenize/test_comments/conftest.py +++ b/tests/plugins/files.py @@ -1,12 +1,10 @@ -# -*- coding: utf-8 -*- - from os import chmod import pytest -TEMP_FOLDER = 'tmp' -MODE_EXECUTABLE = 0o755 -MODE_NON_EXECUTABLE = 0o644 +_TEMP_FOLDER = 'tmp' +_MODE_EXECUTABLE = 0o755 +_MODE_NON_EXECUTABLE = 0o644 @pytest.fixture() @@ -15,16 +13,16 @@ def make_file(tmp_path): def factory( filename: str, file_content: str, - is_executable: bool, + *, + is_executable: bool = False, ) -> str: - temp_folder = tmp_path / TEMP_FOLDER - temp_folder.mkdir() + temp_folder = tmp_path / _TEMP_FOLDER + temp_folder.mkdir(exist_ok=True) test_file = temp_folder / filename - file_mode = MODE_EXECUTABLE if is_executable else MODE_NON_EXECUTABLE + file_mode = _MODE_EXECUTABLE if is_executable else _MODE_NON_EXECUTABLE test_file.write_text(file_content) chmod(test_file.as_posix(), file_mode) return test_file.as_posix() - return factory diff --git a/tests/plugins/tokenize_parser.py b/tests/plugins/tokenize_parser.py index 9c5df52f8..0ad9fb832 100644 --- a/tests/plugins/tokenize_parser.py +++ b/tests/plugins/tokenize_parser.py @@ -19,6 +19,5 @@ def parse_file_tokens(parse_tokens): """Parses tokens from a file.""" def factory(filename: str): with open(filename, 'r', encoding='utf-8') as test_file: - file_content = test_file.read() - return parse_tokens(file_content) + return parse_tokens(test_file.read()) return factory diff --git a/tests/test_pathes/test_baseline/test_baseline_command.py b/tests/test_pathes/test_baseline/test_baseline_command.py new file mode 100644 index 000000000..0bef1285e --- /dev/null +++ b/tests/test_pathes/test_baseline/test_baseline_command.py @@ -0,0 +1,173 @@ +""" +We use this example together with the existing baseline. + +Here are several important things about this example: + +1. There are two violations with the same code and message: ``E225`` + +2. There are two violations with the same code, + but different message: ``WPS110`` + +3. There's a unique violation: ``WPS303`` + +All violations in this example are covered by the baseline. +""" + +import os +import subprocess + +baseline = """ +{ + "paths": { + "wrong.py": { + "132954ef45e1a84ab72bb6e30126a117": 1, + "71b49f6407bbc09ac76c372014207dfb": 1, + "a37c5ca31e3d75d49a018c0bd3ff83f5": 1, + "dd2402e2213add848d53f8580452417e": 2 + } + } +} +""" + +code_template = """ +value =1 +result= 2 +undescored_number = 10_0 +{0} +""" + + +def _assert_output(output: str, errors): + for error_code, error_count in errors.items(): + assert output.count(error_code) == error_count + + +def test_without_baseline(make_file): + """End-to-End test for no baseline, regular mode.""" + filename = make_file('wrong.py', code_template.format('')) + + process = subprocess.Popen( + [ + 'flake8', + '--isolated', + '--baseline', + '--select', + 'WPS,E', + 'wrong.py', + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + encoding='utf8', + cwd=os.path.dirname(filename), + ) + output, _ = process.communicate() + + _assert_output(output, {'WPS110': 2, 'WPS303': 1, 'E225': 2}) + assert process.returncode == 1 + + +def test_with_baseline(make_file): + """End-to-End test for baseline generation.""" + filename = make_file('wrong.py', code_template.format('')) + make_file('.flake8-baseline.json', baseline) + + process = subprocess.Popen( + [ + 'flake8', + '--isolated', + '--baseline', + '--select', + 'WPS,E', + 'wrong.py', + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + encoding='utf8', + cwd=os.path.dirname(filename), + ) + output, _ = process.communicate() + + assert output == '' + assert process.returncode == 0 + + +def test_with_baseline_empty(make_file): + """End-to-End test that removed violations are fine.""" + filename = make_file('wrong.py', '_SOME_CONSTANT = 1') + make_file('.flake8-baseline.json', baseline) + + process = subprocess.Popen( + [ + 'flake8', + '--isolated', + '--baseline', + '--select', + 'WPS,E', + 'wrong.py', + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + encoding='utf8', + cwd=os.path.dirname(filename), + ) + output, _ = process.communicate() + + assert output == '' + assert process.returncode == 0 + + +def test_with_baseline_new_violations(make_file): + """End-to-End test to test that baseline still generates new violations.""" + filename = make_file('wrong.py', code_template.format('x = 1')) + make_file('.flake8-baseline.json', baseline) + + process = subprocess.Popen( + [ + 'flake8', + '--isolated', + '--baseline', + '--select', + 'WPS,E', + 'wrong.py', + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + encoding='utf8', + cwd=os.path.dirname(filename), + ) + output, _ = process.communicate() + + _assert_output(output, {'WPS111': 1}) + assert process.returncode == 1 + + +def test_with_baseline_new_files(make_file): + """End-to-End test to test that baseline still generates new violations.""" + filename = make_file('wrong.py', code_template.format('')) + make_file('correct.py', 'SOME_CONSTANT = 1') + make_file('.flake8-baseline.json', baseline) + + process = subprocess.Popen( + [ + 'flake8', + '--isolated', + '--baseline', + '--select', + 'WPS,E', + 'wrong.py', + 'correct.py', + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + encoding='utf8', + cwd=os.path.dirname(filename), + ) + output, _ = process.communicate() + + assert output == '' + assert process.returncode == 0 diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 9aa54dd7c..35dfd5cc0 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -43,8 +43,6 @@ def test_external_plugins(absolute_path): 'flake8', '--disable-noqa', '--isolated', - '--enable-extensions', - 'G', filename, ], stdout=subprocess.PIPE, @@ -77,8 +75,6 @@ def test_external_plugins_diff(absolute_path): 'flake8', '--disable-noqa', '--isolated', - '--enable-extensions', - 'G', '--diff', # is required to test diffs! ;) '--exit-zero', # to allow failures ], diff --git a/tests/test_visitors/test_tokenize/test_comments/test_shebang.py b/tests/test_visitors/test_tokenize/test_comments/test_shebang.py index 6335a3067..103790e53 100644 --- a/tests/test_visitors/test_tokenize/test_comments/test_shebang.py +++ b/tests/test_visitors/test_tokenize/test_comments/test_shebang.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import pytest from wemake_python_styleguide.violations.best_practices import ShebangViolation @@ -34,7 +32,11 @@ def test_correct_shebang_executable1( executable, ): """Testing cases when no errors should be reported.""" - path_to_file = make_file('test_file.py', template.format(code), executable) + path_to_file = make_file( + 'test_file.py', + template.format(code), + is_executable=executable, + ) file_tokens = parse_file_tokens(path_to_file) visitor = comments.ShebangVisitor( @@ -67,7 +69,11 @@ def test_correct_shebang_executable2( executable, ): """Testing cases when no errors should be reported.""" - path_to_file = make_file('test_file.py', template.format(code), executable) + path_to_file = make_file( + 'test_file.py', + template.format(code), + is_executable=executable, + ) file_tokens = parse_file_tokens(path_to_file) visitor = comments.ShebangVisitor( @@ -104,7 +110,11 @@ def test_shebang_on_windows( ): """Testing cases when no errors should be reported.""" monkeypatch.setattr(comments, 'is_windows', lambda: True) - path_to_file = make_file('test_file.py', template.format(code), executable) + path_to_file = make_file( + 'test_file.py', + template.format(code), + is_executable=executable, + ) file_tokens = parse_file_tokens(path_to_file) visitor = comments.ShebangVisitor( @@ -140,7 +150,11 @@ def test_shebang_with_stdin( executable, ): """Testing cases when no errors should be reported.""" - path_to_file = make_file('test_file.py', template.format(code), executable) + path_to_file = make_file( + 'test_file.py', + template.format(code), + is_executable=executable, + ) file_tokens = parse_file_tokens(path_to_file) visitor = comments.ShebangVisitor( @@ -171,7 +185,11 @@ def test_wrong_shebang_executable( executable, ): """Testing cases when no errors should be reported.""" - path_to_file = make_file('test_file.py', template.format(code), executable) + path_to_file = make_file( + 'test_file.py', + template.format(code), + is_executable=executable, + ) file_tokens = parse_file_tokens(path_to_file) visitor = comments.ShebangVisitor( @@ -202,7 +220,9 @@ def test_wrong_shebang_format( ): """Testing cases when no errors should be reported.""" path_to_file = make_file( - 'test_file.py', template.format(code), is_executable=True, + 'test_file.py', + template.format(code), + is_executable=True, ) file_tokens = parse_file_tokens(path_to_file) diff --git a/wemake_python_styleguide/checker.py b/wemake_python_styleguide/checker.py index 13d81b860..52a24f5d8 100644 --- a/wemake_python_styleguide/checker.py +++ b/wemake_python_styleguide/checker.py @@ -48,6 +48,7 @@ from wemake_python_styleguide import version as pkg_version from wemake_python_styleguide.options.config import Configuration from wemake_python_styleguide.options.validation import validate_options +from wemake_python_styleguide.patches import baseline from wemake_python_styleguide.presets.types import file_tokens as tokens_preset from wemake_python_styleguide.presets.types import filename as filename_preset from wemake_python_styleguide.presets.types import tree as tree_preset @@ -135,6 +136,8 @@ def add_options(cls, parser: OptionManager) -> None: def parse_options(cls, options: types.ConfigurationOptions) -> None: """Parses registered options for providing them to each visitor.""" cls.options = validate_options(options) + if cls.options.baseline: + baseline.apply_patch() def run(self) -> Iterator[types.CheckResult]: """ diff --git a/wemake_python_styleguide/compat/packaging.py b/wemake_python_styleguide/compat/packaging.py index 48c7a1fa6..26e8001b7 100644 --- a/wemake_python_styleguide/compat/packaging.py +++ b/wemake_python_styleguide/compat/packaging.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import sys # Note that we use ``sys.version_info`` directly, diff --git a/wemake_python_styleguide/logic/baseline.py b/wemake_python_styleguide/logic/baseline.py new file mode 100644 index 000000000..1caf7ad03 --- /dev/null +++ b/wemake_python_styleguide/logic/baseline.py @@ -0,0 +1,120 @@ +import json +import os +from collections import Counter, defaultdict +from hashlib import md5 +from typing import DefaultDict, Dict, Iterable, List, Mapping, Optional, Tuple + +import attr +from typing_extensions import Final, final + +#: That's a constant filename where we store our baselines. +_BASELINE_FILE: Final = '.flake8-baseline.json' + +#: Content is: `error_code, line_number, column, text, physical_line`. +CheckReport = Tuple[str, int, int, str, str] + +#: Mapping of filename and the report result. +SavedReports = Dict[str, List[CheckReport]] + + +def _baseline_fullpath() -> str: + """We only store baselines in the current (main) directory.""" + return os.path.join(os.curdir, _BASELINE_FILE) + + +def _unique_paths_converter( + mapping: Mapping[str, Iterable[str]], +) -> Mapping[str, Dict[str, int]]: + return { + path: Counter(violations) + for path, violations in mapping.items() + } + + +@final +@attr.dataclass(slots=True, frozen=True) +class _BaselineFile(object): + """ + Baseline file representation. + + How paths are stored? + We use ``path`` -> ``violations`` mapping, here ``violations`` is + a mutable dict of ``digest`` and ``count``. + + We mutate ``count`` to mark violation as found. + Once there are no more violations to find in the baseline, + we start to report them! + + """ + + paths: Mapping[str, Dict[str, int]] = attr.ib( + converter=_unique_paths_converter, + ) + + def has(self, filename: str, error_code: str, text: str) -> bool: + """ + Tells whether or not this violation is saved in the baseline. + + This operation is impure. Because we mutate the object's state. + After we find a violation once, it's counter is decreased. + That's how we controll violations' count inside a single file. + """ + if filename not in self.paths: + return False + + per_file = self.paths[filename] + digest = self._generate_violation_hash(error_code, text) + + per_file[digest] = per_file[digest] - 1 + return per_file[digest] >= 0 + + @classmethod + def from_report( + cls, saved_reports: SavedReports, + ) -> '_BaselineFile': + """Factory method to construct baselines from ``flake8`` like stats.""" + paths: DefaultDict[str, List[str]] = defaultdict(list) + + for filename, reports in saved_reports.items(): + for report in reports: + paths[filename].append( + cls._generate_violation_hash(report[0], report[3]), + ) + return cls(paths) + + @classmethod + def _generate_violation_hash(cls, error_code: str, message: str) -> str: + digest = md5() # noqa: S303 + digest.update(error_code.encode()) + digest.update(message.encode()) + return digest.hexdigest() + + +def load_from_file() -> Optional[_BaselineFile]: + """ + Loads baseline ``json`` files from current workdir. + + It might return ``None`` when file does not exist. + It means, that we run ``--baseline`` for the very first time. + """ + try: + with open(_baseline_fullpath()) as baseline_file: + return _BaselineFile(**json.load(baseline_file)) + except IOError: + # There was probably no baseline file, that's ok. + # We will create a new one later. + return None + + +def save_to_file(saved_reports: SavedReports) -> _BaselineFile: + """Creates new baseline ``json`` files in current workdir.""" + baseline = _BaselineFile.from_report(saved_reports) + with open(_baseline_fullpath(), 'w') as baseline_file: + json.dump( + attr.asdict(baseline), + baseline_file, + sort_keys=True, + indent=2, + ) + + return baseline diff --git a/wemake_python_styleguide/logic/system.py b/wemake_python_styleguide/logic/system.py index 9370baf83..b78d23253 100644 --- a/wemake_python_styleguide/logic/system.py +++ b/wemake_python_styleguide/logic/system.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import os from wemake_python_styleguide.constants import WINDOWS_OS diff --git a/wemake_python_styleguide/options/config.py b/wemake_python_styleguide/options/config.py index c91521a51..2a30ce860 100644 --- a/wemake_python_styleguide/options/config.py +++ b/wemake_python_styleguide/options/config.py @@ -230,6 +230,7 @@ class Configuration(object): type='string', comma_separated_list=True, ), + _Option( '--allowed-domain-names', defaults.ALLOWED_DOMAIN_NAMES, @@ -237,6 +238,7 @@ class Configuration(object): type='string', comma_separated_list=True, ), + _Option( '--forbidden-domain-names', defaults.FORBIDDEN_DOMAIN_NAMES, @@ -245,6 +247,15 @@ class Configuration(object): comma_separated_list=True, ), + _Option( + '--baseline', + defaults.BASELINE, + 'Run linter in legacy-first mode and ignore current violations.', + action='store_true', + type=None, + dest='baseline', + ), + # Complexity: _Option( diff --git a/wemake_python_styleguide/options/defaults.py b/wemake_python_styleguide/options/defaults.py index 12529c33f..2fe41b7b8 100644 --- a/wemake_python_styleguide/options/defaults.py +++ b/wemake_python_styleguide/options/defaults.py @@ -42,6 +42,9 @@ #: Domain names that extends variable names' blacklist. FORBIDDEN_DOMAIN_NAMES: Final = () +#: Baseline mode trigger, turn it on to integrate this linter into legacy. +BASELINE: Final = False + # =========== # Complexity: diff --git a/wemake_python_styleguide/options/validation.py b/wemake_python_styleguide/options/validation.py index 86a7cc8be..594e3ca3c 100644 --- a/wemake_python_styleguide/options/validation.py +++ b/wemake_python_styleguide/options/validation.py @@ -65,6 +65,7 @@ class _ValidatedOptions(object): nested_classes_whitelist: Tuple[str, ...] = attr.ib(converter=tuple) allowed_domain_names: Tuple[str, ...] = attr.ib(converter=tuple) forbidden_domain_names: Tuple[str, ...] = attr.ib(converter=tuple) + baseline: bool # Complexity: max_arguments: int = attr.ib(validator=[_min_max(min=1)]) diff --git a/wemake_python_styleguide/patches/__init__.py b/wemake_python_styleguide/patches/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/wemake_python_styleguide/patches/baseline.py b/wemake_python_styleguide/patches/baseline.py new file mode 100644 index 000000000..4c426848d --- /dev/null +++ b/wemake_python_styleguide/patches/baseline.py @@ -0,0 +1,135 @@ +""" +Baselines allow us to adopt this linter incrementally. + +You can start using it with just a single command! + +.. code:: bash + + flake8 --baseline your_module + +This guide will explain how it works. + +Steps +~~~~~ + +There are several steps in how baseline works. + +1. We can run the linter with ``--baseline`` mode enabled. + What will happen? If you already have ``.flake8-baseline.json`` file, + than your pre-saved violations will be ignored. + New violations will be reported. + If you don't have ``.flake8-baseline.json``, + then a new one will be created containing all the violations you have. + +2. You can also use ``--refactor-baseline`` to force users + to refactor at least one violation from the existing baseline. + This might be useful if you enforce consistent refactoring. + +Updating baseline +~~~~~~~~~~~~~~~~~ + +To update a baseline you can delete the old one: + +.. code:: bash + + rm .flake8-baseline.json + +And create a new one as always. + +Baseline contents +~~~~~~~~~~~~~~~~~ + +Things we care when working with baselines: + +1. Violation codes and text descriptions +2. Filenames + +When these values change +(for example: file is renamed or violation code is changed), +we will treat these violations as new ones. +And report them as usual. + +Things we don't care when working with baselines: + +1. Violation locations, because lines and columns + can be easily changed by simple refactoring +2. Activated plugins +3. Config values +4. Target files and directories + +So, you add new plugins or change any config values, +then you might want ot update the baseline. + +""" + +from collections import defaultdict +from typing import Iterable, Tuple, Type + +from flake8.checker import Manager + +from wemake_python_styleguide.logic import baseline + + +def _patch_report(manager: Type[Manager]) -> None: + original_report = manager.report + + def report(self) -> Tuple[int, int]: # noqa: WPS430 + # --- patch start + saved_reports: baseline.SavedReports = defaultdict(list) + self.saved_reports = saved_reports # mypy requires that! + # --- patch end + + report_result = original_report(self) + + # --- patch start + if self.baseline is None: + self.baseline = baseline.save_to_file(self.saved_reports) + # --- patch end + + return report_result + + manager.report = report + manager.baseline = baseline.load_from_file() + + +def _patch_handle_results( # noqa: WPS210, WPS231 + manager: Type[Manager], +) -> None: + def _handle_results( # noqa: WPS210, WPS430 + self, + filename: str, + results: Iterable[baseline.CheckReport], # noqa: WPS110 + ) -> int: + style_guide = self.style_guide + reported_results_count = 0 + for (error_code, line_number, column, text, physical_line) in results: + # --- patch start + # Here we ignore violations present in the baseline. + if self.baseline and self.baseline.has(filename, error_code, text): + continue + + handled_error = style_guide.handle_error( + code=error_code, + filename=filename, + line_number=line_number, + column_number=column, + text=text, + physical_line=physical_line, + ) + + reported_results_count += handled_error + + if handled_error: + self.saved_reports[filename].append( + (error_code, line_number, column, text, physical_line), + ) + # --- patch end + return reported_results_count + + manager._handle_results = _handle_results # noqa: WPS437 + + +def apply_patch() -> None: + """This is the only function we export to apply all the patches.""" + _patch_report(Manager) + _patch_handle_results(Manager) diff --git a/wemake_python_styleguide/types.py b/wemake_python_styleguide/types.py index 77eec8591..ef79aa64d 100644 --- a/wemake_python_styleguide/types.py +++ b/wemake_python_styleguide/types.py @@ -144,6 +144,10 @@ def allowed_domain_names(self) -> Tuple[str, ...]: def forbidden_domain_names(self) -> Tuple[str, ...]: ... + @property + def baseline(self) -> bool: + ... + # Complexity: @property def max_arguments(self) -> int: diff --git a/wemake_python_styleguide/violations/complexity.py b/wemake_python_styleguide/violations/complexity.py index b632d72ba..0132735a4 100644 --- a/wemake_python_styleguide/violations/complexity.py +++ b/wemake_python_styleguide/violations/complexity.py @@ -329,7 +329,7 @@ def first_function(param): def second_function(argument): second_var = 1 argument = int(argument) - third_var, _ = some_call() + third_var, _unused = some_call() In this example we will count as locals only several variables: @@ -338,8 +338,9 @@ def second_function(argument): 3. ``argument``, because it is reassigned inside the function's body 4. ``third_var``, because it is assigned inside the function's body - Please, note that ``_`` is a special case. It is not counted as a local - variable. Since by design it means: do not count me as a real variable. + Please, note that ``_unused`` is a special case. + It is not counted as a local variable. + Since by design it means: do not count me as a real variable. Configuration: This rule is configurable with ``--max-local-variables``. From eac93152f4cff5480ce6e4364b4a8fbc2078ada6 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 24 Mar 2020 00:47:45 +0300 Subject: [PATCH 2/7] Adds --baseline --- .importlinter | 1 + 1 file changed, 1 insertion(+) diff --git a/.importlinter b/.importlinter index e58072ee8..65883c6be 100644 --- a/.importlinter +++ b/.importlinter @@ -74,6 +74,7 @@ ignore_imports = # These modules must import from flake8 to provide required API: wemake_python_styleguide.checker -> flake8 wemake_python_styleguide.formatter -> flake8 + wemake_python_styleguide.patches.baseline -> flake8 wemake_python_styleguide.options.config -> flake8 # We disallow direct imports of our dependencies from anywhere, except: wemake_python_styleguide.formatter -> pygments From 5b2bdc82898eb2fa72320e4763b6eb92b170d082 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 24 Mar 2020 00:48:15 +0300 Subject: [PATCH 3/7] Adds --baseline --- .importlinter | 1 + 1 file changed, 1 insertion(+) diff --git a/.importlinter b/.importlinter index 65883c6be..32c4c5b25 100644 --- a/.importlinter +++ b/.importlinter @@ -13,6 +13,7 @@ containers = layers = checker formatter + patches transformations presets visitors From b6790ec9593e0a06dd670eb840cded8fa0f7aacd Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 24 Mar 2020 01:50:23 +0300 Subject: [PATCH 4/7] Adds --baseline --- poetry.lock | 17 +-- pyproject.toml | 1 - setup.cfg | 1 - tests/conftest.py | 10 -- tests/plugins/files.py | 22 +++- .../test_baseline_command.py | 117 +++++++++++++----- .../test_baseline_option/test_no_baseline.py | 32 +++++ wemake_python_styleguide/logic/baseline.py | 4 +- 8 files changed, 144 insertions(+), 60 deletions(-) rename tests/{test_pathes/test_baseline => test_patches/test_baseline/test_baseline_option}/test_baseline_command.py (51%) create mode 100644 tests/test_patches/test_baseline/test_baseline_option/test_no_baseline.py diff --git a/poetry.lock b/poetry.lock index 38d5e0835..3d8a66f89 100644 --- a/poetry.lock +++ b/poetry.lock @@ -369,17 +369,6 @@ version = "19.8.0" attrs = "*" flake8 = ">=3.0.0" -[[package]] -category = "dev" -description = "Adds coding magic comment checks to flake8" -name = "flake8-coding" -optional = false -python-versions = "*" -version = "1.3.2" - -[package.dependencies] -flake8 = "*" - [[package]] category = "main" description = "Flake8 lint for trailing commas." @@ -1635,7 +1624,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["jaraco.itertools", "func-timeout"] [metadata] -content-hash = "9fe876aede33595424a1c277ed5b5dc79cc765f11635e70fe423e47229b236dc" +content-hash = "25d04c702b28cfc663b887aa1764709f30a07753b9677e986254830ff4277957" python-versions = "^3.6" [metadata.files] @@ -1803,10 +1792,6 @@ flake8-bugbear = [ {file = "flake8-bugbear-19.8.0.tar.gz", hash = "sha256:d8c466ea79d5020cb20bf9f11cf349026e09517a42264f313d3f6fddb83e0571"}, {file = "flake8_bugbear-19.8.0-py35.py36.py37-none-any.whl", hash = "sha256:ded4d282778969b5ab5530ceba7aa1a9f1b86fa7618fc96a19a1d512331640f8"}, ] -flake8-coding = [ - {file = "flake8-coding-1.3.2.tar.gz", hash = "sha256:b8f4d5157a8f74670e6cfea732c3d9f4291a4e994c8701d2c55f787c6e6cb741"}, - {file = "flake8_coding-1.3.2-py2.py3-none-any.whl", hash = "sha256:79704112c44d09d4ab6c8965e76a20c3f7073d52146db60303bce777d9612260"}, -] flake8-commas = [ {file = "flake8-commas-2.0.0.tar.gz", hash = "sha256:d3005899466f51380387df7151fb59afec666a0f4f4a2c6a8995b975de0f44b7"}, {file = "flake8_commas-2.0.0-py2.py3-none-any.whl", hash = "sha256:ee2141a3495ef9789a3894ed8802d03eff1eaaf98ce6d8653a7c573ef101935e"}, diff --git a/pyproject.toml b/pyproject.toml index 15573b453..f3fe75e8b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -106,4 +106,3 @@ ipdb = "^0.12.3" ipython = "^7.10" astboom = "^0.4.2" tokelor = "^0.1.4" -flake8-coding = "^1.3.2" diff --git a/setup.cfg b/setup.cfg index bf239fe8d..bda17d33b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,7 +21,6 @@ max-line-length = 80 # Self settings: max-imports = 16 -no-accept-encodings = True # Excluding some directories: exclude = diff --git a/tests/conftest.py b/tests/conftest.py index 4cf497a48..f46576679 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,3 @@ -import os from collections import namedtuple import pytest @@ -14,15 +13,6 @@ ] -@pytest.fixture(scope='session') -def absolute_path(): - """Fixture to create full path relative to `contest.py` inside tests.""" - def factory(*files: str): - dirname = os.path.dirname(__file__) - return os.path.join(dirname, *files) - return factory - - @pytest.fixture(scope='session') def options(): """Returns the options builder.""" diff --git a/tests/plugins/files.py b/tests/plugins/files.py index f84b2a7f9..aa879881e 100644 --- a/tests/plugins/files.py +++ b/tests/plugins/files.py @@ -1,4 +1,4 @@ -from os import chmod +import os import pytest @@ -22,7 +22,25 @@ def factory( file_mode = _MODE_EXECUTABLE if is_executable else _MODE_NON_EXECUTABLE test_file.write_text(file_content) - chmod(test_file.as_posix(), file_mode) + os.chmod(test_file.as_posix(), file_mode) return test_file.as_posix() return factory + + +@pytest.fixture(scope='session') +def read_file(absolute_path): + """Fixture to get the file contents.""" + def factory(filename: str) -> str: + with open(filename) as file_obj: + return file_obj.read() + return factory + + +@pytest.fixture(scope='session') +def absolute_path(): + """Fixture to create full path relative to `contest.py` inside tests.""" + def factory(*files: str): + dirname = os.path.dirname(os.path.dirname(__file__)) + return os.path.join(dirname, *files) + return factory diff --git a/tests/test_pathes/test_baseline/test_baseline_command.py b/tests/test_patches/test_baseline/test_baseline_option/test_baseline_command.py similarity index 51% rename from tests/test_pathes/test_baseline/test_baseline_command.py rename to tests/test_patches/test_baseline/test_baseline_option/test_baseline_command.py index 0bef1285e..adfb9e8dd 100644 --- a/tests/test_pathes/test_baseline/test_baseline_command.py +++ b/tests/test_patches/test_baseline/test_baseline_option/test_baseline_command.py @@ -1,5 +1,5 @@ """ -We use this example together with the existing baseline. +We use this test to ensure that ``--baseline`` works correctly. Here are several important things about this example: @@ -10,15 +10,23 @@ 3. There's a unique violation: ``WPS303`` +We also have the second file around with just one ``WPS304`` inside. + All violations in this example are covered by the baseline. """ import os import subprocess -baseline = """ -{ +import pytest + +from wemake_python_styleguide.logic.baseline import BASELINE_FILE + +baseline = """{ "paths": { + "other_wrong.py": { + "e61cb3c3de1cbdac603069903e4af07c": 1 + }, "wrong.py": { "132954ef45e1a84ab72bb6e30126a117": 1, "71b49f6407bbc09ac76c372014207dfb": 1, @@ -26,25 +34,35 @@ "dd2402e2213add848d53f8580452417e": 2 } } -} -""" +}""" -code_template = """ +# Templates: + +wrong_template = """ value =1 result= 2 undescored_number = 10_0 {0} """ +wrong_other = 'partial = .5' + +# Filenames: + +filename_wrong = 'wrong.py' +filename_other = 'other_wrong.py' + def _assert_output(output: str, errors): for error_code, error_count in errors.items(): assert output.count(error_code) == error_count -def test_without_baseline(make_file): - """End-to-End test for no baseline, regular mode.""" - filename = make_file('wrong.py', code_template.format('')) +def test_without_baseline(make_file, read_file): + """End-to-End test for no baseline yet, initial mode.""" + filename = make_file(filename_wrong, wrong_template.format('')) + make_file(filename_other, wrong_other) + cwd = os.path.dirname(filename) process = subprocess.Popen( [ @@ -53,24 +71,33 @@ def test_without_baseline(make_file): '--baseline', '--select', 'WPS,E', - 'wrong.py', + filename_wrong, + filename_other, ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, encoding='utf8', - cwd=os.path.dirname(filename), + cwd=cwd, ) output, _ = process.communicate() - _assert_output(output, {'WPS110': 2, 'WPS303': 1, 'E225': 2}) + _assert_output(output, {'WPS110': 2, 'WPS303': 1, 'WPS304': 1, 'E225': 2}) assert process.returncode == 1 + assert read_file(os.path.join(cwd, BASELINE_FILE)) == baseline -def test_with_baseline(make_file): +@pytest.mark.parametrize('files_to_check', [ + (filename_wrong,), + (filename_wrong, filename_other), + (filename_wrong, 'missing.py'), + (filename_wrong, filename_other, 'missing.py'), +]) +def test_with_baseline(make_file, read_file, files_to_check): """End-to-End test for baseline generation.""" - filename = make_file('wrong.py', code_template.format('')) - make_file('.flake8-baseline.json', baseline) + filename = make_file(filename_wrong, wrong_template.format('')) + make_file(filename_other, wrong_other) + baseline_path = make_file(BASELINE_FILE, baseline) process = subprocess.Popen( [ @@ -79,7 +106,7 @@ def test_with_baseline(make_file): '--baseline', '--select', 'WPS,E', - 'wrong.py', + *files_to_check, ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -91,12 +118,13 @@ def test_with_baseline(make_file): assert output == '' assert process.returncode == 0 + assert read_file(baseline_path) == baseline -def test_with_baseline_empty(make_file): +def test_with_baseline_empty(make_file, read_file): """End-to-End test that removed violations are fine.""" - filename = make_file('wrong.py', '_SOME_CONSTANT = 1') - make_file('.flake8-baseline.json', baseline) + filename = make_file(filename_wrong, '_SOME_CONSTANT = 1') + baseline_path = make_file(BASELINE_FILE, baseline) process = subprocess.Popen( [ @@ -105,7 +133,7 @@ def test_with_baseline_empty(make_file): '--baseline', '--select', 'WPS,E', - 'wrong.py', + filename_wrong, ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -117,12 +145,13 @@ def test_with_baseline_empty(make_file): assert output == '' assert process.returncode == 0 + assert read_file(baseline_path) == baseline -def test_with_baseline_new_violations(make_file): +def test_with_baseline_new_violations(make_file, read_file): """End-to-End test to test that baseline still generates new violations.""" - filename = make_file('wrong.py', code_template.format('x = 1')) - make_file('.flake8-baseline.json', baseline) + filename = make_file(filename_wrong, wrong_template.format('x = 1')) + baseline_path = make_file(BASELINE_FILE, baseline) process = subprocess.Popen( [ @@ -131,7 +160,7 @@ def test_with_baseline_new_violations(make_file): '--baseline', '--select', 'WPS,E', - 'wrong.py', + filename_wrong, ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -143,13 +172,14 @@ def test_with_baseline_new_violations(make_file): _assert_output(output, {'WPS111': 1}) assert process.returncode == 1 + assert read_file(baseline_path) == baseline -def test_with_baseline_new_files(make_file): +def test_with_baseline_new_correct_files(make_file, read_file): """End-to-End test to test that baseline still generates new violations.""" - filename = make_file('wrong.py', code_template.format('')) + filename = make_file(filename_wrong, wrong_template.format('')) make_file('correct.py', 'SOME_CONSTANT = 1') - make_file('.flake8-baseline.json', baseline) + baseline_path = make_file(BASELINE_FILE, baseline) process = subprocess.Popen( [ @@ -158,7 +188,7 @@ def test_with_baseline_new_files(make_file): '--baseline', '--select', 'WPS,E', - 'wrong.py', + filename_wrong, 'correct.py', ], stdout=subprocess.PIPE, @@ -171,3 +201,34 @@ def test_with_baseline_new_files(make_file): assert output == '' assert process.returncode == 0 + assert read_file(baseline_path) == baseline + + +def test_with_baseline_new_wrong_files(make_file, read_file): + """End-to-End test to test that baseline still generates new violations.""" + filename = make_file(filename_wrong, wrong_template.format('')) + new_wrong = make_file('new_wrong.py', 'wrong__name = 1') + baseline_path = make_file(BASELINE_FILE, baseline) + + process = subprocess.Popen( + [ + 'flake8', + '--isolated', + '--baseline', + '--select', + 'WPS,E', + filename_wrong, + 'correct.py', + new_wrong, + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + encoding='utf8', + cwd=os.path.dirname(filename), + ) + output, _ = process.communicate() + + _assert_output(output, {'WPS116': 1}) + assert process.returncode == 1 + assert read_file(baseline_path) == baseline diff --git a/tests/test_patches/test_baseline/test_baseline_option/test_no_baseline.py b/tests/test_patches/test_baseline/test_baseline_option/test_no_baseline.py new file mode 100644 index 000000000..864254076 --- /dev/null +++ b/tests/test_patches/test_baseline/test_baseline_option/test_no_baseline.py @@ -0,0 +1,32 @@ +import os +import subprocess + +import pytest + +from wemake_python_styleguide.logic.baseline import BASELINE_FILE + + +def test_no_baseline_option(make_file, read_file): + """End-to-End test for no baseline, regular mode.""" + filename = make_file('wrong.py', 'x = 1') + cwd = os.path.dirname(filename) + + process = subprocess.Popen( + [ + 'flake8', + '--isolated', + '--select', + 'WPS,E', + filename, + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + encoding='utf8', + cwd=cwd, + ) + output, _ = process.communicate() + + assert process.returncode == 1 + with pytest.raises(IOError, match=BASELINE_FILE): + read_file(os.path.join(cwd, BASELINE_FILE)) diff --git a/wemake_python_styleguide/logic/baseline.py b/wemake_python_styleguide/logic/baseline.py index 1caf7ad03..c440bd273 100644 --- a/wemake_python_styleguide/logic/baseline.py +++ b/wemake_python_styleguide/logic/baseline.py @@ -8,7 +8,7 @@ from typing_extensions import Final, final #: That's a constant filename where we store our baselines. -_BASELINE_FILE: Final = '.flake8-baseline.json' +BASELINE_FILE: Final = '.flake8-baseline.json' #: Content is: `error_code, line_number, column, text, physical_line`. CheckReport = Tuple[str, int, int, str, str] @@ -19,7 +19,7 @@ def _baseline_fullpath() -> str: """We only store baselines in the current (main) directory.""" - return os.path.join(os.curdir, _BASELINE_FILE) + return os.path.join(os.curdir, BASELINE_FILE) def _unique_paths_converter( From 505837c2bb1dd9b6e9f3033ab93b4d8fea4e82af Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 24 Mar 2020 02:19:49 +0300 Subject: [PATCH 5/7] Adds --baseline --- wemake_python_styleguide/options/config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wemake_python_styleguide/options/config.py b/wemake_python_styleguide/options/config.py index 2a30ce860..96215ae88 100644 --- a/wemake_python_styleguide/options/config.py +++ b/wemake_python_styleguide/options/config.py @@ -56,6 +56,8 @@ :str:`wemake_python_styleguide.options.defaults.ALLOWED_DOMAIN_NAMES` - ``forbidden-domain-names`` - list of forbidden domain names, defaults to :str:`wemake_python_styleguide.options.defaults.FORBIDDEN_DOMAIN_NAMES` +- ``baseline`` - run linter in legacy-first mode and ignore current violations, + defaults to :str:`wemake_python_styleguide.options.defaults.BASELINE` .. rubric:: Complexity options From bc02dfc2ea57f0c65241e3b507a7af642603c118 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 24 Mar 2020 02:31:21 +0300 Subject: [PATCH 6/7] Adds --baseline --- tests/test_violations/test_docs.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_violations/test_docs.py b/tests/test_violations/test_docs.py index 96b958e70..ff0290a87 100644 --- a/tests/test_violations/test_docs.py +++ b/tests/test_violations/test_docs.py @@ -1,5 +1,9 @@ from wemake_python_styleguide.options.config import Configuration +_IGNORED_OPTIONS = frozenset(( + '--baseline', +)) + def test_all_violations_are_documented(all_module_violations): """Ensures that all violations are documented.""" @@ -43,6 +47,7 @@ def test_configuration(all_violations): option_listed = { option.long_option_name: False for option in Configuration._options # noqa: WPS437 + if option.long_option_name not in _IGNORED_OPTIONS } for violation in all_violations: From 4f85fedf7fe608259130a9c7c656d0a19ce78307 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 24 Mar 2020 02:52:59 +0300 Subject: [PATCH 7/7] Adds --baseline --- poetry.lock | 46 +++++++++++++++++++++++----------------------- setup.cfg | 11 ++++++----- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3d8a66f89..28787f425 100644 --- a/poetry.lock +++ b/poetry.lock @@ -634,7 +634,7 @@ marker = "python_version < \"3.7\"" name = "importlib-resources" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -version = "1.3.1" +version = "1.4.0" [package.dependencies] [package.dependencies.importlib-metadata] @@ -646,7 +646,7 @@ python = "<3.8" version = ">=0.4" [package.extras] -docs = ["sphinx", "docutils (0.12)", "rst.linker"] +docs = ["sphinx", "rst.linker", "jaraco.packaging"] [[package]] category = "dev" @@ -1045,7 +1045,7 @@ wcwidth = "*" [[package]] category = "dev" description = "Run a subprocess in a pseudo terminal" -marker = "python_version >= \"3.4\" and sys_platform != \"win32\" or sys_platform != \"win32\" or python_version >= \"3.4\" and sys_platform != \"win32\" and (python_version >= \"3.4\" and sys_platform != \"win32\" or sys_platform != \"win32\")" +marker = "python_version >= \"3.4\" and sys_platform != \"win32\" or sys_platform != \"win32\"" name = "ptyprocess" optional = false python-versions = "*" @@ -1186,7 +1186,7 @@ description = "YAML parser and emitter for Python" name = "pyyaml" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "5.3" +version = "5.3.1" [[package]] category = "dev" @@ -1565,7 +1565,7 @@ description = "Virtual Python Environment builder" name = "virtualenv" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -version = "20.0.10" +version = "20.0.13" [package.dependencies] appdirs = ">=1.4.3,<2" @@ -1608,7 +1608,7 @@ description = "Measures number of Terminal column cells of wide-character codes" name = "wcwidth" optional = false python-versions = "*" -version = "0.1.8" +version = "0.1.9" [[package]] category = "main" @@ -1872,8 +1872,8 @@ importlib-metadata = [ {file = "importlib_metadata-1.5.0.tar.gz", hash = "sha256:06f5b3a99029c7134207dd882428a66992a9de2bef7c2b699b5641f9886c3302"}, ] importlib-resources = [ - {file = "importlib_resources-1.3.1-py2.py3-none-any.whl", hash = "sha256:1dff36d42d94bd523eeb847c25c7dd327cb56686d74a26dfcc8d67c504922d59"}, - {file = "importlib_resources-1.3.1.tar.gz", hash = "sha256:7f0e1b2b5f3981e39c52da0f99b2955353c5a139c314994d1126c2551ace9bdf"}, + {file = "importlib_resources-1.4.0-py2.py3-none-any.whl", hash = "sha256:dd98ceeef3f5ad2ef4cc287b8586da4ebad15877f351e9688987ad663a0a29b8"}, + {file = "importlib_resources-1.4.0.tar.gz", hash = "sha256:4019b6a9082d8ada9def02bece4a76b131518866790d58fdda0b5f8c603b36c2"}, ] ipdb = [ {file = "ipdb-0.12.3.tar.gz", hash = "sha256:5d9a4a0e3b7027a158fc6f2929934341045b9c3b0b86ed5d7e84e409653f72fd"}, @@ -2084,17 +2084,17 @@ pytz = [ {file = "pytz-2019.3.tar.gz", hash = "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"}, ] pyyaml = [ - {file = "PyYAML-5.3-cp27-cp27m-win32.whl", hash = "sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d"}, - {file = "PyYAML-5.3-cp27-cp27m-win_amd64.whl", hash = "sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6"}, - {file = "PyYAML-5.3-cp35-cp35m-win32.whl", hash = "sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e"}, - {file = "PyYAML-5.3-cp35-cp35m-win_amd64.whl", hash = "sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689"}, - {file = "PyYAML-5.3-cp36-cp36m-win32.whl", hash = "sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994"}, - {file = "PyYAML-5.3-cp36-cp36m-win_amd64.whl", hash = "sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e"}, - {file = "PyYAML-5.3-cp37-cp37m-win32.whl", hash = "sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5"}, - {file = "PyYAML-5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf"}, - {file = "PyYAML-5.3-cp38-cp38-win32.whl", hash = "sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811"}, - {file = "PyYAML-5.3-cp38-cp38-win_amd64.whl", hash = "sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20"}, - {file = "PyYAML-5.3.tar.gz", hash = "sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615"}, + {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, + {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, + {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, + {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, + {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, + {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, + {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, + {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, + {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, + {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, + {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] requests = [ {file = "requests-2.23.0-py2.py3-none-any.whl", hash = "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee"}, @@ -2256,8 +2256,8 @@ urllib3 = [ {file = "urllib3-1.25.8.tar.gz", hash = "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"}, ] virtualenv = [ - {file = "virtualenv-20.0.10-py2.py3-none-any.whl", hash = "sha256:10750cac3b5a9e6eed54d0f1f8222c550dc47f84609c95cbc504d44a58a048b8"}, - {file = "virtualenv-20.0.10.tar.gz", hash = "sha256:8512e83f1d90f8e481024d58512ac9c053bf16f54d9138520a0929396820dd78"}, + {file = "virtualenv-20.0.13-py2.py3-none-any.whl", hash = "sha256:87831f1070534b636fea2241dd66f3afe37ac9041bcca6d0af3215cdcfbf7d82"}, + {file = "virtualenv-20.0.13.tar.gz", hash = "sha256:f3128d882383c503003130389bf892856341c1da12c881ae24d6358c82561b55"}, ] virtualenv-clone = [ {file = "virtualenv-clone-0.5.3.tar.gz", hash = "sha256:c88ae171a11b087ea2513f260cdac9232461d8e9369bcd1dc143fc399d220557"}, @@ -2272,8 +2272,8 @@ wasmer = [ {file = "wasmer-0.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fa1c8479781c91e6b814ef006f6e099b6550eba12601a8617475fb514fc09365"}, ] wcwidth = [ - {file = "wcwidth-0.1.8-py2.py3-none-any.whl", hash = "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603"}, - {file = "wcwidth-0.1.8.tar.gz", hash = "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8"}, + {file = "wcwidth-0.1.9-py2.py3-none-any.whl", hash = "sha256:cafe2186b3c009a04067022ce1dcd79cb38d8d65ee4f4791b8888d6599d1bbe1"}, + {file = "wcwidth-0.1.9.tar.gz", hash = "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1"}, ] zipp = [ {file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"}, diff --git a/setup.cfg b/setup.cfg index bda17d33b..c88032d0a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -106,15 +106,16 @@ addopts = [coverage:run] # Coverage configuration: https://coverage.readthedocs.io/ -# We don't need to cover some files. They are fully checked with mypy. -# And don't contain any logic. -omit = - wemake_python_styleguide/types.py - # Here we specify plugins for coverage to be used: plugins = coverage_conditional_plugin +[coverage:report] +# We exclude two lines from default coverage: +exclude_lines = + \# pragma: no cover\b + ^ +\.\.\.$ + [coverage:coverage_conditional_plugin] # Here we specify our pragma rules: rules =