| Installing Ruff |
|
install Ruff into a virtual environment use the command $ python -m pip install ruffcheck installation using version command $ ruff version ruff 0.4.7 |
| Linting - Checking for Errors |
|
below is a simple script called one_ring.py when run it gets a random LotR character name from a tuple ... code has no real practical use beyond being an example the linting steps are going to be the same import os
import random
CHARACTERS = ("Frodo", "Sam", "Merry", "Pippin", "Aragorn", "Legolas", "Gimli", "Boromir", "Gandalf", "Saruman", "Sauron")
def random_character():
return random.choice(CHARACTERS)
def ring_bearer():
return name in ("Frodo", "Sam")
if __name__ == "__main__":
character = random_character()
if ring_bearer(character):
print(f"{character} is a ring bearer")
else:
print(f"{character} is not a ring bearer")
check is a basic CLI commandby default command checks all files in the current directory $ ruff check one_ring.py:1:8: F401 [*] `os` imported but unused one_ring.py:10:12: F821 Undefined name `name` Found 2 errors. [*] 1 fixable with the `--fix` option.options # check a single file in a directory ruff check one_ring.py # check files in a subdirectory ruff check src/ and nested subfoldersRuff can fix errors when the --fix flag is used $ ruff check --fix one_ring.py:9:12: F821 Undefined name `name` Found 2 errors (1 fixed, 1 remaining).the format of error description line is <filename>:<line number>:<character index>: <error code> <description>for more details about the error use the ruff rule command $ ruff rule F821the output from the command # undefined-name (F821)
Derived from the **PyFlakes** linter.
## What it does
Checks for uses of undefined names.
## Why is this bad?
An undefined name is likely to raise `NameError` at runtime.
## Example
```python
def double():
return n * 2 # raises `NameError` if `n` is undefined when `double` is called
```
Use instead:
```python
def double(n):
return n * 2
```
## References
- [Python documentation: Naming and binding](https://docs.python.org/3/reference/executionmodel.html#naming-and-binding)
with the error understood appropriate changes can be made to the code
# ...
def ring_bearer(name):
return name in ("Frodo", "Sam")
# ...
rerunning ruff check resulting output
$ ruff check All checks passed! |
| Linting - Speeding Up the Workflow |
|
ruff can be used to provide continuous linting in a new terminal run the command $ ruff check --watchafter running the command will see output similar to [14:04:01 PM] Starting linter in watch mode... [14:04:01 PM] Found 0 errors. Watching for file changes. |
| Linting - Finding More Errors |
|
by default Ruff enables Flake8's F rules,
along with a subset of the E rules omits any style rules which overlap with the use of a formatter can tell ruff check which additional rules to include or exclude you can ask it to include all E rules or a specific rule with the --select flag $ ruff check --select E one_ring.py:4:89: E501 Line too long (122 > 88) Found 1 error. $ ruff check --select E501 one_ring.py:4:89: E501 Line too long (122 > 88) Found 1 error.the length of a line is a style rule |
| Formatting Python Code |
|
the format command takes optional arguments for a path to a single file or directory with a single file as the example no arguments are needed $ ruff format 1 file reformattedafter running the command the example file now looks like import random
CHARACTERS = (
"Frodo",
"Sam",
"Merry",
"Pippin",
"Aragorn",
"Legolas",
"Gimli",
"Boromir",
"Gandalf",
"Saruman",
"Sauron",
)
def random_character():
return random.choice(CHARACTERS)
def ring_bearer(name):
return name in ("Frodo", "Sam")
if __name__ == "__main__":
character = random_character()
if ring_bearer(character):
print(f"{character} is a ring bearer")
else:
print(f"{character} is not a ring bearer")
the spacing between functions is now consistent (PEP 8 compliant)uses the recommended two spaces between functions to see what changes will be made when ruff format is run, can run it with the --diff flag flag will display the proposed changes before they're made --- one_ring.py
+++ one_ring.py
@@ -1,16 +1,31 @@
import random
-CHARACTERS = ("Frodo", "Sam", "Merry", "Pippin", "Aragorn", "Legolas", "Gimli", "Boromir", "Gandalf", "Saruman", "Sauron")
+CHARACTERS = (
+ "Frodo",
+ "Sam",
+ "Merry",
+ "Pippin",
+ "Aragorn",
+ "Legolas",
+ "Gimli",
+ "Boromir",
+ "Gandalf",
+ "Saruman",
+ "Sauron",
+)
+
def random_character():
return random.choice(CHARACTERS)
+
def ring_bearer(name):
return name in ("Frodo", "Sam")
+
if __name__ == "__main__":
character = random_character()
if ring_bearer(character):
print(f"{character} is a ring bearer")
else:
- print(f"{character} is not a ring bearer")
\ No newline at end of file
+ print(f"{character} is not a ring bearer")
1 file would be reformatted
the minus sign (-) indicates a line to be removedthe plus sign (+) indicates a line which will be added |
| Configuring Ruff |
|
Ruff allows storing its configuration in a TOML file file can be
[tool.ruff]
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"site-packages",
"venv",
]
# Same as Black.
line-length = 88
indent-width = 4
# Assume Python 3.9
target-version = "py39"
[tool.ruff.lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
select = ["E4", "E7", "E9", "F"]
ignore = []
# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
[tool.ruff.format]
# Like Black, use double quotes for strings.
quote-style = "double"
# Like Black, indent with spaces, rather than tabs.
indent-style = "space"
# Like Black, respect magic trailing commas.
skip-magic-trailing-comma = false
# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"
# Enable auto-formatting of code examples in docstrings. Markdown,
# reStructuredText code/literal blocks and doctests are all supported.
#
# This is currently disabled by default, but it is planned for this
# to be opt-out in the future.
docstring-code-format = false
# Set the line length limit used when formatting code snippets in
# docstrings.
#
# This only has an effect when the `docstring-code-format` setting is
# enabled.
docstring-code-line-length = "dynamic"
ruff.toml -Ruff's default configuration
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"site-packages",
"venv",
]
# Same as Black.
line-length = 88
indent-width = 4
# Assume Python 3.9
target-version = "py39"
[lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
select = ["E4", "E7", "E9", "F"]
ignore = []
# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
[format]
# Like Black, use double quotes for strings.
quote-style = "double"
# Like Black, indent with spaces, rather than tabs.
indent-style = "space"
# Like Black, respect magic trailing commas.
skip-magic-trailing-comma = false
# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"
# Enable auto-formatting of code examples in docstrings. Markdown,
# reStructuredText code/literal blocks and doctests are all supported.
#
# This is currently disabled by default, but it is planned for this
# to be opt-out in the future.
docstring-code-format = false
# Set the line length limit used when formatting code snippets in
# docstrings.
#
# This only has an effect when the `docstring-code-format` setting is
# enabled.
docstring-code-line-length = "dynamic"
for further information on Ruff visit
docs.astral.sh/ruff/
|