From ae2ceab7ab66033c1e6d6db3fe6907339f260bd9 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 29 Mar 2026 00:52:34 +0900 Subject: [PATCH] Fix `inspect.getsource` returning truncated source for multi-line function definitions (#7519) * Initial plan * fix: restore def-line source range before entering function scope so co_firstlineno is correct Agent-Logs-Url: https://github.com/RustPython/RustPython/sessions/94701403-2011-4525-88f1-6e06891da6a4 Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com> * fix: remove pre-existing expectedFailure decorators from test_gettext plural form tests Agent-Logs-Url: https://github.com/RustPython/RustPython/sessions/ce27bf53-569f-45a0-ad5a-08e8f322c717 Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com> * remove extra_tests/snippets/inspect_getsource.py (covered by test_inspect) Agent-Logs-Url: https://github.com/RustPython/RustPython/sessions/2b64da1b-8aab-4fec-8b28-3a21d46ac2f9 Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com> --- Lib/test/test_gettext.py | 7 ------- Lib/test/test_inspect/test_inspect.py | 2 -- crates/codegen/src/compile.rs | 8 ++++++++ 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_gettext.py b/Lib/test/test_gettext.py index f40690829..9ad37909a 100644 --- a/Lib/test/test_gettext.py +++ b/Lib/test/test_gettext.py @@ -385,7 +385,6 @@ class PluralFormsTests: x = ngettext(singular, plural, None) self.assertEqual(x, tplural) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_plural_forms(self): self._test_plural_forms( self.ngettext, self.gettext, @@ -396,7 +395,6 @@ class PluralFormsTests: '%d file deleted', '%d files deleted', '%d file deleted', '%d files deleted') - @unittest.expectedFailure # TODO: RUSTPYTHON def test_plural_context_forms(self): ngettext = partial(self.npgettext, 'With context') gettext = partial(self.pgettext, 'With context') @@ -409,7 +407,6 @@ class PluralFormsTests: '%d file deleted', '%d files deleted', '%d file deleted', '%d files deleted') - @unittest.expectedFailure # TODO: RUSTPYTHON def test_plural_wrong_context_forms(self): self._test_plural_forms( partial(self.npgettext, 'Unknown context'), @@ -442,7 +439,6 @@ class GNUTranslationsWithDomainPluralFormsTestCase(PluralFormsTests, GettextBase self.pgettext = partial(gettext.dpgettext, 'gettext') self.npgettext = partial(gettext.dnpgettext, 'gettext') - @unittest.expectedFailure # TODO: RUSTPYTHON def test_plural_forms_wrong_domain(self): self._test_plural_forms( partial(gettext.dngettext, 'unknown'), @@ -451,7 +447,6 @@ class GNUTranslationsWithDomainPluralFormsTestCase(PluralFormsTests, GettextBase 'There is %s file', 'There are %s files', numbers_only=False) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_plural_context_forms_wrong_domain(self): self._test_plural_forms( partial(gettext.dnpgettext, 'unknown', 'With context'), @@ -472,7 +467,6 @@ class GNUTranslationsClassPluralFormsTestCase(PluralFormsTests, GettextBaseTest) self.pgettext = t.pgettext self.npgettext = t.npgettext - @unittest.expectedFailure # TODO: RUSTPYTHON def test_plural_forms_null_translations(self): t = gettext.NullTranslations() self._test_plural_forms( @@ -481,7 +475,6 @@ class GNUTranslationsClassPluralFormsTestCase(PluralFormsTests, GettextBaseTest) 'There is %s file', 'There are %s files', numbers_only=False) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_plural_context_forms_null_translations(self): t = gettext.NullTranslations() self._test_plural_forms( diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 456d94534..ea2ce84a0 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -961,7 +961,6 @@ class TestGettingSourceOfToplevelFrames(GetSourceBase): class TestDecorators(GetSourceBase): fodderModule = mod2 - @unittest.expectedFailure # TODO: RUSTPYTHON; pass def test_wrapped_decorator(self): self.assertSourceEqual(mod2.wrapped, 14, 17) @@ -1259,7 +1258,6 @@ class TestNoEOL(GetSourceBase): class TestComplexDecorator(GetSourceBase): fodderModule = mod2 - @unittest.expectedFailure # TODO: RUSTPYTHON; return foo + bar() def test_parens_in_decorator(self): self.assertSourceEqual(self.fodderModule.complex_decorated, 273, 275) diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index 92b959deb..3b94a36b3 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -4246,11 +4246,19 @@ impl Compiler { is_async: bool, type_params: Option<&ast::TypeParams>, ) -> CompileResult<()> { + // Save the source range of the `def` line before compiling decorators/defaults, + // so that the function code object gets the correct co_firstlineno. + let def_source_range = self.current_source_range; + self.prepare_decorators(decorator_list)?; // compile defaults and return funcflags let funcflags = self.compile_default_arguments(parameters)?; + // Restore the `def` line range so that enter_function → push_output → get_source_line_number() + // records the `def` keyword's line as co_firstlineno, not the last default-argument line. + self.set_source_range(def_source_range); + let is_generic = type_params.is_some(); let mut num_typeparam_args = 0;