diff --git a/Cargo.lock b/Cargo.lock index 0c587ea925..c260589787 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3176,6 +3176,7 @@ dependencies = [ "serde", "ty_module_resolver", "ty_python_semantic", + "ty_site_packages", "zip", ] @@ -4562,7 +4563,6 @@ dependencies = [ "bitflags 2.10.0", "bitvec", "camino", - "colored 3.0.0", "compact_str", "datatest-stable", "drop_bomb", @@ -4578,7 +4578,6 @@ dependencies = [ "pretty_assertions", "quickcheck", "quickcheck_macros", - "ruff_annotate_snippets", "ruff_db", "ruff_diagnostics", "ruff_index", @@ -4606,6 +4605,7 @@ dependencies = [ "tracing", "ty_combine", "ty_module_resolver", + "ty_site_packages", "ty_static", "ty_test", "ty_vendored", @@ -4649,6 +4649,26 @@ dependencies = [ "ty_python_semantic", ] +[[package]] +name = "ty_site_packages" +version = "0.0.0" +dependencies = [ + "camino", + "colored 3.0.0", + "get-size2", + "indexmap", + "ruff_annotate_snippets", + "ruff_db", + "ruff_python_ast", + "ruff_python_trivia", + "ruff_source_file", + "ruff_text_size", + "strum", + "strum_macros", + "tracing", + "ty_static", +] + [[package]] name = "ty_static" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index 7c477de59a..a3065bf422 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ ty_module_resolver = { path = "crates/ty_module_resolver" } ty_project = { path = "crates/ty_project", default-features = false } ty_python_semantic = { path = "crates/ty_python_semantic" } ty_server = { path = "crates/ty_server" } +ty_site_packages = { path = "crates/ty_site_packages" } ty_static = { path = "crates/ty_static" } ty_test = { path = "crates/ty_test" } ty_vendored = { path = "crates/ty_vendored" } diff --git a/crates/ruff_graph/Cargo.toml b/crates/ruff_graph/Cargo.toml index d2d36a4236..266bfeae18 100644 --- a/crates/ruff_graph/Cargo.toml +++ b/crates/ruff_graph/Cargo.toml @@ -22,6 +22,7 @@ ruff_python_ast = { workspace = true } ruff_python_parser = { workspace = true } ty_module_resolver = { workspace = true } ty_python_semantic = { workspace = true } +ty_site_packages = { workspace = true } anyhow = { workspace = true } clap = { workspace = true, optional = true } diff --git a/crates/ruff_graph/src/db.rs b/crates/ruff_graph/src/db.rs index f347e6d563..ce37408946 100644 --- a/crates/ruff_graph/src/db.rs +++ b/crates/ruff_graph/src/db.rs @@ -10,9 +10,10 @@ use ruff_python_ast::PythonVersion; use ty_module_resolver::{SearchPathSettings, SearchPaths}; use ty_python_semantic::lint::{LintRegistry, RuleSelection}; use ty_python_semantic::{ - AnalysisSettings, Db, Program, ProgramSettings, PythonEnvironment, PythonPlatform, - PythonVersionSource, PythonVersionWithSource, SysPrefixPathOrigin, default_lint_registry, + AnalysisSettings, Db, Program, ProgramSettings, PythonPlatform, PythonVersionSource, + PythonVersionWithSource, default_lint_registry, }; +use ty_site_packages::{PythonEnvironment, SysPrefixPathOrigin}; static EMPTY_VENDORED: std::sync::LazyLock = std::sync::LazyLock::new(|| { let mut builder = VendoredFileSystemBuilder::new(CompressionMethod::Stored); diff --git a/crates/ty_python_semantic/Cargo.toml b/crates/ty_python_semantic/Cargo.toml index 317cffae23..08e1f7546d 100644 --- a/crates/ty_python_semantic/Cargo.toml +++ b/crates/ty_python_semantic/Cargo.toml @@ -11,7 +11,6 @@ repository = { workspace = true } license = { workspace = true } [dependencies] -ruff_annotate_snippets = { workspace = true } ruff_db = { workspace = true } ruff_diagnostics = { workspace = true } ruff_index = { workspace = true, features = ["salsa"] } @@ -26,13 +25,11 @@ ruff_source_file = { workspace = true } ruff_text_size = { workspace = true } ty_combine = { workspace = true } ty_module_resolver = { workspace = true } -ty_static = { workspace = true } +ty_site_packages = { workspace = true } anyhow = { workspace = true } bitflags = { workspace = true } bitvec = { workspace = true } -camino = { workspace = true } -colored = { workspace = true } compact_str = { workspace = true } drop_bomb = { workspace = true } get-size2 = { workspace = true, features = ["indexmap", "ordermap"] } @@ -63,6 +60,7 @@ ty_test = { workspace = true } ty_vendored = { workspace = true } anyhow = { workspace = true } +camino = { workspace = true } datatest-stable = { workspace = true } glob = { workspace = true } indoc = { workspace = true } diff --git a/crates/ty_python_semantic/src/lib.rs b/crates/ty_python_semantic/src/lib.rs index 3f078e204f..2b89a71b21 100644 --- a/crates/ty_python_semantic/src/lib.rs +++ b/crates/ty_python_semantic/src/lib.rs @@ -8,17 +8,18 @@ use crate::lint::{LintRegistry, LintRegistryBuilder}; use crate::suppression::{IGNORE_COMMENT_UNKNOWN_RULE, INVALID_IGNORE_COMMENT}; pub use db::Db; pub use diagnostic::add_inferred_python_version_hint_to_diagnostic; -pub use program::{ - Program, ProgramSettings, PythonVersionFileSource, PythonVersionSource, PythonVersionWithSource, -}; +pub use program::{Program, ProgramSettings}; pub use python_platform::PythonPlatform; use rustc_hash::FxHasher; pub use semantic_model::{ Completion, HasDefinition, HasType, MemberDefinition, NameKind, SemanticModel, }; -pub use site_packages::{PythonEnvironment, SitePackagesPaths, SysPrefixPathOrigin}; pub use suppression::{UNUSED_IGNORE_COMMENT, suppress_all, suppress_single}; pub use ty_module_resolver::MisconfigurationMode; +pub use ty_site_packages::{ + PythonEnvironment, PythonVersionFileSource, PythonVersionSource, PythonVersionWithSource, + SitePackagesPaths, SysPrefixPathOrigin, +}; pub use types::DisplaySettings; pub use types::ide_support::{ ImportAliasResolution, ResolvedDefinition, definitions_for_attribute, definitions_for_bin_op, @@ -38,7 +39,6 @@ mod python_platform; mod rank; pub mod semantic_index; mod semantic_model; -pub(crate) mod site_packages; mod subscript; mod suppression; pub mod types; diff --git a/crates/ty_python_semantic/src/program.rs b/crates/ty_python_semantic/src/program.rs index 7a5975cfd7..702c6382b7 100644 --- a/crates/ty_python_semantic/src/program.rs +++ b/crates/ty_python_semantic/src/program.rs @@ -1,16 +1,12 @@ -use std::sync::Arc; - use crate::Db; use crate::python_platform::PythonPlatform; -use ruff_db::diagnostic::Span; -use ruff_db::files::system_path_to_file; -use ruff_db::system::{SystemPath, SystemPathBuf}; +use ruff_db::system::SystemPath; use ruff_python_ast::PythonVersion; -use ruff_text_size::TextRange; use salsa::Durability; use salsa::Setter; use ty_module_resolver::SearchPaths; +use ty_site_packages::PythonVersionWithSource; #[salsa::input(singleton, heap_size=ruff_memory_usage::heap_size)] pub struct Program { @@ -91,73 +87,3 @@ pub struct ProgramSettings { pub python_platform: PythonPlatform, pub search_paths: SearchPaths, } - -#[derive(Clone, Debug, Eq, PartialEq, Default, get_size2::GetSize)] -pub enum PythonVersionSource { - /// Value loaded from a project's configuration file. - ConfigFile(PythonVersionFileSource), - - /// Value loaded from the `pyvenv.cfg` file of the virtual environment. - /// The virtual environment might have been configured, activated or inferred. - PyvenvCfgFile(PythonVersionFileSource), - - /// Value inferred from the layout of the Python installation. - /// - /// This only ever applies on Unix. On Unix, the `site-packages` directory - /// will always be at `sys.prefix/lib/pythonX.Y/site-packages`, - /// so we can infer the Python version from the parent directory of `site-packages`. - InstallationDirectoryLayout { site_packages_parent_dir: Box }, - - /// The value comes from a CLI argument, while it's left open if specified using a short argument, - /// long argument (`--extra-paths`) or `--config key=value`. - Cli, - - /// The value comes from the user's editor, - /// while it's left open if specified as a setting - /// or if the value was auto-discovered by the editor - /// (e.g., the Python environment) - Editor, - - /// We fell back to a default value because the value was not specified via the CLI or a config file. - #[default] - Default, -} - -/// Information regarding the file and [`TextRange`] of the configuration -/// from which we inferred the Python version. -#[derive(Debug, PartialEq, Eq, Clone, get_size2::GetSize)] -pub struct PythonVersionFileSource { - path: Arc, - range: Option, -} - -impl PythonVersionFileSource { - pub fn new(path: Arc, range: Option) -> Self { - Self { path, range } - } - - /// Attempt to resolve a [`Span`] that corresponds to the location of - /// the configuration setting that specified the Python version. - /// - /// Useful for subdiagnostics when informing the user - /// what the inferred Python version of their project is. - pub(crate) fn span(&self, db: &dyn Db) -> Option { - let file = system_path_to_file(db, &*self.path).ok()?; - Some(Span::from(file).with_optional_range(self.range)) - } -} - -#[derive(Eq, PartialEq, Debug, Clone, get_size2::GetSize)] -pub struct PythonVersionWithSource { - pub version: PythonVersion, - pub source: PythonVersionSource, -} - -impl Default for PythonVersionWithSource { - fn default() -> Self { - Self { - version: PythonVersion::latest_ty(), - source: PythonVersionSource::Default, - } - } -} diff --git a/crates/ty_site_packages/Cargo.toml b/crates/ty_site_packages/Cargo.toml new file mode 100644 index 0000000000..1c3451c5e3 --- /dev/null +++ b/crates/ty_site_packages/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "ty_site_packages" +version = "0.0.0" +publish = false +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +homepage = { workspace = true } +documentation = { workspace = true } +repository = { workspace = true } +license = { workspace = true } + +[dependencies] +ruff_annotate_snippets = { workspace = true } +ruff_db = { workspace = true } +ruff_python_ast = { workspace = true } +ruff_python_trivia = { workspace = true } +ruff_source_file = { workspace = true } +ruff_text_size = { workspace = true } +ty_static = { workspace = true } + +camino = { workspace = true } +colored = { workspace = true } +get-size2 = { workspace = true } +indexmap = { workspace = true } +strum = { workspace = true } +strum_macros = { workspace = true } +tracing = { workspace = true } + +[dev-dependencies] +ruff_db = { workspace = true, features = ["testing", "os"] } + +[lints] +workspace = true diff --git a/crates/ty_python_semantic/src/site_packages.rs b/crates/ty_site_packages/src/lib.rs similarity index 99% rename from crates/ty_python_semantic/src/site_packages.rs rename to crates/ty_site_packages/src/lib.rs index 8418db6124..19e01c219a 100644 --- a/crates/ty_python_semantic/src/site_packages.rs +++ b/crates/ty_site_packages/src/lib.rs @@ -8,13 +8,14 @@ //! reasonably ask us to type-check code assuming that the code runs //! on Linux.) +mod version; + use std::io; use std::num::NonZeroUsize; use std::ops::Deref; use std::str::FromStr; use std::{fmt, sync::Arc}; -use crate::{PythonVersionFileSource, PythonVersionSource, PythonVersionWithSource}; use camino::Utf8Component; use indexmap::IndexSet; use ruff_annotate_snippets::{Level, Renderer, Snippet}; @@ -25,6 +26,7 @@ use ruff_source_file::{LineIndex, OneIndexed, SourceCode}; use ruff_text_size::{TextLen, TextRange}; use strum::IntoEnumIterator; use ty_static::EnvVars; +pub use version::{PythonVersionFileSource, PythonVersionSource, PythonVersionWithSource}; type SitePackagesDiscoveryResult = Result; type StdlibDiscoveryResult = Result; diff --git a/crates/ty_site_packages/src/version.rs b/crates/ty_site_packages/src/version.rs new file mode 100644 index 0000000000..4703279e3c --- /dev/null +++ b/crates/ty_site_packages/src/version.rs @@ -0,0 +1,92 @@ +//! Types for representing the Python version and its source. + +use std::sync::Arc; + +use ruff_db::Db; +use ruff_db::diagnostic::Span; +use ruff_db::files::system_path_to_file; +use ruff_db::system::SystemPathBuf; +use ruff_python_ast::PythonVersion; +use ruff_text_size::TextRange; + +/// The source of the Python version. +#[derive(Clone, Debug, Eq, PartialEq, Default, get_size2::GetSize)] +pub enum PythonVersionSource { + /// Value loaded from a project's configuration file. + ConfigFile(PythonVersionFileSource), + + /// Value loaded from the `pyvenv.cfg` file of the virtual environment. + /// The virtual environment might have been configured, activated or inferred. + PyvenvCfgFile(PythonVersionFileSource), + + /// Value inferred from the layout of the Python installation. + /// + /// This only ever applies on Unix. On Unix, the `site-packages` directory + /// will always be at `sys.prefix/lib/pythonX.Y/site-packages`, + /// so we can infer the Python version from the parent directory of `site-packages`. + InstallationDirectoryLayout { site_packages_parent_dir: Box }, + + /// The value comes from a CLI argument, while it's left open if specified using a short argument, + /// long argument (`--extra-paths`) or `--config key=value`. + Cli, + + /// The value comes from the user's editor, + /// while it's left open if specified as a setting + /// or if the value was auto-discovered by the editor + /// (e.g., the Python environment) + Editor, + + /// We fell back to a default value because the value was not specified via the CLI or a config file. + #[default] + Default, +} + +/// Information regarding the file and [`TextRange`] of the configuration +/// from which we inferred the Python version. +#[derive(Debug, PartialEq, Eq, Clone, get_size2::GetSize)] +pub struct PythonVersionFileSource { + path: Arc, + range: Option, +} + +impl PythonVersionFileSource { + pub fn new(path: Arc, range: Option) -> Self { + Self { path, range } + } + + /// Returns the path to the configuration file. + pub fn path(&self) -> &SystemPathBuf { + &self.path + } + + /// Returns the range of the configuration setting. + pub fn range(&self) -> Option { + self.range + } + + /// Attempt to resolve a [`Span`] that corresponds to the location of + /// the configuration setting that specified the Python version. + /// + /// Useful for subdiagnostics when informing the user + /// what the inferred Python version of their project is. + pub fn span(&self, db: &dyn Db) -> Option { + let file = system_path_to_file(db, &*self.path).ok()?; + Some(Span::from(file).with_optional_range(self.range)) + } +} + +/// A Python version with its source. +#[derive(Eq, PartialEq, Debug, Clone, get_size2::GetSize)] +pub struct PythonVersionWithSource { + pub version: PythonVersion, + pub source: PythonVersionSource, +} + +impl Default for PythonVersionWithSource { + fn default() -> Self { + Self { + version: PythonVersion::latest_ty(), + source: PythonVersionSource::Default, + } + } +}