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