mirror of https://github.com/astral-sh/ruff
Add a preliminary tutorial (#3281)
This commit is contained in:
parent
0ed9fccce9
commit
8066607ea3
|
|
@ -30,7 +30,7 @@ An extremely fast Python linter, written in Rust.
|
||||||
- 🤝 Python 3.11 compatibility
|
- 🤝 Python 3.11 compatibility
|
||||||
- 📦 Built-in caching, to avoid re-analyzing unchanged files
|
- 📦 Built-in caching, to avoid re-analyzing unchanged files
|
||||||
- 🔧 Autofix support, for automatic error correction (e.g., automatically remove unused imports)
|
- 🔧 Autofix support, for automatic error correction (e.g., automatically remove unused imports)
|
||||||
- 📏 Over [500 built-in rules](https://beta.ruff.rs/docs/rules/) (and growing)
|
- 📏 Over [500 built-in rules](https://beta.ruff.rs/docs/rules/)
|
||||||
- ⚖️ [Near-parity](https://beta.ruff.rs/docs/faq/#how-does-ruff-compare-to-flake8) with the built-in Flake8 rule set
|
- ⚖️ [Near-parity](https://beta.ruff.rs/docs/faq/#how-does-ruff-compare-to-flake8) with the built-in Flake8 rule set
|
||||||
- 🔌 Native re-implementations of dozens of Flake8 plugins, like flake8-bugbear
|
- 🔌 Native re-implementations of dozens of Flake8 plugins, like flake8-bugbear
|
||||||
- ⌨️ First-party editor integrations for [VS Code](https://github.com/charliermarsh/ruff-vscode) and [more](https://github.com/charliermarsh/ruff-lsp)
|
- ⌨️ First-party editor integrations for [VS Code](https://github.com/charliermarsh/ruff-vscode) and [more](https://github.com/charliermarsh/ruff-lsp)
|
||||||
|
|
@ -186,7 +186,6 @@ exclude = [
|
||||||
"node_modules",
|
"node_modules",
|
||||||
"venv",
|
"venv",
|
||||||
]
|
]
|
||||||
per-file-ignores = {}
|
|
||||||
|
|
||||||
# Same as Black.
|
# Same as Black.
|
||||||
line-length = 88
|
line-length = 88
|
||||||
|
|
@ -225,6 +224,9 @@ and a [subset](https://beta.ruff.rs/docs/rules/#error-e) of the `E` category, om
|
||||||
stylistic rules made obsolete by the use of an autoformatter, like
|
stylistic rules made obsolete by the use of an autoformatter, like
|
||||||
[Black](https://github.com/psf/black).
|
[Black](https://github.com/psf/black).
|
||||||
|
|
||||||
|
If you're just getting started with Ruff, **the default rule set is a great place to start**: it
|
||||||
|
catches a wide variety of common errors (like unused imports) with zero configuration.
|
||||||
|
|
||||||
<!-- End section: Rules -->
|
<!-- End section: Rules -->
|
||||||
|
|
||||||
For a complete enumeration of the supported rules, see [_Rules_](https://beta.ruff.rs/docs/rules/).
|
For a complete enumeration of the supported rules, see [_Rules_](https://beta.ruff.rs/docs/rules/).
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
*
|
*
|
||||||
!assets
|
!assets
|
||||||
!requirements.txt
|
|
||||||
!configuration.md
|
!configuration.md
|
||||||
!editor-integrations.md
|
!editor-integrations.md
|
||||||
!faq.md
|
!faq.md
|
||||||
!installation.md
|
!installation.md
|
||||||
|
!requirements.txt
|
||||||
|
!tutorial.md
|
||||||
!usage.md
|
!usage.md
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,255 @@
|
||||||
|
# Tutorial
|
||||||
|
|
||||||
|
This tutorial will walk you through the process of integrating Ruff into your project. For a more
|
||||||
|
detailed overview, see [_Configuration_](configuration.md).
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
Let's assume that our project structure looks like:
|
||||||
|
|
||||||
|
```text
|
||||||
|
numbers/
|
||||||
|
__init__.py
|
||||||
|
numbers.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Where `numbers.py` contains the following code:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def sum_even_numbers(numbers: List[int]) -> int:
|
||||||
|
"""Given a list of integers, return the sum of all even numbers in the list."""
|
||||||
|
return sum(num for num in numbers if num % 2 == 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
To start, we'll install Ruff through PyPI (or with our [preferred package manager](installation.md)):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
> pip install ruff
|
||||||
|
```
|
||||||
|
|
||||||
|
We can then run Ruff over our project via:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
❯ ruff check .
|
||||||
|
numbers/numbers.py:3:8: F401 [*] `os` imported but unused
|
||||||
|
Found 1 error.
|
||||||
|
[*] 1 potentially fixable with the --fix option.
|
||||||
|
```
|
||||||
|
|
||||||
|
Ruff identified an unused import, which is a common error in Python code. Ruff considers this a
|
||||||
|
"fixable" error, so we can resolve the issue automatically by running:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
❯ ruff check --fix .
|
||||||
|
Found 1 error (1 fixed, 0 renumbersing).
|
||||||
|
```
|
||||||
|
|
||||||
|
Running `git diff` shows the following:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
--- a/numbers/numbers.py
|
||||||
|
+++ b/numbers/numbers.py
|
||||||
|
@@ -1,7 +1,5 @@
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
-import os
|
||||||
|
-
|
||||||
|
|
||||||
|
def sum_even_numbers(numbers: List[int]) -> int:
|
||||||
|
"""Given a list of integers, return the sum of all even numbers in the list."""
|
||||||
|
return sum(num for num in numbers if num % 2 == 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
Thus far, we've been using Ruff's default configuration. Let's take a look at how we can customize
|
||||||
|
Ruff's behavior.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
To determine the appropriate settings for each Python file, Ruff looks for the first
|
||||||
|
`pyproject.toml`, `ruff.toml`, or `.ruff.toml` file in the file's directory or any parent directory.
|
||||||
|
|
||||||
|
Let's create a `pyproject.toml` file in our project's root directory:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[tool.ruff]
|
||||||
|
# Decrease the maximum line length to 79 characters.
|
||||||
|
line-length = 79
|
||||||
|
```
|
||||||
|
|
||||||
|
Running Ruff again, we can see that it now enforces a line length of 79 characters:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
❯ ruff check .
|
||||||
|
numbers/numbers.py:6:80: E501 Line too long (83 > 79 characters)
|
||||||
|
Found 1 error.
|
||||||
|
```
|
||||||
|
|
||||||
|
For a full enumeration of the supported settings, see [_Settings_](settings.md). For our project
|
||||||
|
specifically, we'll want to make note of the minimum supported Python version:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[tool.ruff]
|
||||||
|
# Decrease the maximum line length to 79 characters.
|
||||||
|
line-length = 79
|
||||||
|
# Support Python 3.10+.
|
||||||
|
target-version = "py310"
|
||||||
|
src = ["src"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rule Selection
|
||||||
|
|
||||||
|
Ruff supports [over 500 lint rules](rules.md) split across over 40 built-in plugins, but
|
||||||
|
determining the right set of rules will depend on your project's needs: some rules may be too
|
||||||
|
strict, some are framework-specific, and so on.
|
||||||
|
|
||||||
|
By default, Ruff enforces the `E`- and `F`-prefixed rules, which correspond to those derived from
|
||||||
|
pycodestyle and Pyflakes, respectively.
|
||||||
|
|
||||||
|
If you're introducing a linter for the first time, **the default rule set is a great place to
|
||||||
|
start**: it's narrow and focused while catching a wide variety of common errors (like unused
|
||||||
|
imports) with zero configuration.
|
||||||
|
|
||||||
|
If you're migrating to Ruff from another linter, you can enable rules that are equivalent to
|
||||||
|
those enforced in your previous configuration. For example, if we want to enforce the pyupgrade
|
||||||
|
rules, we can add the following to our `pyproject.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[tool.ruff]
|
||||||
|
select = [
|
||||||
|
"E", # pycodestyle
|
||||||
|
"F", # pyflakes
|
||||||
|
"UP", # pyupgrade
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
If we run Ruff again, we'll see that it now enforces the pyupgrade rules. In particular, Ruff flags
|
||||||
|
the use of `List` instead of its standard-library variant:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
❯ ruff check .
|
||||||
|
numbers/numbers.py:5:31: UP006 [*] Use `list` instead of `List` for type annotations
|
||||||
|
numbers/numbers.py:6:80: E501 Line too long (83 > 79 characters)
|
||||||
|
Found 2 errors.
|
||||||
|
[*] 1 potentially fixable with the --fix option.
|
||||||
|
```
|
||||||
|
|
||||||
|
Over time, we may choose to enforce additional rules. For example, we may want to enforce that
|
||||||
|
all functions have docstrings:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[tool.ruff]
|
||||||
|
select = [
|
||||||
|
"E", # pycodestyle
|
||||||
|
"F", # pyflakes
|
||||||
|
"UP", # pyupgrade
|
||||||
|
"D", # pydocstyle
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff.pydocstyle]
|
||||||
|
convention = "google"
|
||||||
|
```
|
||||||
|
|
||||||
|
If we run Ruff again, we'll see that it now enforces the pydocstyle rules:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
❯ ruff check .
|
||||||
|
numbers/__init__.py:1:1: D104 Missing docstring in public package
|
||||||
|
numbers/numbers.py:1:1: D100 Missing docstring in public module
|
||||||
|
numbers/numbers.py:5:31: UP006 [*] Use `list` instead of `List` for type annotations
|
||||||
|
numbers/numbers.py:5:80: E501 Line too long (83 > 79 characters)
|
||||||
|
Found 3 errors.
|
||||||
|
[*] 1 potentially fixable with the --fix option.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ignoring Errors
|
||||||
|
|
||||||
|
Any lint rule can be ignored by adding a `# noqa` comment to the line in question. For example,
|
||||||
|
let's ignore the `UP006` rule for the `List` import:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
def sum_even_numbers(numbers: List[int]) -> int: # noqa: UP006
|
||||||
|
"""Given a list of integers, return the sum of all even numbers in the list."""
|
||||||
|
return sum(num for num in numbers if num % 2 == 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
Running Ruff again, we'll see that it no longer flags the `List` import:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
❯ ruff check .
|
||||||
|
numbers/__init__.py:1:1: D104 Missing docstring in public package
|
||||||
|
numbers/numbers.py:1:1: D100 Missing docstring in public module
|
||||||
|
numbers/numbers.py:5:80: E501 Line too long (83 > 79 characters)
|
||||||
|
Found 3 errors.
|
||||||
|
```
|
||||||
|
|
||||||
|
If we want to ignore a rule for an entire file, we can add a `# ruff: noqa` comment to the top of
|
||||||
|
the file:
|
||||||
|
|
||||||
|
```py
|
||||||
|
# ruff: noqa: UP006
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
def sum_even_numbers(numbers: List[int]) -> int:
|
||||||
|
"""Given a list of integers, return the sum of all even numbers in the list."""
|
||||||
|
return sum(num for num in numbers if num % 2 == 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
When enabling a new rule on an existing codebase, you may want to ignore all _existing_
|
||||||
|
violations of that rule and instead focus on enforcing it going forward.
|
||||||
|
|
||||||
|
Ruff enables this workflow via the `--add-noqa` flag, which will adds a `# noqa` directive to each
|
||||||
|
line based on its existing violations. We can combine `--add-noqa` with the `--select` command-line
|
||||||
|
flag to add `# noqa` directives to all existing `UP006` violations:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
❯ ruff check --select UP006 --add-noqa .
|
||||||
|
Added 1 noqa directive.
|
||||||
|
```
|
||||||
|
|
||||||
|
Running `git diff` shows the following:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
diff --git a/tutorial/src/main.py b/tutorial/src/main.py
|
||||||
|
index b9291c5ca..b9f15b8c1 100644
|
||||||
|
--- a/numbers/numbers.py
|
||||||
|
+++ b/numbers/numbers.py
|
||||||
|
@@ -1,6 +1,6 @@
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
-def sum_even_numbers(numbers: List[int]) -> int:
|
||||||
|
+def sum_even_numbers(numbers: List[int]) -> int: # noqa: UP006
|
||||||
|
"""Given a list of integers, return the sum of all even numbers in the list."""
|
||||||
|
return sum(num for num in numbers if num % 2 == 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Continuous Integration
|
||||||
|
|
||||||
|
This tutorial has focused on Ruff's command-line interface, but Ruff can also be used as a
|
||||||
|
[pre-commit](https://pre-commit.com) hook:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||||
|
# Ruff version.
|
||||||
|
rev: 'v0.0.253'
|
||||||
|
hooks:
|
||||||
|
- id: ruff
|
||||||
|
```
|
||||||
|
|
||||||
|
See [_Usage_](usage.md) for more.
|
||||||
|
|
||||||
|
## Editor Integrations
|
||||||
|
|
||||||
|
Ruff can also be used as a [VS Code extension](https://github.com/charliermarsh/ruff-vscode) or
|
||||||
|
alongside any other editor through the [Ruff LSP](https://github.com/charliermarsh/ruff-lsp).
|
||||||
|
|
||||||
|
See [_Editor Integrations_](editor-integrations.md).
|
||||||
|
|
@ -19,6 +19,7 @@ class Section(NamedTuple):
|
||||||
|
|
||||||
SECTIONS: list[Section] = [
|
SECTIONS: list[Section] = [
|
||||||
Section("Overview", "index.md", generated=True),
|
Section("Overview", "index.md", generated=True),
|
||||||
|
Section("Tutorial", "tutorial.md", generated=False),
|
||||||
Section("Installation", "installation.md", generated=False),
|
Section("Installation", "installation.md", generated=False),
|
||||||
Section("Usage", "usage.md", generated=False),
|
Section("Usage", "usage.md", generated=False),
|
||||||
Section("Configuration", "configuration.md", generated=False),
|
Section("Configuration", "configuration.md", generated=False),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue