Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/red_knot/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ pub(crate) struct CheckCommand {
///
/// This is used to specialize the type of `sys.platform` and will affect the visibility
/// of platform-specific functions and attributes. If the value is set to `all`, no
/// assumptions are made about the target platform.
/// assumptions are made about the target platform. If unspecified, the current system's
/// platform will be used.
#[arg(long, value_name = "PLATFORM", alias = "platform")]
pub(crate) python_platform: Option<String>,

Expand Down
32 changes: 21 additions & 11 deletions crates/red_knot_project/src/metadata/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,25 @@ impl Options {
project_root: &SystemPath,
system: &dyn System,
) -> ProgramSettings {
let (python_version, python_platform) = self
let python_version = self
.environment
.as_ref()
.map(|env| {
(
env.python_version.as_deref().copied(),
env.python_platform.as_deref(),
)
})
.and_then(|env| env.python_version.as_deref().copied())
.unwrap_or_default();

let python_platform = self
.environment
.as_ref()
.and_then(|env| env.python_platform.as_deref().cloned())
.unwrap_or_else(|| {
let default = PythonPlatform::default();
tracing::info!(
"Defaulting to default python version for this platform: '{default}'",
);
default
});
ProgramSettings {
python_version: python_version.unwrap_or_default(),
python_platform: python_platform.cloned().unwrap_or_default(),
python_version,
python_platform,
search_paths: self.to_search_path_settings(project_root, system),
}
}
Expand Down Expand Up @@ -237,7 +242,12 @@ pub struct EnvironmentOptions {
/// If specified, Red Knot will tailor its use of type stub files,
/// which conditionalize type definitions based on the platform.
///
/// If no platform is specified, knot will use `all` or the current platform in the LSP use case.
/// If no platform is specified, knot will use the current platform:
/// - `win32` for Windows
/// - `darwin` for macOS
/// - `android` for Android
/// - `ios` for iOS
/// - `linux` for everything else
#[serde(skip_serializing_if = "Option::is_none")]
pub python_platform: Option<RangedValue<PythonPlatform>>,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ We can access attributes on objects of all kinds:
```py
import sys

reveal_type(inspect.getattr_static(sys, "platform")) # revealed: LiteralString
reveal_type(inspect.getattr_static(sys, "dont_write_bytecode")) # revealed: bool
reveal_type(inspect.getattr_static(inspect, "getattr_static")) # revealed: Literal[getattr_static]

reveal_type(inspect.getattr_static(1, "real")) # revealed: property
Expand Down
17 changes: 2 additions & 15 deletions crates/red_knot_python_semantic/resources/mdtest/sys_platform.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,10 @@
# `sys.platform`

## Default value
## Explicit selection of `all` platforms

When no target platform is specified, we fall back to the type of `sys.platform` declared in
When `python-platform="all"` is specified, we fall back to the type of `sys.platform` declared in
typeshed:

```toml
[environment]
# No python-platform entry
```

```py
import sys

reveal_type(sys.platform) # revealed: LiteralString
```

## Explicit selection of `all` platforms

```toml
[environment]
python-platform = "all"
Expand Down
18 changes: 0 additions & 18 deletions crates/red_knot_python_semantic/resources/mdtest/unreachable.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,24 +195,6 @@ if sys.platform == "win32":
sys.getwindowsversion()
```

##### Checking without a specified platform

If `python-platform` is not specified, we currently default to `all`:

```toml
[environment]
# python-platform not specified
```

```py
import sys

if sys.platform == "win32":
# TODO: we should not emit an error here
# error: [possibly-unbound-attribute]
sys.getwindowsversion()
```

## No (incorrect) diagnostics in unreachable code

```toml
Expand Down
19 changes: 17 additions & 2 deletions crates/red_knot_python_semantic/src/python_platform.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use std::fmt::{Display, Formatter};

/// The target platform to assume when resolving types.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "kebab-case")
)]
pub enum PythonPlatform {
/// Do not make any assumptions about the target platform.
#[default]
All,

/// Assume a specific target platform like `linux`, `darwin` or `win32`.
Expand Down Expand Up @@ -39,6 +38,22 @@ impl Display for PythonPlatform {
}
}

impl Default for PythonPlatform {
fn default() -> Self {
if cfg!(target_os = "windows") {
PythonPlatform::Identifier("win32".to_string())
} else if cfg!(target_os = "macos") {
PythonPlatform::Identifier("darwin".to_string())
} else if cfg!(target_os = "android") {
PythonPlatform::Identifier("android".to_string())
} else if cfg!(target_os = "ios") {
PythonPlatform::Identifier("ios".to_string())
} else {
PythonPlatform::Identifier("linux".to_string())
}
}
}

#[cfg(feature = "schemars")]
mod schema {
use crate::PythonPlatform;
Expand Down
6 changes: 4 additions & 2 deletions crates/red_knot_test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use config::SystemKind;
use parser as test_parser;
use red_knot_python_semantic::types::check_types;
use red_knot_python_semantic::{
Program, ProgramSettings, PythonPath, SearchPathSettings, SysPrefixPathOrigin,
Program, ProgramSettings, PythonPath, PythonPlatform, SearchPathSettings, SysPrefixPathOrigin,
};
use ruff_db::diagnostic::{create_parse_diagnostic, Diagnostic, DisplayDiagnosticConfig};
use ruff_db::files::{system_path_to_file, File};
Expand Down Expand Up @@ -265,7 +265,9 @@ fn run_test(

let settings = ProgramSettings {
python_version,
python_platform: configuration.python_platform().unwrap_or_default(),
python_platform: configuration
.python_platform()
.unwrap_or(PythonPlatform::Identifier("linux".to_string())),
search_paths: SearchPathSettings {
src_roots: vec![src_path],
extra_paths: configuration.extra_paths().unwrap_or_default().to_vec(),
Expand Down
2 changes: 1 addition & 1 deletion knot.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
]
},
"python-platform": {
"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.",
"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 the current platform: - `win32` for Windows - `darwin` for macOS - `android` for Android - `ios` for iOS - `linux` for everything else",
"anyOf": [
{
"$ref": "#/definitions/PythonPlatform"
Expand Down
Loading