mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 13:51:16 +00:00
[ty] Include python
folder in environment.root
if it exists (#20263)
## Summary I felt it was safer to add the `python` folder *in addition* to a possibly-existing `src` folder, even though the `src` folder only contains Rust code for `maturin`-based projects. There might be non-maturin projects where a `python` folder exists for other reasons, next to a normal `src` layout. closes https://github.com/astral-sh/ty/issues/1120 ## Test Plan Tested locally on the egglog-python project.
This commit is contained in:
parent
8ade6c4eaf
commit
7ee863b6d7
4 changed files with 144 additions and 3 deletions
2
crates/ty/docs/configuration.md
generated
2
crates/ty/docs/configuration.md
generated
|
@ -144,7 +144,7 @@ If left unspecified, ty will try to detect common project layouts and initialize
|
|||
* if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path
|
||||
* otherwise, default to `.` (flat layout)
|
||||
|
||||
Besides, if a `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` file),
|
||||
Besides, if a `./python` or `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` or `__init__.pyi` file),
|
||||
it will also be included in the first party search path.
|
||||
|
||||
**Default value**: `null`
|
||||
|
|
|
@ -1752,3 +1752,126 @@ fn default_root_tests_package() -> anyhow::Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_root_python_folder() -> anyhow::Result<()> {
|
||||
let case = CliTest::with_files([
|
||||
("src/foo.py", "foo = 10"),
|
||||
("python/bar.py", "bar = 20"),
|
||||
(
|
||||
"python/test_bar.py",
|
||||
r#"
|
||||
from foo import foo
|
||||
from bar import bar
|
||||
|
||||
print(f"{foo} {bar}")
|
||||
"#,
|
||||
),
|
||||
])?;
|
||||
|
||||
assert_cmd_snapshot!(case.command(), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// If `python/__init__.py` is present, it is considered a package and `python` is not added to search paths.
|
||||
#[test]
|
||||
fn default_root_python_package() -> anyhow::Result<()> {
|
||||
let case = CliTest::with_files([
|
||||
("src/foo.py", "foo = 10"),
|
||||
("python/__init__.py", ""),
|
||||
("python/bar.py", "bar = 20"),
|
||||
(
|
||||
"python/test_bar.py",
|
||||
r#"
|
||||
from foo import foo
|
||||
from bar import bar # expected unresolved import
|
||||
|
||||
print(f"{foo} {bar}")
|
||||
"#,
|
||||
),
|
||||
])?;
|
||||
|
||||
assert_cmd_snapshot!(case.command(), @r#"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error[unresolved-import]: Cannot resolve imported module `bar`
|
||||
--> python/test_bar.py:3:6
|
||||
|
|
||||
2 | from foo import foo
|
||||
3 | from bar import bar # expected unresolved import
|
||||
| ^^^
|
||||
4 |
|
||||
5 | print(f"{foo} {bar}")
|
||||
|
|
||||
info: Searched in the following paths during module resolution:
|
||||
info: 1. <temp_dir>/ (first-party code)
|
||||
info: 2. <temp_dir>/src (first-party code)
|
||||
info: 3. vendored://stdlib (stdlib typeshed stubs vendored by ty)
|
||||
info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment
|
||||
info: rule `unresolved-import` is enabled by default
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
----- stderr -----
|
||||
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
|
||||
"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Similarly, if `python/__init__.pyi` is present, it is considered a package and `python` is not added to search paths.
|
||||
#[test]
|
||||
fn default_root_python_package_pyi() -> anyhow::Result<()> {
|
||||
let case = CliTest::with_files([
|
||||
("src/foo.py", "foo = 10"),
|
||||
("python/__init__.pyi", ""),
|
||||
("python/bar.py", "bar = 20"),
|
||||
(
|
||||
"python/test_bar.py",
|
||||
r#"
|
||||
from foo import foo
|
||||
from bar import bar # expected unresolved import
|
||||
|
||||
print(f"{foo} {bar}")
|
||||
"#,
|
||||
),
|
||||
])?;
|
||||
|
||||
assert_cmd_snapshot!(case.command(), @r#"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error[unresolved-import]: Cannot resolve imported module `bar`
|
||||
--> python/test_bar.py:3:6
|
||||
|
|
||||
2 | from foo import foo
|
||||
3 | from bar import bar # expected unresolved import
|
||||
| ^^^
|
||||
4 |
|
||||
5 | print(f"{foo} {bar}")
|
||||
|
|
||||
info: Searched in the following paths during module resolution:
|
||||
info: 1. <temp_dir>/ (first-party code)
|
||||
info: 2. <temp_dir>/src (first-party code)
|
||||
info: 3. vendored://stdlib (stdlib typeshed stubs vendored by ty)
|
||||
info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment
|
||||
info: rule `unresolved-import` is enabled by default
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
----- stderr -----
|
||||
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
|
||||
"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -259,11 +259,29 @@ impl Options {
|
|||
vec![project_root.to_path_buf()]
|
||||
};
|
||||
|
||||
let python = project_root.join("python");
|
||||
if system.is_directory(&python)
|
||||
&& !system.is_file(&python.join("__init__.py"))
|
||||
&& !system.is_file(&python.join("__init__.pyi"))
|
||||
&& !roots.contains(&python)
|
||||
{
|
||||
// If a `./python` directory exists, include it as a source root. This is the recommended layout
|
||||
// for maturin-based rust/python projects [1].
|
||||
//
|
||||
// https://github.com/PyO3/maturin/blob/979fe1db42bb9e58bc150fa6fc45360b377288bf/README.md?plain=1#L88-L99
|
||||
tracing::debug!(
|
||||
"Including `./python` in `environment.root` because a `./python` directory exists"
|
||||
);
|
||||
|
||||
roots.push(python);
|
||||
}
|
||||
|
||||
// Considering pytest test discovery conventions,
|
||||
// we also include the `tests` directory if it exists and is not a package.
|
||||
let tests_dir = project_root.join("tests");
|
||||
if system.is_directory(&tests_dir)
|
||||
&& !system.is_file(&tests_dir.join("__init__.py"))
|
||||
&& !system.is_file(&tests_dir.join("__init__.pyi"))
|
||||
&& !roots.contains(&tests_dir)
|
||||
{
|
||||
// If the `tests` directory exists and is not a package, include it as a source root.
|
||||
|
@ -428,7 +446,7 @@ pub struct EnvironmentOptions {
|
|||
/// * if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path
|
||||
/// * otherwise, default to `.` (flat layout)
|
||||
///
|
||||
/// Besides, if a `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` file),
|
||||
/// Besides, if a `./python` or `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` or `__init__.pyi` file),
|
||||
/// it will also be included in the first party search path.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option(
|
||||
|
|
2
ty.schema.json
generated
2
ty.schema.json
generated
|
@ -101,7 +101,7 @@
|
|||
]
|
||||
},
|
||||
"root": {
|
||||
"description": "The root paths of the project, used for finding first-party modules.\n\nAccepts a list of directory paths searched in priority order (first has highest priority).\n\nIf left unspecified, ty will try to detect common project layouts and initialize `root` accordingly:\n\n* if a `./src` directory exists, include `.` and `./src` in the first party search path (src layout or flat) * if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path * otherwise, default to `.` (flat layout)\n\nBesides, if a `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` file), it will also be included in the first party search path.",
|
||||
"description": "The root paths of the project, used for finding first-party modules.\n\nAccepts a list of directory paths searched in priority order (first has highest priority).\n\nIf left unspecified, ty will try to detect common project layouts and initialize `root` accordingly:\n\n* if a `./src` directory exists, include `.` and `./src` in the first party search path (src layout or flat) * if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path * otherwise, default to `.` (flat layout)\n\nBesides, if a `./python` or `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` or `__init__.pyi` file), it will also be included in the first party search path.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue