Skip to content

Commit 72ff26a

Browse files
committed
[ty] Infer the Python version from the environment if feasible
1 parent 2eb2d53 commit 72ff26a

File tree

13 files changed

+85
-58
lines changed

13 files changed

+85
-58
lines changed

crates/ruff_graph/src/db.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ impl ModuleDb {
4444
Program::from_settings(
4545
&db,
4646
ProgramSettings {
47-
python_version,
47+
python_version: Some(python_version),
4848
python_platform: PythonPlatform::default(),
4949
search_paths,
5050
},

crates/ty_ide/src/inlay_hints.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ mod tests {
191191
Program::from_settings(
192192
&db,
193193
ProgramSettings {
194-
python_version: PythonVersion::latest_ty(),
194+
python_version: Some(PythonVersion::latest_ty()),
195195
python_platform: PythonPlatform::default(),
196196
search_paths: SearchPathSettings {
197197
extra_paths: vec![],

crates/ty_ide/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ mod tests {
228228
Program::from_settings(
229229
&db,
230230
ProgramSettings {
231-
python_version: PythonVersion::latest_ty(),
231+
python_version: Some(PythonVersion::latest_ty()),
232232
python_platform: PythonPlatform::default(),
233233
search_paths: SearchPathSettings {
234234
extra_paths: vec![],

crates/ty_project/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ mod tests {
679679
Program::from_settings(
680680
&db,
681681
ProgramSettings {
682-
python_version: PythonVersion::default(),
682+
python_version: Some(PythonVersion::default()),
683683
python_platform: PythonPlatform::default(),
684684
search_paths: SearchPathSettings::new(vec![SystemPathBuf::from(".")]),
685685
},

crates/ty_project/src/metadata/options.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ impl Options {
104104
default
105105
});
106106
ProgramSettings {
107-
python_version,
107+
python_version: Some(python_version),
108108
python_platform,
109109
search_paths: self.to_search_path_settings(project_root, system),
110110
}

crates/ty_python_semantic/src/db.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ pub(crate) mod tests {
179179
Program::from_settings(
180180
&db,
181181
ProgramSettings {
182-
python_version: self.python_version,
182+
python_version: Some(self.python_version),
183183
python_platform: self.python_platform,
184184
search_paths: SearchPathSettings::new(vec![src_root]),
185185
},

crates/ty_python_semantic/src/module_resolver/resolver.rs

Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use ruff_python_ast::PythonVersion;
1414
use crate::db::Db;
1515
use crate::module_name::ModuleName;
1616
use crate::module_resolver::typeshed::{vendored_typeshed_versions, TypeshedVersions};
17-
use crate::site_packages::{PythonEnvironment, SitePackagesDiscoveryError, SysPrefixPathOrigin};
17+
use crate::site_packages::{PythonEnvironment, SysPrefixPathOrigin};
1818
use crate::{Program, PythonPath, SearchPathSettings};
1919

2020
use super::module::{Module, ModuleKind};
@@ -160,6 +160,11 @@ pub struct SearchPaths {
160160
site_packages: Vec<SearchPath>,
161161

162162
typeshed_versions: TypeshedVersions,
163+
164+
/// The Python version for the search paths, if any.
165+
///
166+
/// This is read from the `pyvenv.cfg` if present.
167+
python_version: Option<PythonVersion>,
163168
}
164169

165170
impl SearchPaths {
@@ -234,7 +239,7 @@ impl SearchPaths {
234239

235240
static_paths.push(stdlib_path);
236241

237-
let site_packages_paths = match python_path {
242+
let (site_packages_paths, python_version) = match python_path {
238243
PythonPath::SysPrefix(sys_prefix, origin) => {
239244
tracing::debug!(
240245
"Discovering site-packages paths from sys-prefix `{sys_prefix}` ({origin}')"
@@ -243,8 +248,9 @@ impl SearchPaths {
243248
// than the one resolved in the program settings because it indicates
244249
// that the `target-version` is incorrectly configured or that the
245250
// venv is out of date.
246-
PythonEnvironment::new(sys_prefix, *origin, system)
247-
.and_then(|env| env.site_packages_directories(system))?
251+
PythonEnvironment::new(sys_prefix.clone(), *origin, system).and_then(|env| {
252+
Ok((env.site_packages_directories(system)?, env.python_version()))
253+
})?
248254
}
249255

250256
PythonPath::Resolve(target, origin) => {
@@ -270,45 +276,47 @@ impl SearchPaths {
270276
// handle the error.
271277
.unwrap_or(target);
272278

273-
PythonEnvironment::new(root, *origin, system)
274-
.and_then(|venv| venv.site_packages_directories(system))?
279+
PythonEnvironment::new(root, *origin, system).and_then(|env| {
280+
Ok((env.site_packages_directories(system)?, env.python_version()))
281+
})?
275282
}
276283

277284
PythonPath::Discover(root) => {
278285
tracing::debug!("Discovering virtual environment in `{root}`");
279-
let virtual_env_path = discover_venv_in(db.system(), root);
280-
if let Some(virtual_env_path) = virtual_env_path {
281-
tracing::debug!("Found `.venv` folder at `{}`", virtual_env_path);
282-
283-
let handle_invalid_virtual_env = |error: SitePackagesDiscoveryError| {
284-
tracing::debug!(
285-
"Ignoring automatically detected virtual environment at `{}`: {}",
286-
virtual_env_path,
287-
error
288-
);
289-
vec![]
290-
};
291-
292-
match PythonEnvironment::new(
293-
virtual_env_path.clone(),
294-
SysPrefixPathOrigin::LocalVenv,
295-
system,
296-
) {
297-
Ok(venv) => venv
298-
.site_packages_directories(system)
299-
.unwrap_or_else(handle_invalid_virtual_env),
300-
Err(error) => handle_invalid_virtual_env(error),
301-
}
302-
} else {
303-
tracing::debug!("No virtual environment found");
304-
vec![]
305-
}
286+
discover_venv_in(db.system(), root)
287+
.and_then(|virtual_env_path| {
288+
tracing::debug!("Found `.venv` folder at `{}`", virtual_env_path);
289+
290+
PythonEnvironment::new(
291+
virtual_env_path.clone(),
292+
SysPrefixPathOrigin::LocalVenv,
293+
system,
294+
)
295+
.and_then(|env| {
296+
Ok((env.site_packages_directories(system)?, env.python_version()))
297+
})
298+
.inspect_err(|err| {
299+
tracing::debug!(
300+
"Ignoring automatically detected virtual environment at `{}`: {}",
301+
virtual_env_path,
302+
err
303+
);
304+
})
305+
.ok()
306+
})
307+
.unwrap_or_else(|| {
308+
tracing::debug!("No virtual environment found");
309+
(vec![], None)
310+
})
306311
}
307312

308-
PythonPath::KnownSitePackages(paths) => paths
309-
.iter()
310-
.map(|path| canonicalize(path, system))
311-
.collect(),
313+
PythonPath::KnownSitePackages(paths) => (
314+
paths
315+
.iter()
316+
.map(|path| canonicalize(path, system))
317+
.collect(),
318+
None,
319+
),
312320
};
313321

314322
let mut site_packages: Vec<_> = Vec::with_capacity(site_packages_paths.len());
@@ -342,6 +350,7 @@ impl SearchPaths {
342350
static_paths,
343351
site_packages,
344352
typeshed_versions,
353+
python_version,
345354
})
346355
}
347356

@@ -366,6 +375,10 @@ impl SearchPaths {
366375
pub(super) fn typeshed_versions(&self) -> &TypeshedVersions {
367376
&self.typeshed_versions
368377
}
378+
379+
pub fn python_version(&self) -> Option<PythonVersion> {
380+
self.python_version
381+
}
369382
}
370383

371384
/// Collect all dynamic search paths. For each `site-packages` path:
@@ -384,6 +397,7 @@ pub(crate) fn dynamic_resolution_paths(db: &dyn Db) -> Vec<SearchPath> {
384397
static_paths,
385398
site_packages,
386399
typeshed_versions: _,
400+
python_version: _,
387401
} = Program::get(db).search_paths(db);
388402

389403
let mut dynamic_paths = Vec::new();
@@ -1489,7 +1503,7 @@ mod tests {
14891503
Program::from_settings(
14901504
&db,
14911505
ProgramSettings {
1492-
python_version: PythonVersion::PY38,
1506+
python_version: Some(PythonVersion::PY38),
14931507
python_platform: PythonPlatform::default(),
14941508
search_paths: SearchPathSettings {
14951509
extra_paths: vec![],
@@ -1995,7 +2009,7 @@ not_a_directory
19952009
Program::from_settings(
19962010
&db,
19972011
ProgramSettings {
1998-
python_version: PythonVersion::default(),
2012+
python_version: Some(PythonVersion::default()),
19992013
python_platform: PythonPlatform::default(),
20002014
search_paths: SearchPathSettings {
20012015
extra_paths: vec![],
@@ -2068,7 +2082,7 @@ not_a_directory
20682082
Program::from_settings(
20692083
&db,
20702084
ProgramSettings {
2071-
python_version: PythonVersion::default(),
2085+
python_version: Some(PythonVersion::default()),
20722086
python_platform: PythonPlatform::default(),
20732087
search_paths: SearchPathSettings {
20742088
extra_paths: vec![],
@@ -2108,7 +2122,7 @@ not_a_directory
21082122
Program::from_settings(
21092123
&db,
21102124
ProgramSettings {
2111-
python_version: PythonVersion::default(),
2125+
python_version: Some(PythonVersion::default()),
21122126
python_platform: PythonPlatform::default(),
21132127
search_paths: SearchPathSettings {
21142128
extra_paths: vec![],

crates/ty_python_semantic/src/module_resolver/testing.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ impl TestCaseBuilder<MockedTypeshed> {
235235
Program::from_settings(
236236
&db,
237237
ProgramSettings {
238-
python_version,
238+
python_version: Some(python_version),
239239
python_platform,
240240
search_paths: SearchPathSettings {
241241
extra_paths: vec![],
@@ -293,7 +293,7 @@ impl TestCaseBuilder<VendoredTypeshed> {
293293
Program::from_settings(
294294
&db,
295295
ProgramSettings {
296-
python_version,
296+
python_version: Some(python_version),
297297
python_platform,
298298
search_paths: SearchPathSettings {
299299
python_path: PythonPath::KnownSitePackages(vec![site_packages.clone()]),

crates/ty_python_semantic/src/program.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,15 @@ impl Program {
2828
search_paths,
2929
} = settings;
3030

31-
tracing::info!("Python version: Python {python_version}, platform: {python_platform}");
32-
3331
let search_paths = SearchPaths::from_settings(db, &search_paths)
3432
.with_context(|| "Invalid search path settings")?;
3533

34+
let python_version = python_version
35+
.or_else(|| search_paths.python_version())
36+
.unwrap_or(PythonVersion::latest_ty());
37+
38+
tracing::info!("Python version: Python {python_version}, platform: {python_platform}");
39+
3640
Ok(
3741
Program::builder(python_version, python_platform, search_paths)
3842
.durability(Durability::HIGH)
@@ -56,9 +60,11 @@ impl Program {
5660
self.set_python_platform(db).to(python_platform);
5761
}
5862

59-
if python_version != self.python_version(db) {
60-
tracing::debug!("Updating python version: Python {python_version}");
61-
self.set_python_version(db).to(python_version);
63+
if let Some(python_version) = python_version {
64+
if python_version != self.python_version(db) {
65+
tracing::debug!("Updating python version: Python {python_version}");
66+
self.set_python_version(db).to(python_version);
67+
}
6268
}
6369

6470
self.update_search_paths(db, &search_paths)?;
@@ -89,7 +95,7 @@ impl Program {
8995
#[derive(Clone, Debug, Eq, PartialEq)]
9096
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
9197
pub struct ProgramSettings {
92-
pub python_version: PythonVersion,
98+
pub python_version: Option<PythonVersion>,
9399
pub python_platform: PythonPlatform,
94100
pub search_paths: SearchPathSettings,
95101
}

crates/ty_python_semantic/src/site_packages.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ impl PythonEnvironment {
6060
Self::System(env) => env.site_packages_directories(system),
6161
}
6262
}
63+
64+
pub(crate) fn python_version(&self) -> Option<PythonVersion> {
65+
match self {
66+
Self::Virtual(env) => env.version,
67+
Self::System(_) => None,
68+
}
69+
}
6370
}
6471

6572
/// Abstraction for a Python virtual environment.

crates/ty_test/src/assertion.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ mod tests {
500500
let mut db = Db::setup();
501501

502502
let settings = ProgramSettings {
503-
python_version: PythonVersion::default(),
503+
python_version: Some(PythonVersion::default()),
504504
python_platform: PythonPlatform::default(),
505505
search_paths: SearchPathSettings::new(Vec::new()),
506506
};

crates/ty_test/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ fn run_test(
257257
let configuration = test.configuration();
258258

259259
let settings = ProgramSettings {
260-
python_version,
260+
python_version: Some(python_version),
261261
python_platform: configuration
262262
.python_platform()
263263
.unwrap_or(PythonPlatform::Identifier("linux".to_string())),

crates/ty_test/src/matcher.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ mod tests {
384384
let mut db = crate::db::Db::setup();
385385

386386
let settings = ProgramSettings {
387-
python_version: PythonVersion::default(),
387+
python_version: Some(PythonVersion::default()),
388388
python_platform: PythonPlatform::default(),
389389
search_paths: SearchPathSettings::new(Vec::new()),
390390
};

0 commit comments

Comments
 (0)