Skip to content

Commit 3657f79

Browse files
authored
[red-knot] Add --python-platform CLI option (#17284)
## Summary Add a new `--python-platform` command-line option, in analogy to `--python-version`. ## Test Plan Added new integration test.
1 parent 4a4a376 commit 3657f79

File tree

5 files changed

+87
-7
lines changed

5 files changed

+87
-7
lines changed

crates/red_knot/src/args.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ pub(crate) struct CheckCommand {
7171
#[arg(long, value_name = "VERSION", alias = "target-version")]
7272
pub(crate) python_version: Option<PythonVersion>,
7373

74+
/// Target platform to assume when resolving types.
75+
///
76+
/// This is used to specialize the type of `sys.platform` and will affect the visibility
77+
/// of platform-specific functions and attributes. If the value is set to `all`, no
78+
/// assumptions are made about the target platform.
79+
#[arg(long, value_name = "PLATFORM", alias = "platform")]
80+
pub(crate) python_platform: Option<String>,
81+
7482
#[clap(flatten)]
7583
pub(crate) verbosity: Verbosity,
7684

@@ -116,6 +124,9 @@ impl CheckCommand {
116124
python_version: self
117125
.python_version
118126
.map(|version| RangedValue::cli(version.into())),
127+
python_platform: self
128+
.python_platform
129+
.map(|platform| RangedValue::cli(platform.into())),
119130
python: self.python.map(RelativePathBuf::cli),
120131
typeshed: self.typeshed.map(RelativePathBuf::cli),
121132
extra_paths: self.extra_search_path.map(|extra_search_paths| {
@@ -124,7 +135,6 @@ impl CheckCommand {
124135
.map(RelativePathBuf::cli)
125136
.collect()
126137
}),
127-
..EnvironmentOptions::default()
128138
}),
129139
terminal: Some(TerminalOptions {
130140
output_format: self

crates/red_knot/tests/cli.rs

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ use std::process::Command;
66
use tempfile::TempDir;
77

88
/// Specifying an option on the CLI should take precedence over the same setting in the
9-
/// project's configuration.
9+
/// project's configuration. Here, this is tested for the Python version.
1010
#[test]
11-
fn config_override() -> anyhow::Result<()> {
11+
fn config_override_python_version() -> anyhow::Result<()> {
1212
let case = TestCase::with_files([
1313
(
1414
"pyproject.toml",
@@ -57,6 +57,67 @@ fn config_override() -> anyhow::Result<()> {
5757
Ok(())
5858
}
5959

60+
/// Same as above, but for the Python platform.
61+
#[test]
62+
fn config_override_python_platform() -> anyhow::Result<()> {
63+
let case = TestCase::with_files([
64+
(
65+
"pyproject.toml",
66+
r#"
67+
[tool.knot.environment]
68+
python-platform = "linux"
69+
"#,
70+
),
71+
(
72+
"test.py",
73+
r#"
74+
import sys
75+
from typing_extensions import reveal_type
76+
77+
reveal_type(sys.platform)
78+
"#,
79+
),
80+
])?;
81+
82+
assert_cmd_snapshot!(case.command(), @r#"
83+
success: true
84+
exit_code: 0
85+
----- stdout -----
86+
info: revealed-type
87+
--> <temp_dir>/test.py:5:1
88+
|
89+
3 | from typing_extensions import reveal_type
90+
4 |
91+
5 | reveal_type(sys.platform)
92+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ Revealed type is `Literal["linux"]`
93+
|
94+
95+
Found 1 diagnostic
96+
97+
----- stderr -----
98+
"#);
99+
100+
assert_cmd_snapshot!(case.command().arg("--python-platform").arg("all"), @r"
101+
success: true
102+
exit_code: 0
103+
----- stdout -----
104+
info: revealed-type
105+
--> <temp_dir>/test.py:5:1
106+
|
107+
3 | from typing_extensions import reveal_type
108+
4 |
109+
5 | reveal_type(sys.platform)
110+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ Revealed type is `LiteralString`
111+
|
112+
113+
Found 1 diagnostic
114+
115+
----- stderr -----
116+
");
117+
118+
Ok(())
119+
}
120+
60121
/// Paths specified on the CLI are relative to the current working directory and not the project root.
61122
///
62123
/// We test this by adding an extra search path from the CLI to the libs directory when

crates/red_knot_project/src/metadata/options.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ impl Options {
224224
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
225225
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
226226
pub struct EnvironmentOptions {
227-
/// Specifies the version of Python that will be used to execute the source code.
227+
/// Specifies the version of Python that will be used to analyze the source code.
228228
/// The version should be specified as a string in the format `M.m` where `M` is the major version
229229
/// and `m` is the minor (e.g. "3.0" or "3.6").
230230
/// If a version is provided, knot will generate errors if the source code makes use of language features
@@ -233,7 +233,7 @@ pub struct EnvironmentOptions {
233233
#[serde(skip_serializing_if = "Option::is_none")]
234234
pub python_version: Option<RangedValue<PythonVersion>>,
235235

236-
/// Specifies the target platform that will be used to execute the source code.
236+
/// Specifies the target platform that will be used to analyze the source code.
237237
/// If specified, Red Knot will tailor its use of type stub files,
238238
/// which conditionalize type definitions based on the platform.
239239
///

crates/red_knot_python_semantic/src/python_platform.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ pub enum PythonPlatform {
2121
Identifier(String),
2222
}
2323

24+
impl From<String> for PythonPlatform {
25+
fn from(platform: String) -> Self {
26+
match platform.as_str() {
27+
"all" => PythonPlatform::All,
28+
_ => PythonPlatform::Identifier(platform.to_string()),
29+
}
30+
}
31+
}
32+
2433
impl Display for PythonPlatform {
2534
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2635
match self {

knot.schema.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
]
9090
},
9191
"python-platform": {
92-
"description": "Specifies the target platform that will be used to execute the source code. If specified, Red Knot will tailor its use of type stub files, which conditionalize type definitions based on the platform.\n\nIf no platform is specified, knot will use `all` or the current platform in the LSP use case.",
92+
"description": "Specifies the target platform that will be used to analyze the source code. If specified, Red Knot will tailor its use of type stub files, which conditionalize type definitions based on the platform.\n\nIf no platform is specified, knot will use `all` or the current platform in the LSP use case.",
9393
"anyOf": [
9494
{
9595
"$ref": "#/definitions/PythonPlatform"
@@ -100,7 +100,7 @@
100100
]
101101
},
102102
"python-version": {
103-
"description": "Specifies the version of Python that will be used to execute the source code. The version should be specified as a string in the format `M.m` where `M` is the major version and `m` is the minor (e.g. \"3.0\" or \"3.6\"). If a version is provided, knot will generate errors if the source code makes use of language features that are not supported in that version. It will also tailor its use of type stub files, which conditionalizes type definitions based on the version.",
103+
"description": "Specifies the version of Python that will be used to analyze the source code. The version should be specified as a string in the format `M.m` where `M` is the major version and `m` is the minor (e.g. \"3.0\" or \"3.6\"). If a version is provided, knot will generate errors if the source code makes use of language features that are not supported in that version. It will also tailor its use of type stub files, which conditionalizes type definitions based on the version.",
104104
"anyOf": [
105105
{
106106
"$ref": "#/definitions/PythonVersion"

0 commit comments

Comments
 (0)