Add support for sys.platform

This commit is contained in:
David Peter
2024-12-17 13:25:01 +01:00
parent 76e277b02a
commit 3d85c7d09c
13 changed files with 122 additions and 11 deletions

View File

@@ -0,0 +1,43 @@
# `sys.platform`
## Default value
When no target platform 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: str
```
## Explicit selection of `all` platforms
```toml
[environment]
python-platform = "all"
```
```py
import sys
reveal_type(sys.platform) # revealed: str
```
## Explicit selection of a specific platform
```toml
[environment]
python-platform = "linux"
```
```py
import sys
reveal_type(sys.platform) # revealed: Literal["linux"]
```

View File

@@ -16,7 +16,7 @@ pub(crate) mod tests {
use crate::program::{Program, SearchPathSettings};
use crate::python_version::PythonVersion;
use crate::{default_lint_registry, ProgramSettings};
use crate::{default_lint_registry, ProgramSettings, PythonPlatform};
use super::Db;
use crate::lint::RuleSelection;
@@ -127,6 +127,8 @@ pub(crate) mod tests {
pub(crate) struct TestDbBuilder<'a> {
/// Target Python version
python_version: PythonVersion,
/// Target Python platform
python_platform: PythonPlatform,
/// Path to a custom typeshed directory
custom_typeshed: Option<SystemPathBuf>,
/// Path and content pairs for files that should be present
@@ -137,6 +139,7 @@ pub(crate) mod tests {
pub(crate) fn new() -> Self {
Self {
python_version: PythonVersion::default(),
python_platform: PythonPlatform::default(),
custom_typeshed: None,
files: vec![],
}
@@ -173,6 +176,7 @@ pub(crate) mod tests {
&db,
&ProgramSettings {
python_version: self.python_version,
python_platform: self.python_platform,
search_paths,
},
)

View File

@@ -7,6 +7,7 @@ pub use db::Db;
pub use module_name::ModuleName;
pub use module_resolver::{resolve_module, system_module_search_paths, KnownModule, Module};
pub use program::{Program, ProgramSettings, SearchPathSettings, SitePackages};
pub use python_platform::PythonPlatform;
pub use python_version::PythonVersion;
pub use semantic_model::{HasTy, SemanticModel};
@@ -17,6 +18,7 @@ mod module_name;
mod module_resolver;
mod node_key;
mod program;
mod python_platform;
mod python_version;
pub mod semantic_index;
mod semantic_model;

View File

@@ -721,8 +721,8 @@ mod tests {
use crate::module_name::ModuleName;
use crate::module_resolver::module::ModuleKind;
use crate::module_resolver::testing::{FileSpec, MockedTypeshed, TestCase, TestCaseBuilder};
use crate::ProgramSettings;
use crate::PythonVersion;
use crate::{ProgramSettings, PythonPlatform};
use super::*;
@@ -1262,7 +1262,7 @@ mod tests {
fn symlink() -> anyhow::Result<()> {
use anyhow::Context;
use crate::program::Program;
use crate::{program::Program, PythonPlatform};
use ruff_db::system::{OsSystem, SystemPath};
use crate::db::tests::TestDb;
@@ -1296,6 +1296,7 @@ mod tests {
&db,
&ProgramSettings {
python_version: PythonVersion::PY38,
python_platform: PythonPlatform::default(),
search_paths: SearchPathSettings {
extra_paths: vec![],
src_root: src.clone(),
@@ -1801,6 +1802,7 @@ not_a_directory
&db,
&ProgramSettings {
python_version: PythonVersion::default(),
python_platform: PythonPlatform::default(),
search_paths: SearchPathSettings {
extra_paths: vec![],
src_root: SystemPathBuf::from("/src"),

View File

@@ -4,7 +4,7 @@ use ruff_db::vendored::VendoredPathBuf;
use crate::db::tests::TestDb;
use crate::program::{Program, SearchPathSettings};
use crate::python_version::PythonVersion;
use crate::{ProgramSettings, SitePackages};
use crate::{ProgramSettings, PythonPlatform, SitePackages};
/// A test case for the module resolver.
///
@@ -101,6 +101,7 @@ pub(crate) struct UnspecifiedTypeshed;
pub(crate) struct TestCaseBuilder<T> {
typeshed_option: T,
python_version: PythonVersion,
python_platform: PythonPlatform,
first_party_files: Vec<FileSpec>,
site_packages_files: Vec<FileSpec>,
}
@@ -147,6 +148,7 @@ impl TestCaseBuilder<UnspecifiedTypeshed> {
Self {
typeshed_option: UnspecifiedTypeshed,
python_version: PythonVersion::default(),
python_platform: PythonPlatform::default(),
first_party_files: vec![],
site_packages_files: vec![],
}
@@ -157,12 +159,14 @@ impl TestCaseBuilder<UnspecifiedTypeshed> {
let TestCaseBuilder {
typeshed_option: _,
python_version,
python_platform,
first_party_files,
site_packages_files,
} = self;
TestCaseBuilder {
typeshed_option: VendoredTypeshed,
python_version,
python_platform,
first_party_files,
site_packages_files,
}
@@ -176,6 +180,7 @@ impl TestCaseBuilder<UnspecifiedTypeshed> {
let TestCaseBuilder {
typeshed_option: _,
python_version,
python_platform,
first_party_files,
site_packages_files,
} = self;
@@ -183,6 +188,7 @@ impl TestCaseBuilder<UnspecifiedTypeshed> {
TestCaseBuilder {
typeshed_option: typeshed,
python_version,
python_platform,
first_party_files,
site_packages_files,
}
@@ -212,6 +218,7 @@ impl TestCaseBuilder<MockedTypeshed> {
let TestCaseBuilder {
typeshed_option,
python_version,
python_platform,
first_party_files,
site_packages_files,
} = self;
@@ -227,6 +234,7 @@ impl TestCaseBuilder<MockedTypeshed> {
&db,
&ProgramSettings {
python_version,
python_platform,
search_paths: SearchPathSettings {
extra_paths: vec![],
src_root: src.clone(),
@@ -269,6 +277,7 @@ impl TestCaseBuilder<VendoredTypeshed> {
let TestCaseBuilder {
typeshed_option: VendoredTypeshed,
python_version,
python_platform,
first_party_files,
site_packages_files,
} = self;
@@ -283,6 +292,7 @@ impl TestCaseBuilder<VendoredTypeshed> {
&db,
&ProgramSettings {
python_version,
python_platform,
search_paths: SearchPathSettings {
site_packages: SitePackages::Known(vec![site_packages.clone()]),
..SearchPathSettings::new(src.clone())

View File

@@ -1,3 +1,4 @@
use crate::python_platform::PythonPlatform;
use crate::python_version::PythonVersion;
use anyhow::Context;
use salsa::Durability;
@@ -12,6 +13,8 @@ use crate::Db;
pub struct Program {
pub python_version: PythonVersion,
pub python_platform: PythonPlatform,
#[return_ref]
pub(crate) search_paths: SearchPaths,
}
@@ -20,6 +23,7 @@ impl Program {
pub fn from_settings(db: &dyn Db, settings: &ProgramSettings) -> anyhow::Result<Self> {
let ProgramSettings {
python_version,
python_platform,
search_paths,
} = settings;
@@ -28,9 +32,11 @@ impl Program {
let search_paths = SearchPaths::from_settings(db, search_paths)
.with_context(|| "Invalid search path settings")?;
Ok(Program::builder(settings.python_version, search_paths)
.durability(Durability::HIGH)
.new(db))
Ok(
Program::builder(*python_version, python_platform.clone(), search_paths)
.durability(Durability::HIGH)
.new(db),
)
}
pub fn update_search_paths(
@@ -57,6 +63,7 @@ impl Program {
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ProgramSettings {
pub python_version: PythonVersion,
pub python_platform: PythonPlatform,
pub search_paths: SearchPathSettings,
}

View File

@@ -0,0 +1,13 @@
use serde::{Deserialize, Serialize};
/// The target platform to assume when resolving types.
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum PythonPlatform {
/// Do not make any assumptions about the target platform.
#[default]
All,
/// Assume a target platform like `linux`, `darwin`, `win32`, etc.
#[serde(untagged)]
Individual(String),
}

View File

@@ -137,6 +137,21 @@ fn symbol<'db>(db: &'db dyn Db, scope: ScopeId<'db>, name: &str) -> Symbol<'db>
{
return Symbol::Type(Type::BooleanLiteral(true), Boundness::Bound);
}
if name == "platform"
&& file_to_module(db, scope.file(db)).is_some_and(|module| module.name() == "sys")
{
match Program::get(db).python_platform(db) {
crate::PythonPlatform::Individual(platform) => {
return Symbol::Type(
Type::StringLiteral(StringLiteralType::new(db, platform.as_str())),
Boundness::Bound,
);
}
crate::PythonPlatform::All => {
// Fall through to the looked up type
}
}
}
let table = symbol_table(db, scope);
table

View File

@@ -9,7 +9,7 @@
//! ```
use anyhow::Context;
use red_knot_python_semantic::PythonVersion;
use red_knot_python_semantic::{PythonPlatform, PythonVersion};
use serde::Deserialize;
#[derive(Deserialize, Debug, Default, Clone)]
@@ -28,13 +28,22 @@ impl MarkdownTestConfig {
pub(crate) fn python_version(&self) -> Option<PythonVersion> {
self.environment.as_ref().and_then(|env| env.python_version)
}
pub(crate) fn python_platform(&self) -> Option<PythonPlatform> {
self.environment
.as_ref()
.and_then(|env| env.python_platform.clone())
}
}
#[derive(Deserialize, Debug, Default, Clone)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub(crate) struct Environment {
/// Python version to assume when resolving types.
/// Target Python version to assume when resolving types.
pub(crate) python_version: Option<PythonVersion>,
/// Target platform to assume when resolving types.
pub(crate) python_platform: Option<PythonPlatform>,
}
#[derive(Deserialize, Debug, Clone)]

View File

@@ -1,7 +1,7 @@
use red_knot_python_semantic::lint::RuleSelection;
use red_knot_python_semantic::{
default_lint_registry, Db as SemanticDb, Program, ProgramSettings, PythonVersion,
SearchPathSettings,
default_lint_registry, Db as SemanticDb, Program, ProgramSettings, PythonPlatform,
PythonVersion, SearchPathSettings,
};
use ruff_db::files::{File, Files};
use ruff_db::system::{DbWithTestSystem, System, SystemPath, SystemPathBuf, TestSystem};
@@ -40,6 +40,7 @@ impl Db {
&db,
&ProgramSettings {
python_version: PythonVersion::default(),
python_platform: PythonPlatform::default(),
search_paths: SearchPathSettings::new(db.workspace_root.clone()),
},
)

View File

@@ -52,6 +52,9 @@ pub fn run(path: &Utf8Path, long_title: &str, short_title: &str, test_name: &str
Program::get(&db)
.set_python_version(&mut db)
.to(test.configuration().python_version().unwrap_or_default());
Program::get(&db)
.set_python_platform(&mut db)
.to(test.configuration().python_platform().unwrap_or_default());
// Remove all files so that the db is in a "fresh" state.
db.memory_file_system().remove_all();

View File

@@ -40,6 +40,7 @@ impl Configuration {
WorkspaceSettings {
program: ProgramSettings {
python_version: self.python_version.unwrap_or_default(),
python_platform: Default::default(),
search_paths: self.search_paths.to_settings(workspace_root),
},
}

View File

@@ -49,6 +49,7 @@ impl ModuleDb {
&db,
&ProgramSettings {
python_version,
python_platform: Default::default(),
search_paths,
},
)?;