mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-31 07:37:38 +00:00
Detect imports in src
layouts by default (#12848)
## Summary Occasionally, we receive bug reports that imports in `src` directories aren't correctly detected. The root of the problem is that we default to `src = ["."]`, so users have to set `src = ["src"]` explicitly. This PR extends the default to cover _both_ of them: `src = [".", "src"]`. Closes https://github.com/astral-sh/ruff/issues/12454. ## Test Plan I replicated the structure described in https://github.com/astral-sh/ruff/issues/12453, and verified that the imports were considered sorted, but that adding `src = ["."]` showed an error.
This commit is contained in:
parent
33512a4249
commit
15aa5a6d57
8 changed files with 33 additions and 31 deletions
|
@ -911,9 +911,5 @@ There are three ways in which an import can be categorized as "first-party":
|
|||
the `src` setting and, for each directory, check for the existence of a subdirectory `foo` or a
|
||||
file `foo.py`.
|
||||
|
||||
By default, `src` is set to the project root. In the above example, we'd want to set
|
||||
`src = ["./src"]` to ensure that we locate `./my_project/src/foo` and thus categorize `import foo`
|
||||
as first-party in `baz.py`. In practice, for this limited example, setting `src = ["./src"]` is
|
||||
unnecessary, as all imports within `./my_project/src/foo` would be categorized as first-party via
|
||||
the same-package heuristic; but if your project contains multiple packages, you'll want to set `src`
|
||||
explicitly.
|
||||
By default, `src` is set to the project root, along with `"src"` subdirectory in the project root.
|
||||
This ensures that Ruff supports both flat and "src" layouts out of the box.
|
||||
|
|
|
@ -209,6 +209,7 @@ linter.logger_objects = []
|
|||
linter.namespace_packages = []
|
||||
linter.src = [
|
||||
"[BASEPATH]",
|
||||
"[BASEPATH]/src",
|
||||
]
|
||||
linter.tab_size = 4
|
||||
linter.line_length = 88
|
||||
|
|
|
@ -398,7 +398,7 @@ impl LinterSettings {
|
|||
per_file_ignores: CompiledPerFileIgnoreList::default(),
|
||||
fix_safety: FixSafetyTable::default(),
|
||||
|
||||
src: vec![path_dedot::CWD.clone()],
|
||||
src: vec![path_dedot::CWD.clone(), path_dedot::CWD.join("src")],
|
||||
// Needs duplicating
|
||||
tab_size: IndentWidth::default(),
|
||||
line_length: LineLength::default(),
|
||||
|
|
|
@ -271,7 +271,6 @@ impl Configuration {
|
|||
.chain(lint.extend_per_file_ignores)
|
||||
.collect(),
|
||||
)?,
|
||||
|
||||
fix_safety: FixSafetyTable::from_rule_selectors(
|
||||
&lint.extend_safe_fixes,
|
||||
&lint.extend_unsafe_fixes,
|
||||
|
@ -280,8 +279,9 @@ impl Configuration {
|
|||
require_explicit: false,
|
||||
},
|
||||
),
|
||||
|
||||
src: self.src.unwrap_or_else(|| vec![project_root.to_path_buf()]),
|
||||
src: self
|
||||
.src
|
||||
.unwrap_or_else(|| vec![project_root.to_path_buf(), project_root.join("src")]),
|
||||
explicit_preview_rules: lint.explicit_preview_rules.unwrap_or_default(),
|
||||
|
||||
task_tags: lint
|
||||
|
|
|
@ -323,33 +323,37 @@ pub struct Options {
|
|||
/// The directories to consider when resolving first- vs. third-party
|
||||
/// imports.
|
||||
///
|
||||
/// As an example: given a Python package structure like:
|
||||
/// When omitted, the `src` directory will typically default to including both:
|
||||
///
|
||||
/// 1. The directory containing the nearest `pyproject.toml`, `ruff.toml`, or `.ruff.toml` file (the "project root").
|
||||
/// 2. The `"src"` subdirectory of the project root.
|
||||
///
|
||||
/// These defaults ensure that uv supports both flat layouts and `src` layouts out-of-the-box.
|
||||
/// (If a configuration file is explicitly provided (e.g., via the `--config` command-line
|
||||
/// flag), the current working directory will be considered the project root.)
|
||||
///
|
||||
/// As an example, consider an alternative project structure, like:
|
||||
///
|
||||
/// ```text
|
||||
/// my_project
|
||||
/// ├── pyproject.toml
|
||||
/// └── src
|
||||
/// └── lib
|
||||
/// └── my_package
|
||||
/// ├── __init__.py
|
||||
/// ├── foo.py
|
||||
/// └── bar.py
|
||||
/// ```
|
||||
///
|
||||
/// The `./src` directory should be included in the `src` option
|
||||
/// (e.g., `src = ["src"]`), such that when resolving imports,
|
||||
/// `my_package.foo` is considered a first-party import.
|
||||
///
|
||||
/// When omitted, the `src` directory will typically default to the
|
||||
/// directory containing the nearest `pyproject.toml`, `ruff.toml`, or
|
||||
/// `.ruff.toml` file (the "project root"), unless a configuration file
|
||||
/// is explicitly provided (e.g., via the `--config` command-line flag).
|
||||
/// In this case, the `./lib` directory should be included in the `src` option
|
||||
/// (e.g., `src = ["lib"]`), such that when resolving imports, `my_package.foo`
|
||||
/// is considered first-party.
|
||||
///
|
||||
/// This field supports globs. For example, if you have a series of Python
|
||||
/// packages in a `python_modules` directory, `src = ["python_modules/*"]`
|
||||
/// would expand to incorporate all of the packages in that directory. User
|
||||
/// home directory and environment variables will also be expanded.
|
||||
/// would expand to incorporate all packages in that directory. User home
|
||||
/// directory and environment variables will also be expanded.
|
||||
#[option(
|
||||
default = r#"["."]"#,
|
||||
default = r#"[".", "src"]"#,
|
||||
value_type = "list[str]",
|
||||
example = r#"
|
||||
# Allow imports relative to the "src" and "test" directories.
|
||||
|
|
13
docs/faq.md
13
docs/faq.md
|
@ -292,13 +292,14 @@ When Ruff sees an import like `import foo`, it will then iterate over the `src`
|
|||
looking for a corresponding Python module (in reality, a directory named `foo` or a file named
|
||||
`foo.py`).
|
||||
|
||||
If the `src` field is omitted, Ruff will default to using the "project root" as the only
|
||||
first-party source. The "project root" is typically the directory containing your `pyproject.toml`,
|
||||
`ruff.toml`, or `.ruff.toml` file, unless a configuration file is provided on the command-line via
|
||||
the `--config` option, in which case, the current working directory is used as the project root.
|
||||
If the `src` field is omitted, Ruff will default to using the "project root", along with a `"src"`
|
||||
subdirectory, as the first-party sources, to support both flat and nested project layouts.
|
||||
The "project root" is typically the directory containing your `pyproject.toml`, `ruff.toml`, or
|
||||
`.ruff.toml` file, unless a configuration file is provided on the command-line via the `--config`
|
||||
option, in which case, the current working directory is used as the project root.
|
||||
|
||||
In this case, Ruff would only check the top-level directory. Instead, we can configure Ruff to
|
||||
consider `src` as a first-party source like so:
|
||||
In this case, Ruff would check the `"src"` directory by default, but we can configure it as an
|
||||
explicit, exclusive first-party source like so:
|
||||
|
||||
=== "pyproject.toml"
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ Alternatively, you can include `ruff-action` as a step in any other workflow fil
|
|||
|
||||
- `version`: The Ruff version to install (default: latest).
|
||||
- `args`: The command-line arguments to pass to Ruff (default: `"check"`).
|
||||
- `src`: The source paths to pass to Ruff (default: `"."`).
|
||||
- `src`: The source paths to pass to Ruff (default: `[".", "src"]`).
|
||||
|
||||
For example, to run `ruff check --select B ./src` using Ruff version `0.0.259`:
|
||||
|
||||
|
|
2
ruff.schema.json
generated
2
ruff.schema.json
generated
|
@ -671,7 +671,7 @@
|
|||
]
|
||||
},
|
||||
"src": {
|
||||
"description": "The directories to consider when resolving first- vs. third-party imports.\n\nAs an example: given a Python package structure like:\n\n```text my_project ├── pyproject.toml └── src └── my_package ├── __init__.py ├── foo.py └── bar.py ```\n\nThe `./src` directory should be included in the `src` option (e.g., `src = [\"src\"]`), such that when resolving imports, `my_package.foo` is considered a first-party import.\n\nWhen omitted, the `src` directory will typically default to the directory containing the nearest `pyproject.toml`, `ruff.toml`, or `.ruff.toml` file (the \"project root\"), unless a configuration file is explicitly provided (e.g., via the `--config` command-line flag).\n\nThis field supports globs. For example, if you have a series of Python packages in a `python_modules` directory, `src = [\"python_modules/*\"]` would expand to incorporate all of the packages in that directory. User home directory and environment variables will also be expanded.",
|
||||
"description": "The directories to consider when resolving first- vs. third-party imports.\n\nWhen omitted, the `src` directory will typically default to including both:\n\n1. The directory containing the nearest `pyproject.toml`, `ruff.toml`, or `.ruff.toml` file (the \"project root\"). 2. The `\"src\"` subdirectory of the project root.\n\nThese defaults ensure that uv supports both flat layouts and `src` layouts out-of-the-box. (If a configuration file is explicitly provided (e.g., via the `--config` command-line flag), the current working directory will be considered the project root.)\n\nAs an example, consider an alternative project structure, like:\n\n```text my_project ├── pyproject.toml └── lib └── my_package ├── __init__.py ├── foo.py └── bar.py ```\n\nIn this case, the `./lib` directory should be included in the `src` option (e.g., `src = [\"lib\"]`), such that when resolving imports, `my_package.foo` is considered first-party.\n\nThis field supports globs. For example, if you have a series of Python packages in a `python_modules` directory, `src = [\"python_modules/*\"]` would expand to incorporate all packages in that directory. User home directory and environment variables will also be expanded.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue