diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index a5d1aaeb5a946f4..02133ce022d79ea 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -2285,10 +2285,13 @@ def make_callback(var, config): def default_callback(*params): "Add config values to changes instance." value = var.get() - # A blanked int entry is an empty string; do not save it as an - # invalid config value (gh-83653). if value != '': changes.add_option(*config, value) + else: + # A blanked int entry: do not save an invalid value, and + # forget the value recorded while editing (gh-75487). + config_type, section, item = config + changes[config_type].get(section, {}).pop(item, None) return default_callback def attach(self): diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 367c63dc01126a3..5e209287a52d97c 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -1711,6 +1711,10 @@ def test_make_callback(self): sv.set('5') cb() self.assertEqual(changes['main']['section']['option'], '5') + # gh-75487: blanking the entry forgets the value recorded before. + sv.set('') + cb() + self.assertNotIn('option', changes['main']['section']) changes.clear() def test_attach_detach(self): diff --git a/Lib/idlelib/idle_test/test_pyparse.py b/Lib/idlelib/idle_test/test_pyparse.py index 384db566ac76cdb..601e8a93fdfa858 100644 --- a/Lib/idlelib/idle_test/test_pyparse.py +++ b/Lib/idlelib/idle_test/test_pyparse.py @@ -78,7 +78,7 @@ def char_in_string_false(index): return False ' b=True):\n' ' pass\n' ) - pos0, pos = 33, 42 # Start of 'class...', ' def' lines. + pos0, pos, pos1 = 33, 42, 94 # Start of 'class', 'def', 'pass' lines. # Passing no value or non-callable should fail (issue 32989). with self.assertRaises(TypeError): @@ -91,8 +91,8 @@ def char_in_string_false(index): return False self.assertIsNone(start(is_char_in_string=lambda index: True)) # Make all text look like it's not in a string. This means that it - # found a good start position. - eq(start(char_in_string_false), pos) + # found a good start position: the last statement. + eq(start(char_in_string_false), pos1) # If the beginning of the def line is not in a string, then it # returns that as the index. @@ -100,9 +100,9 @@ def char_in_string_false(index): return False # If the beginning of the def line is in a string, then it # looks for a previous index. eq(start(is_char_in_string=lambda index: index >= pos), pos0) - # If everything before the 'def' is in a string, then returns None. - # The non-continuation def line returns 44 (see below). - eq(start(is_char_in_string=lambda index: index < pos), None) + # If everything before the 'def' is in a string, then returns + # the start of the 'pass' line. + eq(start(is_char_in_string=lambda index: index < pos), pos1) # Code without extra line break in def line - mostly returns the same # values. @@ -111,12 +111,41 @@ def char_in_string_false(index): return False ' def __init__(self, a, b=True):\n' ' pass\n' ) # Does not affect class, def positions. - eq(start(char_in_string_false), pos) + pos1 = 77 # Start of 'pass' line. + eq(start(char_in_string_false), pos1) eq(start(is_char_in_string=lambda index: index > pos), pos) eq(start(is_char_in_string=lambda index: index >= pos), pos0) # When the def line isn't split, this returns which doesn't match the # split line test. - eq(start(is_char_in_string=lambda index: index < pos), pos) + eq(start(is_char_in_string=lambda index: index < pos), pos1) + + # gh-85560: 'else' of a conditional expression at the start of + # a continuation line does not start a statement. + setcode('def f():\n' + ' return (1 if x\n' + ' else 0)\n') + eq(start(char_in_string_false), 9) # Start of 'return' line. + setcode('if x:\n' + ' pass\n' + 'else:\n' + ' x = 1\n') + eq(start(char_in_string_false), 15) + setcode('if x:\n' + ' pass\n' + 'else : # comment\n' + ' x = 1\n') + eq(start(char_in_string_false), 15) + # A yield expression can start a continuation line too. + setcode('def f():\n' + ' x = (\n' + ' yield y)\n') + eq(start(char_in_string_false), 0) + # Other statements which cannot start a continuation line. + for stmt in ('with x:', 'del x', 'global x', 'nonlocal x', 'pass', + 'finally:'): + with self.subTest(stmt=stmt): + setcode(f'if x:\n pass\n{stmt}\n') + eq(start(char_in_string_false), 15) def test_set_lo(self): code = ( diff --git a/Lib/idlelib/pyparse.py b/Lib/idlelib/pyparse.py index 8545c63e1435d46..2271b787f341d64 100644 --- a/Lib/idlelib/pyparse.py +++ b/Lib/idlelib/pyparse.py @@ -22,7 +22,7 @@ ^ [ \t]* (?: while - | else + | else (?= [ \t]* : ) # not "else" of a conditional expression | def | return | assert @@ -34,7 +34,12 @@ | except | raise | import - | yield + | with + | del + | global + | nonlocal + | pass + | finally ) \b """, re.VERBOSE | re.MULTILINE).search diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py index f160cc6a7ce900e..8ac2a537c46deec 100644 --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -2458,6 +2458,8 @@ class _IPv6Constants: _private_networks_exceptions = [ IPv6Network('2001:1::1/128'), IPv6Network('2001:1::2/128'), + # RFC 9665: https://www.rfc-editor.org/rfc/rfc9665.html + IPv6Network('2001:1::3/128'), IPv6Network('2001:3::/32'), IPv6Network('2001:4:112::/48'), IPv6Network('2001:20::/28'), diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index d6d0d8220449bdb..600e1f19a9cdc28 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -2535,6 +2535,10 @@ def testReservedIpv6(self): self.assertFalse(ipaddress.ip_address('2001::').is_global) self.assertTrue(ipaddress.ip_address('2001:1::1').is_global) self.assertTrue(ipaddress.ip_address('2001:1::2').is_global) + # gh-151049: RFC 9665 anycast address (IANA 2024-04 registration) + self.assertTrue(ipaddress.ip_address('2001:1::3').is_global) + self.assertFalse(ipaddress.ip_address('2001:1::3').is_private) + self.assertFalse(ipaddress.ip_address('2001:1::4').is_global) self.assertFalse(ipaddress.ip_address('2001:2::').is_global) self.assertTrue(ipaddress.ip_address('2001:3::').is_global) self.assertFalse(ipaddress.ip_address('2001:4::').is_global) diff --git a/Misc/NEWS.d/next/IDLE/2026-09-17-17-00-00.gh-issue-75487.blankint.rst b/Misc/NEWS.d/next/IDLE/2026-09-17-17-00-00.gh-issue-75487.blankint.rst new file mode 100644 index 000000000000000..5ae2d360f5c6a93 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2026-09-17-17-00-00.gh-issue-75487.blankint.rst @@ -0,0 +1,2 @@ +Fix saving a truncated number when an integer entry in the IDLE Settings +dialog is blanked with the Backspace key. diff --git a/Misc/NEWS.d/next/IDLE/2026-09-17-18-00-00.gh-issue-85560.elseexpr.rst b/Misc/NEWS.d/next/IDLE/2026-09-17-18-00-00.gh-issue-85560.elseexpr.rst new file mode 100644 index 000000000000000..9e20622bb535b0c --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2026-09-17-18-00-00.gh-issue-85560.elseexpr.rst @@ -0,0 +1,3 @@ +Fix IDLE failing to find the opening parenthesis when a continuation line +inside the parentheses starts with ``else`` of a conditional expression or +with ``yield``. diff --git a/Misc/NEWS.d/next/Library/2026-06-07-23-30-34.gh-issue-151049.mBlToO.rst b/Misc/NEWS.d/next/Library/2026-06-07-23-30-34.gh-issue-151049.mBlToO.rst new file mode 100644 index 000000000000000..0fa338e62f56d15 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-07-23-30-34.gh-issue-151049.mBlToO.rst @@ -0,0 +1,4 @@ +:mod:`ipaddress`: fix ``is_global`` and ``is_private`` for the +``2001:1::3/128`` (DNS-SD Service Registration Protocol Anycast) address, +which IANA registered as globally reachable per :rfc:`9665`. It is now +correctly reported with ``is_global`` ``True`` and ``is_private`` ``False``. diff --git a/configure b/configure index 84f2030049ac317..e5d3f0091058acb 100755 --- a/configure +++ b/configure @@ -27593,7 +27593,7 @@ then : else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - +#include int main (void) { @@ -27635,7 +27635,7 @@ then : else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - +#include int main (void) { @@ -27677,7 +27677,7 @@ then : else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - +#include int main (void) { @@ -27719,7 +27719,7 @@ then : else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - +#include int main (void) { @@ -27761,7 +27761,7 @@ then : else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - +#include int main (void) { @@ -27803,7 +27803,7 @@ then : else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - +#include int main (void) { @@ -27845,7 +27845,7 @@ then : else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - +#include int main (void) { diff --git a/configure.ac b/configure.ac index bc94908213d2c7b..82a623fedb8192a 100644 --- a/configure.ac +++ b/configure.ac @@ -6555,13 +6555,13 @@ AC_CHECK_FUNCS( [AC_MSG_ERROR([Python requires C99 compatible libm])] ) -PY_CHECK_FUNC_PRIVATE([acospi]) -PY_CHECK_FUNC_PRIVATE([asinpi]) -PY_CHECK_FUNC_PRIVATE([atanpi]) -PY_CHECK_FUNC_PRIVATE([atan2pi]) -PY_CHECK_FUNC_PRIVATE([cospi]) -PY_CHECK_FUNC_PRIVATE([sinpi]) -PY_CHECK_FUNC_PRIVATE([tanpi]) +PY_CHECK_FUNC_PRIVATE([acospi], [@%:@include ]) +PY_CHECK_FUNC_PRIVATE([asinpi], [@%:@include ]) +PY_CHECK_FUNC_PRIVATE([atanpi], [@%:@include ]) +PY_CHECK_FUNC_PRIVATE([atan2pi], [@%:@include ]) +PY_CHECK_FUNC_PRIVATE([cospi], [@%:@include ]) +PY_CHECK_FUNC_PRIVATE([sinpi], [@%:@include ]) +PY_CHECK_FUNC_PRIVATE([tanpi], [@%:@include ]) LIBS=$LIBS_SAVE dnl For multiprocessing module, check that sem_open