mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 06:42:02 +00:00
[ty] Add tests
to src.root
if it exists and is not a package (#18286)
This commit is contained in:
parent
1f7134f727
commit
97ff015c88
4 changed files with 71 additions and 2 deletions
3
crates/ty/docs/configuration.md
generated
3
crates/ty/docs/configuration.md
generated
|
@ -172,6 +172,9 @@ 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
|
* if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path
|
||||||
* otherwise, default to `.` (flat layout)
|
* 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),
|
||||||
|
it will also be included in the first party search path.
|
||||||
|
|
||||||
**Default value**: `null`
|
**Default value**: `null`
|
||||||
|
|
||||||
**Type**: `str`
|
**Type**: `str`
|
||||||
|
|
|
@ -1029,6 +1029,52 @@ expected `.`, `]`
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn src_root_with_tests() -> anyhow::Result<()> {
|
||||||
|
let system = TestSystem::default();
|
||||||
|
let root = SystemPathBuf::from("/app");
|
||||||
|
|
||||||
|
// pytest will find `tests/test_foo.py` and realize it is NOT part of a package
|
||||||
|
// given that there's no `__init__.py` file in the same folder.
|
||||||
|
// It will then add `tests` to `sys.path`
|
||||||
|
// in order to import `test_foo.py` as the module `test_foo`.
|
||||||
|
system
|
||||||
|
.memory_file_system()
|
||||||
|
.write_files_all([
|
||||||
|
(root.join("src/main.py"), ""),
|
||||||
|
(root.join("tests/conftest.py"), ""),
|
||||||
|
(root.join("tests/test_foo.py"), ""),
|
||||||
|
])
|
||||||
|
.context("Failed to write files")?;
|
||||||
|
|
||||||
|
let metadata = ProjectMetadata::discover(&root, &system)?;
|
||||||
|
let settings = metadata
|
||||||
|
.options
|
||||||
|
.to_program_settings(&root, "my_package", &system);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
settings.search_paths.src_roots,
|
||||||
|
vec![root.clone(), root.join("src"), root.join("tests")]
|
||||||
|
);
|
||||||
|
|
||||||
|
// If `tests/__init__.py` is present, it is considered a package and `tests` is not added to `sys.path`.
|
||||||
|
system
|
||||||
|
.memory_file_system()
|
||||||
|
.write_file(root.join("tests/__init__.py"), "")
|
||||||
|
.context("Failed to write tests/__init__.py")?;
|
||||||
|
let metadata = ProjectMetadata::discover(&root, &system)?;
|
||||||
|
let settings = metadata
|
||||||
|
.options
|
||||||
|
.to_program_settings(&root, "my_package", &system);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
settings.search_paths.src_roots,
|
||||||
|
vec![root.clone(), root.join("src")]
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn assert_error_eq(error: &ProjectDiscoveryError, message: &str) {
|
fn assert_error_eq(error: &ProjectDiscoveryError, message: &str) {
|
||||||
assert_eq!(error.to_string().replace('\\', "/"), message);
|
assert_eq!(error.to_string().replace('\\', "/"), message);
|
||||||
|
|
|
@ -135,7 +135,7 @@ impl Options {
|
||||||
} else {
|
} else {
|
||||||
let src = project_root.join("src");
|
let src = project_root.join("src");
|
||||||
|
|
||||||
if system.is_directory(&src) {
|
let mut roots = if system.is_directory(&src) {
|
||||||
// Default to `src` and the project root if `src` exists and the root hasn't been specified.
|
// Default to `src` and the project root if `src` exists and the root hasn't been specified.
|
||||||
// This corresponds to the `src-layout`
|
// This corresponds to the `src-layout`
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
|
@ -154,7 +154,24 @@ impl Options {
|
||||||
// Default to a [flat project structure](https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/).
|
// Default to a [flat project structure](https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/).
|
||||||
tracing::debug!("Defaulting `src.root` to `.`");
|
tracing::debug!("Defaulting `src.root` to `.`");
|
||||||
vec![project_root.to_path_buf()]
|
vec![project_root.to_path_buf()]
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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"))
|
||||||
|
&& !roots.contains(&tests_dir)
|
||||||
|
{
|
||||||
|
// If the `tests` directory exists and is not a package, include it as a source root.
|
||||||
|
tracing::debug!(
|
||||||
|
"Including `./tests` in `src.root` because a `./tests` directory exists"
|
||||||
|
);
|
||||||
|
|
||||||
|
roots.push(tests_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
roots
|
||||||
};
|
};
|
||||||
|
|
||||||
let (extra_paths, python, typeshed) = self
|
let (extra_paths, python, typeshed) = self
|
||||||
|
@ -392,6 +409,9 @@ pub struct SrcOptions {
|
||||||
/// * if a `./src` directory exists, include `.` and `./src` in the first party search path (src layout or flat)
|
/// * 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
|
/// * if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path
|
||||||
/// * otherwise, default to `.` (flat layout)
|
/// * 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),
|
||||||
|
/// it will also be included in the first party search path.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
#[option(
|
#[option(
|
||||||
default = r#"null"#,
|
default = r#"null"#,
|
||||||
|
|
2
ty.schema.json
generated
2
ty.schema.json
generated
|
@ -859,7 +859,7 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"root": {
|
"root": {
|
||||||
"description": "The root of the project, used for finding first-party modules.\n\nIf left unspecified, ty will try to detect common project layouts and initialize `src.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)",
|
"description": "The root of the project, used for finding first-party modules.\n\nIf left unspecified, ty will try to detect common project layouts and initialize `src.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.",
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
"null"
|
"null"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue