diff --git a/.flake8 b/.flake8 deleted file mode 100644 index eb0260b03..000000000 --- a/.flake8 +++ /dev/null @@ -1,178 +0,0 @@ -[flake8] - -ignore = - # unnecessary list comprehension; A generator only better than a list - # comprehension if we don't always need to iterate through all items in - # the generator (based on the use case). - C407, - # The following codes belong to pycodestyle, and overlap with black: - # indentation contains mixed spaces and tabs - E101, - # indentation is not a multiple of four - E111, - # expected an indented block - E112, - # unexpected indentation - E113, - # indentation is not a multiple of four (comment) - E114, - # expected an indented block (comment) - E115, - # unexpected indentation (comment) - E116, - # continuation line under-indented for hanging indent - E121, - # continuation line missing indentation or outdented - E122, - # closing bracket does not match indentation of opening bracket’s line - E123, - # closing bracket does not match visual indentation - E124, - # continuation line with same indent as next logical line - E125, - # continuation line over-indented for hanging indent - E126, - # continuation line over-indented for visual indent; is harmless - # (over-indent is visually unambiguous) and currently generates too - # many warnings for existing code. - E127, - - # continuation line under-indented for visual indent - E128, - # visually indented line with same indent as next logical line - E129, - # continuation line unaligned for hanging indent - E131, - # closing bracket is missing indentation - E133, - # whitespace after ‘(‘ - E201, - # whitespace before ‘)’ - E202, - # whitespace before ‘:’; this warning is invalid for slices - E203, - # whitespace before ‘(‘ - E211, - # multiple spaces before operator - E221, - # multiple spaces after operator - E222, - # tab before operator - E223, - # tab after operator - E224, - # missing whitespace around operator - E225, - # missing whitespace around arithmetic operator - E226, - # missing whitespace around bitwise or shift operator - E227, - # missing whitespace around modulo operator - E228, - # missing whitespace after ‘,’, ‘;’, or ‘:’ - E231, - # multiple spaces after ‘,’ - E241, - # tab after ‘,’ - E242, - # unexpected spaces around keyword / parameter equals - E251, - # at least two spaces before inline comment - E261, - # inline comment should start with ‘# ‘ - E262, - # block comment should start with ‘# ‘ - E265, - # too many leading ‘#’ for block comment - E266, - # multiple spaces after keyword - E271, - # multiple spaces before keyword - E272, - # tab after keyword - E273, - # tab before keyword - E274, - # missing whitespace after keyword - E275, - # expected 1 blank line, found 0 - E301, - # expected 2 blank lines, found 0 - E302, - # too many blank lines (3) - E303, - # blank lines found after function decorator - E304, - # expected 2 blank lines after end of function or class - E305, - # expected 1 blank line before a nested definition - E306, - # multiple imports on one line - E401, - # line too long (> 79 characters) - E501, - # the backslash is redundant between brackets - E502, - # multiple statements on one line (colon) - E701, - # multiple statements on one line (semicolon) - E702, - # statement ends with a semicolon - E703, - # multiple statements on one line (def) - E704, - # These are pycodestyle lints that black doesn't catch: - # E711, # comparison to None should be ‘if cond is None:’ - # E712, # comparison to True should be ‘if cond is True:’ or ‘if cond:’ - # E713, # test for membership should be ‘not in’ - # E714, # test for object identity should be ‘is not’ - # E721, # do not compare types, use ‘isinstance()’ - # E722, # do not use bare except, specify exception instead - # E731, # do not assign a lambda expression, use a def - # E741, # do not use variables named ‘l’, ‘O’, or ‘I’ - # E742, # do not define classes named ‘l’, ‘O’, or ‘I’ - # E743, # do not define functions named ‘l’, ‘O’, or ‘I’ - # I think these are internal to pycodestyle? - # E901, # SyntaxError or IndentationError - # E902, # IOError - # isn't aware of type-only imports, results in false-positives - F811, - # indentation contains tabs - W191, - # trailing whitespace - W291, - # no newline at end of file - W292, - # blank line contains whitespace - W293, - # blank line at end of file - W391, - # line break before binary operator; binary operator in a new line is - # the standard - W503, - # line break after binary operator - W504, - # not part of PEP8; doc line too long (> 79 characters) - W505, - # These are pycodestyle lints that black doesn't catch: - # W601, # .has_key() is deprecated, use ‘in’ - # W602, # deprecated form of raising exception - # W603, # ‘<>’ is deprecated, use ‘!=’ - # W604, # backticks are deprecated, use ‘repr()’ - # W605, # invalid escape sequence ‘x’ - # W606, # ‘async’ and ‘await’ are reserved keywords starting with Python 3.7 - -# We should've silenced all of Flake8's line-length related lints, in favor of -# Black. However, let's just set this to a large value just to be safe, in case -# we accidentally left in a line-length related lint rule. If we don't set -# anything, it defaults to 79, which is also wrong. -max-line-length = 999 - -exclude = - .pyre, - __pycache__, - .tox, - native, - -max-complexity = 12 - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9af09650b..7858b2d7c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ disclosure of security bugs. In those cases, please go through the process outlined on that page and do not file a public issue. ## Coding Style -We use flake8 and ufmt to enforce coding style. +We use Ruff to enforce coding style. ## License By contributing to LibCST, you agree that your contributions will be licensed diff --git a/README.rst b/README.rst index 5957ba3e8..ef0732798 100644 --- a/README.rst +++ b/README.rst @@ -183,7 +183,7 @@ We have multiple linters, including copyright checks and hatch run lint -We use `ufmt `_ to format code. To format +We use `ruff `_ to format code. To format changes to be conformant, run the following in the root: .. code-block:: shell diff --git a/docs/source/conf.py b/docs/source/conf.py index c210fc1d8..220f31326 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -196,6 +196,7 @@ # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True + # -- autodoc customization def strip_class_signature(app, what, name, obj, options, signature, return_annotation): if what == "class": diff --git a/libcst/_nodes/base.py b/libcst/_nodes/base.py index d9689f8f8..61161923d 100644 --- a/libcst/_nodes/base.py +++ b/libcst/_nodes/base.py @@ -341,7 +341,7 @@ def deep_clone(self: _CSTNodeSelfT) -> _CSTNodeSelfT: True """ cloned_fields: Dict[str, object] = {} - for field in fields(self): + for field in fields(self): # noqa: F402 key = field.name if key[0] == "_": continue diff --git a/libcst/_nodes/tests/test_with.py b/libcst/_nodes/tests/test_with.py index 517ce357f..ef9245a81 100644 --- a/libcst/_nodes/tests/test_with.py +++ b/libcst/_nodes/tests/test_with.py @@ -331,5 +331,6 @@ def test_adding_parens(self) -> None: ) module = cst.Module([]) self.assertEqual( - module.code_for_node(node), ("with ( foo(),\n" "bar(), ): pass\n") # noqa + module.code_for_node(node), + ("with ( foo(),\n" "bar(), ): pass\n"), # noqa ) diff --git a/libcst/_parser/conversions/params.py b/libcst/_parser/conversions/params.py index 9ac7f1d16..311ea02f3 100644 --- a/libcst/_parser/conversions/params.py +++ b/libcst/_parser/conversions/params.py @@ -102,7 +102,7 @@ def convert_argslist( # noqa: C901 kwonly_params: List[Param] = [] star_kwarg: Optional[Param] = None - def add_param( + def add_param( # noqa: C901 current_param: Optional[List[Param]], param: Union[Param, ParamStar] ) -> Optional[List[Param]]: nonlocal star_arg diff --git a/libcst/_typed_visitor.py b/libcst/_typed_visitor.py index 7e1d1c420..cb64ce352 100644 --- a/libcst/_typed_visitor.py +++ b/libcst/_typed_visitor.py @@ -12,7 +12,6 @@ from libcst._removal_sentinel import RemovalSentinel from libcst._typed_visitor_base import mark_no_op - if TYPE_CHECKING: from libcst._nodes.expression import ( # noqa: F401 Annotation, @@ -75,7 +74,9 @@ UnaryOperation, Yield, ) - from libcst._nodes.module import Module # noqa: F401 + from libcst._nodes.module import ( # noqa: F401 + Module, + ) from libcst._nodes.op import ( # noqa: F401 Add, AddAssign, diff --git a/libcst/_typed_visitor_base.py b/libcst/_typed_visitor_base.py index de751a158..5a218347e 100644 --- a/libcst/_typed_visitor_base.py +++ b/libcst/_typed_visitor_base.py @@ -5,7 +5,6 @@ from typing import Any, Callable, cast, TypeVar - # pyre-fixme[24]: Generic type `Callable` expects 2 type parameters. F = TypeVar("F", bound=Callable) diff --git a/libcst/codegen/generate.py b/libcst/codegen/generate.py index 92f131765..3e075c29d 100644 --- a/libcst/codegen/generate.py +++ b/libcst/codegen/generate.py @@ -26,7 +26,7 @@ def format_file(fname: str) -> None: subprocess.check_call( - ["ufmt", "format", fname], + ["ruff", "format", fname], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) diff --git a/libcst/codemod/commands/add_trailing_commas.py b/libcst/codemod/commands/add_trailing_commas.py index 2f33a4bd7..a80472a7e 100644 --- a/libcst/codemod/commands/add_trailing_commas.py +++ b/libcst/codemod/commands/add_trailing_commas.py @@ -10,7 +10,6 @@ import libcst as cst from libcst.codemod import CodemodContext, VisitorBasedCodemodCommand - presets_per_formatter: Dict[str, Dict[str, int]] = { "black": { "parameter_count": 1, diff --git a/libcst/codemod/visitors/_apply_type_annotations.py b/libcst/codemod/visitors/_apply_type_annotations.py index 9c826cc43..20348170e 100644 --- a/libcst/codemod/visitors/_apply_type_annotations.py +++ b/libcst/codemod/visitors/_apply_type_annotations.py @@ -9,7 +9,6 @@ import libcst as cst import libcst.matchers as m - from libcst.codemod._context import CodemodContext from libcst.codemod._visitor import ContextAwareTransformer from libcst.codemod.visitors._add_imports import AddImportsVisitor @@ -19,7 +18,6 @@ from libcst.helpers import get_full_name_for_node from libcst.metadata import PositionProvider, QualifiedNameProvider - NameOrAttribute = Union[cst.Name, cst.Attribute] NAME_OR_ATTRIBUTE = (cst.Name, cst.Attribute) # Union type for *args and **args @@ -881,7 +879,7 @@ def _quote_future_annotations(self, annotation: cst.Annotation) -> cst.Annotatio if ( isinstance(node, cst.Name) and (node.value in self.global_names) - and not (node.value in self.visited_classes) + and node.value not in self.visited_classes ): return annotation.with_changes( annotation=cst.SimpleString(value=f'"{node.value}"') diff --git a/libcst/matchers/__init__.py b/libcst/matchers/__init__.py index 7e3761b82..ed40d70a8 100644 --- a/libcst/matchers/__init__.py +++ b/libcst/matchers/__init__.py @@ -12,7 +12,6 @@ import libcst as cst from libcst.matchers._decorators import call_if_inside, call_if_not_inside, leave, visit - from libcst.matchers._matcher_base import ( AbstractBaseMatcherNodeMeta, AllOf, diff --git a/libcst/matchers/_matcher_base.py b/libcst/matchers/_matcher_base.py index 039694a5f..8792630f6 100644 --- a/libcst/matchers/_matcher_base.py +++ b/libcst/matchers/_matcher_base.py @@ -1017,7 +1017,7 @@ def _matches_zero_nodes( MatchIfTrue[libcst.CSTNode], _BaseMetadataMatcher, DoNotCareSentinel, - ] + ], ) -> bool: if isinstance(matcher, AtLeastN) and matcher.n == 0: return True diff --git a/libcst/matchers/_return_types.py b/libcst/matchers/_return_types.py index 9d20a23ad..e28e58006 100644 --- a/libcst/matchers/_return_types.py +++ b/libcst/matchers/_return_types.py @@ -71,7 +71,6 @@ Yield, ) from libcst._nodes.module import Module - from libcst._nodes.op import ( Add, AddAssign, @@ -202,7 +201,6 @@ ) from libcst._removal_sentinel import RemovalSentinel - TYPED_FUNCTION_RETURN_MAPPING: TypingDict[Type[CSTNode], object] = { Add: BaseBinaryOp, AddAssign: BaseAugOp, diff --git a/libcst/metadata/accessor_provider.py b/libcst/metadata/accessor_provider.py index 5d4f22e42..4563984bc 100644 --- a/libcst/metadata/accessor_provider.py +++ b/libcst/metadata/accessor_provider.py @@ -7,7 +7,6 @@ import dataclasses import libcst as cst - from libcst.metadata.base_provider import VisitorMetadataProvider diff --git a/libcst/metadata/tests/test_accessor_provider.py b/libcst/metadata/tests/test_accessor_provider.py index 6ccfad5ee..148ee9a73 100644 --- a/libcst/metadata/tests/test_accessor_provider.py +++ b/libcst/metadata/tests/test_accessor_provider.py @@ -4,7 +4,6 @@ # LICENSE file in the root directory of this source tree. import dataclasses - from textwrap import dedent import libcst as cst diff --git a/libcst/tests/__main__.py b/libcst/tests/__main__.py index 44e6bbe0a..a4bfb978a 100644 --- a/libcst/tests/__main__.py +++ b/libcst/tests/__main__.py @@ -7,7 +7,6 @@ from libcst._parser.entrypoints import is_native - if __name__ == "__main__": parser_type = "native" if is_native() else "pure" print(f"running tests with {parser_type!r} parser") diff --git a/libcst/tests/test_add_slots.py b/libcst/tests/test_add_slots.py index e354f60b6..5dd1d30d4 100644 --- a/libcst/tests/test_add_slots.py +++ b/libcst/tests/test_add_slots.py @@ -8,7 +8,6 @@ from typing import ClassVar from libcst._add_slots import add_slots - from libcst.testing.utils import UnitTest diff --git a/pyproject.toml b/pyproject.toml index dae305532..7a1d2e70e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,11 +25,10 @@ dependencies = [ [project.optional-dependencies] dev = [ - "black==23.9.1", + "black==23.9.1", # required for testing formatting "coverage>=4.5.4", "build>=0.10.0", "fixit==2.0.0.post1", - "flake8==6.1.0", "Sphinx>=5.1.1", "hypothesis>=4.36.0", "hypothesmith>=0.0.4", @@ -40,8 +39,7 @@ dev = [ "pyre-check==0.9.18; platform_system != 'Windows'", "setuptools_scm>=6.0.1", "sphinx-rtd-theme>=0.4.3", - "ufmt==2.2.0", - "usort==1.0.7", + "ruff==0.1.6", "setuptools-rust>=1.5.2", "slotscheck>=0.7.1", "jinja2==3.1.2", @@ -62,10 +60,9 @@ features = ["dev"] [tool.hatch.envs.default.scripts] docs = "sphinx-build -ab html docs/source docs/build" fixtures = ["python scripts/regenerate-fixtures.py", "git diff --exit-code"] -format = "ufmt format libcst scripts" +format = "ruff format ." lint = [ - "flake8 libcst", - "ufmt check libcst scripts", + "ruff .", "python -m slotscheck libcst", "python scripts/check_copyright.py", ] @@ -75,5 +72,34 @@ typecheck = ["pyre --version", "pyre check"] [tool.slotscheck] exclude-modules = '^libcst\.(testing|tests)' -[tool.ufmt] -excludes = ["native/", "stubs/"] +[tool.ruff] +exclude = [ + ".pyre", + "__pycache__", + ".tox", + "native", + "stubs", +] +ignore = [ + "E101", + "E401", + "E501", + "F811", + "W292", + "W293", + "W505", +] +extend-select = [ + "C9", + "E", + "F", + "I", + "W", +] + +[tool.ruff.isort] +order-by-type = false +combine-as-imports = true + +[tool.ruff.mccabe] +max-complexity = 12