Skip to content

Commit 80f65b7

Browse files
committed
Merge branch 'main' into dcreager/module-resolution
* main: [red-knot] Display definition range in trace logs (#14955) [red-knot] Move the `ClassBase` enum to its own submodule (#14957) [red-knot] mdtest: python version requirements (#14954) [airflow]: Import modules that has been moved to airflow providers (AIR303) (#14764) [red-knot] Support `typing.TYPE_CHECKING` (#14952) Add tracing support to mdtest (#14935) Re-enable the fuzzer job on PRs (#14953) [red-knot] Improve `match` mdtests (#14951) Rename `custom-typeshed-dir`, `target-version` and `current-directory` CLI options (#14930) [red-knot] Add narrowing for 'while' loops (#14947) [`ruff`] Skip SQLModel base classes for `mutable-class-default` (`RUF012`) (#14949) [red-knot] Tests for 'while' loop boundness (#14944)
2 parents 9d4f731 + 3533d7f commit 80f65b7

File tree

62 files changed

+1111
-548
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1111
-548
lines changed

.github/workflows/ci.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ jobs:
312312
name: "cargo fuzz build"
313313
runs-on: ubuntu-latest
314314
needs: determine_changes
315-
if: ${{ github.ref == 'refs/heads/main' || needs.determine_changes.outputs.fuzz == 'true' }}
315+
if: ${{ github.ref == 'refs/heads/main' || needs.determine_changes.outputs.fuzz == 'true' || needs.determine_changes.outputs.code == 'true' }}
316316
timeout-minutes: 10
317317
steps:
318318
- uses: actions/checkout@v4

crates/red_knot/src/main.rs

+33-51
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use anyhow::{anyhow, Context};
55
use clap::Parser;
66
use colored::Colorize;
77
use crossbeam::channel as crossbeam_channel;
8+
use python_version::PythonVersion;
89
use red_knot_python_semantic::SitePackages;
910
use red_knot_server::run_server;
1011
use red_knot_workspace::db::RootDatabase;
@@ -15,12 +16,11 @@ use red_knot_workspace::workspace::WorkspaceMetadata;
1516
use ruff_db::diagnostic::Diagnostic;
1617
use ruff_db::system::{OsSystem, System, SystemPath, SystemPathBuf};
1718
use salsa::plumbing::ZalsaDatabase;
18-
use target_version::TargetVersion;
1919

2020
use crate::logging::{setup_tracing, Verbosity};
2121

2222
mod logging;
23-
mod target_version;
23+
mod python_version;
2424
mod verbosity;
2525

2626
#[derive(Debug, Parser)]
@@ -34,63 +34,48 @@ struct Args {
3434
#[command(subcommand)]
3535
pub(crate) command: Option<Command>,
3636

37-
#[arg(
38-
long,
39-
help = "Changes the current working directory.",
40-
long_help = "Changes the current working directory before any specified operations. This affects the workspace and configuration discovery.",
41-
value_name = "PATH"
42-
)]
43-
current_directory: Option<SystemPathBuf>,
44-
45-
#[arg(
46-
long,
47-
help = "Path to the virtual environment the project uses",
48-
long_help = "\
49-
Path to the virtual environment the project uses. \
50-
If provided, red-knot will use the `site-packages` directory of this virtual environment \
51-
to resolve type information for the project's third-party dependencies.",
52-
value_name = "PATH"
53-
)]
37+
/// Run the command within the given project directory.
38+
///
39+
/// All `pyproject.toml` files will be discovered by walking up the directory tree from the given project directory,
40+
/// as will the project's virtual environment (`.venv`) unless the `venv-path` option is set.
41+
///
42+
/// Other command-line arguments (such as relative paths) will be resolved relative to the current working directory.
43+
#[arg(long, value_name = "PROJECT")]
44+
project: Option<SystemPathBuf>,
45+
46+
/// Path to the virtual environment the project uses.
47+
///
48+
/// If provided, red-knot will use the `site-packages` directory of this virtual environment
49+
/// to resolve type information for the project's third-party dependencies.
50+
#[arg(long, value_name = "PATH")]
5451
venv_path: Option<SystemPathBuf>,
5552

56-
#[arg(
57-
long,
58-
value_name = "DIRECTORY",
59-
help = "Custom directory to use for stdlib typeshed stubs"
60-
)]
61-
custom_typeshed_dir: Option<SystemPathBuf>,
62-
63-
#[arg(
64-
long,
65-
value_name = "PATH",
66-
help = "Additional path to use as a module-resolution source (can be passed multiple times)"
67-
)]
53+
/// Custom directory to use for stdlib typeshed stubs.
54+
#[arg(long, value_name = "PATH", alias = "custom-typeshed-dir")]
55+
typeshed: Option<SystemPathBuf>,
56+
57+
/// Additional path to use as a module-resolution source (can be passed multiple times).
58+
#[arg(long, value_name = "PATH")]
6859
extra_search_path: Option<Vec<SystemPathBuf>>,
6960

70-
#[arg(
71-
long,
72-
help = "Python version to assume when resolving types",
73-
value_name = "VERSION"
74-
)]
75-
target_version: Option<TargetVersion>,
61+
/// Python version to assume when resolving types.
62+
#[arg(long, value_name = "VERSION", alias = "target-version")]
63+
python_version: Option<PythonVersion>,
7664

7765
#[clap(flatten)]
7866
verbosity: Verbosity,
7967

80-
#[arg(
81-
long,
82-
help = "Run in watch mode by re-running whenever files change",
83-
short = 'W'
84-
)]
68+
/// Run in watch mode by re-running whenever files change.
69+
#[arg(long, short = 'W')]
8570
watch: bool,
8671
}
8772

