Rename custom-typeshed-dir, target-version and current-directory CLI options (#14930)

## Summary

This PR renames the `--custom-typeshed-dir`, `target-version`, and
`--current-directory` cli options to `--typeshed`,
`--python-version`, and `--project` as discussed in the CLI proposal
document.
I added aliases for `--target-version` (for Ruff compat) and
`--custom-typeshed-dir` (for Alex)

## Test Plan

Long help

```
An extremely fast Python type checker.

Usage: red_knot [OPTIONS] [COMMAND]

Commands:
  server  Start the language server
  help    Print this message or the help of the given subcommand(s)

Options:
      --project <PROJECT>
          Run the command within the given project directory.
          
          All `pyproject.toml` files will be discovered by walking up the directory tree from the project root, as will the project's virtual environment (`.venv`).
          
          Other command-line arguments (such as relative paths) will be resolved relative to the current working directory."#,

      --venv-path <PATH>
          Path to the virtual environment the project uses.
          
          If provided, red-knot will use the `site-packages` directory of this virtual environment to resolve type information for the project's third-party dependencies.

      --typeshed-path <PATH>
          Custom directory to use for stdlib typeshed stubs

      --extra-search-path <PATH>
          Additional path to use as a module-resolution source (can be passed multiple times)

      --python-version <VERSION>
          Python version to assume when resolving types
          
          [possible values: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13]

  -v, --verbose...
          Use verbose output (or `-vv` and `-vvv` for more verbose output)

  -W, --watch
          Run in watch mode by re-running whenever files change

  -h, --help
          Print help (see a summary with '-h')

  -V, --version
          Print version
```

Short help 

```
An extremely fast Python type checker.

Usage: red_knot [OPTIONS] [COMMAND]

Commands:
  server  Start the language server
  help    Print this message or the help of the given subcommand(s)

Options:
      --project <PROJECT>         Run the command within the given project directory
      --venv-path <PATH>          Path to the virtual environment the project uses
      --typeshed-path <PATH>      Custom directory to use for stdlib typeshed stubs
      --extra-search-path <PATH>  Additional path to use as a module-resolution source (can be passed multiple times)
      --python-version <VERSION>  Python version to assume when resolving types [possible values: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13]
  -v, --verbose...                Use verbose output (or `-vv` and `-vvv` for more verbose output)
  -W, --watch                     Run in watch mode by re-running whenever files change
  -h, --help                      Print help (see more with '--help')
  -V, --version                   Print version

```

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
Micha Reiser 2024-12-13 09:21:52 +01:00 committed by GitHub
parent d7ce548893
commit c1837e4189
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 280 additions and 297 deletions

View file

@ -5,6 +5,7 @@ use anyhow::{anyhow, Context};
use clap::Parser; use clap::Parser;
use colored::Colorize; use colored::Colorize;
use crossbeam::channel as crossbeam_channel; use crossbeam::channel as crossbeam_channel;
use python_version::PythonVersion;
use red_knot_python_semantic::SitePackages; use red_knot_python_semantic::SitePackages;
use red_knot_server::run_server; use red_knot_server::run_server;
use red_knot_workspace::db::RootDatabase; use red_knot_workspace::db::RootDatabase;
@ -15,12 +16,11 @@ use red_knot_workspace::workspace::WorkspaceMetadata;
use ruff_db::diagnostic::Diagnostic; use ruff_db::diagnostic::Diagnostic;
use ruff_db::system::{OsSystem, System, SystemPath, SystemPathBuf}; use ruff_db::system::{OsSystem, System, SystemPath, SystemPathBuf};
use salsa::plumbing::ZalsaDatabase; use salsa::plumbing::ZalsaDatabase;
use target_version::TargetVersion;
use crate::logging::{setup_tracing, Verbosity}; use crate::logging::{setup_tracing, Verbosity};
mod logging; mod logging;
mod target_version; mod python_version;
mod verbosity; mod verbosity;
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
@ -34,54 +34,39 @@ struct Args {
#[command(subcommand)] #[command(subcommand)]
pub(crate) command: Option<Command>, pub(crate) command: Option<Command>,
#[arg( /// Run the command within the given project directory.
long, ///
help = "Changes the current working directory.", /// All `pyproject.toml` files will be discovered by walking up the directory tree from the given project directory,
long_help = "Changes the current working directory before any specified operations. This affects the workspace and configuration discovery.", /// as will the project's virtual environment (`.venv`) unless the `venv-path` option is set.
value_name = "PATH" ///
)] /// Other command-line arguments (such as relative paths) will be resolved relative to the current working directory.
current_directory: Option<SystemPathBuf>, #[arg(long, value_name = "PROJECT")]
project: Option<SystemPathBuf>,
#[arg( /// Path to the virtual environment the project uses.
long, ///
help = "Path to the virtual environment the project uses", /// If provided, red-knot will use the `site-packages` directory of this virtual environment
long_help = "\ /// to resolve type information for the project's third-party dependencies.
Path to the virtual environment the project uses. \ #[arg(long, value_name = "PATH")]
If provided, red-knot will use the `site-packages` directory of this virtual environment \
to resolve type information for the project's third-party dependencies.",
value_name = "PATH"
)]
venv_path: Option<SystemPathBuf>, venv_path: Option<SystemPathBuf>,
#[arg( /// Custom directory to use for stdlib typeshed stubs.
long, #[arg(long, value_name = "PATH", alias = "custom-typeshed-dir")]
value_name = "DIRECTORY", typeshed: Option<SystemPathBuf>,
help = "Custom directory to use for stdlib typeshed stubs"
)]
custom_typeshed_dir: Option<SystemPathBuf>,
#[arg( /// Additional path to use as a module-resolution source (can be passed multiple times).
long, #[arg(long, value_name = "PATH")]
value_name = "PATH",
help = "Additional path to use as a module-resolution source (can be passed multiple times)"
)]
extra_search_path: Option<Vec<SystemPathBuf>>, extra_search_path: Option<Vec<SystemPathBuf>>,
#[arg( /// Python version to assume when resolving types.
long, #[arg(long, value_name = "VERSION", alias = "target-version")]
help = "Python version to assume when resolving types", python_version: Option<PythonVersion>,
value_name = "VERSION"
)]
target_version: Option<TargetVersion>,
#[clap(flatten)] #[clap(flatten)]
verbosity: Verbosity, verbosity: Verbosity,
#[arg( /// Run in watch mode by re-running whenever files change.
long, #[arg(long, short = 'W')]
help = "Run in watch mode by re-running whenever files change",
short = 'W'
)]
watch: bool, watch: bool,
} }
@ -89,8 +74,8 @@ impl Args {
fn to_configuration(&self, cli_cwd: &SystemPath) -> Configuration { fn to_configuration(&self, cli_cwd: &SystemPath) -> Configuration {
let mut configuration = Configuration::default(); let mut configuration = Configuration::default();
if let Some(target_version) = self.target_version { if let Some(python_version) = self.python_version {
configuration.target_version = Some(target_version.into()); configuration.python_version = Some(python_version.into());
} }
if let Some(venv_path) = &self.venv_path { if let Some(venv_path) = &self.venv_path {
@ -99,9 +84,8 @@ impl Args {
}); });
} }
if let Some(custom_typeshed_dir) = &self.custom_typeshed_dir { if let Some(typeshed) = &self.typeshed {
configuration.search_paths.custom_typeshed = configuration.search_paths.typeshed = Some(SystemPath::absolute(typeshed, cli_cwd));
Some(SystemPath::absolute(custom_typeshed_dir, cli_cwd));
} }
if let Some(extra_search_paths) = &self.extra_search_path { if let Some(extra_search_paths) = &self.extra_search_path {
@ -167,15 +151,13 @@ fn run() -> anyhow::Result<ExitStatus> {
}; };
let cwd = args let cwd = args
.current_directory .project
.as_ref() .as_ref()
.map(|cwd| { .map(|cwd| {
if cwd.as_std_path().is_dir() { if cwd.as_std_path().is_dir() {
Ok(SystemPath::absolute(cwd, &cli_base_path)) Ok(SystemPath::absolute(cwd, &cli_base_path))
} else { } else {
Err(anyhow!( Err(anyhow!("Provided project path `{cwd}` is not a directory"))
"Provided current-directory path `{cwd}` is not a directory"
))
} }
}) })
.transpose()? .transpose()?

View file

@ -0,0 +1,68 @@
/// Enumeration of all supported Python versions
///
/// TODO: unify with the `PythonVersion` enum in the linter/formatter crates?
#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Default, clap::ValueEnum)]
pub enum PythonVersion {
#[value(name = "3.7")]
Py37,
#[value(name = "3.8")]
Py38,
#[default]
#[value(name = "3.9")]
Py39,
#[value(name = "3.10")]
Py310,
#[value(name = "3.11")]
Py311,
#[value(name = "3.12")]
Py312,
#[value(name = "3.13")]
Py313,
}
impl PythonVersion {
const fn as_str(self) -> &'static str {
match self {
Self::Py37 => "3.7",
Self::Py38 => "3.8",
Self::Py39 => "3.9",
Self::Py310 => "3.10",
Self::Py311 => "3.11",
Self::Py312 => "3.12",
Self::Py313 => "3.13",
}
}
}
impl std::fmt::Display for PythonVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl From<PythonVersion> for red_knot_python_semantic::PythonVersion {
fn from(value: PythonVersion) -> Self {
match value {
PythonVersion::Py37 => Self::PY37,
PythonVersion::Py38 => Self::PY38,
PythonVersion::Py39 => Self::PY39,
PythonVersion::Py310 => Self::PY310,
PythonVersion::Py311 => Self::PY311,
PythonVersion::Py312 => Self::PY312,
PythonVersion::Py313 => Self::PY313,
}
}
}
#[cfg(test)]
mod tests {
use crate::python_version::PythonVersion;
#[test]
fn same_default_as_python_version() {
assert_eq!(
red_knot_python_semantic::PythonVersion::from(PythonVersion::default()),
red_knot_python_semantic::PythonVersion::default()
);
}
}

View file

@ -1,62 +0,0 @@
/// Enumeration of all supported Python versions
///
/// TODO: unify with the `PythonVersion` enum in the linter/formatter crates?
#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Default, clap::ValueEnum)]
pub enum TargetVersion {
Py37,
Py38,
#[default]
Py39,
Py310,
Py311,
Py312,
Py313,
}
impl TargetVersion {
const fn as_str(self) -> &'static str {
match self {
Self::Py37 => "py37",
Self::Py38 => "py38",
Self::Py39 => "py39",
Self::Py310 => "py310",
Self::Py311 => "py311",
Self::Py312 => "py312",
Self::Py313 => "py313",
}
}
}
impl std::fmt::Display for TargetVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl From<TargetVersion> for red_knot_python_semantic::PythonVersion {
fn from(value: TargetVersion) -> Self {
match value {
TargetVersion::Py37 => Self::PY37,
TargetVersion::Py38 => Self::PY38,
TargetVersion::Py39 => Self::PY39,
TargetVersion::Py310 => Self::PY310,
TargetVersion::Py311 => Self::PY311,
TargetVersion::Py312 => Self::PY312,
TargetVersion::Py313 => Self::PY313,
}
}
}
#[cfg(test)]
mod tests {
use crate::target_version::TargetVersion;
use red_knot_python_semantic::PythonVersion;
#[test]
fn same_default_as_python_version() {
assert_eq!(
PythonVersion::from(TargetVersion::default()),
PythonVersion::default()
);
}
}

View file

@ -282,7 +282,7 @@ where
.extra_paths .extra_paths
.iter() .iter()
.flatten() .flatten()
.chain(search_paths.custom_typeshed.iter()) .chain(search_paths.typeshed.iter())
.chain(search_paths.site_packages.iter().flat_map(|site_packages| { .chain(search_paths.site_packages.iter().flat_map(|site_packages| {
if let SitePackages::Known(path) = site_packages { if let SitePackages::Known(path) = site_packages {
path.as_slice() path.as_slice()
@ -296,7 +296,7 @@ where
} }
let configuration = Configuration { let configuration = Configuration {
target_version: Some(PythonVersion::PY312), python_version: Some(PythonVersion::PY312),
search_paths, search_paths,
}; };
@ -888,7 +888,7 @@ fn changed_versions_file() -> anyhow::Result<()> {
Ok(()) Ok(())
}, },
|root_path, _workspace_path| SearchPathConfiguration { |root_path, _workspace_path| SearchPathConfiguration {
custom_typeshed: Some(root_path.join("typeshed")), typeshed: Some(root_path.join("typeshed")),
..SearchPathConfiguration::default() ..SearchPathConfiguration::default()
}, },
)?; )?;

View file

@ -135,7 +135,7 @@ if "" < lorem == "ipsum":
```toml ```toml
[environment] [environment]
target-version = "3.11" python-version = "3.11"
``` ```
```py ```py

View file

@ -51,7 +51,7 @@ def f():
```toml ```toml
[environment] [environment]
target-version = "3.11" python-version = "3.11"
``` ```
```py ```py

View file

@ -5,7 +5,7 @@ The following configuration will be attached to the *root* section (without any
```toml ```toml
[environment] [environment]
target-version = "3.10" python-version = "3.10"
``` ```
# Basic # Basic
@ -34,7 +34,7 @@ Here, we make sure that we can overwrite the global configuration in a child sec
```toml ```toml
[environment] [environment]
target-version = "3.11" python-version = "3.11"
``` ```
```py ```py
@ -55,7 +55,7 @@ Children in this section should all use the section configuration:
```toml ```toml
[environment] [environment]
target-version = "3.12" python-version = "3.12"
``` ```
## Child ## Child

View file

@ -77,7 +77,7 @@ def _(m: int, n: int):
```toml ```toml
[environment] [environment]
target-version = "3.9" python-version = "3.9"
``` ```
```py ```py

View file

@ -2,7 +2,7 @@
```toml ```toml
[environment] [environment]
target-version = "3.9" python-version = "3.9"
``` ```
## The type of `sys.version_info` ## The type of `sys.version_info`

View file

@ -166,12 +166,12 @@ pub(crate) mod tests {
.context("Failed to write test files")?; .context("Failed to write test files")?;
let mut search_paths = SearchPathSettings::new(src_root); let mut search_paths = SearchPathSettings::new(src_root);
search_paths.custom_typeshed = self.custom_typeshed; search_paths.typeshed = self.custom_typeshed;
Program::from_settings( Program::from_settings(
&db, &db,
&ProgramSettings { &ProgramSettings {
target_version: self.python_version, python_version: self.python_version,
search_paths, search_paths,
}, },
) )

View file

@ -283,9 +283,9 @@ fn query_stdlib_version(
let Some(module_name) = stdlib_path_to_module_name(relative_path) else { let Some(module_name) = stdlib_path_to_module_name(relative_path) else {
return TypeshedVersionsQueryResult::DoesNotExist; return TypeshedVersionsQueryResult::DoesNotExist;
}; };
let ResolverContext { db, target_version } = context; let ResolverContext { db, python_version } = context;
typeshed_versions(*db).query_module(&module_name, *target_version) typeshed_versions(*db).query_module(&module_name, *python_version)
} }
/// Enumeration describing the various ways in which validation of a search path might fail. /// Enumeration describing the various ways in which validation of a search path might fail.
@ -658,7 +658,7 @@ mod tests {
let TestCase { let TestCase {
db, src, stdlib, .. db, src, stdlib, ..
} = TestCaseBuilder::new() } = TestCaseBuilder::new()
.with_custom_typeshed(MockedTypeshed::default()) .with_mocked_typeshed(MockedTypeshed::default())
.build(); .build();
assert_eq!( assert_eq!(
@ -779,7 +779,7 @@ mod tests {
#[should_panic(expected = "Extension must be `pyi`; got `py`")] #[should_panic(expected = "Extension must be `pyi`; got `py`")]
fn stdlib_path_invalid_join_py() { fn stdlib_path_invalid_join_py() {
let TestCase { db, stdlib, .. } = TestCaseBuilder::new() let TestCase { db, stdlib, .. } = TestCaseBuilder::new()
.with_custom_typeshed(MockedTypeshed::default()) .with_mocked_typeshed(MockedTypeshed::default())
.build(); .build();
SearchPath::custom_stdlib(&db, stdlib.parent().unwrap()) SearchPath::custom_stdlib(&db, stdlib.parent().unwrap())
.unwrap() .unwrap()
@ -791,7 +791,7 @@ mod tests {
#[should_panic(expected = "Extension must be `pyi`; got `rs`")] #[should_panic(expected = "Extension must be `pyi`; got `rs`")]
fn stdlib_path_invalid_join_rs() { fn stdlib_path_invalid_join_rs() {
let TestCase { db, stdlib, .. } = TestCaseBuilder::new() let TestCase { db, stdlib, .. } = TestCaseBuilder::new()
.with_custom_typeshed(MockedTypeshed::default()) .with_mocked_typeshed(MockedTypeshed::default())
.build(); .build();
SearchPath::custom_stdlib(&db, stdlib.parent().unwrap()) SearchPath::custom_stdlib(&db, stdlib.parent().unwrap())
.unwrap() .unwrap()
@ -822,7 +822,7 @@ mod tests {
#[test] #[test]
fn relativize_stdlib_path_errors() { fn relativize_stdlib_path_errors() {
let TestCase { db, stdlib, .. } = TestCaseBuilder::new() let TestCase { db, stdlib, .. } = TestCaseBuilder::new()
.with_custom_typeshed(MockedTypeshed::default()) .with_mocked_typeshed(MockedTypeshed::default())
.build(); .build();
let root = SearchPath::custom_stdlib(&db, stdlib.parent().unwrap()).unwrap(); let root = SearchPath::custom_stdlib(&db, stdlib.parent().unwrap()).unwrap();
@ -867,11 +867,11 @@ mod tests {
fn typeshed_test_case( fn typeshed_test_case(
typeshed: MockedTypeshed, typeshed: MockedTypeshed,
target_version: PythonVersion, python_version: PythonVersion,
) -> (TestDb, SearchPath) { ) -> (TestDb, SearchPath) {
let TestCase { db, stdlib, .. } = TestCaseBuilder::new() let TestCase { db, stdlib, .. } = TestCaseBuilder::new()
.with_custom_typeshed(typeshed) .with_mocked_typeshed(typeshed)
.with_target_version(target_version) .with_python_version(python_version)
.build(); .build();
let stdlib = SearchPath::custom_stdlib(&db, stdlib.parent().unwrap()).unwrap(); let stdlib = SearchPath::custom_stdlib(&db, stdlib.parent().unwrap()).unwrap();
(db, stdlib) (db, stdlib)

View file

@ -160,7 +160,7 @@ impl SearchPaths {
let SearchPathSettings { let SearchPathSettings {
extra_paths, extra_paths,
src_root, src_root,
custom_typeshed, typeshed,
site_packages: site_packages_paths, site_packages: site_packages_paths,
} = settings; } = settings;
@ -180,17 +180,13 @@ impl SearchPaths {
tracing::debug!("Adding first-party search path '{src_root}'"); tracing::debug!("Adding first-party search path '{src_root}'");
static_paths.push(SearchPath::first_party(system, src_root.to_path_buf())?); static_paths.push(SearchPath::first_party(system, src_root.to_path_buf())?);
let (typeshed_versions, stdlib_path) = if let Some(custom_typeshed) = custom_typeshed { let (typeshed_versions, stdlib_path) = if let Some(typeshed) = typeshed {
let custom_typeshed = canonicalize(custom_typeshed, system); let typeshed = canonicalize(typeshed, system);
tracing::debug!("Adding custom-stdlib search path '{custom_typeshed}'"); tracing::debug!("Adding custom-stdlib search path '{typeshed}'");
files.try_add_root( files.try_add_root(db.upcast(), &typeshed, FileRootKind::LibrarySearchPath);
db.upcast(),
&custom_typeshed,
FileRootKind::LibrarySearchPath,
);
let versions_path = custom_typeshed.join("stdlib/VERSIONS"); let versions_path = typeshed.join("stdlib/VERSIONS");
let versions_content = system.read_to_string(&versions_path).map_err(|error| { let versions_content = system.read_to_string(&versions_path).map_err(|error| {
SearchPathValidationError::FailedToReadVersionsFile { SearchPathValidationError::FailedToReadVersionsFile {
@ -201,7 +197,7 @@ impl SearchPaths {
let parsed: TypeshedVersions = versions_content.parse()?; let parsed: TypeshedVersions = versions_content.parse()?;
let search_path = SearchPath::custom_stdlib(db, &custom_typeshed)?; let search_path = SearchPath::custom_stdlib(db, &typeshed)?;
(parsed, search_path) (parsed, search_path)
} else { } else {
@ -530,10 +526,10 @@ struct ModuleNameIngredient<'db> {
/// attempt to resolve the module name /// attempt to resolve the module name
fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<(SearchPath, File, ModuleKind)> { fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<(SearchPath, File, ModuleKind)> {
let program = Program::get(db); let program = Program::get(db);
let target_version = program.target_version(db); let python_version = program.python_version(db);
let resolver_state = ResolverContext::new(db, target_version); let resolver_state = ResolverContext::new(db, python_version);
let is_builtin_module = let is_builtin_module =
ruff_python_stdlib::sys::is_builtin_module(target_version.minor, name.as_str()); ruff_python_stdlib::sys::is_builtin_module(python_version.minor, name.as_str());
for search_path in search_paths(db) { for search_path in search_paths(db) {
// When a builtin module is imported, standard module resolution is bypassed: // When a builtin module is imported, standard module resolution is bypassed:
@ -690,12 +686,12 @@ impl PackageKind {
pub(super) struct ResolverContext<'db> { pub(super) struct ResolverContext<'db> {
pub(super) db: &'db dyn Db, pub(super) db: &'db dyn Db,
pub(super) target_version: PythonVersion, pub(super) python_version: PythonVersion,
} }
impl<'db> ResolverContext<'db> { impl<'db> ResolverContext<'db> {
pub(super) fn new(db: &'db dyn Db, target_version: PythonVersion) -> Self { pub(super) fn new(db: &'db dyn Db, python_version: PythonVersion) -> Self {
Self { db, target_version } Self { db, python_version }
} }
pub(super) fn vendored(&self) -> &VendoredFileSystem { pub(super) fn vendored(&self) -> &VendoredFileSystem {
@ -771,8 +767,8 @@ mod tests {
let TestCase { db, stdlib, .. } = TestCaseBuilder::new() let TestCase { db, stdlib, .. } = TestCaseBuilder::new()
.with_src_files(SRC) .with_src_files(SRC)
.with_custom_typeshed(TYPESHED) .with_mocked_typeshed(TYPESHED)
.with_target_version(PythonVersion::PY38) .with_python_version(PythonVersion::PY38)
.build(); .build();
let builtins_module_name = ModuleName::new_static("builtins").unwrap(); let builtins_module_name = ModuleName::new_static("builtins").unwrap();
@ -789,8 +785,8 @@ mod tests {
}; };
let TestCase { db, stdlib, .. } = TestCaseBuilder::new() let TestCase { db, stdlib, .. } = TestCaseBuilder::new()
.with_custom_typeshed(TYPESHED) .with_mocked_typeshed(TYPESHED)
.with_target_version(PythonVersion::PY38) .with_python_version(PythonVersion::PY38)
.build(); .build();
let functools_module_name = ModuleName::new_static("functools").unwrap(); let functools_module_name = ModuleName::new_static("functools").unwrap();
@ -842,8 +838,8 @@ mod tests {
}; };
let TestCase { db, stdlib, .. } = TestCaseBuilder::new() let TestCase { db, stdlib, .. } = TestCaseBuilder::new()
.with_custom_typeshed(TYPESHED) .with_mocked_typeshed(TYPESHED)
.with_target_version(PythonVersion::PY38) .with_python_version(PythonVersion::PY38)
.build(); .build();
let existing_modules = create_module_names(&["asyncio", "functools", "xml.etree"]); let existing_modules = create_module_names(&["asyncio", "functools", "xml.etree"]);
@ -887,8 +883,8 @@ mod tests {
}; };
let TestCase { db, .. } = TestCaseBuilder::new() let TestCase { db, .. } = TestCaseBuilder::new()
.with_custom_typeshed(TYPESHED) .with_mocked_typeshed(TYPESHED)
.with_target_version(PythonVersion::PY38) .with_python_version(PythonVersion::PY38)
.build(); .build();
let nonexisting_modules = create_module_names(&[ let nonexisting_modules = create_module_names(&[
@ -931,8 +927,8 @@ mod tests {
}; };
let TestCase { db, stdlib, .. } = TestCaseBuilder::new() let TestCase { db, stdlib, .. } = TestCaseBuilder::new()
.with_custom_typeshed(TYPESHED) .with_mocked_typeshed(TYPESHED)
.with_target_version(PythonVersion::PY39) .with_python_version(PythonVersion::PY39)
.build(); .build();
let existing_modules = create_module_names(&[ let existing_modules = create_module_names(&[
@ -973,8 +969,8 @@ mod tests {
}; };
let TestCase { db, .. } = TestCaseBuilder::new() let TestCase { db, .. } = TestCaseBuilder::new()
.with_custom_typeshed(TYPESHED) .with_mocked_typeshed(TYPESHED)
.with_target_version(PythonVersion::PY39) .with_python_version(PythonVersion::PY39)
.build(); .build();
let nonexisting_modules = create_module_names(&["importlib", "xml", "xml.etree"]); let nonexisting_modules = create_module_names(&["importlib", "xml", "xml.etree"]);
@ -997,8 +993,8 @@ mod tests {
let TestCase { db, src, .. } = TestCaseBuilder::new() let TestCase { db, src, .. } = TestCaseBuilder::new()
.with_src_files(SRC) .with_src_files(SRC)
.with_custom_typeshed(TYPESHED) .with_mocked_typeshed(TYPESHED)
.with_target_version(PythonVersion::PY38) .with_python_version(PythonVersion::PY38)
.build(); .build();
let functools_module_name = ModuleName::new_static("functools").unwrap(); let functools_module_name = ModuleName::new_static("functools").unwrap();
@ -1022,7 +1018,7 @@ mod tests {
fn stdlib_uses_vendored_typeshed_when_no_custom_typeshed_supplied() { fn stdlib_uses_vendored_typeshed_when_no_custom_typeshed_supplied() {
let TestCase { db, stdlib, .. } = TestCaseBuilder::new() let TestCase { db, stdlib, .. } = TestCaseBuilder::new()
.with_vendored_typeshed() .with_vendored_typeshed()
.with_target_version(PythonVersion::default()) .with_python_version(PythonVersion::default())
.build(); .build();
let pydoc_data_topics_name = ModuleName::new_static("pydoc_data.topics").unwrap(); let pydoc_data_topics_name = ModuleName::new_static("pydoc_data.topics").unwrap();
@ -1290,11 +1286,11 @@ mod tests {
Program::from_settings( Program::from_settings(
&db, &db,
&ProgramSettings { &ProgramSettings {
target_version: PythonVersion::PY38, python_version: PythonVersion::PY38,
search_paths: SearchPathSettings { search_paths: SearchPathSettings {
extra_paths: vec![], extra_paths: vec![],
src_root: src.clone(), src_root: src.clone(),
custom_typeshed: Some(custom_typeshed), typeshed: Some(custom_typeshed),
site_packages: SitePackages::Known(vec![site_packages]), site_packages: SitePackages::Known(vec![site_packages]),
}, },
}, },
@ -1333,7 +1329,7 @@ mod tests {
fn deleting_an_unrelated_file_doesnt_change_module_resolution() { fn deleting_an_unrelated_file_doesnt_change_module_resolution() {
let TestCase { mut db, src, .. } = TestCaseBuilder::new() let TestCase { mut db, src, .. } = TestCaseBuilder::new()
.with_src_files(&[("foo.py", "x = 1"), ("bar.py", "x = 2")]) .with_src_files(&[("foo.py", "x = 1"), ("bar.py", "x = 2")])
.with_target_version(PythonVersion::PY38) .with_python_version(PythonVersion::PY38)
.build(); .build();
let foo_module_name = ModuleName::new_static("foo").unwrap(); let foo_module_name = ModuleName::new_static("foo").unwrap();
@ -1420,8 +1416,8 @@ mod tests {
site_packages, site_packages,
.. ..
} = TestCaseBuilder::new() } = TestCaseBuilder::new()
.with_custom_typeshed(TYPESHED) .with_mocked_typeshed(TYPESHED)
.with_target_version(PythonVersion::PY38) .with_python_version(PythonVersion::PY38)
.build(); .build();
let functools_module_name = ModuleName::new_static("functools").unwrap(); let functools_module_name = ModuleName::new_static("functools").unwrap();
@ -1468,8 +1464,8 @@ mod tests {
src, src,
.. ..
} = TestCaseBuilder::new() } = TestCaseBuilder::new()
.with_custom_typeshed(TYPESHED) .with_mocked_typeshed(TYPESHED)
.with_target_version(PythonVersion::PY38) .with_python_version(PythonVersion::PY38)
.build(); .build();
let functools_module_name = ModuleName::new_static("functools").unwrap(); let functools_module_name = ModuleName::new_static("functools").unwrap();
@ -1508,8 +1504,8 @@ mod tests {
.. ..
} = TestCaseBuilder::new() } = TestCaseBuilder::new()
.with_src_files(SRC) .with_src_files(SRC)
.with_custom_typeshed(TYPESHED) .with_mocked_typeshed(TYPESHED)
.with_target_version(PythonVersion::PY38) .with_python_version(PythonVersion::PY38)
.build(); .build();
let functools_module_name = ModuleName::new_static("functools").unwrap(); let functools_module_name = ModuleName::new_static("functools").unwrap();
@ -1795,11 +1791,11 @@ not_a_directory
Program::from_settings( Program::from_settings(
&db, &db,
&ProgramSettings { &ProgramSettings {
target_version: PythonVersion::default(), python_version: PythonVersion::default(),
search_paths: SearchPathSettings { search_paths: SearchPathSettings {
extra_paths: vec![], extra_paths: vec![],
src_root: SystemPathBuf::from("/src"), src_root: SystemPathBuf::from("/src"),
custom_typeshed: None, typeshed: None,
site_packages: SitePackages::Known(vec![ site_packages: SitePackages::Known(vec![
venv_site_packages, venv_site_packages,
system_site_packages, system_site_packages,

View file

@ -18,7 +18,7 @@ pub(crate) struct TestCase<T> {
// so this is a single directory instead of a `Vec` of directories, // so this is a single directory instead of a `Vec` of directories,
// like it is in `ruff_db::Program`. // like it is in `ruff_db::Program`.
pub(crate) site_packages: SystemPathBuf, pub(crate) site_packages: SystemPathBuf,
pub(crate) target_version: PythonVersion, pub(crate) python_version: PythonVersion,
} }
/// A `(file_name, file_contents)` tuple /// A `(file_name, file_contents)` tuple
@ -67,7 +67,7 @@ pub(crate) struct UnspecifiedTypeshed;
/// ```rs /// ```rs
/// let test_case = TestCaseBuilder::new() /// let test_case = TestCaseBuilder::new()
/// .with_src_files(...) /// .with_src_files(...)
/// .with_target_version(...) /// .with_python_version(...)
/// .build(); /// .build();
/// ``` /// ```
/// ///
@ -85,13 +85,13 @@ pub(crate) struct UnspecifiedTypeshed;
/// const TYPESHED = MockedTypeshed { ... }; /// const TYPESHED = MockedTypeshed { ... };
/// ///
/// let test_case = resolver_test_case() /// let test_case = resolver_test_case()
/// .with_custom_typeshed(TYPESHED) /// .with_mocked_typeshed(TYPESHED)
/// .with_target_version(...) /// .with_python_version(...)
/// .build(); /// .build();
/// ///
/// let test_case2 = resolver_test_case() /// let test_case2 = resolver_test_case()
/// .with_vendored_typeshed() /// .with_vendored_typeshed()
/// .with_target_version(...) /// .with_python_version(...)
/// .build(); /// .build();
/// ``` /// ```
/// ///
@ -100,7 +100,7 @@ pub(crate) struct UnspecifiedTypeshed;
/// to `()`. /// to `()`.
pub(crate) struct TestCaseBuilder<T> { pub(crate) struct TestCaseBuilder<T> {
typeshed_option: T, typeshed_option: T,
target_version: PythonVersion, python_version: PythonVersion,
first_party_files: Vec<FileSpec>, first_party_files: Vec<FileSpec>,
site_packages_files: Vec<FileSpec>, site_packages_files: Vec<FileSpec>,
} }
@ -118,9 +118,9 @@ impl<T> TestCaseBuilder<T> {
self self
} }
/// Specify the target Python version the module resolver should assume /// Specify the Python version the module resolver should assume
pub(crate) fn with_target_version(mut self, target_version: PythonVersion) -> Self { pub(crate) fn with_python_version(mut self, python_version: PythonVersion) -> Self {
self.target_version = target_version; self.python_version = python_version;
self self
} }
@ -146,7 +146,7 @@ impl TestCaseBuilder<UnspecifiedTypeshed> {
pub(crate) fn new() -> TestCaseBuilder<UnspecifiedTypeshed> { pub(crate) fn new() -> TestCaseBuilder<UnspecifiedTypeshed> {
Self { Self {
typeshed_option: UnspecifiedTypeshed, typeshed_option: UnspecifiedTypeshed,
target_version: PythonVersion::default(), python_version: PythonVersion::default(),
first_party_files: vec![], first_party_files: vec![],
site_packages_files: vec![], site_packages_files: vec![],
} }
@ -156,33 +156,33 @@ impl TestCaseBuilder<UnspecifiedTypeshed> {
pub(crate) fn with_vendored_typeshed(self) -> TestCaseBuilder<VendoredTypeshed> { pub(crate) fn with_vendored_typeshed(self) -> TestCaseBuilder<VendoredTypeshed> {
let TestCaseBuilder { let TestCaseBuilder {
typeshed_option: _, typeshed_option: _,
target_version, python_version,
first_party_files, first_party_files,
site_packages_files, site_packages_files,
} = self; } = self;
TestCaseBuilder { TestCaseBuilder {
typeshed_option: VendoredTypeshed, typeshed_option: VendoredTypeshed,
target_version, python_version,
first_party_files, first_party_files,
site_packages_files, site_packages_files,
} }
} }
/// Use a mock typeshed directory for this test case /// Use a mock typeshed directory for this test case
pub(crate) fn with_custom_typeshed( pub(crate) fn with_mocked_typeshed(
self, self,
typeshed: MockedTypeshed, typeshed: MockedTypeshed,
) -> TestCaseBuilder<MockedTypeshed> { ) -> TestCaseBuilder<MockedTypeshed> {
let TestCaseBuilder { let TestCaseBuilder {
typeshed_option: _, typeshed_option: _,
target_version, python_version,
first_party_files, first_party_files,
site_packages_files, site_packages_files,
} = self; } = self;
TestCaseBuilder { TestCaseBuilder {
typeshed_option: typeshed, typeshed_option: typeshed,
target_version, python_version,
first_party_files, first_party_files,
site_packages_files, site_packages_files,
} }
@ -194,15 +194,15 @@ impl TestCaseBuilder<UnspecifiedTypeshed> {
src, src,
stdlib: _, stdlib: _,
site_packages, site_packages,
target_version, python_version,
} = self.with_custom_typeshed(MockedTypeshed::default()).build(); } = self.with_mocked_typeshed(MockedTypeshed::default()).build();
TestCase { TestCase {
db, db,
src, src,
stdlib: (), stdlib: (),
site_packages, site_packages,
target_version, python_version,
} }
} }
} }
@ -211,7 +211,7 @@ impl TestCaseBuilder<MockedTypeshed> {
pub(crate) fn build(self) -> TestCase<SystemPathBuf> { pub(crate) fn build(self) -> TestCase<SystemPathBuf> {
let TestCaseBuilder { let TestCaseBuilder {
typeshed_option, typeshed_option,
target_version, python_version,
first_party_files, first_party_files,
site_packages_files, site_packages_files,
} = self; } = self;
@ -226,11 +226,11 @@ impl TestCaseBuilder<MockedTypeshed> {
Program::from_settings( Program::from_settings(
&db, &db,
&ProgramSettings { &ProgramSettings {
target_version, python_version,
search_paths: SearchPathSettings { search_paths: SearchPathSettings {
extra_paths: vec![], extra_paths: vec![],
src_root: src.clone(), src_root: src.clone(),
custom_typeshed: Some(typeshed.clone()), typeshed: Some(typeshed.clone()),
site_packages: SitePackages::Known(vec![site_packages.clone()]), site_packages: SitePackages::Known(vec![site_packages.clone()]),
}, },
}, },
@ -242,7 +242,7 @@ impl TestCaseBuilder<MockedTypeshed> {
src, src,
stdlib: typeshed.join("stdlib"), stdlib: typeshed.join("stdlib"),
site_packages, site_packages,
target_version, python_version,
} }
} }
@ -268,7 +268,7 @@ impl TestCaseBuilder<VendoredTypeshed> {
pub(crate) fn build(self) -> TestCase<VendoredPathBuf> { pub(crate) fn build(self) -> TestCase<VendoredPathBuf> {
let TestCaseBuilder { let TestCaseBuilder {
typeshed_option: VendoredTypeshed, typeshed_option: VendoredTypeshed,
target_version, python_version,
first_party_files, first_party_files,
site_packages_files, site_packages_files,
} = self; } = self;
@ -282,7 +282,7 @@ impl TestCaseBuilder<VendoredTypeshed> {
Program::from_settings( Program::from_settings(
&db, &db,
&ProgramSettings { &ProgramSettings {
target_version, python_version,
search_paths: SearchPathSettings { search_paths: SearchPathSettings {
site_packages: SitePackages::Known(vec![site_packages.clone()]), site_packages: SitePackages::Known(vec![site_packages.clone()]),
..SearchPathSettings::new(src.clone()) ..SearchPathSettings::new(src.clone())
@ -296,7 +296,7 @@ impl TestCaseBuilder<VendoredTypeshed> {
src, src,
stdlib: VendoredPathBuf::from("stdlib"), stdlib: VendoredPathBuf::from("stdlib"),
site_packages, site_packages,
target_version, python_version,
} }
} }
} }

View file

@ -112,10 +112,10 @@ impl TypeshedVersions {
pub(in crate::module_resolver) fn query_module( pub(in crate::module_resolver) fn query_module(
&self, &self,
module: &ModuleName, module: &ModuleName,
target_version: PythonVersion, python_version: PythonVersion,
) -> TypeshedVersionsQueryResult { ) -> TypeshedVersionsQueryResult {
if let Some(range) = self.exact(module) { if let Some(range) = self.exact(module) {
if range.contains(target_version) { if range.contains(python_version) {
TypeshedVersionsQueryResult::Exists TypeshedVersionsQueryResult::Exists
} else { } else {
TypeshedVersionsQueryResult::DoesNotExist TypeshedVersionsQueryResult::DoesNotExist
@ -125,7 +125,7 @@ impl TypeshedVersions {
while let Some(module_to_try) = module { while let Some(module_to_try) = module {
if let Some(range) = self.exact(&module_to_try) { if let Some(range) = self.exact(&module_to_try) {
return { return {
if range.contains(target_version) { if range.contains(python_version) {
TypeshedVersionsQueryResult::MaybeExists TypeshedVersionsQueryResult::MaybeExists
} else { } else {
TypeshedVersionsQueryResult::DoesNotExist TypeshedVersionsQueryResult::DoesNotExist

View file

@ -10,7 +10,7 @@ use crate::Db;
#[salsa::input(singleton)] #[salsa::input(singleton)]
pub struct Program { pub struct Program {
pub target_version: PythonVersion, pub python_version: PythonVersion,
#[return_ref] #[return_ref]
pub(crate) search_paths: SearchPaths, pub(crate) search_paths: SearchPaths,
@ -19,16 +19,16 @@ pub struct Program {
impl Program { impl Program {
pub fn from_settings(db: &dyn Db, settings: &ProgramSettings) -> anyhow::Result<Self> { pub fn from_settings(db: &dyn Db, settings: &ProgramSettings) -> anyhow::Result<Self> {
let ProgramSettings { let ProgramSettings {
target_version, python_version,
search_paths, search_paths,
} = settings; } = settings;
tracing::info!("Target version: Python {target_version}"); tracing::info!("Python version: Python {python_version}");
let search_paths = SearchPaths::from_settings(db, search_paths) let search_paths = SearchPaths::from_settings(db, search_paths)
.with_context(|| "Invalid search path settings")?; .with_context(|| "Invalid search path settings")?;
Ok(Program::builder(settings.target_version, search_paths) Ok(Program::builder(settings.python_version, search_paths)
.durability(Durability::HIGH) .durability(Durability::HIGH)
.new(db)) .new(db))
} }
@ -56,7 +56,7 @@ impl Program {
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ProgramSettings { pub struct ProgramSettings {
pub target_version: PythonVersion, pub python_version: PythonVersion,
pub search_paths: SearchPathSettings, pub search_paths: SearchPathSettings,
} }
@ -75,7 +75,7 @@ pub struct SearchPathSettings {
/// Optional path to a "custom typeshed" directory on disk for us to use for standard-library types. /// Optional path to a "custom typeshed" directory on disk for us to use for standard-library types.
/// If this is not provided, we will fallback to our vendored typeshed stubs for the stdlib, /// If this is not provided, we will fallback to our vendored typeshed stubs for the stdlib,
/// bundled as a zip file in the binary /// bundled as a zip file in the binary
pub custom_typeshed: Option<SystemPathBuf>, pub typeshed: Option<SystemPathBuf>,
/// The path to the user's `site-packages` directory, where third-party packages from ``PyPI`` are installed. /// The path to the user's `site-packages` directory, where third-party packages from ``PyPI`` are installed.
pub site_packages: SitePackages, pub site_packages: SitePackages,
@ -86,7 +86,7 @@ impl SearchPathSettings {
Self { Self {
src_root, src_root,
extra_paths: vec![], extra_paths: vec![],
custom_typeshed: None, typeshed: None,
site_packages: SitePackages::Known(vec![]), site_packages: SitePackages::Known(vec![]),
} }
} }

View file

@ -321,7 +321,7 @@ fn site_packages_directory_from_sys_prefix(
// the parsed version // the parsed version
// //
// Note: the `python3.x` part of the `site-packages` path can't be computed from // Note: the `python3.x` part of the `site-packages` path can't be computed from
// the `--target-version` the user has passed, as they might be running Python 3.12 locally // the `--python-version` the user has passed, as they might be running Python 3.12 locally
// even if they've requested that we type check their code "as if" they're running 3.8. // even if they've requested that we type check their code "as if" they're running 3.8.
for entry_result in system for entry_result in system
.read_directory(&sys_prefix_path.join("lib")) .read_directory(&sys_prefix_path.join("lib"))

View file

@ -1344,10 +1344,10 @@ impl<'db> Type<'db> {
Type::Instance(InstanceType { class }) => { Type::Instance(InstanceType { class }) => {
let ty = match (class.known(db), name) { let ty = match (class.known(db), name) {
(Some(KnownClass::VersionInfo), "major") => { (Some(KnownClass::VersionInfo), "major") => {
Type::IntLiteral(Program::get(db).target_version(db).major.into()) Type::IntLiteral(Program::get(db).python_version(db).major.into())
} }
(Some(KnownClass::VersionInfo), "minor") => { (Some(KnownClass::VersionInfo), "minor") => {
Type::IntLiteral(Program::get(db).target_version(db).minor.into()) Type::IntLiteral(Program::get(db).python_version(db).minor.into())
} }
// TODO MRO? get_own_instance_member, get_instance_member // TODO MRO? get_own_instance_member, get_instance_member
_ => todo_type!("instance attributes"), _ => todo_type!("instance attributes"),
@ -1799,7 +1799,7 @@ impl<'db> Type<'db> {
/// This is not exactly the type that `sys.version_info` has at runtime, /// This is not exactly the type that `sys.version_info` has at runtime,
/// but it's a useful fallback for us in order to infer `Literal` types from `sys.version_info` comparisons. /// but it's a useful fallback for us in order to infer `Literal` types from `sys.version_info` comparisons.
fn version_info_tuple(db: &'db dyn Db) -> Self { fn version_info_tuple(db: &'db dyn Db) -> Self {
let target_version = Program::get(db).target_version(db); let python_version = Program::get(db).python_version(db);
let int_instance_ty = KnownClass::Int.to_instance(db); let int_instance_ty = KnownClass::Int.to_instance(db);
// TODO: just grab this type from typeshed (it's a `sys._ReleaseLevel` type alias there) // TODO: just grab this type from typeshed (it's a `sys._ReleaseLevel` type alias there)
@ -1817,8 +1817,8 @@ impl<'db> Type<'db> {
}; };
let version_info_elements = &[ let version_info_elements = &[
Type::IntLiteral(target_version.major.into()), Type::IntLiteral(python_version.major.into()),
Type::IntLiteral(target_version.minor.into()), Type::IntLiteral(python_version.minor.into()),
int_instance_ty, int_instance_ty,
release_level_ty, release_level_ty,
int_instance_ty, int_instance_ty,
@ -2035,7 +2035,7 @@ impl<'db> KnownClass {
CoreStdlibModule::Typing CoreStdlibModule::Typing
} }
Self::NoDefaultType => { Self::NoDefaultType => {
let python_version = Program::get(db).target_version(db); let python_version = Program::get(db).python_version(db);
// typing_extensions has a 3.13+ re-export for the `typing.NoDefault` // typing_extensions has a 3.13+ re-export for the `typing.NoDefault`
// singleton, but not for `typing._NoDefaultType`. So we need to switch // singleton, but not for `typing._NoDefaultType`. So we need to switch

View file

@ -234,7 +234,7 @@ language tag:
````markdown ````markdown
```toml ```toml
[environment] [environment]
target-version = "3.10" python-version = "3.10"
``` ```
```` ````

View file

@ -4,7 +4,7 @@
//! //!
//! ```toml //! ```toml
//! [environment] //! [environment]
//! target-version = "3.10" //! python-version = "3.10"
//! ``` //! ```
use anyhow::Context; use anyhow::Context;
@ -22,7 +22,7 @@ impl MarkdownTestConfig {
} }
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
pub(crate) struct Environment { pub(crate) struct Environment {
#[serde(rename = "target-version")] pub(crate) python_version: String,
pub(crate) target_version: String,
} }

View file

@ -38,7 +38,7 @@ impl Db {
Program::from_settings( Program::from_settings(
&db, &db,
&ProgramSettings { &ProgramSettings {
target_version: PythonVersion::default(), python_version: PythonVersion::default(),
search_paths: SearchPathSettings::new(db.workspace_root.clone()), search_paths: SearchPathSettings::new(db.workspace_root.clone()),
}, },
) )

View file

@ -43,8 +43,8 @@ pub fn run(path: &Utf8Path, long_title: &str, short_title: &str, test_name: &str
} }
Program::get(&db) Program::get(&db)
.set_target_version(&mut db) .set_python_version(&mut db)
.to(test.target_version()); .to(test.python_version());
// Remove all files so that the db is in a "fresh" state. // Remove all files so that the db is in a "fresh" state.
db.memory_file_system().remove_all(); db.memory_file_system().remove_all();

View file

@ -74,8 +74,8 @@ impl<'m, 's> MarkdownTest<'m, 's> {
self.files.iter() self.files.iter()
} }
pub(crate) fn target_version(&self) -> PythonVersion { pub(crate) fn python_version(&self) -> PythonVersion {
self.section.target_version self.section.python_version
} }
} }
@ -125,7 +125,7 @@ struct Section<'s> {
title: &'s str, title: &'s str,
level: u8, level: u8,
parent_id: Option<SectionId>, parent_id: Option<SectionId>,
target_version: PythonVersion, python_version: PythonVersion,
} }
#[newtype_index] #[newtype_index]
@ -222,7 +222,7 @@ impl<'s> Parser<'s> {
title, title,
level: 0, level: 0,
parent_id: None, parent_id: None,
target_version: PythonVersion::default(), python_version: PythonVersion::default(),
}); });
Self { Self {
sections, sections,
@ -305,7 +305,7 @@ impl<'s> Parser<'s> {
title, title,
level: header_level.try_into()?, level: header_level.try_into()?,
parent_id: Some(parent), parent_id: Some(parent),
target_version: self.sections[parent].target_version, python_version: self.sections[parent].python_version,
}; };
if self.current_section_files.is_some() { if self.current_section_files.is_some() {
@ -399,22 +399,22 @@ impl<'s> Parser<'s> {
} }
let config = MarkdownTestConfig::from_str(code)?; let config = MarkdownTestConfig::from_str(code)?;
let target_version = config.environment.target_version; let python_version = config.environment.python_version;
let parts = target_version let parts = python_version
.split('.') .split('.')
.map(str::parse) .map(str::parse)
.collect::<Result<Vec<_>, _>>() .collect::<Result<Vec<_>, _>>()
.context(format!( .context(format!(
"Invalid 'target-version' component: '{target_version}'" "Invalid 'python-version' component: '{python_version}'"
))?; ))?;
if parts.len() != 2 { if parts.len() != 2 {
bail!("Invalid 'target-version': expected MAJOR.MINOR, got '{target_version}'.",); bail!("Invalid 'python-version': expected MAJOR.MINOR, got '{python_version}'.",);
} }
let current_section = &mut self.sections[self.stack.top()]; let current_section = &mut self.sections[self.stack.top()];
current_section.target_version = PythonVersion::from((parts[0], parts[1])); current_section.python_version = PythonVersion::from((parts[0], parts[1]));
self.current_section_has_config = true; self.current_section_has_config = true;

View file

@ -46,7 +46,7 @@ impl Workspace {
SystemPath::new(root), SystemPath::new(root),
&system, &system,
Some(&Configuration { Some(&Configuration {
target_version: Some(settings.target_version.into()), python_version: Some(settings.python_version.into()),
..Configuration::default() ..Configuration::default()
}), }),
) )
@ -170,19 +170,19 @@ impl FileHandle {
#[wasm_bindgen] #[wasm_bindgen]
pub struct Settings { pub struct Settings {
pub target_version: TargetVersion, pub python_version: PythonVersion,
} }
#[wasm_bindgen] #[wasm_bindgen]
impl Settings { impl Settings {
#[wasm_bindgen(constructor)] #[wasm_bindgen(constructor)]
pub fn new(target_version: TargetVersion) -> Self { pub fn new(python_version: PythonVersion) -> Self {
Self { target_version } Self { python_version }
} }
} }
#[wasm_bindgen] #[wasm_bindgen]
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)] #[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum TargetVersion { pub enum PythonVersion {
Py37, Py37,
Py38, Py38,
#[default] #[default]
@ -193,16 +193,16 @@ pub enum TargetVersion {
Py313, Py313,
} }
impl From<TargetVersion> for red_knot_python_semantic::PythonVersion { impl From<PythonVersion> for red_knot_python_semantic::PythonVersion {
fn from(value: TargetVersion) -> Self { fn from(value: PythonVersion) -> Self {
match value { match value {
TargetVersion::Py37 => Self::PY37, PythonVersion::Py37 => Self::PY37,
TargetVersion::Py38 => Self::PY38, PythonVersion::Py38 => Self::PY38,
TargetVersion::Py39 => Self::PY39, PythonVersion::Py39 => Self::PY39,
TargetVersion::Py310 => Self::PY310, PythonVersion::Py310 => Self::PY310,
TargetVersion::Py311 => Self::PY311, PythonVersion::Py311 => Self::PY311,
TargetVersion::Py312 => Self::PY312, PythonVersion::Py312 => Self::PY312,
TargetVersion::Py313 => Self::PY313, PythonVersion::Py313 => Self::PY313,
} }
} }
} }
@ -251,7 +251,7 @@ impl System for WasmSystem {
fn read_virtual_path_to_notebook( fn read_virtual_path_to_notebook(
&self, &self,
_path: &SystemVirtualPath, _path: &SystemVirtualPath,
) -> Result<ruff_notebook::Notebook, ruff_notebook::NotebookError> { ) -> Result<Notebook, ruff_notebook::NotebookError> {
Err(ruff_notebook::NotebookError::Io(not_found())) Err(ruff_notebook::NotebookError::Io(not_found()))
} }
@ -283,7 +283,7 @@ impl System for WasmSystem {
self self
} }
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { fn as_any_mut(&mut self) -> &mut dyn Any {
self self
} }
} }
@ -294,14 +294,13 @@ fn not_found() -> std::io::Error {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::TargetVersion; use crate::PythonVersion;
use red_knot_python_semantic::PythonVersion;
#[test] #[test]
fn same_default_as_python_version() { fn same_default_as_python_version() {
assert_eq!( assert_eq!(
PythonVersion::from(TargetVersion::default()), red_knot_python_semantic::PythonVersion::from(PythonVersion::default()),
PythonVersion::default() red_knot_python_semantic::PythonVersion::default()
); );
} }
} }

View file

@ -2,12 +2,12 @@
use wasm_bindgen_test::wasm_bindgen_test; use wasm_bindgen_test::wasm_bindgen_test;
use red_knot_wasm::{Settings, TargetVersion, Workspace}; use red_knot_wasm::{PythonVersion, Settings, Workspace};
#[wasm_bindgen_test] #[wasm_bindgen_test]
fn check() { fn check() {
let settings = Settings { let settings = Settings {
target_version: TargetVersion::Py312, python_version: PythonVersion::Py312,
}; };
let mut workspace = Workspace::new("/", &settings).expect("Workspace to be created"); let mut workspace = Workspace::new("/", &settings).expect("Workspace to be created");

View file

@ -21,14 +21,14 @@ impl WorkspaceSettings {
#[derive(Debug, Default, Clone, PartialEq, Eq)] #[derive(Debug, Default, Clone, PartialEq, Eq)]
#[cfg_attr(test, derive(serde::Serialize))] #[cfg_attr(test, derive(serde::Serialize))]
pub struct Configuration { pub struct Configuration {
pub target_version: Option<PythonVersion>, pub python_version: Option<PythonVersion>,
pub search_paths: SearchPathConfiguration, pub search_paths: SearchPathConfiguration,
} }
impl Configuration { impl Configuration {
/// Extends this configuration by using the values from `with` for all values that are absent in `self`. /// Extends this configuration by using the values from `with` for all values that are absent in `self`.
pub fn extend(&mut self, with: Configuration) { pub fn extend(&mut self, with: Configuration) {
self.target_version = self.target_version.or(with.target_version); self.python_version = self.python_version.or(with.python_version);
self.search_paths.extend(with.search_paths); self.search_paths.extend(with.search_paths);
} }
@ -39,7 +39,7 @@ impl Configuration {
) -> WorkspaceSettings { ) -> WorkspaceSettings {
WorkspaceSettings { WorkspaceSettings {
program: ProgramSettings { program: ProgramSettings {
target_version: self.target_version.unwrap_or_default(), python_version: self.python_version.unwrap_or_default(),
search_paths: self.search_paths.to_settings(workspace_root), search_paths: self.search_paths.to_settings(workspace_root),
}, },
} }
@ -57,10 +57,10 @@ pub struct SearchPathConfiguration {
/// The root of the workspace, used for finding first-party modules. /// The root of the workspace, used for finding first-party modules.
pub src_root: Option<SystemPathBuf>, pub src_root: Option<SystemPathBuf>,
/// Optional path to a "custom typeshed" directory on disk for us to use for standard-library types. /// Optional path to a "typeshed" directory on disk for us to use for standard-library types.
/// If this is not provided, we will fallback to our vendored typeshed stubs for the stdlib, /// If this is not provided, we will fallback to our vendored typeshed stubs for the stdlib,
/// bundled as a zip file in the binary /// bundled as a zip file in the binary
pub custom_typeshed: Option<SystemPathBuf>, pub typeshed: Option<SystemPathBuf>,
/// The path to the user's `site-packages` directory, where third-party packages from ``PyPI`` are installed. /// The path to the user's `site-packages` directory, where third-party packages from ``PyPI`` are installed.
pub site_packages: Option<SitePackages>, pub site_packages: Option<SitePackages>,
@ -79,7 +79,7 @@ impl SearchPathConfiguration {
.clone() .clone()
.src_root .src_root
.unwrap_or_else(|| workspace_root.to_path_buf()), .unwrap_or_else(|| workspace_root.to_path_buf()),
custom_typeshed: self.custom_typeshed.clone(), typeshed: self.typeshed.clone(),
site_packages, site_packages,
} }
} }
@ -91,8 +91,8 @@ impl SearchPathConfiguration {
if let Some(src_root) = with.src_root { if let Some(src_root) = with.src_root {
self.src_root.get_or_insert(src_root); self.src_root.get_or_insert(src_root);
} }
if let Some(custom_typeshed) = with.custom_typeshed { if let Some(typeshed) = with.typeshed {
self.custom_typeshed.get_or_insert(custom_typeshed); self.typeshed.get_or_insert(typeshed);
} }
if let Some(site_packages) = with.site_packages { if let Some(site_packages) = with.site_packages {
self.site_packages.get_or_insert(site_packages); self.site_packages.get_or_insert(site_packages);

View file

@ -10,11 +10,11 @@ WorkspaceMetadata(
name: Name("workspace-root"), name: Name("workspace-root"),
root: "/app", root: "/app",
configuration: Configuration( configuration: Configuration(
target_version: None, python_version: None,
search_paths: SearchPathConfiguration( search_paths: SearchPathConfiguration(
extra_paths: None, extra_paths: None,
src_root: None, src_root: None,
custom_typeshed: None, typeshed: None,
site_packages: None, site_packages: None,
), ),
), ),
@ -22,14 +22,14 @@ WorkspaceMetadata(
], ],
settings: WorkspaceSettings( settings: WorkspaceSettings(
program: ProgramSettings( program: ProgramSettings(
target_version: PythonVersion( python_version: PythonVersion(
major: 3, major: 3,
minor: 9, minor: 9,
), ),
search_paths: SearchPathSettings( search_paths: SearchPathSettings(
extra_paths: [], extra_paths: [],
src_root: "/app", src_root: "/app",
custom_typeshed: None, typeshed: None,
site_packages: Known([]), site_packages: Known([]),
), ),
), ),

View file

@ -10,11 +10,11 @@ WorkspaceMetadata(
name: Name("workspace-root"), name: Name("workspace-root"),
root: "/app", root: "/app",
configuration: Configuration( configuration: Configuration(
target_version: None, python_version: None,
search_paths: SearchPathConfiguration( search_paths: SearchPathConfiguration(
extra_paths: None, extra_paths: None,
src_root: None, src_root: None,
custom_typeshed: None, typeshed: None,
site_packages: None, site_packages: None,
), ),
), ),
@ -22,14 +22,14 @@ WorkspaceMetadata(
], ],
settings: WorkspaceSettings( settings: WorkspaceSettings(
program: ProgramSettings( program: ProgramSettings(
target_version: PythonVersion( python_version: PythonVersion(
major: 3, major: 3,
minor: 9, minor: 9,
), ),
search_paths: SearchPathSettings( search_paths: SearchPathSettings(
extra_paths: [], extra_paths: [],
src_root: "/app", src_root: "/app",
custom_typeshed: None, typeshed: None,
site_packages: Known([]), site_packages: Known([]),
), ),
), ),

View file

@ -10,11 +10,11 @@ WorkspaceMetadata(
name: Name("app"), name: Name("app"),
root: "/app", root: "/app",
configuration: Configuration( configuration: Configuration(
target_version: None, python_version: None,
search_paths: SearchPathConfiguration( search_paths: SearchPathConfiguration(
extra_paths: None, extra_paths: None,
src_root: None, src_root: None,
custom_typeshed: None, typeshed: None,
site_packages: None, site_packages: None,
), ),
), ),
@ -22,14 +22,14 @@ WorkspaceMetadata(
], ],
settings: WorkspaceSettings( settings: WorkspaceSettings(
program: ProgramSettings( program: ProgramSettings(
target_version: PythonVersion( python_version: PythonVersion(
major: 3, major: 3,
minor: 9, minor: 9,
), ),
search_paths: SearchPathSettings( search_paths: SearchPathSettings(
extra_paths: [], extra_paths: [],
src_root: "/app", src_root: "/app",
custom_typeshed: None, typeshed: None,
site_packages: Known([]), site_packages: Known([]),
), ),
), ),

View file

@ -10,11 +10,11 @@ WorkspaceMetadata(
name: Name("backend"), name: Name("backend"),
root: "/app", root: "/app",
configuration: Configuration( configuration: Configuration(
target_version: None, python_version: None,
search_paths: SearchPathConfiguration( search_paths: SearchPathConfiguration(
extra_paths: None, extra_paths: None,
src_root: None, src_root: None,
custom_typeshed: None, typeshed: None,
site_packages: None, site_packages: None,
), ),
), ),
@ -22,14 +22,14 @@ WorkspaceMetadata(
], ],
settings: WorkspaceSettings( settings: WorkspaceSettings(
program: ProgramSettings( program: ProgramSettings(
target_version: PythonVersion( python_version: PythonVersion(
major: 3, major: 3,
minor: 9, minor: 9,
), ),
search_paths: SearchPathSettings( search_paths: SearchPathSettings(
extra_paths: [], extra_paths: [],
src_root: "/app", src_root: "/app",
custom_typeshed: None, typeshed: None,
site_packages: Known([]), site_packages: Known([]),
), ),
), ),

View file

@ -10,11 +10,11 @@ WorkspaceMetadata(
name: Name("workspace-root"), name: Name("workspace-root"),
root: "/app", root: "/app",
configuration: Configuration( configuration: Configuration(
target_version: None, python_version: None,
search_paths: SearchPathConfiguration( search_paths: SearchPathConfiguration(
extra_paths: None, extra_paths: None,
src_root: None, src_root: None,
custom_typeshed: None, typeshed: None,
site_packages: None, site_packages: None,
), ),
), ),
@ -23,11 +23,11 @@ WorkspaceMetadata(
name: Name("member-a"), name: Name("member-a"),
root: "/app/packages/a", root: "/app/packages/a",
configuration: Configuration( configuration: Configuration(
target_version: None, python_version: None,
search_paths: SearchPathConfiguration( search_paths: SearchPathConfiguration(
extra_paths: None, extra_paths: None,
src_root: None, src_root: None,
custom_typeshed: None, typeshed: None,
site_packages: None, site_packages: None,
), ),
), ),
@ -35,14 +35,14 @@ WorkspaceMetadata(
], ],
settings: WorkspaceSettings( settings: WorkspaceSettings(
program: ProgramSettings( program: ProgramSettings(
target_version: PythonVersion( python_version: PythonVersion(
major: 3, major: 3,
minor: 9, minor: 9,
), ),
search_paths: SearchPathSettings( search_paths: SearchPathSettings(
extra_paths: [], extra_paths: [],
src_root: "/app", src_root: "/app",
custom_typeshed: None, typeshed: None,
site_packages: Known([]), site_packages: Known([]),
), ),
), ),

View file

@ -10,11 +10,11 @@ WorkspaceMetadata(
name: Name("workspace-root"), name: Name("workspace-root"),
root: "/app", root: "/app",
configuration: Configuration( configuration: Configuration(
target_version: None, python_version: None,
search_paths: SearchPathConfiguration( search_paths: SearchPathConfiguration(
extra_paths: None, extra_paths: None,
src_root: None, src_root: None,
custom_typeshed: None, typeshed: None,
site_packages: None, site_packages: None,
), ),
), ),
@ -23,11 +23,11 @@ WorkspaceMetadata(
name: Name("member-a"), name: Name("member-a"),
root: "/app/packages/a", root: "/app/packages/a",
configuration: Configuration( configuration: Configuration(
target_version: None, python_version: None,
search_paths: SearchPathConfiguration( search_paths: SearchPathConfiguration(
extra_paths: None, extra_paths: None,
src_root: None, src_root: None,
custom_typeshed: None, typeshed: None,
site_packages: None, site_packages: None,
), ),
), ),
@ -36,11 +36,11 @@ WorkspaceMetadata(
name: Name("member-x"), name: Name("member-x"),
root: "/app/packages/x", root: "/app/packages/x",
configuration: Configuration( configuration: Configuration(
target_version: None, python_version: None,
search_paths: SearchPathConfiguration( search_paths: SearchPathConfiguration(
extra_paths: None, extra_paths: None,
src_root: None, src_root: None,
custom_typeshed: None, typeshed: None,
site_packages: None, site_packages: None,
), ),
), ),
@ -48,14 +48,14 @@ WorkspaceMetadata(
], ],
settings: WorkspaceSettings( settings: WorkspaceSettings(
program: ProgramSettings( program: ProgramSettings(
target_version: PythonVersion( python_version: PythonVersion(
major: 3, major: 3,
minor: 9, minor: 9,
), ),
search_paths: SearchPathSettings( search_paths: SearchPathSettings(
extra_paths: [], extra_paths: [],
src_root: "/app", src_root: "/app",
custom_typeshed: None, typeshed: None,
site_packages: Known([]), site_packages: Known([]),
), ),
), ),

View file

@ -79,7 +79,7 @@ fn setup_case() -> Case {
src_root, src_root,
&system, &system,
Some(&Configuration { Some(&Configuration {
target_version: Some(PythonVersion::PY312), python_version: Some(PythonVersion::PY312),
..Configuration::default() ..Configuration::default()
}), }),
) )

View file

@ -28,7 +28,7 @@ impl ModuleDb {
/// Initialize a [`ModuleDb`] from the given source root. /// Initialize a [`ModuleDb`] from the given source root.
pub fn from_src_roots( pub fn from_src_roots(
mut src_roots: impl Iterator<Item = SystemPathBuf>, mut src_roots: impl Iterator<Item = SystemPathBuf>,
target_version: PythonVersion, python_version: PythonVersion,
) -> Result<Self> { ) -> Result<Self> {
let search_paths = { let search_paths = {
// Use the first source root. // Use the first source root.
@ -48,7 +48,7 @@ impl ModuleDb {
Program::from_settings( Program::from_settings(
&db, &db,
&ProgramSettings { &ProgramSettings {
target_version, python_version,
search_paths, search_paths,
}, },
)?; )?;