Skip to content

Commit 21bacec

Browse files
committed
Simplifications thanks to Micha
1 parent 45e067a commit 21bacec

File tree

2 files changed

+29
-25
lines changed

2 files changed

+29
-25
lines changed

crates/ty/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ wild = { workspace = true }
4141
ruff_db = { workspace = true, features = ["testing"] }
4242
ruff_python_trivia = { workspace = true }
4343

44-
dunce ={ workspace = true}
44+
dunce = { workspace = true }
4545
insta = { workspace = true, features = ["filters"] }
4646
insta-cmd = { workspace = true }
4747
filetime = { workspace = true }

crates/ty_python_semantic/src/site_packages.rs

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ System site-packages will not be used for module resolution.",
382382
/// See also: <https://snarky.ca/how-virtual-environments-work/>
383383
#[derive(Debug)]
384384
struct PyvenvCfgParser<'s> {
385+
source: &'s str,
385386
cursor: Cursor<'s>,
386387
line_number: NonZeroUsize,
387388
data: RawPyvenvCfg<'s>,
@@ -390,6 +391,7 @@ struct PyvenvCfgParser<'s> {
390391
impl<'s> PyvenvCfgParser<'s> {
391392
fn new(source: &'s str) -> Self {
392393
Self {
394+
source,
393395
cursor: Cursor::new(source),
394396
line_number: NonZeroUsize::new(1).unwrap(),
395397
data: RawPyvenvCfg::default(),
@@ -409,6 +411,7 @@ impl<'s> PyvenvCfgParser<'s> {
409411
/// to the beginning of the next line.
410412
fn parse_line(&mut self) -> Result<(), PyvenvCfgParseErrorKind> {
411413
let PyvenvCfgParser {
414+
source,
412415
cursor,
413416
line_number,
414417
data,
@@ -418,35 +421,24 @@ impl<'s> PyvenvCfgParser<'s> {
418421

419422
cursor.eat_while(|c| c.is_whitespace() && c != '\n');
420423

421-
let remaining_file = cursor.chars().as_str();
424+
let key_start = cursor.offset();
425+
cursor.eat_while(|c| !matches!(c, '\n' | '='));
426+
let key_end = cursor.offset();
422427

423-
let next_newline_position = remaining_file
424-
.find('\n')
425-
.unwrap_or(cursor.text_len().to_usize());
426-
427-
// The Python standard-library's `site` module parses these files by splitting each line on
428-
// '=' characters, so that's what we should do as well.
429-
let Some(eq_position) = remaining_file[..next_newline_position].find('=') else {
428+
if !cursor.eat_char('=') {
430429
// Skip over any lines that do not contain '=' characters, same as the CPython stdlib
431430
// <https://github.com/python/cpython/blob/e64395e8eb8d3a9e35e3e534e87d427ff27ab0a5/Lib/site.py#L625-L632>
432-
cursor.skip_bytes(next_newline_position);
433-
434-
debug_assert!(
435-
matches!(cursor.first(), '\n' | ruff_python_trivia::EOF_CHAR,),
436-
"{}",
437-
cursor.first()
438-
);
439-
440431
cursor.eat_char('\n');
441432
return Ok(());
442-
};
433+
}
443434

444-
let key = remaining_file[..eq_position].trim();
435+
let key = source[TextRange::new(key_start, key_end)].trim();
445436

446-
cursor.skip_bytes(eq_position + 1);
447437
cursor.eat_while(|c| c.is_whitespace() && c != '\n');
448-
449-
let value = remaining_file[eq_position + 1..next_newline_position].trim();
438+
let value_start = cursor.offset();
439+
cursor.eat_while(|c| c != '\n');
440+
let value = source[TextRange::new(value_start, cursor.offset())].trim();
441+
cursor.eat_char('\n');
450442

451443
if value.is_empty() {
452444
return Err(PyvenvCfgParseErrorKind::MalformedKeyValuePair { line_number });
@@ -460,7 +452,7 @@ impl<'s> PyvenvCfgParser<'s> {
460452
// `virtualenv` and `uv` call this key `version_info`,
461453
// but the stdlib venv module calls it `version`
462454
"version" | "version_info" => {
463-
let version_range = TextRange::at(cursor.offset(), value.text_len());
455+
let version_range = TextRange::at(value_start, value.text_len());
464456
data.version = Some((value, version_range));
465457
}
466458
"implementation" => {
@@ -479,8 +471,6 @@ impl<'s> PyvenvCfgParser<'s> {
479471
_ => {}
480472
}
481473

482-
cursor.eat_while(|c| c != '\n');
483-
cursor.eat_char('\n');
484474
Ok(())
485475
}
486476
}
@@ -1581,4 +1571,18 @@ mod tests {
15811571
assert_eq!(&pyvenv_cfg[version.1], version.0);
15821572
assert_eq!(parsed.implementation, PythonImplementation::PyPy);
15831573
}
1574+
1575+
#[test]
1576+
fn pyvenv_cfg_with_strange_whitespace_parses() {
1577+
let pyvenv_cfg = " home= /a path with whitespace/python\t \t \nversion_info = 3.13 \n\n\n\nimplementation =PyPy";
1578+
let parsed = PyvenvCfgParser::new(pyvenv_cfg).parse().unwrap();
1579+
assert_eq!(
1580+
parsed.base_executable_home_path,
1581+
Some("/a path with whitespace/python")
1582+
);
1583+
let version = parsed.version.unwrap();
1584+
assert_eq!(version.0, "3.13");
1585+
assert_eq!(&pyvenv_cfg[version.1], version.0);
1586+
assert_eq!(parsed.implementation, PythonImplementation::PyPy);
1587+
}
15841588
}

0 commit comments

Comments
 (0)