mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-29 03:04:38 +08:00
* Align compiler futures and annotations with CPython Handle Barry parsing consistently across AST, type-comment, REPL, and WASM paths. Preserve deferred annotation source and scope metadata, and make _symtable conversion cached and linear-time. Assisted-by: Codex:gpt-5.4 * Fix f-string debug ranges and update Barry/annotate test expectations Compute the debug f-string source range from the raw leading and trailing text instead of the comment-stripped text, so a comment inside a multi-line replacement field no longer shifts the emitted LOAD_CONST location. Restrict the obsolete-operator location shift to `<>` by also requiring a `>` at the reported offset; without it any `ExpectedExpression` that followed a `<` (`2 <;`) was reported one column to the left. Add `CodeFlags::FUTURE_MASK` and use it in builtins, the shell, and the WASM VM instead of repeating the eight-flag union, and collapse the duplicated Barry diagnostic sequences into `BarrySource::diagnostic`. Update the `__annotate__` disassembly snapshot to LOAD_FAST_BORROW and replace the RustPython-only `barry_as_FLUFL` co_flags assertion in extra_tests with the CPython-matching one. Assisted-by: Claude Code:claude-opus-5 * Restore the annotated function in __annotate__ qualnames Signature annotation scopes are compiled in the scope enclosing the annotated function, so the function name has to be folded back into the qualname: `f.__annotate__`, `C.m.__annotate__`, `outer.<locals>.inner.__annotate__`. test_type_annotations test_annotate_qualname covers this. test_pyrepl test_future_barry_as_flufl now passes, so drop its expectedFailure marker. Assisted-by: Claude Code:claude-opus-5 * Pass safepoints in the attached-thread interpreter tests `main_and_subinterpreter_run_sections_overlap` parked its workers on a condvar while attached, and `busy_main_interpreter_does_not_block_subinterpreter` looped over protocol calls that never reach a safepoint. An attached thread that never reaches a safepoint cannot be suspended, so a concurrent process-wide stop-the-world (from the gc test running in parallel) never completes and the already-stopped sibling interpreter stays stopped; the second worker then waits in `wait_while_suspended` until the 30s deadline. Both loops now call `vm.check_signals()` each iteration, and the condvar wait uses `wait_timeout` so the lock is released between safepoints. Reproduced on Linux (4 CPUs): the two tests with `--test-threads=2` failed 2/40 before and 0/40 after; the full `rustpython-vm` lib binary failed 1/30 before and 0/30 after. Assisted-by: Claude Code:claude-opus-5 * Report the obsolete `<>` operator from one place `<>` was located twice: `BarrySource` recorded the token ranges it rewrote to `!=`, and `cpython_parse_diagnostic_override` separately peeked at the bytes around an `ExpectedExpression` location. Both produced "invalid syntax" spanning the whole operator. `prepare_barry_as_flufl_source` now records the operator ranges in the non-Barry case too -- by plain text search, since nothing is rewritten there -- so `BarrySource::invalid_legacy_operator` covers both, and the byte-peeking helper is gone. It also defers to an unclosed bracket earlier in the source, which the diagnostic chain it used to sit in did for it. A `<>` that starts a statement now spans both characters: the parser stops at the `<` rather than one character in, which the byte peek did not handle. Assisted-by: Claude Code:claude-opus-5 * Drop a py object's payload before its class `PyInner` declares `typ` before `payload`, so `drop_in_place` dropped the class first and `PyAtomicRef::drop` left it null. A `PyWeak` payload is still linked into the weakref list of the object it points at until its own `Drop` unlinks it under the stripe lock, which happens after that. `WeakRefList::add` walks the same list looking for a proxy to reuse and reads `node.class()` off every node it passes, so it could dereference the null class of a node on its way out. `PyInner::dealloc` now runs the two destructors in the other order at all three of its exit paths, with a compile-time check that no other field has one. Reproduced by the new test in about 0.1s, as the panic CI hit in the c-api `weakrefobject::tests::new_proxy` run: panicked at crates/vm/src/object/ext.rs:312: unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached Assisted-by: Claude Code:claude-opus-5
66 lines
2.8 KiB
Python
Vendored
66 lines
2.8 KiB
Python
Vendored
import __future__
|
|
import unittest
|
|
|
|
|
|
class FLUFLTests(unittest.TestCase):
|
|
|
|
def test_barry_as_bdfl(self):
|
|
code = "from __future__ import barry_as_FLUFL\n2 {0} 3"
|
|
compile(code.format('<>'), '<BDFL test>', 'exec',
|
|
__future__.CO_FUTURE_BARRY_AS_BDFL)
|
|
with self.assertRaises(SyntaxError) as cm:
|
|
compile(code.format('!='), '<FLUFL test>', 'exec',
|
|
__future__.CO_FUTURE_BARRY_AS_BDFL)
|
|
self.assertRegex(str(cm.exception),
|
|
"with Barry as BDFL, use '<>' instead of '!='")
|
|
self.assertIn('2 != 3', cm.exception.text)
|
|
self.assertEqual(cm.exception.filename, '<FLUFL test>')
|
|
|
|
self.assertEqual(cm.exception.lineno, 2)
|
|
# The old parser reports the end of the token and the new
|
|
# parser reports the start of the token
|
|
self.assertEqual(cm.exception.offset, 3)
|
|
|
|
def test_guido_as_bdfl(self):
|
|
code = '2 {0} 3'
|
|
compile(code.format('!='), '<BDFL test>', 'exec')
|
|
with self.assertRaises(SyntaxError) as cm:
|
|
compile(code.format('<>'), '<FLUFL test>', 'exec')
|
|
self.assertRegex(str(cm.exception), "invalid syntax")
|
|
self.assertIn('2 <> 3', cm.exception.text)
|
|
self.assertEqual(cm.exception.filename, '<FLUFL test>')
|
|
self.assertEqual(cm.exception.lineno, 1)
|
|
# The old parser reports the end of the token and the new
|
|
# parser reports the start of the token
|
|
self.assertEqual(cm.exception.offset, 3)
|
|
|
|
def test_barry_as_bdfl_look_ma_with_no_compiler_flags(self):
|
|
# Check that the future import is handled by the parser
|
|
# even if the compiler flags are not passed.
|
|
code = "from __future__ import barry_as_FLUFL;2 {0} 3"
|
|
compile(code.format('<>'), '<BDFL test>', 'exec')
|
|
with self.assertRaises(SyntaxError) as cm:
|
|
compile(code.format('!='), '<FLUFL test>', 'exec')
|
|
self.assertRegex(str(cm.exception), "with Barry as BDFL, use '<>' instead of '!='")
|
|
self.assertIn('2 != 3', cm.exception.text)
|
|
self.assertEqual(cm.exception.filename, '<FLUFL test>')
|
|
self.assertEqual(cm.exception.lineno, 1)
|
|
self.assertEqual(cm.exception.offset, len(code) - 4)
|
|
|
|
def test_barry_as_bdfl_relative_import(self):
|
|
code = "from .__future__ import barry_as_FLUFL;2 {0} 3"
|
|
compile(code.format('!='), '<FLUFL test>', 'exec')
|
|
with self.assertRaises(SyntaxError) as cm:
|
|
compile(code.format('<>'), '<BDFL test>', 'exec')
|
|
self.assertRegex(str(cm.exception), "<BDFL test>")
|
|
self.assertIn('2 <> 3', cm.exception.text)
|
|
self.assertEqual(cm.exception.filename, '<BDFL test>')
|
|
self.assertEqual(cm.exception.lineno, 1)
|
|
self.assertEqual(cm.exception.offset, len(code) - 4)
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|