8873
impl Args {
8974
fn to_configuration(&self, cli_cwd: &SystemPath) -> Configuration {
9075
let mut configuration = Configuration::default();
9176

92-
if let Some(target_version) = self.target_version {
93-
configuration.target_version = Some(target_version.into());
77+
if let Some(python_version) = self.python_version {
78+
configuration.python_version = Some(python_version.into());
9479
}
9580

9681
if let Some(venv_path) = &self.venv_path {
@@ -99,9 +84,8 @@ impl Args {
9984
});
10085
}
10186

102-
if let Some(custom_typeshed_dir) = &self.custom_typeshed_dir {
103-
configuration.search_paths.custom_typeshed =
104-
Some(SystemPath::absolute(custom_typeshed_dir, cli_cwd));
87+
if let Some(typeshed) = &self.typeshed {
88+
configuration.search_paths.typeshed = Some(SystemPath::absolute(typeshed, cli_cwd));
10589
}
10690

10791
if let Some(extra_search_paths) = &self.extra_search_path {
@@ -167,15 +151,13 @@ fn run() -> anyhow::Result<ExitStatus> {
167151
};
168152

169153
let cwd = args
170-
.current_directory
154+
.project
171155
.as_ref()
172156
.map(|cwd| {
173157
if cwd.as_std_path().is_dir() {
174158
Ok(SystemPath::absolute(cwd, &cli_base_path))
175159
} else {
176-
Err(anyhow!(
177-
"Provided current-directory path `{cwd}` is not a directory"
178-
))
160+
Err(anyhow!("Provided project path `{cwd}` is not a directory"))
179161
}
180162
})
181163
.transpose()?

crates/red_knot/src/python_version.rs

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/// Enumeration of all supported Python versions
2+
///
3+
/// TODO: unify with the `PythonVersion` enum in the linter/formatter crates?
4+
#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Default, clap::ValueEnum)]
5+
pub enum PythonVersion {
6+
#[value(name = "3.7")]
7+
Py37,
8+
#[value(name = "3.8")]
9+
Py38,
10+
#[default]
11+
#[value(name = "3.9")]
12+
Py39,
13+
#[value(name = "3.10")]
14+
Py310,
15+
#[value(name = "3.11")]
16+
Py311,
17+
#[value(name = "3.12")]
18+
Py312,
19+
#[value(name = "3.13")]
20+
Py313,
21+
}
22+
23+
impl PythonVersion {
24+
const fn as_str(self) -> &'static str {
25+
match self {
26+
Self::Py37 => "3.7",
27+
Self::Py38 => "3.8",
28+
Self::Py39 => "3.9",
29+
Self::Py310 => "3.10",
30+
Self::Py311 => "3.11",
31+
Self::Py312 => "3.12",
32+
Self::Py313 => "3.13",
33+
}
34+
}
35+
}
36+
37+
impl std::fmt::Display for PythonVersion {
38+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39+
f.write_str(self.as_str())
40+
}
41+
}
42+
43+
impl From<PythonVersion> for red_knot_python_semantic::PythonVersion {
44+
fn from(value: PythonVersion) -> Self {
45+
match value {
46+
PythonVersion::Py37 => Self::PY37,
47+
PythonVersion::Py38 => Self::PY38,
48+
PythonVersion::Py39 => Self::PY39,
49+
PythonVersion::Py310 => Self::PY310,
50+
PythonVersion::Py311 => Self::PY311,
51+
PythonVersion::Py312 => Self::PY312,
52+
PythonVersion::Py313 => Self::PY313,
53+
}
54+
}
55+
}
56+
57+
#[cfg(test)]
58+
mod tests {
59+
use crate::python_version::PythonVersion;
60+
61+
#[test]
62+
fn same_default_as_python_version() {
63+
assert_eq!(
64+
red_knot_python_semantic::PythonVersion::from(PythonVersion::default()),
65+
red_knot_python_semantic::PythonVersion::default()
66+
);
67+
}
68+
}

crates/red_knot/src/target_version.rs

-62
This file was deleted.

crates/red_knot/tests/file_watching.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ where
282282
.extra_paths
283283
.iter()
284284
.flatten()
285-
.chain(search_paths.custom_typeshed.iter())
285+
.chain(search_paths.typeshed.iter())
286286
.chain(search_paths.site_packages.iter().flat_map(|site_packages| {
287287
if let SitePackages::Known(path) = site_packages {
288288
path.as_slice()
@@ -296,7 +296,7 @@ where
296296
}
297297

298298
let configuration = Configuration {
299-
target_version: Some(PythonVersion::PY312),
299+
python_version: Some(PythonVersion::PY312),
300300
search_paths,
301301
};
302302

@@ -888,7 +888,7 @@ fn changed_versions_file() -> anyhow::Result<()> {
888888
Ok(())
889889
},
890890
|root_path, _workspace_path| SearchPathConfiguration {
891-
custom_typeshed: Some(root_path.join("typeshed")),
891+
typeshed: Some(root_path.join("typeshed")),
892892
..SearchPathConfiguration::default()
893893
},
894894
)?;

crates/red_knot_python_semantic/Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,9 @@ tempfile = { workspace = true }
5353
quickcheck = { version = "1.0.3", default-features = false }
5454
quickcheck_macros = { version = "1.0.0" }
5555

56+
[features]
57+
serde = ["ruff_db/serde", "dep:serde"]
58+
5659
[lints]
5760
workspace = true
61+

crates/red_knot_python_semantic/resources/mdtest/annotations/literal_string.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ if "" < lorem == "ipsum":
135135

136136
```toml
137137
[environment]
138-
target-version = "3.11"
138+
python-version = "3.11"
139139
```
140140

141141
```py

crates/red_knot_python_semantic/resources/mdtest/annotations/never.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def f():
5151

5252
```toml
5353
[environment]
54-
target-version = "3.11"
54+
python-version = "3.11"
5555
```
5656

5757
```py

0 commit comments

Comments
 (0)