ty_site_packages/version.rs
1//! Types for representing the Python version and its source.
2
3use std::sync::Arc;
4
5use ruff_db::Db;
6use ruff_db::diagnostic::Span;
7use ruff_db::files::system_path_to_file;
8use ruff_db::system::SystemPathBuf;
9use ruff_python_ast::PythonVersion;
10use ruff_text_size::TextRange;
11
12/// The source of the Python version.
13#[derive(Clone, Debug, Eq, PartialEq, Hash, Default, get_size2::GetSize)]
14pub enum PythonVersionSource {
15 /// Value loaded from a project's configuration file.
16 ConfigFile(PythonVersionFileSource),
17
18 /// Value configured in a standalone script's inline metadata.
19 ScriptMetadata(Span),
20
21 /// Value loaded from the `pyvenv.cfg` file of the virtual environment.
22 /// The virtual environment might have been configured, activated or inferred.
23 PyvenvCfgFile(PythonVersionFileSource),
24
25 /// Value inferred from the layout of the Python installation.
26 ///
27 /// This only ever applies on Unix. On Unix, the `site-packages` directory
28 /// will always be at `sys.prefix/lib/pythonX.Y/site-packages`,
29 /// so we can infer the Python version from the parent directory of `site-packages`.
30 InstallationDirectoryLayout {
31 site_packages_parent_dir: Box<str>,
32 source: Option<PythonVersionFileSource>,
33 },
34
35 /// The value comes from a CLI argument, while it's left open if specified using a short argument,
36 /// long argument (`--extra-paths`) or `--config key=value`.
37 Cli,
38
39 /// The value comes from the user's editor,
40 /// while it's left open if specified as a setting
41 /// or if the value was auto-discovered by the editor
42 /// (e.g., the Python environment)
43 Editor,
44
45 /// The value was provided by `uv workspace metadata`.
46 UvWorkspace,
47
48 /// We fell back to a default value because the value was not specified via the CLI or a config file.
49 #[default]
50 Default,
51}
52
53/// Information regarding the file and [`TextRange`] of the configuration
54/// from which we inferred the Python version.
55#[derive(Debug, PartialEq, Eq, Hash, Clone, get_size2::GetSize)]
56pub struct PythonVersionFileSource {
57 path: Arc<SystemPathBuf>,
58 range: Option<TextRange>,
59}
60
61impl PythonVersionFileSource {
62 pub fn new(path: Arc<SystemPathBuf>, range: Option<TextRange>) -> Self {
63 Self { path, range }
64 }
65
66 /// Attempt to resolve a [`Span`] that corresponds to the location of
67 /// the configuration setting that specified the Python version.
68 ///
69 /// Useful for subdiagnostics when informing the user
70 /// what the inferred Python version of their project is.
71 pub fn span(&self, db: &dyn Db) -> Option<Span> {
72 let file = system_path_to_file(db, &*self.path).ok()?;
73 Some(Span::from(file).with_optional_range(self.range))
74 }
75}
76
77/// A Python version with its source.
78#[derive(Eq, PartialEq, Hash, Debug, Clone, get_size2::GetSize)]
79pub struct PythonVersionWithSource {
80 pub version: PythonVersion,
81 pub source: PythonVersionSource,
82}
83
84impl Default for PythonVersionWithSource {
85 fn default() -> Self {
86 Self {
87 version: PythonVersion::latest_ty(),
88 source: PythonVersionSource::Default,
89 }
90 }
91}