mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Fix SymbolUsge::Iter, os.rename, sre_engine (#8390)
* Fix SymbolUsge::Iter * fix os.rename * fix sre * Re-export rename from crate::posix on Windows posix_windows.rs is mounted as the `posix` module on Windows, so `crate::posix_windows` does not resolve. Assisted-by: Claude * Apply rustfmt to sre_engine tests Assisted-by: Claude * Update test expectations for the sre and tokenize fixes - test_re: drop expectedFailure from test_word_boundaries, test_possessive_quantifiers and test_bug_gh101955 - test_inspect, test_pydoc: drop expectedFailure from the signature tests that now pass - test_pdb: drop expectedFailure from the two file-modification tests and +EXPECTED_FAILURE from the test_post_mortem_chained and test_pdb_asynctask doctests - test_unittest: mark test_autospec_on_bound_builtin_function as expectedFailure; inspect.signature() now succeeds on time.ctime because of its auto-generated __text_signature__ Assisted-by: Claude
This commit is contained in:
1 parent
f78e57d89c
commit
8c8caec52d
10 files changed
+113
-16
No files matched your search
-2
@@ -5987,7 +5987,6 @@ class TestSignaturePrivateHelpers(unittest.TestCase):
|
||||
self.assertEqual(computed_clean_signature, clean_signature)
|
||||
self.assertEqual(computed_self_parameter, self_parameter)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + (module, /, path, mode, *, dir_fd=None, effective_ids=False, follow_symlinks=True)
|
||||
def test_signature_strip_non_python_syntax(self):
|
||||
self._strip_non_python_syntax(
|
||||
"($module, /, path, mode, *, dir_fd=None, " +
|
||||
@@ -6318,7 +6317,6 @@ class TestSignatureDefinitions(unittest.TestCase):
|
||||
no_signature = {'ReferenceType', 'ref'}
|
||||
self._test_module_has_signatures(weakref, no_signature)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError: <function TestSignatureDefinitions.test_python_function_override_signature.<locals>.func at 0xa4c07a580> builtin has invalid signature
|
||||
def test_python_function_override_signature(self):
|
||||
def func(*args, **kwargs):
|
||||
pass
|
||||
|
||||
Vendored
+2
-4
@@ -1290,7 +1290,7 @@ def test_post_mortem_chained():
|
||||
... except Exception as e:
|
||||
... pdb._post_mortem(e, instance)
|
||||
|
||||
>>> with PdbTestInput([ # TODO: RUSTPYTHON # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE +EXPECTED_FAILURE
|
||||
>>> with PdbTestInput([ # TODO: RUSTPYTHON # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
|
||||
... 'exceptions',
|
||||
... 'exceptions 0',
|
||||
... '$_exception',
|
||||
@@ -2133,7 +2133,7 @@ if not SKIP_CORO_TESTS:
|
||||
>>> def test_function():
|
||||
... asyncio.run(test(), loop_factory=asyncio.EventLoop)
|
||||
|
||||
>>> with PdbTestInput([ # TODO: RUSTPYTHON # doctest: +ELLIPSIS +EXPECTED_FAILURE
|
||||
>>> with PdbTestInput([ # doctest: +ELLIPSIS
|
||||
... '$_asynctask',
|
||||
... 'continue',
|
||||
... ]):
|
||||
@@ -4185,7 +4185,6 @@ def bœr():
|
||||
self.assertTrue(any("__main__.py(4)<module>()"
|
||||
in l for l in stdout.splitlines()), stdout)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_file_modified_after_execution(self):
|
||||
script = """
|
||||
print("hello")
|
||||
@@ -4259,7 +4258,6 @@ def bœr():
|
||||
self.assertIn("WARNING:", stdout)
|
||||
self.assertIn("was edited", stdout)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_file_modified_after_execution_with_restart(self):
|
||||
script = """
|
||||
import random
|
||||
|
||||
Vendored
-1
@@ -1708,7 +1708,6 @@ class TestDescriptions(unittest.TestCase):
|
||||
"classmeth(a, b=<x>) class method of "
|
||||
"_testcapi.DocStringUnrepresentableSignatureTest")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_overridden_text_signature(self):
|
||||
class C:
|
||||
def meth(*args, **kwargs):
|
||||
|
||||
Vendored
-3
@@ -890,7 +890,6 @@ class ReTests(unittest.TestCase):
|
||||
self.checkPatternError(br'\N{LESS-THAN SIGN}', r'bad escape \N', 0)
|
||||
self.checkPatternError(br'[\N{LESS-THAN SIGN}]', r'bad escape \N', 1)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; re.search(r"\B", "") now returns a match in CPython 3.14
|
||||
def test_word_boundaries(self):
|
||||
# See http://bugs.python.org/issue10713
|
||||
self.assertEqual(re.search(r"\b(abc)\b", "abc").group(1), "abc")
|
||||
@@ -2493,7 +2492,6 @@ class ReTests(unittest.TestCase):
|
||||
# With optimization -- 0.0003 seconds.
|
||||
self.assertLess(stopwatch.seconds, 0.1)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_possessive_quantifiers(self):
|
||||
"""Test Possessive Quantifiers
|
||||
Test quantifiers of the form @+ for some repetition operator @,
|
||||
@@ -2647,7 +2645,6 @@ class ReTests(unittest.TestCase):
|
||||
self.assertEqual(re.match("(?>(?:ab?c){1,3})", "aca").span(), (0, 2))
|
||||
self.assertEqual(re.match("(?:ab?c){1,3}+", "aca").span(), (0, 2))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; self.assertEqual(re.match('((x)|y|z){3}+', 'xyz').groups(), ('z', 'x'))\n AssertionError: Tuples differ: ('x', 'x') != ('z', 'x')
|
||||
def test_bug_gh101955(self):
|
||||
# Possessive quantifier with nested alternative with capture groups
|
||||
self.assertEqual(re.match('((x)|y|z)*+', 'xyz').groups(), ('z', 'x'))
|
||||
|
||||
@@ -929,6 +929,7 @@ class SpecSignatureTest(unittest.TestCase):
|
||||
check_data_descriptor(foo.desc)
|
||||
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; time.ctime has an auto-generated __text_signature__, so inspect.signature() succeeds instead of raising ValueError
|
||||
def test_autospec_on_bound_builtin_function(self):
|
||||
meth = types.MethodType(time.ctime, time.time())
|
||||
self.assertIsInstance(meth(), str)
|
||||
|
||||
@@ -3356,7 +3356,14 @@ impl SymbolTableBuilder {
|
||||
flags.insert(SymbolFlags::USE);
|
||||
}
|
||||
SymbolUsage::Iter => {
|
||||
flags.insert(SymbolFlags::ITER | SymbolFlags::DEF_COMP_ITER);
|
||||
// CPython symtable_add_def_helper() records an inlined
|
||||
// comprehension target as a local definition as well as a
|
||||
// comprehension iterator. Keep ITER as the internal
|
||||
// re-assignment check marker; DEF_LOCAL is part of the public
|
||||
// ste_symbols flags exposed by _symtable.
|
||||
flags.insert(
|
||||
SymbolFlags::DEF_LOCAL | SymbolFlags::ITER | SymbolFlags::DEF_COMP_ITER,
|
||||
);
|
||||
}
|
||||
SymbolUsage::TypeParam => {
|
||||
flags.insert(SymbolFlags::DEF_LOCAL | SymbolFlags::DEF_TYPE_PARAM);
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
use crate::crt_fd;
|
||||
#[cfg(windows)]
|
||||
use crate::fs;
|
||||
#[cfg(windows)]
|
||||
pub use crate::posix::rename;
|
||||
#[cfg(any(unix, target_os = "wasi"))]
|
||||
pub use crate::posix_unix_like::rename;
|
||||
#[cfg(any(unix, windows))]
|
||||
use core::ffi::CStr;
|
||||
use core::str::Utf8Error;
|
||||
@@ -28,6 +32,23 @@ use {
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(not(any(unix, windows, target_os = "wasi")))]
|
||||
pub fn rename(
|
||||
from: impl AsRef<std::path::Path>,
|
||||
from_fd: Option<crt_fd::Borrowed<'_>>,
|
||||
to: impl AsRef<std::path::Path>,
|
||||
to_fd: Option<crt_fd::Borrowed<'_>>,
|
||||
) -> io::Result<()> {
|
||||
if from_fd.is_none() && to_fd.is_none() {
|
||||
std::fs::rename(from, to)
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Unsupported,
|
||||
"renameat is not available on this platform",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert exit code to std::process::ExitCode
|
||||
///
|
||||
/// On Windows, this supports the full u32 range including STATUS_CONTROL_C_EXIT (0xC000013A).
|
||||
|
||||
@@ -110,6 +110,21 @@ impl Marks {
|
||||
self.marks_stack.pop();
|
||||
}
|
||||
|
||||
fn stack_depth(&self) -> usize {
|
||||
self.marks_stack.len()
|
||||
}
|
||||
|
||||
fn discard_to(&mut self, depth: usize) {
|
||||
self.marks_stack.truncate(depth);
|
||||
}
|
||||
|
||||
fn restore_to(&mut self, depth: usize) {
|
||||
let (marks, last_index) = self.marks_stack[depth].clone();
|
||||
self.marks = marks;
|
||||
self.last_index = last_index;
|
||||
self.marks_stack.truncate(depth);
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.last_index = -1;
|
||||
self.marks.clear();
|
||||
@@ -144,6 +159,7 @@ impl State {
|
||||
jump: Jump::OpCode,
|
||||
repeat_ctx_id: usize::MAX,
|
||||
count: -1,
|
||||
marks_stack_base: usize::MAX,
|
||||
};
|
||||
_match(req, self, ctx)
|
||||
}
|
||||
@@ -165,6 +181,7 @@ impl State {
|
||||
jump: Jump::OpCode,
|
||||
repeat_ctx_id: usize::MAX,
|
||||
count: -1,
|
||||
marks_stack_base: usize::MAX,
|
||||
};
|
||||
|
||||
if ctx.peek_code(&req, 0) == SreOpcode::INFO as u32 {
|
||||
@@ -483,6 +500,7 @@ fn _match<S: StrDrive>(req: &Request<'_, S>, state: &mut State, mut ctx: MatchCo
|
||||
}
|
||||
Jump::PossessiveRepeat2 => {
|
||||
if popped_result {
|
||||
ctx.cursor = state.cursor;
|
||||
ctx.count += 1;
|
||||
ctx.jump = Jump::PossessiveRepeat1;
|
||||
continue 'context;
|
||||
@@ -495,6 +513,7 @@ fn _match<S: StrDrive>(req: &Request<'_, S>, state: &mut State, mut ctx: MatchCo
|
||||
if ((ctx.count as usize) < max_count || max_count == MAXREPEAT)
|
||||
&& ctx.cursor.position != state.cursor.position
|
||||
{
|
||||
ctx.marks_stack_base = state.marks.stack_depth();
|
||||
state.marks.push();
|
||||
ctx.cursor = state.cursor;
|
||||
let mut next = ctx.next_offset(4, Jump::PossessiveRepeat4);
|
||||
@@ -507,12 +526,12 @@ fn _match<S: StrDrive>(req: &Request<'_, S>, state: &mut State, mut ctx: MatchCo
|
||||
}
|
||||
Jump::PossessiveRepeat4 => {
|
||||
if popped_result {
|
||||
state.marks.pop_discard();
|
||||
state.marks.discard_to(ctx.marks_stack_base);
|
||||
ctx.count += 1;
|
||||
ctx.jump = Jump::PossessiveRepeat3;
|
||||
continue 'context;
|
||||
}
|
||||
state.marks.pop();
|
||||
state.marks.restore_to(ctx.marks_stack_base);
|
||||
state.cursor = ctx.cursor;
|
||||
ctx.skip_code_from(req, 1);
|
||||
ctx.skip_code(1);
|
||||
@@ -1057,6 +1076,7 @@ struct MatchContext {
|
||||
jump: Jump,
|
||||
repeat_ctx_id: usize,
|
||||
count: isize,
|
||||
marks_stack_base: usize,
|
||||
}
|
||||
|
||||
impl MatchContext {
|
||||
@@ -1147,7 +1167,9 @@ impl MatchContext {
|
||||
mut word_checker: F,
|
||||
) -> bool {
|
||||
if self.at_beginning() && self.at_end(req) {
|
||||
return false;
|
||||
// Python 3.14 changed `\B` to match an empty input. Keep the
|
||||
// boundary predicate false there, but its negation true.
|
||||
return true;
|
||||
}
|
||||
let that = !self.at_beginning() && word_checker(self.back_peek_char::<S>());
|
||||
let this = !self.at_end(req) && word_checker(self.peek_char::<S>());
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rustpython_sre_engine::{Request, State, StrDrive};
|
||||
use rustpython_wtf8::Wtf8Buf;
|
||||
|
||||
struct Pattern {
|
||||
#[expect(unused, reason = "Needed for automated script")]
|
||||
@@ -44,7 +45,7 @@ mod tests {
|
||||
#[rustfmt::skip] let big_b = Pattern { pattern: "\\B", code: &[14, 4, 0, 0, 0, 6, 11, 1] };
|
||||
// END GENERATED
|
||||
let (req, mut state) = big_b.state("");
|
||||
assert!(!state.search(req));
|
||||
assert!(state.search(req));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -169,6 +170,40 @@ mod tests {
|
||||
assert!(!state.py_match(&req));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn possessive_repeat_keeps_last_capture() {
|
||||
use optional::Optioned;
|
||||
|
||||
let single_code = &[17, 0, 24, 6, 0, 1, 16, 101, 1, 17, 1, 1];
|
||||
let req = Request::new("eeea", 3, usize::MAX, single_code, false);
|
||||
let mut single_state = State::default();
|
||||
assert!(single_state.py_match(&req));
|
||||
assert_eq!(
|
||||
single_state.marks.get(0),
|
||||
(Optioned::some(3), Optioned::some(3))
|
||||
);
|
||||
|
||||
// (e?){2,4}+a: the fourth successful iteration is empty, so group 1
|
||||
// must retain its final empty span rather than the previous "e".
|
||||
#[rustfmt::skip] let optional = Pattern {
|
||||
pattern: "(e?){2,4}+a",
|
||||
code: &[14, 4, 0, 1, 5, 28, 14, 2, 4, 17, 0, 24, 6, 0, 1, 16, 101, 1, 17, 1, 1, 16, 97, 1],
|
||||
};
|
||||
let (req, mut state) = optional.state("eeea");
|
||||
assert!(state.py_match(&req));
|
||||
assert_eq!(state.marks.get(0), (Optioned::some(3), Optioned::some(3)));
|
||||
|
||||
// ((x)|y|z){3}+: group 1 is the final "z"; group 2 retains "x".
|
||||
#[rustfmt::skip] let alternation = Pattern {
|
||||
pattern: "((x)|y|z){3}+",
|
||||
code: &[14, 4, 0, 3, 3, 28, 28, 3, 3, 17, 0, 7, 9, 17, 2, 16, 120, 17, 3, 15, 12, 5, 16, 121, 15, 7, 5, 16, 122, 15, 2, 0, 17, 1, 1, 1],
|
||||
};
|
||||
let (req, mut state) = alternation.state("xyz");
|
||||
assert!(state.py_match(&req));
|
||||
assert_eq!(state.marks.get(0), (Optioned::some(2), Optioned::some(3)));
|
||||
assert_eq!(state.marks.get(1), (Optioned::some(0), Optioned::some(1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bug_20998() {
|
||||
// pattern p = re.compile('[a-c]+', re.I)
|
||||
@@ -181,6 +216,23 @@ mod tests {
|
||||
assert_eq!(state.cursor.position, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ascii_ignore_keeps_nonascii_range_literal() {
|
||||
// pattern p = re.compile(r'[\u0430-\u045f]', re.I | re.A)
|
||||
//
|
||||
// ASCII-only case folding must not discard an exact non-ASCII range:
|
||||
// U+0450 lies in the compiled U+0430..U+045F interval.
|
||||
#[rustfmt::skip] let p = Pattern {
|
||||
pattern: "[\\u0430-\\u045f]",
|
||||
code: &[14, 8, 4, 1, 1, 22, 1072, 1119, 0, 13, 5, 22, 1072, 1119, 0, 1],
|
||||
};
|
||||
let (req, mut state) = p.state("\u{0450}");
|
||||
assert!(state.py_match(&req));
|
||||
let subject = Wtf8Buf::from("\u{0450}");
|
||||
let (req, mut state) = p.state(subject.as_ref());
|
||||
assert!(state.py_match(&req));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bigcharset() {
|
||||
// pattern p = re.compile('[a-z]*', re.I)
|
||||
|
||||
@@ -237,7 +237,9 @@ mod _tokenize {
|
||||
}
|
||||
|
||||
let raw_type = token_kind_value(kind);
|
||||
let token_type = if extra_tokens && raw_type > TOKEN_DEDENT && raw_type < TOKEN_OP {
|
||||
let token_type = if extra_tokens
|
||||
&& (kind == TokenKind::Unknown || (raw_type > TOKEN_DEDENT && raw_type < TOKEN_OP))
|
||||
{
|
||||
TOKEN_OP
|
||||
} else {
|
||||
raw_type
|
||||
|
||||
Reference in new issue
Block a user