diff --git a/.fixit.config.yaml b/.fixit.config.yaml new file mode 100644 index 000000000..077e120c1 --- /dev/null +++ b/.fixit.config.yaml @@ -0,0 +1,10 @@ +block_list_patterns: +- '@generated' +- '@nolint' +block_list_rules: ["UseFstringRule", "CompareSingletonPrimitivesByIsRule"] +fixture_dir: ./fixtures +formatter: ["black", "-"] +packages: +- fixit.rules +repo_root: libcst +rule_config: {} diff --git a/libcst/_parser/parso/pgen2/generator.py b/libcst/_parser/parso/pgen2/generator.py index c08b164f1..546cc85f0 100644 --- a/libcst/_parser/parso/pgen2/generator.py +++ b/libcst/_parser/parso/pgen2/generator.py @@ -321,8 +321,10 @@ def _calculate_tree_traversal(nonterminal_to_dfas): ] ) raise ValueError( - "Rule %s is ambiguous; given a %s token, we " - "can't determine if we should evaluate %s or %s." + ( + "Rule %s is ambiguous; given a %s token, we " + + "can't determine if we should evaluate %s or %s." + ) % ((dfa_state.from_rule, transition) + tuple(choices)) ) transitions[transition] = DFAPlan(next_dfa, pushes) diff --git a/libcst/_parser/parso/python/tokenize.py b/libcst/_parser/parso/python/tokenize.py index 4b1905cbb..0f60472ed 100644 --- a/libcst/_parser/parso/python/tokenize.py +++ b/libcst/_parser/parso/python/tokenize.py @@ -108,7 +108,7 @@ def different_case_versions(prefix): if version_info < (3, 0) or version_info >= (3, 3): valid_string_prefixes.append("u") - result = set([""]) + result = {""} if version_info >= (3, 6) and include_fstring: f = ["f", "fr"] if only_fstring: @@ -326,7 +326,7 @@ def __repr__(self): ) -class FStringNode(object): +class FStringNode: def __init__(self, quote): self.quote = quote self.parentheses_count = 0 diff --git a/libcst/_parser/parso/tests/test_utils.py b/libcst/_parser/parso/tests/test_utils.py index 03ba9a683..17bdb8a06 100644 --- a/libcst/_parser/parso/tests/test_utils.py +++ b/libcst/_parser/parso/tests/test_utils.py @@ -49,7 +49,7 @@ def test_split_lines(self, string, expected_result, keepends): def test_python_bytes_to_unicode_unicode_text(self): source = ( b"# vim: fileencoding=utf-8\n" - b"# \xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a\n" + + b"# \xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a\n" ) actual = python_bytes_to_unicode(source) expected = source.decode("utf-8") diff --git a/libcst/_parser/parso/utils.py b/libcst/_parser/parso/utils.py index a6388040b..27b937319 100644 --- a/libcst/_parser/parso/utils.py +++ b/libcst/_parser/parso/utils.py @@ -184,8 +184,10 @@ def _parse_version(version: str) -> PythonVersionInfo: match = re.match(r"(\d+)(?:\.(\d+)(?:\.\d+)?)?$", version) if match is None: raise ValueError( - "The given version is not in the right format. " - 'Use something like "3.2" or "3".' + ( + "The given version is not in the right format. " + + 'Use something like "3.2" or "3".' + ) ) major = int(match.group(1)) diff --git a/libcst/_parser/tests/test_detect_config.py b/libcst/_parser/tests/test_detect_config.py index a46106dcc..b17c9fe58 100644 --- a/libcst/_parser/tests/test_detect_config.py +++ b/libcst/_parser/tests/test_detect_config.py @@ -284,7 +284,7 @@ class TestDetectConfig(UnitTest): "future_imports_in_mixed_position": { "source": ( b"from __future__ import a, b\nimport os\n" - b"from __future__ import c\n" + + b"from __future__ import c\n" ), "partial": PartialParserConfig(python_version="3.7"), "detect_trailing_newline": True, diff --git a/libcst/codemod/_cli.py b/libcst/codemod/_cli.py index 73e443ec9..d4ced9007 100644 --- a/libcst/codemod/_cli.py +++ b/libcst/codemod/_cli.py @@ -585,7 +585,7 @@ def parallel_exec_transform_with_prettyprint( # noqa: C901 # Ensure that we have no duplicates, otherwise we might get race conditions # on write. - files = sorted(list(set(os.path.abspath(f) for f in files))) + files = sorted(list({os.path.abspath(f) for f in files})) total = len(files) progress = Progress(enabled=not hide_progress, total=total) diff --git a/libcst/codemod/commands/ensure_import_present.py b/libcst/codemod/commands/ensure_import_present.py index 04b1d1292..c2ec033f3 100644 --- a/libcst/codemod/commands/ensure_import_present.py +++ b/libcst/codemod/commands/ensure_import_present.py @@ -44,7 +44,7 @@ def add_args(parser: argparse.ArgumentParser) -> None: metavar="ALIAS", help=( "Alias that will be used for the imported module or entity. If left " - "empty, no alias will be applied." + + "empty, no alias will be applied." ), type=str, default=None, diff --git a/libcst/codemod/commands/remove_unused_imports.py b/libcst/codemod/commands/remove_unused_imports.py index 1f23a264b..741f9a46c 100644 --- a/libcst/codemod/commands/remove_unused_imports.py +++ b/libcst/codemod/commands/remove_unused_imports.py @@ -30,7 +30,7 @@ class RemoveUnusedImportsCommand(VisitorBasedCodemodCommand): DESCRIPTION: str = ( "Remove all imports that are not used in a file. " - "Note: only considers the file in isolation. " + + "Note: only considers the file in isolation. " ) METADATA_DEPENDENCIES: Tuple[ProviderT] = (PositionProvider,) diff --git a/libcst/codemod/visitors/tests/test_gather_unused_imports.py b/libcst/codemod/visitors/tests/test_gather_unused_imports.py index bd63f26d2..5fb3cba2a 100644 --- a/libcst/codemod/visitors/tests/test_gather_unused_imports.py +++ b/libcst/codemod/visitors/tests/test_gather_unused_imports.py @@ -17,10 +17,10 @@ def gather_imports(self, code: str) -> Set[str]: mod.resolve_many(GatherUnusedImportsVisitor.METADATA_DEPENDENCIES) instance = GatherUnusedImportsVisitor(CodemodContext(wrapper=mod)) mod.visit(instance) - return set( + return { alias.evaluated_alias or alias.evaluated_name for alias, _ in instance.unused_imports - ) + } def test_no_imports(self) -> None: imports = self.gather_imports( diff --git a/libcst/matchers/_matcher_base.py b/libcst/matchers/_matcher_base.py index 6f1a1c886..532cb53dd 100644 --- a/libcst/matchers/_matcher_base.py +++ b/libcst/matchers/_matcher_base.py @@ -352,8 +352,10 @@ def __and__(self, other: _OtherNodeT) -> "AllOf[Union[_MatcherT, _OtherNodeT]]": # same node, or none of them. It makes more sense to move the SaveMatchedNode # up to wrap the AllOf. raise Exception( - "Cannot use AllOf with SavedMatchedNode children! Instead, you should " - "use SaveMatchedNode(AllOf(options...))." + ( + "Cannot use AllOf with SavedMatchedNode children! Instead, you should " + + "use SaveMatchedNode(AllOf(options...))." + ) ) def __getattr__(self, key: str) -> object: @@ -366,8 +368,10 @@ def __invert__(self) -> "_MatcherT": # This doesn't make sense. We don't want to capture a node only if it # doesn't match, since this will never capture anything. raise Exception( - "Cannot invert a SaveMatchedNode. Instead you should wrap SaveMatchedNode " - "around your inversion itself" + ( + "Cannot invert a SaveMatchedNode. Instead you should wrap SaveMatchedNode " + + "around your inversion itself" + ) ) def __repr__(self) -> str: diff --git a/requirements-dev.txt b/requirements-dev.txt index c0ccdbe16..84fd1d137 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,7 @@ black>=19.10b0 codecov>=2.1.4 coverage>=4.5.4 +fixit>=0.1.0 flake8>=3.7.8 hypothesis>=4.36.0 hypothesmith>=0.0.4 diff --git a/tox.ini b/tox.ini index 5e8b21bd0..a9947c2a3 100644 --- a/tox.ini +++ b/tox.ini @@ -16,6 +16,7 @@ commands = flake8 {posargs} isort --check-only {posargs:.} black --check {posargs:libcst/} + python3 -m fixit.cli.run_rules [testenv:docs] deps = @@ -32,6 +33,7 @@ commands = flake8 {posargs} isort -q {posargs:.} black {posargs:libcst/} + python3 -m fixit.cli.apply_fix [testenv:coverage] deps =