diff --git a/README.md b/README.md index 5861b206e1..c70fa4709b 100644 --- a/README.md +++ b/README.md @@ -97,21 +97,19 @@ developer of [Zulip](https://github.com/zulip/zulip): For more, see the [documentation](https://beta.ruff.rs/docs/). -1. [Installation and Usage](#installation-and-usage) +1. [Getting Started](#getting-started) 2. [Configuration](#configuration) -3. [Supported Rules](#supported-rules) +3. [Rules](#rules) 4. [Contributing](#contributing) 5. [Support](#support) 6. [Acknowledgements](#acknowledgements) 7. [Who's Using Ruff?](#whos-using-ruff) 8. [License](#license) -## Installation and Usage +## Getting Started For more, see the [documentation](https://beta.ruff.rs/docs/). - - ### Installation Ruff is available as [`ruff`](https://pypi.org/project/ruff/) on PyPI: @@ -120,31 +118,8 @@ Ruff is available as [`ruff`](https://pypi.org/project/ruff/) on PyPI: pip install ruff ``` -For **macOS Homebrew** and **Linuxbrew** users, Ruff is also available as [`ruff`](https://formulae.brew.sh/formula/ruff) on Homebrew: - -```shell -brew install ruff -``` - -For **Conda** users, Ruff is also available as [`ruff`](https://anaconda.org/conda-forge/ruff) on `conda-forge`: - -```shell -conda install -c conda-forge ruff -``` - -For **Arch Linux** users, Ruff is also available as [`ruff`](https://archlinux.org/packages/community/x86_64/ruff/) on the official repositories: - -```shell -pacman -S ruff -``` - -For **Alpine** users, Ruff is also available as [`ruff`](https://pkgs.alpinelinux.org/package/edge/testing/x86_64/ruff) on the testing repositories: - -```shell -apk add ruff -``` - -[![Packaging status](https://repology.org/badge/vertical-allrepos/ruff-python-linter.svg?exclude_unsupported=1)](https://repology.org/project/ruff-python-linter/versions) +You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff), +and with [a variety of other package managers](https://beta.ruff.rs/docs/installation-and-usage/). ### Usage @@ -157,13 +132,7 @@ ruff check path/to/code/*.py # Lint all `.py` files in `/path/to/code` ruff check path/to/code/to/file.py # Lint `file.py` ``` -You can run Ruff in `--watch` mode to automatically re-run on-change: - -```shell -ruff check path/to/code/ --watch -``` - -Ruff also works with [pre-commit](https://pre-commit.com): +Ruff can also be used as a [pre-commit](https://pre-commit.com) hook: ```yaml - repo: https://github.com/charliermarsh/ruff-pre-commit @@ -173,37 +142,20 @@ Ruff also works with [pre-commit](https://pre-commit.com): - id: ruff ``` -Or, to enable autofix: +Ruff can also be used as a [VS Code extension](https://github.com/charliermarsh/ruff-vscode) or +with other editors through the [Ruff LSP](https://github.com/charliermarsh/ruff-lsp). -```yaml -- repo: https://github.com/charliermarsh/ruff-pre-commit - # Ruff version. - rev: 'v0.0.252' - hooks: - - id: ruff - args: [--fix, --exit-non-zero-on-fix] -``` +### Configuration -Note that Ruff's pre-commit hook should run before Black, isort, and other formatting tools. - - - -## Configuration - - - -Ruff can be configured via a `pyproject.toml` file, a `ruff.toml` file, or through the command line. - -For a complete enumeration of the available configuration options, see the -[documentation](https://beta.ruff.rs/docs/settings/). - -### Configure via `pyproject.toml` +Ruff can be configured via a `pyproject.toml` file, a `ruff.toml` file, or through the command line +(see: [_Configuration_](https://beta.ruff.rs/docs/configuration/), or +[_Settings_](https://beta.ruff.rs/docs/settings/) for a complete list of all configuration options). If left unspecified, the default configuration is equivalent to: ```toml [tool.ruff] -# Enable Pyflakes `E` and `F` codes by default. +# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default. select = ["E", "F"] ignore = [] @@ -250,428 +202,39 @@ target-version = "py310" max-complexity = 10 ``` -As an example, the following would configure Ruff to: (1) enforce flake8-bugbear rules, in addition -to the defaults; (2) avoid enforcing line-length violations (`E501`); (3) avoid attempting to fix -flake8-bugbear (`B`) violations; and (3) ignore import-at-top-of-file violations (`E402`) in -`__init__.py` files: - -```toml -[tool.ruff] -# Enable flake8-bugbear (`B`) rules. -select = ["E", "F", "B"] - -# Never enforce `E501` (line length violations). -ignore = ["E501"] - -# Avoid trying to fix flake8-bugbear (`B`) violations. -unfixable = ["B"] - -# Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`. -[tool.ruff.per-file-ignores] -"__init__.py" = ["E402"] -"path/to/file.py" = ["E402"] -``` - -Plugin configurations should be expressed as subsections, e.g.: - -```toml -[tool.ruff] -# Add "Q" to the list of enabled codes. -select = ["E", "F", "Q"] - -[tool.ruff.flake8-quotes] -docstring-quotes = "double" -``` - -Ruff mirrors Flake8's rule code system, in which each rule code consists of a one-to-three letter -prefix, followed by three digits (e.g., `F401`). The prefix indicates that "source" of the rule -(e.g., `F` for Pyflakes, `E` for pycodestyle, `ANN` for flake8-annotations). The set of enabled -rules is determined by the `select` and `ignore` options, which support both the full code (e.g., -`F401`) and the prefix (e.g., `F`). - -As a special-case, Ruff also supports the `ALL` code, which enables all rules. Note that some of the -pydocstyle rules conflict (e.g., `D203` and `D211`) as they represent alternative docstring -formats. Enabling `ALL` without further configuration may result in suboptimal behavior, especially -for the pydocstyle plugin. - -If you're wondering how to configure Ruff, here are some **recommended guidelines**: - -* Prefer `select` and `ignore` over `extend-select` and `extend-ignore`, to make your rule set - explicit. -* Use `ALL` with discretion. Enabling `ALL` will implicitly enable new rules whenever you upgrade. -* Start with a small set of rules (`select = ["E", "F"]`) and add a category at-a-time. For example, - you might consider expanding to `select = ["E", "F", "B"]` to enable the popular flake8-bugbear - extension. -* By default, Ruff's autofix is aggressive. If you find that it's too aggressive for your liking, - consider turning off autofix for specific rules or categories (see: [FAQ](https://beta.ruff.rs/docs/faq/#ruff-tried-to-fix-something-but-it-broke-my-code-what-should-i-do)). - -### Configure via `ruff.toml` - -As an alternative to `pyproject.toml`, Ruff will also respect a `ruff.toml` file, which implements -an equivalent schema (though the `[tool.ruff]` hierarchy can be omitted). For example, the -`pyproject.toml` described above would be represented via the following `ruff.toml`: - -```toml -# Enable flake8-bugbear (`B`) rules. -select = ["E", "F", "B"] - -# Never enforce `E501` (line length violations). -ignore = ["E501"] - -# Avoid trying to fix flake8-bugbear (`B`) violations. -unfixable = ["B"] - -# Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`. -[per-file-ignores] -"__init__.py" = ["E402"] -"path/to/file.py" = ["E402"] -``` - -For a full list of configurable options, see the [list of all options](https://beta.ruff.rs/docs/settings/). - -### Command-line interface - -Some configuration settings can be provided via the command-line, such as those related to +Some configuration options can be provided via the command-line, such as those related to rule enablement and disablement, file discovery, logging level, and more: ```shell ruff check path/to/code/ --select F401 --select F403 --quiet ``` -See `ruff help` for more on Ruff's top-level commands: +See `ruff help` for more on Ruff's top-level commands, or `ruff help check` for more on the +linting command. - - -```text -Ruff: An extremely fast Python linter. - -Usage: ruff [OPTIONS] - -Commands: - check Run Ruff on the given files or directories (default) - rule Explain a rule - config List or describe the available configuration options - linter List all supported upstream linters - clean Clear any caches in the current directory and any subdirectories - help Print this message or the help of the given subcommand(s) - -Options: - -h, --help Print help - -V, --version Print version - -Log levels: - -v, --verbose Enable verbose logging - -q, --quiet Print lint violations, but nothing else - -s, --silent Disable all logging (but still exit with status code "1" upon detecting lint violations) - -For help with a specific command, see: `ruff help `. -``` - - - -Or `ruff help check` for more on the linting command: - - - -```text -Run Ruff on the given files or directories (default) - -Usage: ruff check [OPTIONS] [FILES]... - -Arguments: - [FILES]... List of files or directories to check - -Options: - --fix - Attempt to automatically fix lint violations - --show-source - Show violations with source code - --show-fixes - Show an enumeration of all autofixed lint violations - --diff - Avoid writing any fixed files back; instead, output a diff for each changed file to stdout - -w, --watch - Run in watch mode by re-running whenever files change - --fix-only - Fix any fixable lint violations, but don't report on leftover violations. Implies `--fix` - --format - Output serialization format for violations [env: RUFF_FORMAT=] [possible values: text, json, junit, grouped, github, gitlab, pylint] - --target-version - The minimum Python version that should be supported - --config - Path to the `pyproject.toml` or `ruff.toml` file to use for configuration - --statistics - Show counts for every rule with at least one violation - --add-noqa - Enable automatic additions of `noqa` directives to failing lines - --show-files - See the files Ruff will be run against with the current settings - --show-settings - See the settings Ruff will use to lint a given Python file - -h, --help - Print help - -Rule selection: - --select - Comma-separated list of rule codes to enable (or ALL, to enable all rules) - --ignore - Comma-separated list of rule codes to disable - --extend-select - Like --select, but adds additional rule codes on top of the selected ones - --per-file-ignores - List of mappings from file pattern to code to exclude - --fixable - List of rule codes to treat as eligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`) - --unfixable - List of rule codes to treat as ineligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`) - -File selection: - --exclude List of paths, used to omit files and/or directories from analysis - --extend-exclude Like --exclude, but adds additional files and directories on top of those already excluded - --respect-gitignore Respect file exclusions via `.gitignore` and other standard ignore files - --force-exclude Enforce exclusions, even for paths passed to Ruff directly on the command-line - -Miscellaneous: - -n, --no-cache - Disable cache reads - --isolated - Ignore all configuration files - --cache-dir - Path to the cache directory [env: RUFF_CACHE_DIR=] - --stdin-filename - The name of the file when passing it through stdin - -e, --exit-zero - Exit with status code "0", even upon detecting lint violations - --exit-non-zero-on-fix - Exit with a non-zero status code if any files were modified via autofix, even if no lint violations remain - -Log levels: - -v, --verbose Enable verbose logging - -q, --quiet Print lint violations, but nothing else - -s, --silent Disable all logging (but still exit with status code "1" upon detecting lint violations) -``` - - - -### `pyproject.toml` discovery - -Similar to [ESLint](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#cascading-and-hierarchy), -Ruff supports hierarchical configuration, such that the "closest" `pyproject.toml` file in the -directory hierarchy is used for every individual file, with all paths in the `pyproject.toml` file -(e.g., `exclude` globs, `src` paths) being resolved relative to the directory containing the -`pyproject.toml` file. - -There are a few exceptions to these rules: - -1. In locating the "closest" `pyproject.toml` file for a given path, Ruff ignores any - `pyproject.toml` files that lack a `[tool.ruff]` section. -2. If a configuration file is passed directly via `--config`, those settings are used for across - files. Any relative paths in that configuration file (like `exclude` globs or `src` paths) are - resolved relative to the _current working directory_. -3. If no `pyproject.toml` file is found in the filesystem hierarchy, Ruff will fall back to using - a default configuration. If a user-specific configuration file exists - at `${config_dir}/ruff/pyproject.toml`, that file will be used instead of the default - configuration, with `${config_dir}` being determined via the [`dirs`](https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html) - crate, and all relative paths being again resolved relative to the _current working directory_. -4. Any `pyproject.toml`-supported settings that are provided on the command-line (e.g., via - `--select`) will override the settings in _every_ resolved configuration file. - -Unlike [ESLint](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#cascading-and-hierarchy), -Ruff does not merge settings across configuration files; instead, the "closest" configuration file -is used, and any parent configuration files are ignored. In lieu of this implicit cascade, Ruff -supports an [`extend`](https://beta.ruff.rs/docs/settings#extend) field, which allows you to inherit the settings from another -`pyproject.toml` file, like so: - -```toml -# Extend the `pyproject.toml` file in the parent directory. -extend = "../pyproject.toml" -# But use a different line length. -line-length = 100 -``` - -All of the above rules apply equivalently to `ruff.toml` files. If Ruff detects both a `ruff.toml` -and `pyproject.toml` file, it will defer to the `ruff.toml`. - -### Python file discovery - -When passed a path on the command-line, Ruff will automatically discover all Python files in that -path, taking into account the [`exclude`](https://beta.ruff.rs/docs/settings#exclude) and -[`extend-exclude`](https://beta.ruff.rs/docs/settings#extend-exclude) settings in each directory's -`pyproject.toml` file. - -By default, Ruff will also skip any files that are omitted via `.ignore`, `.gitignore`, -`.git/info/exclude`, and global `gitignore` files (see: [`respect-gitignore`](https://beta.ruff.rs/docs/settings#respect-gitignore)). - -Files that are passed to `ruff` directly are always linted, regardless of the above criteria. -For example, `ruff check /path/to/excluded/file.py` will always lint `file.py`. - -### Rule resolution - -The set of enabled rules is controlled via the [`select`](https://beta.ruff.rs/docs/settings#select) -and [`ignore`](https://beta.ruff.rs/docs/settings#ignore) settings, along with the -[`extend-select`](https://beta.ruff.rs/docs/settings#extend-select) and -[`extend-ignore`](https://beta.ruff.rs/docs/settings#extend-ignore) modifiers. - -To resolve the enabled rule set, Ruff may need to reconcile `select` and `ignore` from a variety -of sources, including the current `pyproject.toml`, any inherited `pyproject.toml` files, and the -CLI (e.g., `--select`). - -In those scenarios, Ruff uses the "highest-priority" `select` as the basis for the rule set, and -then applies any `extend-select`, `ignore`, and `extend-ignore` adjustments. CLI options are given -higher priority than `pyproject.toml` options, and the current `pyproject.toml` file is given higher -priority than any inherited `pyproject.toml` files. - -For example, given the following `pyproject.toml` file: - -```toml -[tool.ruff] -select = ["E", "F"] -ignore = ["F401"] -``` - -Running `ruff check --select F401` would result in Ruff enforcing `F401`, and no other rules. - -Running `ruff check --extend-select B` would result in Ruff enforcing the `E`, `F`, and `B` rules, with -the exception of `F401`. - -### Suppressing errors - -To omit a lint rule entirely, add it to the "ignore" list via [`ignore`](https://beta.ruff.rs/docs/settings#ignore) -or [`extend-ignore`](https://beta.ruff.rs/docs/settings#extend-ignore), either on the command-line -or in your `pyproject.toml` file. - -To ignore a violation inline, Ruff uses a `noqa` system similar to [Flake8](https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html). -To ignore an individual violation, add `# noqa: {code}` to the end of the line, like so: - -```python -# Ignore F841. -x = 1 # noqa: F841 - -# Ignore E741 and F841. -i = 1 # noqa: E741, F841 - -# Ignore _all_ violations. -x = 1 # noqa -``` - -Note that, for multi-line strings, the `noqa` directive should come at the end of the string, and -will apply to the entire string, like so: - -```python -"""Lorem ipsum dolor sit amet. - -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor. -""" # noqa: E501 -``` - -To ignore all violations across an entire file, add `# ruff: noqa` to any line in the file, like so: - -```python -# ruff: noqa -``` - -To ignore a specific rule across an entire file, add `# ruff: noqa: {code}` to any line in the file, -like so: - -```python -# ruff: noqa: F841 -``` - -Or see the [`per-file-ignores`](https://beta.ruff.rs/docs/settings#per-file-ignores) configuration -setting, which enables the same functionality via a `pyproject.toml` file. - -Note that Ruff will also respect Flake8's `# flake8: noqa` directive, and will treat it as -equivalent to `# ruff: noqa`. - -#### Automatic error suppression - -Ruff supports several workflows to aid in `noqa` management. - -First, Ruff provides a special rule code, `RUF100`, to enforce that your `noqa` directives are -"valid", in that the violations they _say_ they ignore are actually being triggered on that line (and -thus suppressed). You can run `ruff check /path/to/file.py --extend-select RUF100` to flag unused `noqa` -directives. - -Second, Ruff can _automatically remove_ unused `noqa` directives via its autofix functionality. -You can run `ruff check /path/to/file.py --extend-select RUF100 --fix` to automatically remove unused -`noqa` directives. - -Third, Ruff can _automatically add_ `noqa` directives to all failing lines. This is useful when -migrating a new codebase to Ruff. You can run `ruff check /path/to/file.py --add-noqa` to automatically -add `noqa` directives to all failing lines, with the appropriate rule codes. - -#### Action comments - -Ruff respects `isort`'s [action comments](https://pycqa.github.io/isort/docs/configuration/action_comments.html) -(`# isort: skip_file`, `# isort: on`, `# isort: off`, `# isort: skip`, and `# isort: split`), which -enable selectively enabling and disabling import sorting for blocks of code and other inline -configuration. - -See the [`isort` documentation](https://pycqa.github.io/isort/docs/configuration/action_comments.html) -for more. - -### Exit codes - -By default, Ruff exits with the following status codes: - -* `0` if no violations were found, or if all present violations were fixed automatically. -* `1` if violations were found. -* `2` if Ruff terminates abnormally due to invalid configuration, invalid CLI options, or an internal error. - -This convention mirrors that of tools like ESLint, Prettier, and RuboCop. - -Ruff supports two command-line flags that alter its exit code behavior: - -* `--exit-zero` will cause Ruff to exit with a status code of `0` even if violations were found. - Note that Ruff will still exit with a status code of `2` if it terminates abnormally. -* `--exit-non-zero-on-fix` will cause Ruff to exit with a status code of `1` if violations were - found, _even if_ all such violations were fixed automatically. Note that the use of - `--exit-non-zero-on-fix` can result in a non-zero exit code even if no violations remain after - autofixing. - -### Autocompletion - -Ruff supports autocompletion for most shells. A shell-specific completion script can be generated -by `ruff generate-shell-completion `, where `` is one of `bash`, `elvish`, `fig`, `fish`, -`powershell`, or `zsh`. - -The exact steps required to enable autocompletion will vary by shell. For example instructions, -see the [Poetry](https://python-poetry.org/docs/#enable-tab-completion-for-bash-fish-or-zsh) or -[ripgrep](https://github.com/BurntSushi/ripgrep/blob/master/FAQ.md#complete) documentation. - -As an example: to enable autocompletion for Zsh, run -`ruff generate-shell-completion zsh > ~/.zfunc/_ruff`. Then add the following line to your -`~/.zshrc` file, if they're not already present: - -```zsh -fpath+=~/.zfunc -autoload -Uz compinit && compinit -``` - - - -## Supported Rules +## Rules -Ruff supports over 400 lint rules, many of which are inspired by popular tools like Flake8, isort, -pyupgrade, and others. Regardless of the rule's origin, Ruff re-implements every rule in +**Ruff supports over 400 lint rules**, many of which are inspired by popular tools like Flake8, +isort, pyupgrade, and others. Regardless of the rule's origin, Ruff re-implements every rule in Rust as a first-party feature. By default, Ruff enables Flake8's `E` and `F` rules. Ruff supports all rules from the `F` category, and a [subset](https://beta.ruff.rs/docs/rules/#error-e) of the `E` category, omitting those -stylistic rules made obsolete by the use of an autoformatter, like [Black](https://github.com/psf/black). +stylistic rules made obsolete by the use of an autoformatter, like +[Black](https://github.com/psf/black). -For a complete enumeration, see the [list of rules](https://beta.ruff.rs/docs/rules/) in the -Ruff documentation. +For a complete enumeration of the supported rules, see [_Rules_](https://beta.ruff.rs/docs/rules/). ## Contributing Contributions are welcome and highly appreciated. To get started, check out the -[**contributing guidelines**](https://github.com/charliermarsh/ruff/blob/main/CONTRIBUTING.md). You -can also join us on [**Discord**](https://discord.gg/c9MhzV8aU5). +[**contributing guidelines**](https://beta.ruff.rs/docs/contributing/). + +You can also join us on [**Discord**](https://discord.gg/c9MhzV8aU5). ## Support @@ -680,8 +243,6 @@ or feel free to [**open a new one**](https://github.com/charliermarsh/ruff/issue You can also ask for help on [**Discord**](https://discord.gg/c9MhzV8aU5). - - ## Acknowledgements Ruff's linter draws on both the APIs and implementation details of many other @@ -704,8 +265,6 @@ Ruff is the beneficiary of a large number of [contributors](https://github.com/c Ruff is released under the MIT license. - - ## Who's Using Ruff? Ruff is used in a number of major open-source projects, including: diff --git a/crates/ruff_dev/src/generate_cli_help.rs b/crates/ruff_dev/src/generate_cli_help.rs index f529a0729c..a285b10b45 100644 --- a/crates/ruff_dev/src/generate_cli_help.rs +++ b/crates/ruff_dev/src/generate_cli_help.rs @@ -1,11 +1,14 @@ //! Generate CLI help. #![allow(clippy::print_stdout, clippy::print_stderr)] -use std::str; +use std::fs::OpenOptions; +use std::io::Write; +use std::path::PathBuf; +use std::{fs, str}; use anyhow::Result; -use crate::utils::replace_readme_section; +use crate::ROOT_DIR; const COMMAND_HELP_BEGIN_PRAGMA: &str = "\n"; const COMMAND_HELP_END_PRAGMA: &str = ""; @@ -15,7 +18,7 @@ const SUBCOMMAND_HELP_END_PRAGMA: &str = " + +```text +Ruff: An extremely fast Python linter. + +Usage: ruff [OPTIONS] + +Commands: + check Run Ruff on the given files or directories (default) + rule Explain a rule + config List or describe the available configuration options + linter List all supported upstream linters + clean Clear any caches in the current directory and any subdirectories + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help + -V, --version Print version + +Log levels: + -v, --verbose Enable verbose logging + -q, --quiet Print lint violations, but nothing else + -s, --silent Disable all logging (but still exit with status code "1" upon detecting lint violations) + +For help with a specific command, see: `ruff help `. +``` + + + +Or `ruff help check` for more on the linting command: + + + +```text +Run Ruff on the given files or directories (default) + +Usage: ruff check [OPTIONS] [FILES]... + +Arguments: + [FILES]... List of files or directories to check + +Options: + --fix + Attempt to automatically fix lint violations + --show-source + Show violations with source code + --show-fixes + Show an enumeration of all autofixed lint violations + --diff + Avoid writing any fixed files back; instead, output a diff for each changed file to stdout + -w, --watch + Run in watch mode by re-running whenever files change + --fix-only + Fix any fixable lint violations, but don't report on leftover violations. Implies `--fix` + --format + Output serialization format for violations [env: RUFF_FORMAT=] [possible values: text, json, junit, grouped, github, gitlab, pylint] + --target-version + The minimum Python version that should be supported + --config + Path to the `pyproject.toml` or `ruff.toml` file to use for configuration + --statistics + Show counts for every rule with at least one violation + --add-noqa + Enable automatic additions of `noqa` directives to failing lines + --show-files + See the files Ruff will be run against with the current settings + --show-settings + See the settings Ruff will use to lint a given Python file + -h, --help + Print help + +Rule selection: + --select + Comma-separated list of rule codes to enable (or ALL, to enable all rules) + --ignore + Comma-separated list of rule codes to disable + --extend-select + Like --select, but adds additional rule codes on top of the selected ones + --per-file-ignores + List of mappings from file pattern to code to exclude + --fixable + List of rule codes to treat as eligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`) + --unfixable + List of rule codes to treat as ineligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`) + +File selection: + --exclude List of paths, used to omit files and/or directories from analysis + --extend-exclude Like --exclude, but adds additional files and directories on top of those already excluded + --respect-gitignore Respect file exclusions via `.gitignore` and other standard ignore files + --force-exclude Enforce exclusions, even for paths passed to Ruff directly on the command-line + +Miscellaneous: + -n, --no-cache + Disable cache reads + --isolated + Ignore all configuration files + --cache-dir + Path to the cache directory [env: RUFF_CACHE_DIR=] + --stdin-filename + The name of the file when passing it through stdin + -e, --exit-zero + Exit with status code "0", even upon detecting lint violations + --exit-non-zero-on-fix + Exit with a non-zero status code if any files were modified via autofix, even if no lint violations remain + +Log levels: + -v, --verbose Enable verbose logging + -q, --quiet Print lint violations, but nothing else + -s, --silent Disable all logging (but still exit with status code "1" upon detecting lint violations) +``` + + + +### `pyproject.toml` discovery + +Similar to [ESLint](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#cascading-and-hierarchy), +Ruff supports hierarchical configuration, such that the "closest" `pyproject.toml` file in the +directory hierarchy is used for every individual file, with all paths in the `pyproject.toml` file +(e.g., `exclude` globs, `src` paths) being resolved relative to the directory containing the +`pyproject.toml` file. + +There are a few exceptions to these rules: + +1. In locating the "closest" `pyproject.toml` file for a given path, Ruff ignores any + `pyproject.toml` files that lack a `[tool.ruff]` section. +2. If a configuration file is passed directly via `--config`, those settings are used for across + files. Any relative paths in that configuration file (like `exclude` globs or `src` paths) are + resolved relative to the _current working directory_. +3. If no `pyproject.toml` file is found in the filesystem hierarchy, Ruff will fall back to using + a default configuration. If a user-specific configuration file exists + at `${config_dir}/ruff/pyproject.toml`, that file will be used instead of the default + configuration, with `${config_dir}` being determined via the [`dirs`](https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html) + crate, and all relative paths being again resolved relative to the _current working directory_. +4. Any `pyproject.toml`-supported settings that are provided on the command-line (e.g., via + `--select`) will override the settings in _every_ resolved configuration file. + +Unlike [ESLint](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#cascading-and-hierarchy), +Ruff does not merge settings across configuration files; instead, the "closest" configuration file +is used, and any parent configuration files are ignored. In lieu of this implicit cascade, Ruff +supports an [`extend`](/docs/settings#extend) field, which allows you to inherit the settings from another +`pyproject.toml` file, like so: + +```toml +# Extend the `pyproject.toml` file in the parent directory. +extend = "../pyproject.toml" +# But use a different line length. +line-length = 100 +``` + +All of the above rules apply equivalently to `ruff.toml` files. If Ruff detects both a `ruff.toml` +and `pyproject.toml` file, it will defer to the `ruff.toml`. + +### Python file discovery + +When passed a path on the command-line, Ruff will automatically discover all Python files in that +path, taking into account the [`exclude`](/docs/settings#exclude) and +[`extend-exclude`](/docs/settings#extend-exclude) settings in each directory's +`pyproject.toml` file. + +By default, Ruff will also skip any files that are omitted via `.ignore`, `.gitignore`, +`.git/info/exclude`, and global `gitignore` files (see: [`respect-gitignore`](/docs/settings#respect-gitignore)). + +Files that are passed to `ruff` directly are always linted, regardless of the above criteria. +For example, `ruff check /path/to/excluded/file.py` will always lint `file.py`. + +### Rule resolution + +The set of enabled rules is controlled via the [`select`](/docs/settings#select) +and [`ignore`](/docs/settings#ignore) settings, along with the +[`extend-select`](/docs/settings#extend-select) and +[`extend-ignore`](/docs/settings#extend-ignore) modifiers. + +To resolve the enabled rule set, Ruff may need to reconcile `select` and `ignore` from a variety +of sources, including the current `pyproject.toml`, any inherited `pyproject.toml` files, and the +CLI (e.g., `--select`). + +In those scenarios, Ruff uses the "highest-priority" `select` as the basis for the rule set, and +then applies any `extend-select`, `ignore`, and `extend-ignore` adjustments. CLI options are given +higher priority than `pyproject.toml` options, and the current `pyproject.toml` file is given higher +priority than any inherited `pyproject.toml` files. + +For example, given the following `pyproject.toml` file: + +```toml +[tool.ruff] +select = ["E", "F"] +ignore = ["F401"] +``` + +Running `ruff check --select F401` would result in Ruff enforcing `F401`, and no other rules. + +Running `ruff check --extend-select B` would result in Ruff enforcing the `E`, `F`, and `B` rules, with +the exception of `F401`. + +### Suppressing errors + +To omit a lint rule entirely, add it to the "ignore" list via [`ignore`](/docs/settings#ignore) +or [`extend-ignore`](/docs/settings#extend-ignore), either on the command-line +or in your `pyproject.toml` file. + +To ignore a violation inline, Ruff uses a `noqa` system similar to [Flake8](https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html). +To ignore an individual violation, add `# noqa: {code}` to the end of the line, like so: + +```python +# Ignore F841. +x = 1 # noqa: F841 + +# Ignore E741 and F841. +i = 1 # noqa: E741, F841 + +# Ignore _all_ violations. +x = 1 # noqa +``` + +Note that, for multi-line strings, the `noqa` directive should come at the end of the string, and +will apply to the entire string, like so: + +```python +"""Lorem ipsum dolor sit amet. + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor. +""" # noqa: E501 +``` + +To ignore all violations across an entire file, add `# ruff: noqa` to any line in the file, like so: + +```python +# ruff: noqa +``` + +To ignore a specific rule across an entire file, add `# ruff: noqa: {code}` to any line in the file, +like so: + +```python +# ruff: noqa: F841 +``` + +Or see the [`per-file-ignores`](/docs/settings#per-file-ignores) configuration +setting, which enables the same functionality via a `pyproject.toml` file. + +Note that Ruff will also respect Flake8's `# flake8: noqa` directive, and will treat it as +equivalent to `# ruff: noqa`. + +#### Automatic error suppression + +Ruff supports several workflows to aid in `noqa` management. + +First, Ruff provides a special rule code, `RUF100`, to enforce that your `noqa` directives are +"valid", in that the violations they _say_ they ignore are actually being triggered on that line (and +thus suppressed). You can run `ruff check /path/to/file.py --extend-select RUF100` to flag unused `noqa` +directives. + +Second, Ruff can _automatically remove_ unused `noqa` directives via its autofix functionality. +You can run `ruff check /path/to/file.py --extend-select RUF100 --fix` to automatically remove unused +`noqa` directives. + +Third, Ruff can _automatically add_ `noqa` directives to all failing lines. This is useful when +migrating a new codebase to Ruff. You can run `ruff check /path/to/file.py --add-noqa` to automatically +add `noqa` directives to all failing lines, with the appropriate rule codes. + +#### Action comments + +Ruff respects `isort`'s [action comments](https://pycqa.github.io/isort/docs/configuration/action_comments.html) +(`# isort: skip_file`, `# isort: on`, `# isort: off`, `# isort: skip`, and `# isort: split`), which +enable selectively enabling and disabling import sorting for blocks of code and other inline +configuration. + +See the [`isort` documentation](https://pycqa.github.io/isort/docs/configuration/action_comments.html) +for more. + +### Exit codes + +By default, Ruff exits with the following status codes: + +* `0` if no violations were found, or if all present violations were fixed automatically. +* `1` if violations were found. +* `2` if Ruff terminates abnormally due to invalid configuration, invalid CLI options, or an internal error. + +This convention mirrors that of tools like ESLint, Prettier, and RuboCop. + +Ruff supports two command-line flags that alter its exit code behavior: + +* `--exit-zero` will cause Ruff to exit with a status code of `0` even if violations were found. + Note that Ruff will still exit with a status code of `2` if it terminates abnormally. +* `--exit-non-zero-on-fix` will cause Ruff to exit with a status code of `1` if violations were + found, _even if_ all such violations were fixed automatically. Note that the use of + `--exit-non-zero-on-fix` can result in a non-zero exit code even if no violations remain after + autofixing. + +### Autocompletion + +Ruff supports autocompletion for most shells. A shell-specific completion script can be generated +by `ruff generate-shell-completion `, where `` is one of `bash`, `elvish`, `fig`, `fish`, +`powershell`, or `zsh`. + +The exact steps required to enable autocompletion will vary by shell. For example instructions, +see the [Poetry](https://python-poetry.org/docs/#enable-tab-completion-for-bash-fish-or-zsh) or +[ripgrep](https://github.com/BurntSushi/ripgrep/blob/master/FAQ.md#complete) documentation. + +As an example: to enable autocompletion for Zsh, run +`ruff generate-shell-completion zsh > ~/.zfunc/_ruff`. Then add the following line to your +`~/.zshrc` file, if they're not already present: + +```zsh +fpath+=~/.zfunc +autoload -Uz compinit && compinit +``` + diff --git a/docs/faq.md b/docs/faq.md index f49b380814..6f7cd08f8c 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -21,7 +21,7 @@ implements all of the `F` rules (which originate from Pyflakes), along with a su Ruff also re-implements some of the most popular Flake8 plugins and related code quality tools natively, including: -* [autoflake](https://pypi.org/project/autoflake/) ([#1647](https://github.com/charliermarsh/ruff/issues/1647)) +* [autoflake](https://pypi.org/project/autoflake/) * [eradicate](https://pypi.org/project/eradicate/) * [flake8-2020](https://pypi.org/project/flake8-2020/) * [flake8-annotations](https://pypi.org/project/flake8-annotations/) @@ -51,7 +51,7 @@ natively, including: * [flake8-raise](https://pypi.org/project/flake8-raise/) * [flake8-return](https://pypi.org/project/flake8-return/) * [flake8-self](https://pypi.org/project/flake8-self/) -* [flake8-simplify](https://pypi.org/project/flake8-simplify/) ([#998](https://github.com/charliermarsh/ruff/issues/998)) +* [flake8-simplify](https://pypi.org/project/flake8-simplify/) * [flake8-super](https://pypi.org/project/flake8-super/) * [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/) * [flake8-type-checking](https://pypi.org/project/flake8-type-checking/) @@ -62,7 +62,7 @@ natively, including: * [pep8-naming](https://pypi.org/project/pep8-naming/) * [pydocstyle](https://pypi.org/project/pydocstyle/) * [pygrep-hooks](https://github.com/pre-commit/pygrep-hooks) ([#980](https://github.com/charliermarsh/ruff/issues/980)) -* [pyupgrade](https://pypi.org/project/pyupgrade/) ([#827](https://github.com/charliermarsh/ruff/issues/827)) +* [pyupgrade](https://pypi.org/project/pyupgrade/) * [yesqa](https://github.com/asottile/yesqa) Note that, in some cases, Ruff uses different rule codes and prefixes than would be found in the @@ -149,7 +149,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl * [flake8-raise](https://pypi.org/project/flake8-raise/) * [flake8-return](https://pypi.org/project/flake8-return/) * [flake8-self](https://pypi.org/project/flake8-self/) -* [flake8-simplify](https://pypi.org/project/flake8-simplify/) ([#998](https://github.com/charliermarsh/ruff/issues/998)) +* [flake8-simplify](https://pypi.org/project/flake8-simplify/) * [flake8-super](https://pypi.org/project/flake8-super/) * [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/) * [flake8-type-checking](https://pypi.org/project/flake8-type-checking/) @@ -160,11 +160,11 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl * [pydocstyle](https://pypi.org/project/pydocstyle/) Ruff can also replace [isort](https://pypi.org/project/isort/), -[yesqa](https://github.com/asottile/yesqa), [eradicate](https://pypi.org/project/eradicate/), -[pygrep-hooks](https://github.com/pre-commit/pygrep-hooks) ([#980](https://github.com/charliermarsh/ruff/issues/980)), and a subset of the rules -implemented in [pyupgrade](https://pypi.org/project/pyupgrade/) ([#827](https://github.com/charliermarsh/ruff/issues/827)). +[yesqa](https://github.com/asottile/yesqa), [eradicate](https://pypi.org/project/eradicate/), and +most of the rules implemented in [pyupgrade](https://pypi.org/project/pyupgrade/). -If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, feel free to file an Issue. +If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, feel free to file an +[issue](https://github.com/charliermarsh/ruff/issues/new). ## What versions of Python does Ruff support? @@ -309,7 +309,7 @@ On Windows, Ruff expects that file to be located at `C:\Users\Alice\AppData\Roam For more, see the [`dirs`](https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html) crate. -## Ruff tried to fix something, but it broke my code. What should I do? +## Ruff tried to fix something — but it broke my code? Ruff's autofix is a best-effort mechanism. Given the dynamic nature of Python, it's difficult to have _complete_ certainty when making changes to code, even for the seemingly trivial fixes. diff --git a/docs/installation-and-usage.md b/docs/installation-and-usage.md new file mode 100644 index 0000000000..e88ab6c9ed --- /dev/null +++ b/docs/installation-and-usage.md @@ -0,0 +1,73 @@ +### Installation + +Ruff is available as [`ruff`](https://pypi.org/project/ruff/) on PyPI: + +```shell +pip install ruff +``` + +For **macOS Homebrew** and **Linuxbrew** users, Ruff is also available as [`ruff`](https://formulae.brew.sh/formula/ruff) on Homebrew: + +```shell +brew install ruff +``` + +For **Conda** users, Ruff is also available as [`ruff`](https://anaconda.org/conda-forge/ruff) on `conda-forge`: + +```shell +conda install -c conda-forge ruff +``` + +For **Arch Linux** users, Ruff is also available as [`ruff`](https://archlinux.org/packages/community/x86_64/ruff/) on the official repositories: + +```shell +pacman -S ruff +``` + +For **Alpine** users, Ruff is also available as [`ruff`](https://pkgs.alpinelinux.org/package/edge/testing/x86_64/ruff) on the testing repositories: + +```shell +apk add ruff +``` + +[![Packaging status](https://repology.org/badge/vertical-allrepos/ruff-python-linter.svg?exclude_unsupported=1)](https://repology.org/project/ruff-python-linter/versions) + +### Usage + +To run Ruff, try any of the following: + +```shell +ruff check . # Lint all files in the current directory (and any subdirectories) +ruff check path/to/code/ # Lint all files in `/path/to/code` (and any subdirectories) +ruff check path/to/code/*.py # Lint all `.py` files in `/path/to/code` +ruff check path/to/code/to/file.py # Lint `file.py` +``` + +You can run Ruff in `--watch` mode to automatically re-run on-change: + +```shell +ruff check path/to/code/ --watch +``` + +Ruff also works with [pre-commit](https://pre-commit.com): + +```yaml +- repo: https://github.com/charliermarsh/ruff-pre-commit + # Ruff version. + rev: 'v0.0.252' + hooks: + - id: ruff +``` + +Or, to enable autofix: + +```yaml +- repo: https://github.com/charliermarsh/ruff-pre-commit + # Ruff version. + rev: 'v0.0.252' + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix] +``` + +Note that Ruff's pre-commit hook should run before Black, isort, and other formatting tools. diff --git a/mkdocs.template.yml b/mkdocs.template.yml index 4ce6d30a39..b3fbb34e06 100644 --- a/mkdocs.template.yml +++ b/mkdocs.template.yml @@ -11,7 +11,6 @@ theme: - toc.follow - navigation.path - navigation.top - - navigation.footer - content.code.copy palette: - media: "(prefers-color-scheme: light)" diff --git a/scripts/generate_mkdocs.py b/scripts/generate_mkdocs.py index 664d41a93d..406f4ae991 100644 --- a/scripts/generate_mkdocs.py +++ b/scripts/generate_mkdocs.py @@ -3,16 +3,28 @@ import argparse import shutil import subprocess from pathlib import Path +from typing import NamedTuple import yaml -SECTIONS: list[tuple[str, str]] = [ - ("Overview", "index.md"), - ("Installation and Usage", "installation-and-usage.md"), - ("Configuration", "configuration.md"), - ("Rules", "rules.md"), - ("Settings", "settings.md"), - ("Acknowledgements", "acknowledgements.md"), + +class Section(NamedTuple): + """A section to include in the MkDocs documentation.""" + + title: str + filename: str + generated: bool + + +SECTIONS: list[Section] = [ + Section("Overview", "index.md", generated=True), + Section("Installation and Usage", "installation-and-usage.md", generated=False), + Section("Configuration", "configuration.md", generated=False), + Section("Rules", "rules.md", generated=True), + Section("Settings", "settings.md", generated=True), + Section("Editor Integrations", "editor-integrations.md", generated=False), + Section("FAQ", "faq.md", generated=False), + Section("Contributing", "contributing.md", generated=True), ] DOCUMENTATION_LINK: str = ( @@ -39,16 +51,29 @@ def main() -> None: raise ValueError(msg) content = content.replace(DOCUMENTATION_LINK, "") - # Make the documentation links in the README more relative. + # Convert any inter-documentation links to relative links. content = content.replace("https://beta.ruff.rs", "") Path("docs").mkdir(parents=True, exist_ok=True) # Split the README.md into sections. - for title, filename in SECTIONS: + for title, filename, generated in SECTIONS: + if not generated: + continue + with Path(f"docs/{filename}").open("w+") as f: + if filename == "contributing.md": + # Copy the CONTRIBUTING.md. + shutil.copy("CONTRIBUTING.md", "docs/contributing.md") + continue + if filename == "settings.md": - f.write(subprocess.check_output(["cargo", "dev", "generate-options"], encoding="utf-8")) + f.write( + subprocess.check_output( + ["cargo", "dev", "generate-options"], + encoding="utf-8", + ), + ) continue block = content.split(f"") @@ -64,24 +89,17 @@ def main() -> None: f.write(block[0]) if filename == "rules.md": - f.write(subprocess.check_output(["cargo", "dev", "generate-rules-table"], encoding="utf-8")) - - # Copy the CONTRIBUTING.md. - shutil.copy("CONTRIBUTING.md", "docs/contributing.md") + f.write( + subprocess.check_output( + ["cargo", "dev", "generate-rules-table"], + encoding="utf-8", + ), + ) # Add the nav section to mkdocs.yml. with Path("mkdocs.template.yml").open(encoding="utf8") as fp: config = yaml.safe_load(fp) - config["nav"] = [ - {"Overview": "index.md"}, - {"Installation and Usage": "installation-and-usage.md"}, - {"Configuration": "configuration.md"}, - {"Rules": "rules.md"}, - {"Settings": "settings.md"}, - {"Editor Integrations": "editor-integrations.md"}, - {"FAQ": "faq.md"}, - {"Contributing": "contributing.md"}, - ] + config["nav"] = [{section.title: section.filename} for section in SECTIONS] config["extra"] = {"analytics": {"provider": "fathom"}} Path(".overrides/partials/integrations/analytics").mkdir(