[Python-checkins] [3.12] Tee of tee was not producing n independent iterators (gh-123884) (gh-125153)

2024-10-08 Thread rhettinger
https://github.com/python/cpython/commit/cf2532b39d099e004d1c07b2d0fcc46567b68e75
commit: cf2532b39d099e004d1c07b2d0fcc46567b68e75
branch: 3.12
author: Raymond Hettinger 
committer: rhettinger 
date: 2024-10-08T20:16:18Z
summary:

[3.12] Tee of tee was not producing n independent iterators (gh-123884) 
(gh-125153)

files:
A Misc/NEWS.d/next/Library/2024-09-24-22-38-51.gh-issue-123884.iEPTK4.rst
M Doc/library/itertools.rst
M Lib/test/test_itertools.py
M Modules/itertoolsmodule.c

diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index 3fab46c3c0a5b4..047d805eda628a 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -676,24 +676,37 @@ loops that truncate the stream.
Roughly equivalent to::
 
 def tee(iterable, n=2):
-iterator = iter(iterable)
-shared_link = [None, None]
-return tuple(_tee(iterator, shared_link) for _ in range(n))
-
-def _tee(iterator, link):
-try:
-while True:
-if link[1] is None:
-link[0] = next(iterator)
-link[1] = [None, None]
-value, link = link
-yield value
-except StopIteration:
-return
-
-   Once a :func:`tee` has been created, the original *iterable* should not be
-   used anywhere else; otherwise, the *iterable* could get advanced without
-   the tee objects being informed.
+if n < 0:
+raise ValueError
+if n == 0:
+return ()
+iterator = _tee(iterable)
+result = [iterator]
+for _ in range(n - 1):
+result.append(_tee(iterator))
+return tuple(result)
+
+class _tee:
+
+def __init__(self, iterable):
+it = iter(iterable)
+if isinstance(it, _tee):
+self.iterator = it.iterator
+self.link = it.link
+else:
+self.iterator = it
+self.link = [None, None]
+
+def __iter__(self):
+return self
+
+def __next__(self):
+link = self.link
+if link[1] is None:
+link[0] = next(self.iterator)
+link[1] = [None, None]
+value, self.link = link
+return value
 
``tee`` iterators are not threadsafe. A :exc:`RuntimeError` may be
raised when simultaneously using iterators returned by the same :func:`tee`
diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py
index 3d20e70fc1b63f..b6404f4366ca0e 100644
--- a/Lib/test/test_itertools.py
+++ b/Lib/test/test_itertools.py
@@ -1612,10 +1612,11 @@ def test_tee(self):
 self.assertEqual(len(result), n)
 self.assertEqual([list(x) for x in result], [list('abc')]*n)
 
-# tee pass-through to copyable iterator
+# tee objects are independent (see bug gh-123884)
 a, b = tee('abc')
 c, d = tee(a)
-self.assertTrue(a is c)
+e, f = tee(c)
+self.assertTrue(len({a, b, c, d, e, f}) == 6)
 
 # test tee_new
 t1, t2 = tee('abc')
@@ -2029,6 +2030,172 @@ def test_islice_recipe(self):
 self.assertEqual(next(c), 3)
 
 
+def test_tee_recipe(self):
+
+# Begin tee() recipe ###
+
+def tee(iterable, n=2):
+if n < 0:
+raise ValueError
+if n == 0:
+return ()
+iterator = _tee(iterable)
+result = [iterator]
+for _ in range(n - 1):
+result.append(_tee(iterator))
+return tuple(result)
+
+class _tee:
+
+def __init__(self, iterable):
+it = iter(iterable)
+if isinstance(it, _tee):
+self.iterator = it.iterator
+self.link = it.link
+else:
+self.iterator = it
+self.link = [None, None]
+
+def __iter__(self):
+return self
+
+def __next__(self):
+link = self.link
+if link[1] is None:
+link[0] = next(self.iterator)
+link[1] = [None, None]
+value, self.link = link
+return value
+
+# End tee() recipe #
+
+n = 200
+
+a, b = tee([])# test empty iterator
+self.assertEqual(list(a), [])
+self.assertEqual(list(b), [])
+
+a, b = tee(irange(n)) # test 100% interleaved
+self.assertEqual(lzip(a,b), lzip(range(n), range(n)))
+
+a, b = tee(irange(n)) # test 0% interleaved
+self.assertEqual(list(a), list(range(n)))
+self.assertEqual(list(b), list(range(n))

[Python-checkins] gh-125084: Resolve paths in generator common code (GH-125085)

2024-10-08 Thread zware
https://github.com/python/cpython/commit/7dca7322cca7ff146444e56f28f21f1090987fff
commit: 7dca7322cca7ff146444e56f28f21f1090987fff
branch: main
author: Cody Maloney 
committer: zware 
date: 2024-10-08T12:16:02-05:00
summary:

gh-125084: Resolve paths in generator common code (GH-125085)

In out of tree builds, the paths can contain `../ which needs to be
resolved for the relative path calculation to work.

files:
M Tools/cases_generator/generators_common.py

diff --git a/Tools/cases_generator/generators_common.py 
b/Tools/cases_generator/generators_common.py
index f32a20b304c354..0bfa1a3b56fbc2 100644
--- a/Tools/cases_generator/generators_common.py
+++ b/Tools/cases_generator/generators_common.py
@@ -43,13 +43,13 @@ def peek(self) -> Token | None:
 break
 return self.look_ahead
 
-ROOT = Path(__file__).parent.parent.parent
-DEFAULT_INPUT = (ROOT / "Python/bytecodes.c").absolute().as_posix()
+ROOT = Path(__file__).parent.parent.parent.resolve()
+DEFAULT_INPUT = (ROOT / "Python/bytecodes.c").as_posix()
 
 
 def root_relative_path(filename: str) -> str:
 try:
-return Path(filename).absolute().relative_to(ROOT).as_posix()
+return Path(filename).resolve().relative_to(ROOT).as_posix()
 except ValueError:
 # Not relative to root, just return original path.
 return filename

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] gh-124832: Add a note to indicate that `datetime.now` may return the same instant (#124834)

2024-10-08 Thread willingc
https://github.com/python/cpython/commit/760b1e103a0aa696cdf448e0d500cd1bac2213fa
commit: 760b1e103a0aa696cdf448e0d500cd1bac2213fa
branch: main
author: spacemanspiff2007 <[email protected]>
committer: willingc 
date: 2024-10-08T11:17:53-07:00
summary:

gh-124832: Add a note to indicate that `datetime.now` may return the same 
instant (#124834)

* Update datetime.rst

* Update datetime.rst

replace warning with note

* Update Doc/library/datetime.rst

Co-authored-by: Victor Stinner 

* Update Doc/library/datetime.rst

Co-authored-by: Bénédikt Tran <[email protected]>

-

Co-authored-by: Victor Stinner 
Co-authored-by: Bénédikt Tran <[email protected]>

files:
M Doc/library/datetime.rst

diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst
index 64510a77c67c11..f0b465bc9ce39c 100644
--- a/Doc/library/datetime.rst
+++ b/Doc/library/datetime.rst
@@ -948,6 +948,10 @@ Other constructors, all class methods:
 
This function is preferred over :meth:`today` and :meth:`utcnow`.
 
+   .. note::
+
+  Subsequent calls to :meth:`!datetime.now` may return the same
+  instant depending on the precision of the underlying clock.
 
 .. classmethod:: datetime.utcnow()
 

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] gh-70870: Clarify dual usage of 'free variable' (#122545)

2024-10-08 Thread ncoghlan
https://github.com/python/cpython/commit/27390990fa9306e2a797a4eb2bd83c5bfc7cb186
commit: 27390990fa9306e2a797a4eb2bd83c5bfc7cb186
branch: main
author: Alyssa Coghlan 
committer: ncoghlan 
date: 2024-10-08T07:52:12Z
summary:

gh-70870: Clarify dual usage of 'free variable' (#122545)

The term "free variable" has unfortunately become genuinely
ambiguous over the years (presumably due to the names of
some relevant code object instance attributes).

While we can't eliminate that ambiguity at this late date, we can
at least alert people to the potential ambiguity by describing
both the formal meaning of the term and the common
alternative use as a direct synonym for "closure variable".

-

Co-authored-by: Carol Willing 

files:
A Misc/NEWS.d/next/Documentation/2024-08-01-17-18-21.gh-issue-70870.fZnBM9.rst
M Doc/c-api/code.rst
M Doc/glossary.rst
M Doc/library/dis.rst
M Doc/library/functions.rst
M Doc/library/symtable.rst
M Doc/library/types.rst
M Doc/reference/datamodel.rst
M Doc/reference/executionmodel.rst

diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst
index 6ae6bfe4aa6ab4..6eae24b38fae48 100644
--- a/Doc/c-api/code.rst
+++ b/Doc/c-api/code.rst
@@ -32,11 +32,13 @@ bound into a function.
 
 .. c:function:: Py_ssize_t PyCode_GetNumFree(PyCodeObject *co)
 
-   Return the number of free variables in a code object.
+   Return the number of :term:`free (closure) variables `
+   in a code object.
 
 .. c:function:: int PyUnstable_Code_GetFirstFree(PyCodeObject *co)
 
-   Return the position of the first free variable in a code object.
+   Return the position of the first :term:`free (closure) variable `
+   in a code object.
 
.. versionchanged:: 3.13
 
@@ -144,7 +146,8 @@ bound into a function.
 
Equivalent to the Python code ``getattr(co, 'co_freevars')``.
Returns a new reference to a :c:type:`PyTupleObject` containing the names of
-   the free variables. On error, ``NULL`` is returned and an exception is 
raised.
+   the :term:`free (closure) variables `. On error, ``NULL`` 
is returned
+   and an exception is raised.
 
.. versionadded:: 3.11
 
diff --git a/Doc/glossary.rst b/Doc/glossary.rst
index e72a8d002d507d..933fb0319452a6 100644
--- a/Doc/glossary.rst
+++ b/Doc/glossary.rst
@@ -231,6 +231,28 @@ Glossary
   A variable defined in a class and intended to be modified only at
   class level (i.e., not in an instance of the class).
 
+   closure variable
+  A :term:`free variable` referenced from a :term:`nested scope` that is 
defined in an outer
+  scope rather than being resolved at runtime from the globals or builtin 
namespaces.
+  May be explicitly defined with the :keyword:`nonlocal` keyword to allow 
write access,
+  or implicitly defined if the variable is only being read.
+
+  For example, in the ``inner`` function in the following code, both ``x`` 
and ``print`` are
+  :term:`free variables `, but only ``x`` is a *closure 
variable*::
+
+  def outer():
+  x = 0
+  def inner():
+  nonlocal x
+  x += 1
+  print(x)
+  return inner
+
+  Due to the :attr:`codeobject.co_freevars` attribute (which, despite its 
name, only
+  includes the names of closure variables rather than listing all 
referenced free
+  variables), the more general :term:`free variable` term is sometimes 
used even
+  when the intended meaning is to refer specifically to closure variables.
+
complex number
   An extension of the familiar real number system in which all numbers are
   expressed as a sum of a real part and an imaginary part.  Imaginary
@@ -454,6 +476,13 @@ Glossary
   the :term:`global interpreter lock` which allows only one thread to
   execute Python bytecode at a time.  See :pep:`703`.
 
+   free variable
+  Formally, as defined in the :ref:`language execution model 
`, a free
+  variable is any variable used in a namespace which is not a local 
variable in that
+  namespace. See :term:`closure variable` for an example.
+  Pragmatically, due to the name of the :attr:`codeobject.co_freevars` 
attribute,
+  the term is also sometimes used as a synonym for :term:`closure 
variable`.
+
function
   A series of statements which returns some value to a caller. It can also
   be passed zero or more :term:`arguments ` which may be used in
diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index e3919c2ffad84c..75b84a8d827bc9 100644
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -1434,7 +1434,7 @@ iterations of the loop.
slot ``i`` of the "fast locals" storage in this mapping.
If the name is not found there, loads it from the cell contained in
slot ``i``, similar to :opcode:`LOAD_DEREF`. This is used for loading
-   free variables in class bodies (which previously used
+   :term:`closure variables ` in class bodies (which 
previously used
:opcode:`!LOAD_CLASSDEREF`) and in
:ref:`

[Python-checkins] [3.13] gh-70870: Clarify dual usage of 'free variable' (GH-122545) (#125088)

2024-10-08 Thread ncoghlan
https://github.com/python/cpython/commit/0e0a2dadecae8342782dde5694d1269f248a5473
commit: 0e0a2dadecae8342782dde5694d1269f248a5473
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: ncoghlan 
date: 2024-10-08T17:58:47+10:00
summary:

[3.13] gh-70870: Clarify dual usage of 'free variable' (GH-122545) (#125088)

The term "free variable" has unfortunately become genuinely
ambiguous over the years (presumably due to the names of
some relevant code object instance attributes).

While we can't eliminate that ambiguity at this late date, we can
at least alert people to the potential ambiguity by describing
both the formal meaning of the term and the common
alternative use as a direct synonym for "closure variable".

-

(cherry picked from commit 27390990fa9306e2a797a4eb2bd83c5bfc7cb186)

Co-authored-by: Alyssa Coghlan 
Co-authored-by: Carol Willing 

files:
A Misc/NEWS.d/next/Documentation/2024-08-01-17-18-21.gh-issue-70870.fZnBM9.rst
M Doc/c-api/code.rst
M Doc/glossary.rst
M Doc/library/dis.rst
M Doc/library/functions.rst
M Doc/library/symtable.rst
M Doc/library/types.rst
M Doc/reference/datamodel.rst
M Doc/reference/executionmodel.rst

diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst
index 6ae6bfe4aa6ab4..6eae24b38fae48 100644
--- a/Doc/c-api/code.rst
+++ b/Doc/c-api/code.rst
@@ -32,11 +32,13 @@ bound into a function.
 
 .. c:function:: Py_ssize_t PyCode_GetNumFree(PyCodeObject *co)
 
-   Return the number of free variables in a code object.
+   Return the number of :term:`free (closure) variables `
+   in a code object.
 
 .. c:function:: int PyUnstable_Code_GetFirstFree(PyCodeObject *co)
 
-   Return the position of the first free variable in a code object.
+   Return the position of the first :term:`free (closure) variable `
+   in a code object.
 
.. versionchanged:: 3.13
 
@@ -144,7 +146,8 @@ bound into a function.
 
Equivalent to the Python code ``getattr(co, 'co_freevars')``.
Returns a new reference to a :c:type:`PyTupleObject` containing the names of
-   the free variables. On error, ``NULL`` is returned and an exception is 
raised.
+   the :term:`free (closure) variables `. On error, ``NULL`` 
is returned
+   and an exception is raised.
 
.. versionadded:: 3.11
 
diff --git a/Doc/glossary.rst b/Doc/glossary.rst
index 17461e23e71557..7a319e1376aa85 100644
--- a/Doc/glossary.rst
+++ b/Doc/glossary.rst
@@ -226,6 +226,28 @@ Glossary
   A variable defined in a class and intended to be modified only at
   class level (i.e., not in an instance of the class).
 
+   closure variable
+  A :term:`free variable` referenced from a :term:`nested scope` that is 
defined in an outer
+  scope rather than being resolved at runtime from the globals or builtin 
namespaces.
+  May be explicitly defined with the :keyword:`nonlocal` keyword to allow 
write access,
+  or implicitly defined if the variable is only being read.
+
+  For example, in the ``inner`` function in the following code, both ``x`` 
and ``print`` are
+  :term:`free variables `, but only ``x`` is a *closure 
variable*::
+
+  def outer():
+  x = 0
+  def inner():
+  nonlocal x
+  x += 1
+  print(x)
+  return inner
+
+  Due to the :attr:`codeobject.co_freevars` attribute (which, despite its 
name, only
+  includes the names of closure variables rather than listing all 
referenced free
+  variables), the more general :term:`free variable` term is sometimes 
used even
+  when the intended meaning is to refer specifically to closure variables.
+
complex number
   An extension of the familiar real number system in which all numbers are
   expressed as a sum of a real part and an imaginary part.  Imaginary
@@ -444,6 +466,13 @@ Glossary
   the :term:`global interpreter lock` which allows only one thread to
   execute Python bytecode at a time.  See :pep:`703`.
 
+   free variable
+  Formally, as defined in the :ref:`language execution model 
`, a free
+  variable is any variable used in a namespace which is not a local 
variable in that
+  namespace. See :term:`closure variable` for an example.
+  Pragmatically, due to the name of the :attr:`codeobject.co_freevars` 
attribute,
+  the term is also sometimes used as a synonym for :term:`closure 
variable`.
+
function
   A series of statements which returns some value to a caller. It can also
   be passed zero or more :term:`arguments ` which may be used in
diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index c5507e89a527ed..ad8ef5dfc76ab8 100644
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -1431,7 +1431,7 @@ iterations of the loop.
slot ``i`` of the "fast locals" storage in this mapping.
If the name is not found there, loads it from the cell contained in
slot ``i``, similar to :opcode:`LOAD_DEREF`. This is used for load

[Python-checkins] [3.13] gh-85935: Improve tests for invalid arguments in test_argparse (GH-124891) (GH-124901)

2024-10-08 Thread serhiy-storchaka
https://github.com/python/cpython/commit/ea2ccb3d436b8909555c3684f1137485afc12731
commit: ea2ccb3d436b8909555c3684f1137485afc12731
branch: 3.13
author: Serhiy Storchaka 
committer: serhiy-storchaka 
date: 2024-10-08T11:19:30+03:00
summary:

[3.13] gh-85935: Improve tests for invalid arguments in test_argparse 
(GH-124891) (GH-124901)

Check also specific error messages.
(cherry picked from commit 2c050d4bc28bffd2990b5a0bd03fb6fc56b13656)

files:
M Lib/test/test_argparse.py

diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 0084df8ec64706..a5940c5554e5a4 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -5288,15 +5288,15 @@ def custom_formatter(prog):
 class TestInvalidArgumentConstructors(TestCase):
 """Test a bunch of invalid Argument constructors"""
 
-def assertTypeError(self, *args, **kwargs):
+def assertTypeError(self, *args, errmsg=None, **kwargs):
 parser = argparse.ArgumentParser()
-self.assertRaises(TypeError, parser.add_argument,
-  *args, **kwargs)
+self.assertRaisesRegex(TypeError, errmsg, parser.add_argument,
+   *args, **kwargs)
 
-def assertValueError(self, *args, **kwargs):
+def assertValueError(self, *args, errmsg=None, **kwargs):
 parser = argparse.ArgumentParser()
-self.assertRaises(ValueError, parser.add_argument,
-  *args, **kwargs)
+self.assertRaisesRegex(ValueError, errmsg, parser.add_argument,
+   *args, **kwargs)
 
 def test_invalid_keyword_arguments(self):
 self.assertTypeError('-x', bar=None)
@@ -5306,8 +5306,9 @@ def test_invalid_keyword_arguments(self):
 
 def test_missing_destination(self):
 self.assertTypeError()
-for action in ['append', 'store']:
-self.assertTypeError(action=action)
+for action in ['store', 'append', 'extend']:
+with self.subTest(action=action):
+self.assertTypeError(action=action)
 
 def test_invalid_option_strings(self):
 self.assertValueError('--')
@@ -5324,10 +5325,8 @@ def test_invalid_action(self):
 self.assertValueError('-x', action='foo')
 self.assertValueError('foo', action='baz')
 self.assertValueError('--foo', action=('store', 'append'))
-parser = argparse.ArgumentParser()
-with self.assertRaises(ValueError) as cm:
-parser.add_argument("--foo", action="store-true")
-self.assertIn('unknown action', str(cm.exception))
+self.assertValueError('--foo', action="store-true",
+  errmsg='unknown action')
 
 def test_multiple_dest(self):
 parser = argparse.ArgumentParser()
@@ -5340,39 +5339,47 @@ def test_multiple_dest(self):
 def test_no_argument_actions(self):
 for action in ['store_const', 'store_true', 'store_false',
'append_const', 'count']:
-for attrs in [dict(type=int), dict(nargs='+'),
-  dict(choices=['a', 'b'])]:
-self.assertTypeError('-x', action=action, **attrs)
+with self.subTest(action=action):
+for attrs in [dict(type=int), dict(nargs='+'),
+  dict(choices=['a', 'b'])]:
+with self.subTest(attrs=attrs):
+self.assertTypeError('-x', action=action, **attrs)
+self.assertTypeError('x', action=action, **attrs)
+self.assertTypeError('-x', action=action, nargs=0)
+self.assertTypeError('x', action=action, nargs=0)
 
 def test_no_argument_no_const_actions(self):
 # options with zero arguments
 for action in ['store_true', 'store_false', 'count']:
+with self.subTest(action=action):
+# const is always disallowed
+self.assertTypeError('-x', const='foo', action=action)
 
-# const is always disallowed
-self.assertTypeError('-x', const='foo', action=action)
-
-# nargs is always disallowed
-self.assertTypeError('-x', nargs='*', action=action)
+# nargs is always disallowed
+self.assertTypeError('-x', nargs='*', action=action)
 
 def test_more_than_one_argument_actions(self):
-for action in ['store', 'append']:
-
-# nargs=0 is disallowed
-self.assertValueError('-x', nargs=0, action=action)
-self.assertValueError('spam', nargs=0, action=action)
-
-# const is disallowed with non-optional arguments
-for nargs in [1, '*', '+']:
-self.assertValueError('-x', const='foo',
-  nargs=nargs, action=action)
-self.assertValueError('spam', const='foo',
-  nargs=nargs, action=action)
+for action in ['sto

[Python-checkins] gh-69998: Fix decoding error in locale.nl_langinfo() (GH-124963)

2024-10-08 Thread serhiy-storchaka
https://github.com/python/cpython/commit/93b9e6bd7d48150d8a5d16cea39246a980e073cb
commit: 93b9e6bd7d48150d8a5d16cea39246a980e073cb
branch: main
author: Serhiy Storchaka 
committer: serhiy-storchaka 
date: 2024-10-08T11:27:49+03:00
summary:

gh-69998: Fix decoding error in locale.nl_langinfo() (GH-124963)

The function now sets temporarily the LC_CTYPE locale to the locale
of the category that determines the requested value if the locales are
different and the resulting string is non-ASCII.
This temporary change affects other threads.

files:
A Misc/NEWS.d/next/Library/2024-10-04-12-43-03.gh-issue-69998.DVqOXX.rst
M Doc/library/locale.rst
M Doc/whatsnew/3.14.rst
M Lib/test/test__locale.py
M Modules/_localemodule.c

diff --git a/Doc/library/locale.rst b/Doc/library/locale.rst
index 0246f99157024a..04035b33d0ed48 100644
--- a/Doc/library/locale.rst
+++ b/Doc/library/locale.rst
@@ -314,6 +314,15 @@ The :mod:`locale` module defines the following exception 
and functions:
   Get a representation of up to 100 values used to represent the values
   0 to 99.
 
+   The function temporarily sets the ``LC_CTYPE`` locale to the locale
+   of the category that determines the requested value (``LC_TIME``,
+   ``LC_NUMERIC``, ``LC_MONETARY`` or ``LC_MESSAGES``) if locales are
+   different and the resulting string is non-ASCII.
+   This temporary change affects other threads.
+
+   .. versionchanged:: 3.14
+  The function now temporarily sets the ``LC_CTYPE`` locale in some cases.
+
 
 .. function:: getdefaultlocale([envvars])
 
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index f1f78ed843f313..4d71a24e9cc9ca 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -587,6 +587,12 @@ Changes in the Python API
   Wrap it in :func:`staticmethod` if you want to preserve the old behavior.
   (Contributed by Serhiy Storchaka and Dominykas Grigonis in :gh:`121027`.)
 
+* The :func:`locale.nl_langinfo` function now sets temporarily the ``LC_CTYPE``
+  locale in some cases.
+  This temporary change affects other threads.
+  (Contributed by Serhiy Storchaka in :gh:`69998`.)
+
+
 Build Changes
 =
 
diff --git a/Lib/test/test__locale.py b/Lib/test/test__locale.py
index 0947464bb8c04e..ba2d31f9c1ee9d 100644
--- a/Lib/test/test__locale.py
+++ b/Lib/test/test__locale.py
@@ -115,16 +115,17 @@ def numeric_tester(self, calc_type, calc_value, 
data_type, used_locale):
 def test_lc_numeric_nl_langinfo(self):
 # Test nl_langinfo against known values
 tested = False
+oldloc = setlocale(LC_CTYPE)
 for loc in candidate_locales:
 try:
 setlocale(LC_NUMERIC, loc)
-setlocale(LC_CTYPE, loc)
 except Error:
 continue
 for li, lc in ((RADIXCHAR, "decimal_point"),
 (THOUSEP, "thousands_sep")):
 if self.numeric_tester('nl_langinfo', nl_langinfo(li), lc, 
loc):
 tested = True
+self.assertEqual(setlocale(LC_CTYPE), oldloc)
 if not tested:
 self.skipTest('no suitable locales')
 
@@ -135,10 +136,10 @@ def test_lc_numeric_nl_langinfo(self):
 def test_lc_numeric_localeconv(self):
 # Test localeconv against known values
 tested = False
+oldloc = setlocale(LC_CTYPE)
 for loc in candidate_locales:
 try:
 setlocale(LC_NUMERIC, loc)
-setlocale(LC_CTYPE, loc)
 except Error:
 continue
 formatting = localeconv()
@@ -146,6 +147,7 @@ def test_lc_numeric_localeconv(self):
 "thousands_sep"):
 if self.numeric_tester('localeconv', formatting[lc], lc, loc):
 tested = True
+self.assertEqual(setlocale(LC_CTYPE), oldloc)
 if not tested:
 self.skipTest('no suitable locales')
 
@@ -153,10 +155,10 @@ def test_lc_numeric_localeconv(self):
 def test_lc_numeric_basic(self):
 # Test nl_langinfo against localeconv
 tested = False
+oldloc = setlocale(LC_CTYPE)
 for loc in candidate_locales:
 try:
 setlocale(LC_NUMERIC, loc)
-setlocale(LC_CTYPE, loc)
 except Error:
 continue
 for li, lc in ((RADIXCHAR, "decimal_point"),
@@ -173,6 +175,7 @@ def test_lc_numeric_basic(self):
 nl_radixchar, li_radixchar,
 loc, set_locale))
 tested = True
+self.assertEqual(setlocale(LC_CTYPE), oldloc)
 if not tested:
 self.skipTest('no suitable locales')
 
@@ -180,10 +183,10 @@ def test_float_parsing(self):
 # Bug #1391872: Test whether float parsing is okay on European
 # locales.
 tested = False
+oldloc = setlocale(LC_CTYPE)
 for loc in candid

[Python-checkins] bpo-34206: Improve docs and test coverage for pre-init functions (#8023)

2024-10-08 Thread ncoghlan
https://github.com/python/cpython/commit/7c4b6a68f263320a2dd19cd5ff63b35c964b1fa8
commit: 7c4b6a68f263320a2dd19cd5ff63b35c964b1fa8
branch: main
author: Alyssa Coghlan 
committer: ncoghlan 
date: 2024-10-08T08:34:11Z
summary:

bpo-34206: Improve docs and test coverage for pre-init functions (#8023)

- move the Py_Main documentation from the very high level API section
  to the initialization and finalization section
- make it clear that it encapsulates a full Py_Initialize/Finalize
  cycle of its own
- point out that exactly which settings will be read and applied
  correctly when Py_Main is called after a separate runtime
  initialization call is version dependent
- be explicit that Py_IsInitialized can be called prior to
  initialization
- actually test that Py_IsInitialized can be called prior to
  initialization
- flush stdout in the embedding tests that run code so it appears
  in the expected order when running with "-vv"
- make "-vv" on the subinterpreter embedding tests less spammy

-

Co-authored-by: Carol Willing 

files:
A Misc/NEWS.d/next/C API/2018-06-30-21-48-16.bpo-34008.2Wjtm0.rst
A Misc/NEWS.d/next/Documentation/2018-07-04-20-35-25.bpo-34008.bqecIb.rst
M Doc/c-api/init.rst
M Doc/c-api/init_config.rst
M Doc/c-api/veryhigh.rst
M Lib/test/test_embed.py
M Programs/_testembed.c

diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst
index 6d16e04ac3d864..8e0cf7bb0fc088 100644
--- a/Doc/c-api/init.rst
+++ b/Doc/c-api/init.rst
@@ -7,7 +7,8 @@
 Initialization, Finalization, and Threads
 *
 
-See also the :ref:`Python Initialization Configuration `.
+See :ref:`Python Initialization Configuration ` for details
+on how to configure the interpreter prior to initialization.
 
 .. _pre-init-safe:
 
@@ -21,6 +22,15 @@ a few functions and the :ref:`global configuration variables
 
 The following functions can be safely called before Python is initialized:
 
+* Functions that initialize the interpreter:
+
+  * :c:func:`Py_Initialize`
+  * :c:func:`Py_InitializeEx`
+  * :c:func:`Py_InitializeFromConfig`
+  * :c:func:`Py_BytesMain`
+  * :c:func:`Py_Main`
+  * the runtime pre-initialization functions covered in :ref:`init-config`
+
 * Configuration functions:
 
   * :c:func:`PyImport_AppendInittab`
@@ -32,6 +42,7 @@ The following functions can be safely called before Python is 
initialized:
   * :c:func:`Py_SetProgramName`
   * :c:func:`Py_SetPythonHome`
   * :c:func:`PySys_ResetWarnOptions`
+  * the configuration functions covered in :ref:`init-config`
 
 * Informative functions:
 
@@ -43,10 +54,12 @@ The following functions can be safely called before Python 
is initialized:
   * :c:func:`Py_GetCopyright`
   * :c:func:`Py_GetPlatform`
   * :c:func:`Py_GetVersion`
+  * :c:func:`Py_IsInitialized`
 
 * Utilities:
 
   * :c:func:`Py_DecodeLocale`
+  * the status reporting and utility functions covered in :ref:`init-config`
 
 * Memory allocators:
 
@@ -62,11 +75,13 @@ The following functions can be safely called before Python 
is initialized:
 
 .. note::
 
-   The following functions **should not be called** before
-   :c:func:`Py_Initialize`: :c:func:`Py_EncodeLocale`, :c:func:`Py_GetPath`,
+   Despite their apparent similarity to some of the functions listed above,
+   the following functions **should not be called** before the interpreter has
+   been initialized: :c:func:`Py_EncodeLocale`, :c:func:`Py_GetPath`,
:c:func:`Py_GetPrefix`, :c:func:`Py_GetExecPrefix`,
:c:func:`Py_GetProgramFullPath`, :c:func:`Py_GetPythonHome`,
-   :c:func:`Py_GetProgramName` and :c:func:`PyEval_InitThreads`.
+   :c:func:`Py_GetProgramName`, :c:func:`PyEval_InitThreads`, and
+   :c:func:`Py_RunMain`.
 
 
 .. _global-conf-vars:
@@ -346,34 +361,42 @@ Initializing and finalizing the interpreter
this should be called before using any other Python/C API functions; see
:ref:`Before Python Initialization ` for the few exceptions.
 
-   This initializes
-   the table of loaded modules (``sys.modules``), and creates the fundamental
-   modules :mod:`builtins`, :mod:`__main__` and :mod:`sys`.  It also 
initializes
-   the module search path (``sys.path``). It does not set ``sys.argv``; use
-   the new :c:type:`PyConfig` API of the :ref:`Python Initialization
-   Configuration ` for that.  This is a no-op when called for a
-   second time
-   (without calling :c:func:`Py_FinalizeEx` first).  There is no return value; 
it is a
-   fatal error if the initialization fails.
-
-   Use the :c:func:`Py_InitializeFromConfig` function to customize the
+   This initializes the table of loaded modules (``sys.modules``), and creates
+   the fundamental modules :mod:`builtins`, :mod:`__main__` and :mod:`sys`.
+   It also initializes the module search path (``sys.path``). It does not set
+   ``sys.argv``; use the :ref:`Python Initialization Configuration 
`
+   API for that. This is a no-op when called for a second time (without calling
+   :c:func:`Py_FinalizeEx` first).  There is no return v

[Python-checkins] GH-124478: Cleanup argparse documentation (#124877)

2024-10-08 Thread JelleZijlstra
https://github.com/python/cpython/commit/37228bd16e3ef97d32da08848552f7ef016d68ab
commit: 37228bd16e3ef97d32da08848552f7ef016d68ab
branch: main
author: Savannah Ostrowski 
committer: JelleZijlstra 
date: 2024-10-08T15:07:29-07:00
summary:

GH-124478: Cleanup argparse documentation (#124877)

Co-authored-by: Jelle Zijlstra 
Co-authored-by: Tomas R 

files:
A Doc/howto/argparse-optparse.rst
M Doc/library/argparse.rst

diff --git a/Doc/howto/argparse-optparse.rst b/Doc/howto/argparse-optparse.rst
new file mode 100644
index 00..cef2d893b28a62
--- /dev/null
+++ b/Doc/howto/argparse-optparse.rst
@@ -0,0 +1,55 @@
+.. currentmodule:: argparse
+
+.. _upgrading-optparse-code:
+
+==
+Upgrading optparse code
+==
+
+Originally, the :mod:`argparse` module had attempted to maintain compatibility
+with :mod:`optparse`.  However, :mod:`optparse` was difficult to extend
+transparently, particularly with the changes required to support
+``nargs=`` specifiers and better usage messages.  When most everything in
+:mod:`optparse` had either been copy-pasted over or monkey-patched, it no
+longer seemed practical to try to maintain the backwards compatibility.
+
+The :mod:`argparse` module improves on the :mod:`optparse`
+module in a number of ways including:
+
+* Handling positional arguments.
+* Supporting subcommands.
+* Allowing alternative option prefixes like ``+`` and ``/``.
+* Handling zero-or-more and one-or-more style arguments.
+* Producing more informative usage messages.
+* Providing a much simpler interface for custom ``type`` and ``action``.
+
+A partial upgrade path from :mod:`optparse` to :mod:`argparse`:
+
+* Replace all :meth:`optparse.OptionParser.add_option` calls with
+  :meth:`ArgumentParser.add_argument` calls.
+
+* Replace ``(options, args) = parser.parse_args()`` with ``args =
+  parser.parse_args()`` and add additional :meth:`ArgumentParser.add_argument`
+  calls for the positional arguments. Keep in mind that what was previously
+  called ``options``, now in the :mod:`argparse` context is called ``args``.
+
+* Replace :meth:`optparse.OptionParser.disable_interspersed_args`
+  by using :meth:`~ArgumentParser.parse_intermixed_args` instead of
+  :meth:`~ArgumentParser.parse_args`.
+
+* Replace callback actions and the ``callback_*`` keyword arguments with
+  ``type`` or ``action`` arguments.
+
+* Replace string names for ``type`` keyword arguments with the corresponding
+  type objects (e.g. int, float, complex, etc).
+
+* Replace :class:`optparse.Values` with :class:`Namespace` and
+  :exc:`optparse.OptionError` and :exc:`optparse.OptionValueError` with
+  :exc:`ArgumentError`.
+
+* Replace strings with implicit arguments such as ``%default`` or ``%prog`` 
with
+  the standard Python syntax to use dictionaries to format strings, that is,
+  ``%(default)s`` and ``%(prog)s``.
+
+* Replace the OptionParser constructor ``version`` argument with a call to
+  ``parser.add_argument('--version', action='version', version='')``.
diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst
index 83d0a9ed7b1d0a..e9a08984f77c3a 100644
--- a/Doc/library/argparse.rst
+++ b/Doc/library/argparse.rst
@@ -1,4 +1,4 @@
-:mod:`!argparse` --- Parser for command-line options, arguments and 
sub-commands
+:mod:`!argparse` --- Parser for command-line options, arguments and subcommands
 

 
 .. module:: argparse
@@ -19,36 +19,13 @@
introduction to Python command-line parsing, have a look at the
:ref:`argparse tutorial `.
 
-The :mod:`argparse` module makes it easy to write user-friendly command-line
-interfaces. The program defines what arguments it requires, and :mod:`argparse`
-will figure out how to parse those out of :data:`sys.argv`.  The 
:mod:`argparse`
+The :mod:`!argparse` module makes it easy to write user-friendly command-line
+interfaces. The program defines what arguments it requires, and 
:mod:`!argparse`
+will figure out how to parse those out of :data:`sys.argv`.  The 
:mod:`!argparse`
 module also automatically generates help and usage messages.  The module
 will also issue errors when users give the program invalid arguments.
 
-Quick Links for ArgumentParser

-= 
===
 
==
-Name  Description  
   Values
-= 
===
 
==
-prog_ The name of the program
-usage_The string de

[Python-checkins] gh-101552: Allow pydoc to display signatures in source format (#124669)

2024-10-08 Thread JelleZijlstra
https://github.com/python/cpython/commit/78406382c97207b985b5c1d24db244ec398b7e3f
commit: 78406382c97207b985b5c1d24db244ec398b7e3f
branch: main
author: Jelle Zijlstra 
committer: JelleZijlstra 
date: 2024-10-09T05:03:53Z
summary:

gh-101552: Allow pydoc to display signatures in source format (#124669)

Co-authored-by: Alex Waygood 

files:
A Lib/test/test_inspect/inspect_deferred_annotations.py
A Misc/NEWS.d/next/Library/2024-09-27-06-39-32.gh-issue-101552.xYkzag.rst
M Doc/library/inspect.rst
M Doc/whatsnew/3.14.rst
M Lib/inspect.py
M Lib/pydoc.py
M Lib/test/test_inspect/test_inspect.py
M Lib/test/test_pydoc/test_pydoc.py

diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst
index 853671856b2a14..1eaf1cc5d9a68e 100644
--- a/Doc/library/inspect.rst
+++ b/Doc/library/inspect.rst
@@ -694,7 +694,7 @@ and its return annotation. To retrieve a 
:class:`!Signature` object,
 use the :func:`!signature`
 function.
 
-.. function:: signature(callable, *, follow_wrapped=True, globals=None, 
locals=None, eval_str=False)
+.. function:: signature(callable, *, follow_wrapped=True, globals=None, 
locals=None, eval_str=False, annotation_format=Format.VALUE)
 
Return a :class:`Signature` object for the given *callable*:
 
@@ -725,7 +725,12 @@ function.
*globals*, *locals*, and *eval_str* parameters are passed
into :func:`!annotationlib.get_annotations` when resolving the
annotations; see the documentation for 
:func:`!annotationlib.get_annotations`
-   for instructions on how to use these parameters.
+   for instructions on how to use these parameters. A member of the
+   :class:`annotationlib.Format` enum can be passed to the
+   *annotation_format* parameter to control the format of the returned
+   annotations. For example, use
+   ``annotation_format=annotationlib.Format.STRING`` to return annotations in 
string
+   format.
 
Raises :exc:`ValueError` if no signature can be provided, and
:exc:`TypeError` if that type of object is not supported.  Also,
@@ -733,7 +738,7 @@ function.
the ``eval()`` call(s) to un-stringize the annotations in 
:func:`annotationlib.get_annotations`
could potentially raise any kind of exception.
 
-   A slash(/) in the signature of a function denotes that the parameters prior
+   A slash (/) in the signature of a function denotes that the parameters prior
to it are positional-only. For more info, see
:ref:`the FAQ entry on positional-only parameters 
`.
 
@@ -746,6 +751,9 @@ function.
.. versionchanged:: 3.10
   The *globals*, *locals*, and *eval_str* parameters were added.
 
+   .. versionchanged:: 3.14
+  The *annotation_format* parameter was added.
+
.. note::
 
   Some callables may not be introspectable in certain implementations of
@@ -838,7 +846,7 @@ function.
   :class:`Signature` objects are also supported by the generic function
   :func:`copy.replace`.
 
-   .. method:: format(*, max_width=None)
+   .. method:: format(*, max_width=None, quote_annotation_strings=True)
 
   Create a string representation of the :class:`Signature` object.
 
@@ -847,8 +855,17 @@ function.
   If the signature is longer than *max_width*,
   all parameters will be on separate lines.
 
+  If *quote_annotation_strings* is False, :term:`annotations `
+  in the signature are displayed without opening and closing quotation
+  marks if they are strings. This is useful if the signature was created 
with the
+  :attr:`~annotationlib.Format.STRING` format or if
+  ``from __future__ import annotations`` was used.
+
   .. versionadded:: 3.13
 
+  .. versionchanged:: 3.14
+ The *unquote_annotations* parameter was added.
+
.. classmethod:: Signature.from_callable(obj, *, follow_wrapped=True, 
globals=None, locals=None, eval_str=False)
 
Return a :class:`Signature` (or its subclass) object for a given 
callable
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 4d71a24e9cc9ca..c62a3ca5872eef 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -281,6 +281,18 @@ http
   (Contributed by Yorik Hansen in :gh:`123430`.)
 
 
+inspect
+---
+
+* :func:`inspect.signature` takes a new argument *annotation_format* to control
+  the :class:`annotationlib.Format` used for representing annotations.
+  (Contributed by Jelle Zijlstra in :gh:`101552`.)
+
+* :meth:`inspect.Signature.format` takes a new argument *unquote_annotations*.
+  If true, string :term:`annotations ` are displayed without 
surrounding quotes.
+  (Contributed by Jelle Zijlstra in :gh:`101552`.)
+
+
 json
 
 
@@ -356,6 +368,14 @@ pickle
   of the error.
   (Contributed by Serhiy Storchaka in :gh:`122213`.)
 
+pydoc
+-
+
+* :term:`Annotations ` in help output are now usually
+  displayed in a format closer to that in the original source.
+  (Contributed by Jelle Zijlstra in :gh:`101552`.)
+
+
 symtable
 
 
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 1763ef640bbe04..0c33c6cc995a03 10

[Python-checkins] gh-112433 add versionadded for `ctypes.Structure._align_` (#125087)

2024-10-08 Thread kumaraditya303
https://github.com/python/cpython/commit/5967dd8a4de60a418de84d1d1d9efc063ad12c47
commit: 5967dd8a4de60a418de84d1d1d9efc063ad12c47
branch: main
author: monkeyman192 
committer: kumaraditya303 
date: 2024-10-08T12:41:46Z
summary:

gh-112433 add versionadded for `ctypes.Structure._align_` (#125087)


Co-authored-by: Kumar Aditya 

files:
M Doc/library/ctypes.rst
M Doc/whatsnew/3.13.rst

diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst
index 535c5173be50de..d76b8d4809c078 100644
--- a/Doc/library/ctypes.rst
+++ b/Doc/library/ctypes.rst
@@ -2589,6 +2589,8 @@ fields, or any other data types containing pointer type 
fields.
   the structure when being packed or unpacked to/from memory.
   Setting this attribute to 0 is the same as not setting it at all.
 
+  .. versionadded:: 3.13
+
.. attribute:: _layout_
 
   An optional string naming the struct/union layout. It can currently
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index a47d5e077a357b..565f74149725d5 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -814,6 +814,10 @@ ctypes
   See :gh:`124520` for discussion and links to changes in some affected
   projects.
 
+* :class:`ctypes.Structure` objects have a new 
:attr:`~ctypes.Structure._align_`
+  attribute which allows the alignment of the structure being packed to/from
+  memory to be specified explicitly.
+  (Contributed by Matt Sanderson in :gh:`112433`)
 
 dbm
 ---

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] gh-53203: Improve tests for strptime() (GH-125090)

2024-10-08 Thread serhiy-storchaka
https://github.com/python/cpython/commit/19984fe024bfd90649f1c36b78c9abf3ed72b27d
commit: 19984fe024bfd90649f1c36b78c9abf3ed72b27d
branch: main
author: Serhiy Storchaka 
committer: serhiy-storchaka 
date: 2024-10-08T08:40:02Z
summary:

gh-53203: Improve tests for strptime() (GH-125090)

Run them with different locales and different date and time.

Add the @run_with_locales() decorator to run the test with multiple
locales.

Improve the run_with_locale() context manager/decorator -- it now
catches only expected exceptions and reports the test as skipped if no
appropriate locale is available.

files:
M Lib/test/pickletester.py
M Lib/test/support/__init__.py
M Lib/test/test_codecs.py
M Lib/test/test_decimal.py
M Lib/test/test_float.py
M Lib/test/test_imaplib.py
M Lib/test/test_str.py
M Lib/test/test_strptime.py
M Lib/test/test_time.py
M Lib/test/test_types.py

diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py
index 1722cc8612ca6b..8ef8fae44f1d25 100644
--- a/Lib/test/pickletester.py
+++ b/Lib/test/pickletester.py
@@ -26,7 +26,7 @@
 from test import support
 from test.support import os_helper
 from test.support import (
-TestFailed, run_with_locale, no_tracing,
+TestFailed, run_with_locales, no_tracing,
 _2G, _4G, bigmemtest
 )
 from test.support.import_helper import forget
@@ -2895,7 +2895,7 @@ def test_float(self):
 got = self.loads(pickle)
 self.assert_is_copy(value, got)
 
-@run_with_locale('LC_ALL', 'de_DE', 'fr_FR')
+@run_with_locales('LC_ALL', 'de_DE', 'fr_FR', '')
 def test_float_format(self):
 # make sure that floats are formatted locale independent with proto 0
 self.assertEqual(self.dumps(1.2, 0)[0:3], b'F1.')
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index 1a44cc638b5714..72ce5dacd1be4c 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -930,8 +930,8 @@ def check_sizeof(test, o, size):
 test.assertEqual(result, size, msg)
 
 #===
-# Decorator for running a function in a different locale, correctly resetting
-# it afterwards.
+# Decorator/context manager for running a code in a different locale,
+# correctly resetting it afterwards.
 
 @contextlib.contextmanager
 def run_with_locale(catstr, *locales):
@@ -942,16 +942,21 @@ def run_with_locale(catstr, *locales):
 except AttributeError:
 # if the test author gives us an invalid category string
 raise
-except:
+except Exception:
 # cannot retrieve original locale, so do nothing
 locale = orig_locale = None
+if '' not in locales:
+raise unittest.SkipTest('no locales')
 else:
 for loc in locales:
 try:
 locale.setlocale(category, loc)
 break
-except:
+except locale.Error:
 pass
+else:
+if '' not in locales:
+raise unittest.SkipTest(f'no locales {locales}')
 
 try:
 yield
@@ -959,6 +964,46 @@ def run_with_locale(catstr, *locales):
 if locale and orig_locale:
 locale.setlocale(category, orig_locale)
 
+#===
+# Decorator for running a function in multiple locales (if they are
+# availasble) and resetting the original locale afterwards.
+
+def run_with_locales(catstr, *locales):
+def deco(func):
[email protected](func)
+def wrapper(self, /, *args, **kwargs):
+dry_run = '' in locales
+try:
+import locale
+category = getattr(locale, catstr)
+orig_locale = locale.setlocale(category)
+except AttributeError:
+# if the test author gives us an invalid category string
+raise
+except Exception:
+# cannot retrieve original locale, so do nothing
+pass
+else:
+try:
+for loc in locales:
+with self.subTest(locale=loc):
+try:
+locale.setlocale(category, loc)
+except locale.Error:
+self.skipTest(f'no locale {loc!r}')
+else:
+dry_run = False
+func(self, *args, **kwargs)
+finally:
+locale.setlocale(category, orig_locale)
+if dry_run:
+# no locales available, so just run the test
+# with the current locale
+with self.subTest(locale=None):
+func(self, *args, **kwargs)
+return wrapper
+return deco
+
 #===
 # Decorator for 

[Python-checkins] gh-125096: Don't import _pyrepl in site if PYTHON_BASIC_REPL (#125097)

2024-10-08 Thread vstinner
https://github.com/python/cpython/commit/65ce228d63878d8b6d0005f682e89ad9d5289c4b
commit: 65ce228d63878d8b6d0005f682e89ad9d5289c4b
branch: main
author: Victor Stinner 
committer: vstinner 
date: 2024-10-08T15:48:40+02:00
summary:

gh-125096: Don't import _pyrepl in site if PYTHON_BASIC_REPL (#125097)

If the PYTHON_BASIC_REPL environment variable is set, the site module
no longer imports the _pyrepl module.

Moreover, the site module now respects -E and -I command line
options: ignore PYTHON_BASIC_REPL in this case.

files:
A Misc/NEWS.d/next/Library/2024-10-08-13-28-22.gh-issue-125096.Vz0W5g.rst
M Lib/site.py
M Lib/test/test_pyrepl/test_pyrepl.py

diff --git a/Lib/site.py b/Lib/site.py
index cafd3ab70b2cac..b3194d79fb5ab8 100644
--- a/Lib/site.py
+++ b/Lib/site.py
@@ -491,12 +491,21 @@ def register_readline():
 This can be overridden in the sitecustomize or usercustomize module,
 or in a PYTHONSTARTUP file.
 """
+if not sys.flags.ignore_environment:
+PYTHON_BASIC_REPL = os.getenv("PYTHON_BASIC_REPL")
+else:
+PYTHON_BASIC_REPL = False
+
 import atexit
 try:
 import readline
 import rlcompleter  # noqa: F401
-import _pyrepl.readline
-import _pyrepl.unix_console
+if PYTHON_BASIC_REPL:
+CAN_USE_PYREPL = False
+else:
+import _pyrepl.readline
+import _pyrepl.unix_console
+from _pyrepl.main import CAN_USE_PYREPL
 except ImportError:
 return
 
@@ -517,7 +526,6 @@ def register_readline():
 pass
 
 if readline.get_current_history_length() == 0:
-from _pyrepl.main import CAN_USE_PYREPL
 # If no history was loaded, default to .python_history,
 # or PYTHON_HISTORY.
 # The guard is necessary to avoid doubling history size at
@@ -525,13 +533,17 @@ def register_readline():
 # through a PYTHONSTARTUP hook, see:
 # http://bugs.python.org/issue5845#msg198636
 history = gethistoryfile()
-if os.getenv("PYTHON_BASIC_REPL") or not CAN_USE_PYREPL:
-readline_module = readline
-else:
+
+if CAN_USE_PYREPL:
 readline_module = _pyrepl.readline
+exceptions = (OSError, *_pyrepl.unix_console._error)
+else:
+readline_module = readline
+exceptions = OSError
+
 try:
 readline_module.read_history_file(history)
-except (OSError,* _pyrepl.unix_console._error):
+except exceptions:
 pass
 
 def write_history():
diff --git a/Lib/test/test_pyrepl/test_pyrepl.py 
b/Lib/test/test_pyrepl/test_pyrepl.py
index 36f940eaea4eac..1a76832386bf1d 100644
--- a/Lib/test/test_pyrepl/test_pyrepl.py
+++ b/Lib/test/test_pyrepl/test_pyrepl.py
@@ -1204,6 +1204,18 @@ def test_python_basic_repl(self):
 self.assertNotIn("Exception", output)
 self.assertNotIn("Traceback", output)
 
+# The site module must not load _pyrepl if PYTHON_BASIC_REPL is set
+commands = ("import sys\n"
+"print('_pyrepl' in sys.modules)\n"
+"exit()\n")
+env["PYTHON_BASIC_REPL"] = "1"
+output, exit_code = self.run_repl(commands, env=env)
+self.assertEqual(exit_code, 0)
+self.assertIn("False", output)
+self.assertNotIn("True", output)
+self.assertNotIn("Exception", output)
+self.assertNotIn("Traceback", output)
+
 @force_not_colorized
 def test_bad_sys_excepthook_doesnt_crash_pyrepl(self):
 env = os.environ.copy()
diff --git 
a/Misc/NEWS.d/next/Library/2024-10-08-13-28-22.gh-issue-125096.Vz0W5g.rst 
b/Misc/NEWS.d/next/Library/2024-10-08-13-28-22.gh-issue-125096.Vz0W5g.rst
new file mode 100644
index 00..c582a2dfe7243c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-10-08-13-28-22.gh-issue-125096.Vz0W5g.rst
@@ -0,0 +1,5 @@
+If the :envvar:`PYTHON_BASIC_REPL` environment variable is set, the
+:mod:`site` module no longer imports the :mod:`!_pyrepl` module. Moreover,
+the :mod:`site` module now respects :option:`-E` and :option:`-I` command
+line options: ignore :envvar:`PYTHON_BASIC_REPL` in this case. Patch by
+Victor Stinner.

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] gh-117721: use PyMutex in `_thread.lock` (#125110)

2024-10-08 Thread kumaraditya303
https://github.com/python/cpython/commit/fca552993da32044165223eec2297b6c60ad
commit: fca552993da32044165223eec2297b6c60ad
branch: main
author: Kumar Aditya 
committer: kumaraditya303 
date: 2024-10-08T20:17:32+05:30
summary:

gh-117721: use PyMutex in `_thread.lock` (#125110)

files:
M Modules/_threadmodule.c

diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
index 153fe85597749d..9617f9cafe76ff 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -705,9 +705,7 @@ static PyType_Spec ThreadHandle_Type_spec = {
 
 typedef struct {
 PyObject_HEAD
-PyThread_type_lock lock_lock;
-PyObject *in_weakreflist;
-char locked; /* for sanity checking */
+PyMutex lock;
 } lockobject;
 
 static int
@@ -722,15 +720,7 @@ lock_dealloc(PyObject *op)
 {
 lockobject *self = (lockobject*)op;
 PyObject_GC_UnTrack(self);
-if (self->in_weakreflist != NULL) {
-PyObject_ClearWeakRefs((PyObject *) self);
-}
-if (self->lock_lock != NULL) {
-/* Unlock the lock so it's safe to free it */
-if (self->locked)
-PyThread_release_lock(self->lock_lock);
-PyThread_free_lock(self->lock_lock);
-}
+PyObject_ClearWeakRefs((PyObject *) self);
 PyTypeObject *tp = Py_TYPE(self);
 tp->tp_free((PyObject*)self);
 Py_DECREF(tp);
@@ -798,13 +788,12 @@ lock_PyThread_acquire_lock(PyObject *op, PyObject *args, 
PyObject *kwds)
 return NULL;
 }
 
-PyLockStatus r = acquire_timed(self->lock_lock, timeout);
+PyLockStatus r = _PyMutex_LockTimed(&self->lock, timeout,
+_PY_LOCK_HANDLE_SIGNALS | 
_PY_LOCK_DETACH);
 if (r == PY_LOCK_INTR) {
 return NULL;
 }
 
-if (r == PY_LOCK_ACQUIRED)
-self->locked = 1;
 return PyBool_FromLong(r == PY_LOCK_ACQUIRED);
 }
 
@@ -836,13 +825,11 @@ lock_PyThread_release_lock(PyObject *op, PyObject 
*Py_UNUSED(ignored))
 {
 lockobject *self = (lockobject*)op;
 /* Sanity check: the lock must be locked */
-if (!self->locked) {
+if (_PyMutex_TryUnlock(&self->lock) < 0) {
 PyErr_SetString(ThreadError, "release unlocked lock");
 return NULL;
 }
 
-self->locked = 0;
-PyThread_release_lock(self->lock_lock);
 Py_RETURN_NONE;
 }
 
@@ -870,7 +857,7 @@ static PyObject *
 lock_locked_lock(PyObject *op, PyObject *Py_UNUSED(ignored))
 {
 lockobject *self = (lockobject*)op;
-return PyBool_FromLong((long)self->locked);
+return PyBool_FromLong(PyMutex_IsLocked(&self->lock));
 }
 
 PyDoc_STRVAR(locked_doc,
@@ -890,21 +877,15 @@ lock_repr(PyObject *op)
 {
 lockobject *self = (lockobject*)op;
 return PyUnicode_FromFormat("<%s %s object at %p>",
-self->locked ? "locked" : "unlocked", Py_TYPE(self)->tp_name, self);
+PyMutex_IsLocked(&self->lock) ? "locked" : "unlocked", 
Py_TYPE(self)->tp_name, self);
 }
 
 #ifdef HAVE_FORK
 static PyObject *
 lock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(args))
 {
-lockobject *self = (lockobject*)op;
-if (_PyThread_at_fork_reinit(&self->lock_lock) < 0) {
-PyErr_SetString(ThreadError, "failed to reinitialize lock at fork");
-return NULL;
-}
-
-self->locked = 0;
-
+lockobject *self = (lockobject *)op;
+_PyMutex_at_fork_reinit(&self->lock);
 Py_RETURN_NONE;
 }
 #endif  /* HAVE_FORK */
@@ -970,18 +951,12 @@ A lock is not owned by the thread that locked it; another 
thread may\n\
 unlock it.  A thread attempting to lock a lock that it has already locked\n\
 will block until another thread unlocks it.  Deadlocks may ensue.");
 
-static PyMemberDef lock_type_members[] = {
-{"__weaklistoffset__", Py_T_PYSSIZET, offsetof(lockobject, 
in_weakreflist), Py_READONLY},
-{NULL},
-};
-
 static PyType_Slot lock_type_slots[] = {
 {Py_tp_dealloc, lock_dealloc},
 {Py_tp_repr, lock_repr},
 {Py_tp_doc, (void *)lock_doc},
 {Py_tp_methods, lock_methods},
 {Py_tp_traverse, lock_traverse},
-{Py_tp_members, lock_type_members},
 {Py_tp_new, lock_new},
 {0, 0}
 };
@@ -990,7 +965,7 @@ static PyType_Spec lock_type_spec = {
 .name = "_thread.lock",
 .basicsize = sizeof(lockobject),
 .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
-  Py_TPFLAGS_IMMUTABLETYPE),
+  Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_MANAGED_WEAKREF),
 .slots = lock_type_slots,
 };
 
@@ -1340,16 +1315,7 @@ newlockobject(PyObject *module)
 if (self == NULL) {
 return NULL;
 }
-
-self->lock_lock = PyThread_allocate_lock();
-self->locked = 0;
-self->in_weakreflist = NULL;
-
-if (self->lock_lock == NULL) {
-Py_DECREF(self);
-PyErr_SetString(ThreadError, "can't allocate lock");
-return NULL;
-}
+self->lock = (PyMutex){0};
 return self;
 }
 

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to pytho

[Python-checkins] gh-115999: Stop the world when invalidating function versions (#124997)

2024-10-08 Thread colesbury
https://github.com/python/cpython/commit/e99f159be4f70cf9e40865d638e79fa426968827
commit: e99f159be4f70cf9e40865d638e79fa426968827
branch: main
author: mpage 
committer: colesbury 
date: 2024-10-08T10:04:35-04:00
summary:

gh-115999: Stop the world when invalidating function versions (#124997)

Stop the world when invalidating function versions

The tier1 interpreter specializes `CALL` instructions based on the values
of certain function attributes (e.g. `__code__`, `__defaults__`). The tier1
interpreter uses function versions to verify that the attributes of a function
during execution of a specialization match those seen during specialization.
A function's version is initialized in `MAKE_FUNCTION` and is invalidated when
any of the critical function attributes are changed. The tier1 interpreter 
stores
the function version in the inline cache during specialization. A guard is used 
by
the specialized instruction to verify that the version of the function on the 
operand
stack matches the cached version (and therefore has all of the expected 
attributes).
It is assumed that once the guard passes, all attributes will remain unchanged
while executing the rest of the specialized instruction.

Stopping the world when invalidating function versions ensures that all critical
function attributes will remain unchanged after the function version guard 
passes
in free-threaded builds. It's important to note that this is only true if the 
remainder
of the specialized instruction does not enter and exit a stop-the-world point.

We will stop the world the first time any of the following function attributes
are mutated:

- defaults
- vectorcall
- kwdefaults
- closure
- code

This should happen rarely and only happens once per function, so the performance
impact on majority of code should be minimal.

Additionally, refactor the API for manipulating function versions to more 
clearly
match the stated semantics.

files:
M Include/internal/pycore_function.h
M Include/internal/pycore_runtime_init.h
M Objects/funcobject.c
M Python/specialize.c

diff --git a/Include/internal/pycore_function.h 
b/Include/internal/pycore_function.h
index 6d44e933e8a8cb..c45d281125febb 100644
--- a/Include/internal/pycore_function.h
+++ b/Include/internal/pycore_function.h
@@ -18,6 +18,10 @@ extern PyObject* _PyFunction_Vectorcall(
 
 #define FUNC_MAX_WATCHERS 8
 
+#define FUNC_VERSION_UNSET 0
+#define FUNC_VERSION_CLEARED 1
+#define FUNC_VERSION_FIRST_VALID 2
+
 #define FUNC_VERSION_CACHE_SIZE (1<<12)  /* Must be a power of 2 */
 
 struct _func_version_cache_item {
@@ -41,6 +45,12 @@ struct _py_func_state {
 
 extern PyFunctionObject* _PyFunction_FromConstructor(PyFrameConstructor 
*constr);
 
+static inline int
+_PyFunction_IsVersionValid(uint32_t version)
+{
+return version >= FUNC_VERSION_FIRST_VALID;
+}
+
 extern uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func);
 PyAPI_FUNC(void) _PyFunction_SetVersion(PyFunctionObject *func, uint32_t 
version);
 void _PyFunction_ClearCodeByVersion(uint32_t version);
diff --git a/Include/internal/pycore_runtime_init.h 
b/Include/internal/pycore_runtime_init.h
index e6adb98eb19130..a17ba46966daa1 100644
--- a/Include/internal/pycore_runtime_init.h
+++ b/Include/internal/pycore_runtime_init.h
@@ -11,6 +11,7 @@ extern "C" {
 #include "pycore_ceval_state.h"   // _PyEval_RUNTIME_PERF_INIT
 #include "pycore_faulthandler.h"  // _faulthandler_runtime_state_INIT
 #include "pycore_floatobject.h"   // _py_float_format_unknown
+#include "pycore_function.h"
 #include "pycore_object.h"// _PyObject_HEAD_INIT
 #include "pycore_obmalloc_init.h" // _obmalloc_global_state_INIT
 #include "pycore_parser.h"// _parser_runtime_state_INIT
@@ -243,7 +244,7 @@ extern PyTypeObject _PyExc_MemoryError;
 .dict_state = _dict_state_INIT, \
 .mem_free_queue = _Py_mem_free_queue_INIT(INTERP.mem_free_queue), \
 .func_state = { \
-.next_version = 1, \
+.next_version = FUNC_VERSION_FIRST_VALID, \
 }, \
 .types = { \
 .next_version_tag = _Py_TYPE_BASE_VERSION_TAG, \
diff --git a/Objects/funcobject.c b/Objects/funcobject.c
index 98e6766d0ca0d8..855d1a2eeca819 100644
--- a/Objects/funcobject.c
+++ b/Objects/funcobject.c
@@ -128,7 +128,7 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr)
 op->func_annotate = NULL;
 op->func_typeparams = NULL;
 op->vectorcall = _PyFunction_Vectorcall;
-op->func_version = 0;
+op->func_version = FUNC_VERSION_UNSET;
 // NOTE: functions created via FrameConstructor do not use deferred
 // reference counting because they are typically not part of cycles
 // nor accessed by multiple threads.
@@ -207,7 +207,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject 
*globals, PyObject *qualname
 op->func_annotate = NULL;
 op->func_typeparams = NULL;
 op->vectorcall = _PyFunction_Vectorcall;
-op->func_version = 0;
+op->func_version = FUNC_VERSION_UNSE

[Python-checkins] [3.13] gh-125096: Don't import _pyrepl in site if PYTHON_BASIC_REPL (#125097) (#125111)

2024-10-08 Thread vstinner
https://github.com/python/cpython/commit/5df6a756a56ff77eb9341de5e3764e582ef3fcf0
commit: 5df6a756a56ff77eb9341de5e3764e582ef3fcf0
branch: 3.13
author: Victor Stinner 
committer: vstinner 
date: 2024-10-08T14:20:05Z
summary:

[3.13] gh-125096: Don't import _pyrepl in site if PYTHON_BASIC_REPL (#125097) 
(#125111)

gh-125096: Don't import _pyrepl in site if PYTHON_BASIC_REPL (#125097)

If the PYTHON_BASIC_REPL environment variable is set, the site module
no longer imports the _pyrepl module.

Moreover, the site module now respects -E and -I command line
options: ignore PYTHON_BASIC_REPL in this case.

(cherry picked from commit 65ce228d63878d8b6d0005f682e89ad9d5289c4b)

files:
A Misc/NEWS.d/next/Library/2024-10-08-13-28-22.gh-issue-125096.Vz0W5g.rst
M Lib/site.py
M Lib/test/test_pyrepl/test_pyrepl.py

diff --git a/Lib/site.py b/Lib/site.py
index 0a0dc47b174d47..d31bc772334151 100644
--- a/Lib/site.py
+++ b/Lib/site.py
@@ -491,12 +491,21 @@ def register_readline():
 This can be overridden in the sitecustomize or usercustomize module,
 or in a PYTHONSTARTUP file.
 """
+if not sys.flags.ignore_environment:
+PYTHON_BASIC_REPL = os.getenv("PYTHON_BASIC_REPL")
+else:
+PYTHON_BASIC_REPL = False
+
 import atexit
 try:
 import readline
 import rlcompleter
-import _pyrepl.readline
-import _pyrepl.unix_console
+if PYTHON_BASIC_REPL:
+CAN_USE_PYREPL = False
+else:
+import _pyrepl.readline
+import _pyrepl.unix_console
+from _pyrepl.main import CAN_USE_PYREPL
 except ImportError:
 return
 
@@ -517,7 +526,6 @@ def register_readline():
 pass
 
 if readline.get_current_history_length() == 0:
-from _pyrepl.main import CAN_USE_PYREPL
 # If no history was loaded, default to .python_history,
 # or PYTHON_HISTORY.
 # The guard is necessary to avoid doubling history size at
@@ -525,13 +533,17 @@ def register_readline():
 # through a PYTHONSTARTUP hook, see:
 # http://bugs.python.org/issue5845#msg198636
 history = gethistoryfile()
-if os.getenv("PYTHON_BASIC_REPL") or not CAN_USE_PYREPL:
-readline_module = readline
-else:
+
+if CAN_USE_PYREPL:
 readline_module = _pyrepl.readline
+exceptions = (OSError, *_pyrepl.unix_console._error)
+else:
+readline_module = readline
+exceptions = OSError
+
 try:
 readline_module.read_history_file(history)
-except (OSError,* _pyrepl.unix_console._error):
+except exceptions:
 pass
 
 def write_history():
diff --git a/Lib/test/test_pyrepl/test_pyrepl.py 
b/Lib/test/test_pyrepl/test_pyrepl.py
index 0f3e9996e77e45..5538c248fdbace 100644
--- a/Lib/test/test_pyrepl/test_pyrepl.py
+++ b/Lib/test/test_pyrepl/test_pyrepl.py
@@ -1204,6 +1204,18 @@ def test_python_basic_repl(self):
 self.assertNotIn("Exception", output)
 self.assertNotIn("Traceback", output)
 
+# The site module must not load _pyrepl if PYTHON_BASIC_REPL is set
+commands = ("import sys\n"
+"print('_pyrepl' in sys.modules)\n"
+"exit()\n")
+env["PYTHON_BASIC_REPL"] = "1"
+output, exit_code = self.run_repl(commands, env=env)
+self.assertEqual(exit_code, 0)
+self.assertIn("False", output)
+self.assertNotIn("True", output)
+self.assertNotIn("Exception", output)
+self.assertNotIn("Traceback", output)
+
 @force_not_colorized
 def test_bad_sys_excepthook_doesnt_crash_pyrepl(self):
 env = os.environ.copy()
diff --git 
a/Misc/NEWS.d/next/Library/2024-10-08-13-28-22.gh-issue-125096.Vz0W5g.rst 
b/Misc/NEWS.d/next/Library/2024-10-08-13-28-22.gh-issue-125096.Vz0W5g.rst
new file mode 100644
index 00..c582a2dfe7243c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-10-08-13-28-22.gh-issue-125096.Vz0W5g.rst
@@ -0,0 +1,5 @@
+If the :envvar:`PYTHON_BASIC_REPL` environment variable is set, the
+:mod:`site` module no longer imports the :mod:`!_pyrepl` module. Moreover,
+the :mod:`site` module now respects :option:`-E` and :option:`-I` command
+line options: ignore :envvar:`PYTHON_BASIC_REPL` in this case. Patch by
+Victor Stinner.

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] gh-117721: use PyMutex in `_thread.lock` (#125110) (#125116)

2024-10-08 Thread kumaraditya303
https://github.com/python/cpython/commit/20242a437c116c93a82635879689f29f3910f436
commit: 20242a437c116c93a82635879689f29f3910f436
branch: 3.13
author: Kumar Aditya 
committer: kumaraditya303 
date: 2024-10-08T21:16:48+05:30
summary:

[3.13] gh-117721: use PyMutex in `_thread.lock` (#125110) (#125116)

* gh-117721: use PyMutex in `_thread.lock` (#125110)

(cherry picked from commit fca552993da32044165223eec2297b6c60ad)

files:
M Modules/_threadmodule.c

diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
index b3ed8e7bc56b9e..6183d0608b92b6 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -701,9 +701,7 @@ static PyType_Spec ThreadHandle_Type_spec = {
 
 typedef struct {
 PyObject_HEAD
-PyThread_type_lock lock_lock;
-PyObject *in_weakreflist;
-char locked; /* for sanity checking */
+PyMutex lock;
 } lockobject;
 
 static int
@@ -717,15 +715,7 @@ static void
 lock_dealloc(lockobject *self)
 {
 PyObject_GC_UnTrack(self);
-if (self->in_weakreflist != NULL) {
-PyObject_ClearWeakRefs((PyObject *) self);
-}
-if (self->lock_lock != NULL) {
-/* Unlock the lock so it's safe to free it */
-if (self->locked)
-PyThread_release_lock(self->lock_lock);
-PyThread_free_lock(self->lock_lock);
-}
+PyObject_ClearWeakRefs((PyObject *) self);
 PyTypeObject *tp = Py_TYPE(self);
 tp->tp_free((PyObject*)self);
 Py_DECREF(tp);
@@ -790,13 +780,12 @@ lock_PyThread_acquire_lock(lockobject *self, PyObject 
*args, PyObject *kwds)
 if (lock_acquire_parse_args(args, kwds, &timeout) < 0)
 return NULL;
 
-PyLockStatus r = acquire_timed(self->lock_lock, timeout);
+PyLockStatus r = _PyMutex_LockTimed(&self->lock, timeout,
+_PY_LOCK_HANDLE_SIGNALS | 
_PY_LOCK_DETACH);
 if (r == PY_LOCK_INTR) {
 return NULL;
 }
 
-if (r == PY_LOCK_ACQUIRED)
-self->locked = 1;
 return PyBool_FromLong(r == PY_LOCK_ACQUIRED);
 }
 
@@ -827,13 +816,11 @@ static PyObject *
 lock_PyThread_release_lock(lockobject *self, PyObject *Py_UNUSED(ignored))
 {
 /* Sanity check: the lock must be locked */
-if (!self->locked) {
+if (_PyMutex_TryUnlock(&self->lock) < 0) {
 PyErr_SetString(ThreadError, "release unlocked lock");
 return NULL;
 }
 
-self->locked = 0;
-PyThread_release_lock(self->lock_lock);
 Py_RETURN_NONE;
 }
 
@@ -860,7 +847,7 @@ Release the lock.");
 static PyObject *
 lock_locked_lock(lockobject *self, PyObject *Py_UNUSED(ignored))
 {
-return PyBool_FromLong((long)self->locked);
+return PyBool_FromLong(PyMutex_IsLocked(&self->lock));
 }
 
 PyDoc_STRVAR(locked_doc,
@@ -879,20 +866,14 @@ static PyObject *
 lock_repr(lockobject *self)
 {
 return PyUnicode_FromFormat("<%s %s object at %p>",
-self->locked ? "locked" : "unlocked", Py_TYPE(self)->tp_name, self);
+PyMutex_IsLocked(&self->lock) ? "locked" : "unlocked", 
Py_TYPE(self)->tp_name, self);
 }
 
 #ifdef HAVE_FORK
 static PyObject *
 lock__at_fork_reinit(lockobject *self, PyObject *Py_UNUSED(args))
 {
-if (_PyThread_at_fork_reinit(&self->lock_lock) < 0) {
-PyErr_SetString(ThreadError, "failed to reinitialize lock at fork");
-return NULL;
-}
-
-self->locked = 0;
-
+_PyMutex_at_fork_reinit(&self->lock);
 Py_RETURN_NONE;
 }
 #endif  /* HAVE_FORK */
@@ -958,18 +939,12 @@ A lock is not owned by the thread that locked it; another 
thread may\n\
 unlock it.  A thread attempting to lock a lock that it has already locked\n\
 will block until another thread unlocks it.  Deadlocks may ensue.");
 
-static PyMemberDef lock_type_members[] = {
-{"__weaklistoffset__", Py_T_PYSSIZET, offsetof(lockobject, 
in_weakreflist), Py_READONLY},
-{NULL},
-};
-
 static PyType_Slot lock_type_slots[] = {
 {Py_tp_dealloc, (destructor)lock_dealloc},
 {Py_tp_repr, (reprfunc)lock_repr},
 {Py_tp_doc, (void *)lock_doc},
 {Py_tp_methods, lock_methods},
 {Py_tp_traverse, lock_traverse},
-{Py_tp_members, lock_type_members},
 {Py_tp_new, lock_new},
 {0, 0}
 };
@@ -978,7 +953,7 @@ static PyType_Spec lock_type_spec = {
 .name = "_thread.lock",
 .basicsize = sizeof(lockobject),
 .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
-  Py_TPFLAGS_IMMUTABLETYPE),
+  Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_MANAGED_WEAKREF),
 .slots = lock_type_slots,
 };
 
@@ -1320,16 +1295,7 @@ newlockobject(PyObject *module)
 if (self == NULL) {
 return NULL;
 }
-
-self->lock_lock = PyThread_allocate_lock();
-self->locked = 0;
-self->in_weakreflist = NULL;
-
-if (self->lock_lock == NULL) {
-Py_DECREF(self);
-PyErr_SetString(ThreadError, "can't allocate lock");
-return NULL;
-}
+self->lock = (PyMutex){0};
 return self;
 }
 

___
Python-checkins mailing list --

[Python-checkins] [3.13] gh-112433 add versionadded for `ctypes.Structure._align_` (GH-125087) (#125113)

2024-10-08 Thread JelleZijlstra
https://github.com/python/cpython/commit/089c6d2961984ac8b10fc8a217b79555b61fd9eb
commit: 089c6d2961984ac8b10fc8a217b79555b61fd9eb
branch: 3.13
author: Jelle Zijlstra 
committer: JelleZijlstra 
date: 2024-10-08T14:24:27Z
summary:

[3.13] gh-112433 add versionadded for `ctypes.Structure._align_` (GH-125087) 
(#125113)

(cherry picked from commit 5967dd8a4de60a418de84d1d1d9efc063ad12c47)

Co-authored-by: monkeyman192 
Co-authored-by: Kumar Aditya 

files:
M Doc/library/ctypes.rst
M Doc/whatsnew/3.13.rst

diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst
index 12b44569271efc..bc3a50a633f2ff 100644
--- a/Doc/library/ctypes.rst
+++ b/Doc/library/ctypes.rst
@@ -2540,6 +2540,8 @@ fields, or any other data types containing pointer type 
fields.
   the structure when being packed or unpacked to/from memory.
   Setting this attribute to 0 is the same as not setting it at all.
 
+  .. versionadded:: 3.13
+
.. attribute:: _anonymous_
 
   An optional sequence that lists the names of unnamed (anonymous) fields.
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index a47d5e077a357b..565f74149725d5 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -814,6 +814,10 @@ ctypes
   See :gh:`124520` for discussion and links to changes in some affected
   projects.
 
+* :class:`ctypes.Structure` objects have a new 
:attr:`~ctypes.Structure._align_`
+  attribute which allows the alignment of the structure being packed to/from
+  memory to be specified explicitly.
+  (Contributed by Matt Sanderson in :gh:`112433`)
 
 dbm
 ---

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] gh-124502: Optimize unicode_eq() (#125105)

2024-10-08 Thread vstinner
https://github.com/python/cpython/commit/c203955f3b433e06118d00a2fe7215546a0b7fe6
commit: c203955f3b433e06118d00a2fe7215546a0b7fe6
branch: main
author: Victor Stinner 
committer: vstinner 
date: 2024-10-08T16:25:24+02:00
summary:

gh-124502: Optimize unicode_eq() (#125105)

files:
M Objects/stringlib/eq.h

diff --git a/Objects/stringlib/eq.h b/Objects/stringlib/eq.h
index 2eac4baf5ca9ce..821b692f26b830 100644
--- a/Objects/stringlib/eq.h
+++ b/Objects/stringlib/eq.h
@@ -4,14 +4,19 @@
  * unicode_eq() is called when the hash of two unicode objects is equal.
  */
 Py_LOCAL_INLINE(int)
-unicode_eq(PyObject *a, PyObject *b)
+unicode_eq(PyObject *str1, PyObject *str2)
 {
-if (PyUnicode_GET_LENGTH(a) != PyUnicode_GET_LENGTH(b))
+Py_ssize_t len = PyUnicode_GET_LENGTH(str1);
+if (PyUnicode_GET_LENGTH(str2) != len) {
 return 0;
-if (PyUnicode_GET_LENGTH(a) == 0)
-return 1;
-if (PyUnicode_KIND(a) != PyUnicode_KIND(b))
+}
+
+int kind = PyUnicode_KIND(str1);
+if (PyUnicode_KIND(str2) != kind) {
 return 0;
-return memcmp(PyUnicode_1BYTE_DATA(a), PyUnicode_1BYTE_DATA(b),
-  PyUnicode_GET_LENGTH(a) * PyUnicode_KIND(a)) == 0;
+}
+
+const void *data1 = PyUnicode_DATA(str1);
+const void *data2 = PyUnicode_DATA(str2);
+return (memcmp(data1, data2, len * kind) == 0);
 }

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.10] gh-89452: GHA: Set --with-dbmliborder to avoid issues with homebrew's gdbm 1.24 (#125112)

2024-10-08 Thread ambv
https://github.com/python/cpython/commit/850189a64e7f0b920fe48cb12a5da3e648435680
commit: 850189a64e7f0b920fe48cb12a5da3e648435680
branch: 3.10
author: Petr Viktorin 
committer: ambv 
date: 2024-10-08T16:37:22+02:00
summary:

[3.10] gh-89452: GHA: Set --with-dbmliborder to avoid issues with homebrew's 
gdbm 1.24 (#125112)

Per https://github.com/python/cpython/issues/89452#issuecomment-1116329316,
the issue is fixed in configure for 3.11+, and

> For older Python versions, the workaround is to build with:
>
> ./configure --with-dbmliborder=gdbm:ndbm

We need this workaround in GitHub Actions, otherwise the tests fail.

files:
M .github/workflows/build.yml

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 0d7545368d7a70..eecca69316f6f3 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -193,7 +193,9 @@ jobs:
 ./configure \
   --with-pydebug \
   --prefix=/opt/python-dev \
-  --with-openssl="$(brew --prefix [email protected])"
+  --with-openssl="$(brew --prefix [email protected])" \
+  --with-dbmliborder=gdbm:ndbm
+  # (--with-dbmliborder needed for homebrew's gdbm 1.24: see gh-89452)
 - name: Build CPython
   run: make -j4
 - name: Display build info

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] gh-53203: Improve tests for strptime() (GH-125090) (GH-125091)

2024-10-08 Thread serhiy-storchaka
https://github.com/python/cpython/commit/9e61aa9b389a79412fb5bf757e3b9131527256a7
commit: 9e61aa9b389a79412fb5bf757e3b9131527256a7
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-10-08T12:12:15+03:00
summary:

[3.13] gh-53203: Improve tests for strptime() (GH-125090) (GH-125091)

Run them with different locales and different date and time.

Add the @run_with_locales() decorator to run the test with multiple
locales.

Improve the run_with_locale() context manager/decorator -- it now
catches only expected exceptions and reports the test as skipped if no
appropriate locale is available.
(cherry picked from commit 19984fe024bfd90649f1c36b78c9abf3ed72b27d)

Co-authored-by: Serhiy Storchaka 

files:
M Lib/test/pickletester.py
M Lib/test/support/__init__.py
M Lib/test/test_codecs.py
M Lib/test/test_decimal.py
M Lib/test/test_float.py
M Lib/test/test_imaplib.py
M Lib/test/test_str.py
M Lib/test/test_strptime.py
M Lib/test/test_time.py
M Lib/test/test_types.py

diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py
index 38b87c42ca5ee0..f216136fa0f26d 100644
--- a/Lib/test/pickletester.py
+++ b/Lib/test/pickletester.py
@@ -26,7 +26,7 @@
 from test import support
 from test.support import os_helper
 from test.support import (
-TestFailed, run_with_locale, no_tracing,
+TestFailed, run_with_locales, no_tracing,
 _2G, _4G, bigmemtest
 )
 from test.support.import_helper import forget
@@ -2589,7 +2589,7 @@ def test_float(self):
 got = self.loads(pickle)
 self.assert_is_copy(value, got)
 
-@run_with_locale('LC_ALL', 'de_DE', 'fr_FR')
+@run_with_locales('LC_ALL', 'de_DE', 'fr_FR', '')
 def test_float_format(self):
 # make sure that floats are formatted locale independent with proto 0
 self.assertEqual(self.dumps(1.2, 0)[0:3], b'F1.')
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index ed23f73e3cfd6d..057b3bbd22944f 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -921,8 +921,8 @@ def check_sizeof(test, o, size):
 test.assertEqual(result, size, msg)
 
 #===
-# Decorator for running a function in a different locale, correctly resetting
-# it afterwards.
+# Decorator/context manager for running a code in a different locale,
+# correctly resetting it afterwards.
 
 @contextlib.contextmanager
 def run_with_locale(catstr, *locales):
@@ -933,16 +933,21 @@ def run_with_locale(catstr, *locales):
 except AttributeError:
 # if the test author gives us an invalid category string
 raise
-except:
+except Exception:
 # cannot retrieve original locale, so do nothing
 locale = orig_locale = None
+if '' not in locales:
+raise unittest.SkipTest('no locales')
 else:
 for loc in locales:
 try:
 locale.setlocale(category, loc)
 break
-except:
+except locale.Error:
 pass
+else:
+if '' not in locales:
+raise unittest.SkipTest(f'no locales {locales}')
 
 try:
 yield
@@ -950,6 +955,46 @@ def run_with_locale(catstr, *locales):
 if locale and orig_locale:
 locale.setlocale(category, orig_locale)
 
+#===
+# Decorator for running a function in multiple locales (if they are
+# availasble) and resetting the original locale afterwards.
+
+def run_with_locales(catstr, *locales):
+def deco(func):
[email protected](func)
+def wrapper(self, /, *args, **kwargs):
+dry_run = '' in locales
+try:
+import locale
+category = getattr(locale, catstr)
+orig_locale = locale.setlocale(category)
+except AttributeError:
+# if the test author gives us an invalid category string
+raise
+except Exception:
+# cannot retrieve original locale, so do nothing
+pass
+else:
+try:
+for loc in locales:
+with self.subTest(locale=loc):
+try:
+locale.setlocale(category, loc)
+except locale.Error:
+self.skipTest(f'no locale {loc!r}')
+else:
+dry_run = False
+func(self, *args, **kwargs)
+finally:
+locale.setlocale(category, orig_locale)
+if dry_run:
+# no locales available, so just run the test
+# with the current locale
+with self.subTest(locale=None):
+

[Python-checkins] [3.13] bpo-34206: Improve docs and test coverage for pre-init functions (GH-8023) (#125092)

2024-10-08 Thread ncoghlan
https://github.com/python/cpython/commit/64391363ede1dd7af6566db4c9b6e4dbce3b6cb5
commit: 64391363ede1dd7af6566db4c9b6e4dbce3b6cb5
branch: 3.13
author: Alyssa Coghlan 
committer: ncoghlan 
date: 2024-10-08T09:30:06Z
summary:

[3.13] bpo-34206: Improve docs and test coverage for pre-init functions 
(GH-8023) (#125092)

- move the Py_Main documentation from the very high level API section
  to the initialization and finalization section
- make it clear that it encapsulates a full Py_Initialize/Finalize
  cycle of its own
- point out that exactly which settings will be read and applied
  correctly when Py_Main is called after a separate runtime
  initialization call is version dependent
- be explicit that Py_IsInitialized can be called prior to
  initialization
- actually test that Py_IsInitialized can be called prior to
  initialization
- flush stdout in the embedding tests that run code so it appears
  in the expected order when running with "-vv"
- make "-vv" on the subinterpreter embedding tests less spammy

-

(cherry picked from commit 7c4b6a68f263320a2dd19cd5ff63b35c964b1fa8)

Co-authored-by: Carol Willing 

files:
A Misc/NEWS.d/next/C API/2018-06-30-21-48-16.bpo-34008.2Wjtm0.rst
A Misc/NEWS.d/next/Documentation/2018-07-04-20-35-25.bpo-34008.bqecIb.rst
M Doc/c-api/init.rst
M Doc/c-api/init_config.rst
M Doc/c-api/veryhigh.rst
M Lib/test/test_embed.py
M Programs/_testembed.c

diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst
index fd97d1d6ec3a0b..8578ad301d11ba 100644
--- a/Doc/c-api/init.rst
+++ b/Doc/c-api/init.rst
@@ -7,7 +7,8 @@
 Initialization, Finalization, and Threads
 *
 
-See also :ref:`Python Initialization Configuration `.
+See :ref:`Python Initialization Configuration ` for details
+on how to configure the interpreter prior to initialization.
 
 .. _pre-init-safe:
 
@@ -21,6 +22,15 @@ a few functions and the :ref:`global configuration variables
 
 The following functions can be safely called before Python is initialized:
 
+* Functions that initialize the interpreter:
+
+  * :c:func:`Py_Initialize`
+  * :c:func:`Py_InitializeEx`
+  * :c:func:`Py_InitializeFromConfig`
+  * :c:func:`Py_BytesMain`
+  * :c:func:`Py_Main`
+  * the runtime pre-initialization functions covered in :ref:`init-config`
+
 * Configuration functions:
 
   * :c:func:`PyImport_AppendInittab`
@@ -32,6 +42,7 @@ The following functions can be safely called before Python is 
initialized:
   * :c:func:`Py_SetProgramName`
   * :c:func:`Py_SetPythonHome`
   * :c:func:`PySys_ResetWarnOptions`
+  * the configuration functions covered in :ref:`init-config`
 
 * Informative functions:
 
@@ -43,10 +54,12 @@ The following functions can be safely called before Python 
is initialized:
   * :c:func:`Py_GetCopyright`
   * :c:func:`Py_GetPlatform`
   * :c:func:`Py_GetVersion`
+  * :c:func:`Py_IsInitialized`
 
 * Utilities:
 
   * :c:func:`Py_DecodeLocale`
+  * the status reporting and utility functions covered in :ref:`init-config`
 
 * Memory allocators:
 
@@ -62,11 +75,13 @@ The following functions can be safely called before Python 
is initialized:
 
 .. note::
 
-   The following functions **should not be called** before
-   :c:func:`Py_Initialize`: :c:func:`Py_EncodeLocale`, :c:func:`Py_GetPath`,
+   Despite their apparent similarity to some of the functions listed above,
+   the following functions **should not be called** before the interpreter has
+   been initialized: :c:func:`Py_EncodeLocale`, :c:func:`Py_GetPath`,
:c:func:`Py_GetPrefix`, :c:func:`Py_GetExecPrefix`,
:c:func:`Py_GetProgramFullPath`, :c:func:`Py_GetPythonHome`,
-   :c:func:`Py_GetProgramName` and :c:func:`PyEval_InitThreads`.
+   :c:func:`Py_GetProgramName`, :c:func:`PyEval_InitThreads`, and
+   :c:func:`Py_RunMain`.
 
 
 .. _global-conf-vars:
@@ -346,34 +361,42 @@ Initializing and finalizing the interpreter
this should be called before using any other Python/C API functions; see
:ref:`Before Python Initialization ` for the few exceptions.
 
-   This initializes
-   the table of loaded modules (``sys.modules``), and creates the fundamental
-   modules :mod:`builtins`, :mod:`__main__` and :mod:`sys`.  It also 
initializes
-   the module search path (``sys.path``). It does not set ``sys.argv``; use
-   the new :c:type:`PyConfig` API of the :ref:`Python Initialization
-   Configuration ` for that.  This is a no-op when called for a
-   second time
-   (without calling :c:func:`Py_FinalizeEx` first).  There is no return value; 
it is a
-   fatal error if the initialization fails.
-
-   Use the :c:func:`Py_InitializeFromConfig` function to customize the
+   This initializes the table of loaded modules (``sys.modules``), and creates
+   the fundamental modules :mod:`builtins`, :mod:`__main__` and :mod:`sys`.
+   It also initializes the module search path (``sys.path``). It does not set
+   ``sys.argv``; use the :ref:`Python Initialization Configuration 
`
+   API for that. This is a no-op when called for a 

[Python-checkins] gh-124612: Use ghcr.io/python/autoconf instead of public image (#124657)

2024-10-08 Thread corona10
https://github.com/python/cpython/commit/b502573f7f800dbb2e401fa2a7a05eceac692c7a
commit: b502573f7f800dbb2e401fa2a7a05eceac692c7a
branch: main
author: Donghee Na 
committer: corona10 
date: 2024-10-09T09:33:18+09:00
summary:

gh-124612: Use ghcr.io/python/autoconf instead of public image (#124657)

* gh-124612: Use ghcr.io/python/autoconf instead of public image

* Update

files:
M Tools/build/regen-configure.sh

diff --git a/Tools/build/regen-configure.sh b/Tools/build/regen-configure.sh
index e34a36c1a573e5..efc80c8527885c 100755
--- a/Tools/build/regen-configure.sh
+++ b/Tools/build/regen-configure.sh
@@ -5,12 +5,10 @@ set -e -x
 # The check_generated_files job of .github/workflows/build.yml must kept in
 # sync with this script. Use the same container image than the job so the job
 # doesn't need to run autoreconf in a container.
-IMAGE="ubuntu:22.04"
-DEPENDENCIES="autotools-dev autoconf autoconf-archive pkg-config"
+IMAGE="ghcr.io/python/autoconf:2024.10.06.11200919239"
 AUTORECONF="autoreconf -ivf -Werror"
 
 WORK_DIR="/src"
-SHELL_CMD="apt-get update && apt-get -yq install $DEPENDENCIES && cd $WORK_DIR 
&& $AUTORECONF"
 
 abs_srcdir=$(cd $(dirname $0)/../..; pwd)
 
@@ -28,4 +26,4 @@ if command -v selinuxenabled >/dev/null && selinuxenabled; 
then
 PATH_OPT=":Z"
 fi
 
-"$RUNTIME" run --rm -v "$abs_srcdir:$WORK_DIR$PATH_OPT" "$IMAGE" /usr/bin/bash 
-c "$SHELL_CMD"
+"$RUNTIME" run --rm -v "$abs_srcdir:$WORK_DIR$PATH_OPT" "$IMAGE"

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] gh-123849: Fix test_sqlite3.test_table_dump when foreign keys are enabled by default (#123859)

2024-10-08 Thread erlend-aasland
https://github.com/python/cpython/commit/14b44c58e195c4cdee6594a4aacf8bf95b19fcd7
commit: 14b44c58e195c4cdee6594a4aacf8bf95b19fcd7
branch: main
author: Mariusz Felisiak 
committer: erlend-aasland 
date: 2024-10-08T22:46:11Z
summary:

gh-123849: Fix test_sqlite3.test_table_dump when foreign keys are enabled by 
default (#123859)

files:
M Lib/test/test_sqlite3/test_dump.py

diff --git a/Lib/test/test_sqlite3/test_dump.py 
b/Lib/test/test_sqlite3/test_dump.py
index d508f238f84fb5..550cea41976441 100644
--- a/Lib/test/test_sqlite3/test_dump.py
+++ b/Lib/test/test_sqlite3/test_dump.py
@@ -10,6 +10,7 @@ class DumpTests(MemoryDatabaseMixin, unittest.TestCase):
 
 def test_table_dump(self):
 expected_sqls = [
+"PRAGMA foreign_keys=OFF;",
 """CREATE TABLE "index"("index" blob);"""
 ,
 """INSERT INTO "index" VALUES(X'01');"""
@@ -48,7 +49,7 @@ def test_table_dump(self):
 expected_sqls = [
 "PRAGMA foreign_keys=OFF;",
 "BEGIN TRANSACTION;",
-*expected_sqls,
+*expected_sqls[1:],
 "COMMIT;",
 ]
 [self.assertEqual(expected_sqls[i], actual_sqls[i])

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] gh-124832: Add a note to indicate that `datetime.now` may return the same instant (GH-124834) (#125145)

2024-10-08 Thread willingc
https://github.com/python/cpython/commit/f5fea4dec6a23f92ffd367d26648f7a6dbd7745e
commit: f5fea4dec6a23f92ffd367d26648f7a6dbd7745e
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: willingc 
date: 2024-10-08T18:26:07Z
summary:

[3.13] gh-124832: Add a note to indicate that `datetime.now` may return the 
same instant (GH-124834) (#125145)

gh-124832: Add a note to indicate that `datetime.now` may return the same 
instant (GH-124834)

* Update datetime.rst

* Update datetime.rst

replace warning with note

* Update Doc/library/datetime.rst



* Update Doc/library/datetime.rst



-

(cherry picked from commit 760b1e103a0aa696cdf448e0d500cd1bac2213fa)

Co-authored-by: spacemanspiff2007 
<[email protected]>
Co-authored-by: Victor Stinner 
Co-authored-by: Bénédikt Tran <[email protected]>

files:
M Doc/library/datetime.rst

diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst
index 9246aff12a6707..4775aa628e9385 100644
--- a/Doc/library/datetime.rst
+++ b/Doc/library/datetime.rst
@@ -915,6 +915,10 @@ Other constructors, all class methods:
 
This function is preferred over :meth:`today` and :meth:`utcnow`.
 
+   .. note::
+
+  Subsequent calls to :meth:`!datetime.now` may return the same
+  instant depending on the precision of the underlying clock.
 
 .. classmethod:: datetime.utcnow()
 

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.12] gh-124832: Add a note to indicate that `datetime.now` may return the same instant (GH-124834) (#125146)

2024-10-08 Thread willingc
https://github.com/python/cpython/commit/382ee1c7bd292f964f5d97384b93dfbdf7b64061
commit: 382ee1c7bd292f964f5d97384b93dfbdf7b64061
branch: 3.12
author: Miss Islington (bot) <[email protected]>
committer: willingc 
date: 2024-10-08T18:27:50Z
summary:

[3.12] gh-124832: Add a note to indicate that `datetime.now` may return the 
same instant (GH-124834) (#125146)

gh-124832: Add a note to indicate that `datetime.now` may return the same 
instant (GH-124834)

* Update datetime.rst

* Update datetime.rst

replace warning with note

* Update Doc/library/datetime.rst



* Update Doc/library/datetime.rst



-

(cherry picked from commit 760b1e103a0aa696cdf448e0d500cd1bac2213fa)

Co-authored-by: spacemanspiff2007 
<[email protected]>
Co-authored-by: Victor Stinner 
Co-authored-by: Bénédikt Tran <[email protected]>

files:
M Doc/library/datetime.rst

diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst
index 8f5ea47da0db25..671554f2cf3d0d 100644
--- a/Doc/library/datetime.rst
+++ b/Doc/library/datetime.rst
@@ -896,6 +896,10 @@ Other constructors, all class methods:
 
This function is preferred over :meth:`today` and :meth:`utcnow`.
 
+   .. note::
+
+  Subsequent calls to :meth:`!datetime.now` may return the same
+  instant depending on the precision of the underlying clock.
 
 .. classmethod:: datetime.utcnow()
 

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] Itertool docs: Minor clarifications, wording tweaks, spacing, and active voice. (gh-124690) (gh-125148)

2024-10-08 Thread rhettinger
https://github.com/python/cpython/commit/988cdccbe06cd938c83e4fd8dc4bfb6d88e6b9d8
commit: 988cdccbe06cd938c83e4fd8dc4bfb6d88e6b9d8
branch: 3.13
author: Raymond Hettinger 
committer: rhettinger 
date: 2024-10-08T14:29:15-05:00
summary:

[3.13] Itertool docs: Minor clarifications, wording tweaks, spacing, and active 
voice. (gh-124690) (gh-125148)

Minor clarifications, wording tweaks, spacing, and active voice.

files:
M Doc/library/itertools.rst

diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index 8eb8d07b3fb064..e9af6829267fdf 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -58,7 +58,7 @@ IteratorArguments 
  Results
 :func:`compress`data, selectors (d[0] if 
s[0]), (d[1] if s[1]), ... ``compress('ABCDEF', [1,0,1,0,1,1]) 
→ A C E F``
 :func:`dropwhile`   predicate, seq  seq[n], 
seq[n+1], starting when predicate fails ``dropwhile(lambda x: x<5, 
[1,4,6,3,8]) → 6 3 8``
 :func:`filterfalse` predicate, seq  elements of 
seq where predicate(elem) fails ``filterfalse(lambda x: x<5, 
[1,4,6,3,8]) → 6 8``
-:func:`groupby` iterable[, key] sub-iterators 
grouped by value of key(v)
+:func:`groupby` iterable[, key] sub-iterators 
grouped by value of key(v)``groupby(['A','B','DEF'], len) → (1, A 
B) (3, DEF)``
 :func:`islice`  seq, [start,] stop [, step] elements from 
seq[start:stop:step]  ``islice('ABCDEFG', 2, None) → C D E F G``
 :func:`pairwise`iterable(p[0], p[1]), 
(p[1], p[2])  ``pairwise('ABCDEFG') → AB BC CD DE EF 
FG``
 :func:`starmap` func, seq   
func(\*seq[0]), func(\*seq[1]), ... ``starmap(pow, [(2,5), 
(3,2), (10,3)]) → 32 9 1000``
@@ -93,7 +93,7 @@ Examples Results
 Itertool Functions
 --
 
-The following module functions all construct and return iterators. Some provide
+The following functions all construct and return iterators. Some provide
 streams of infinite length, so they should only be accessed by functions or
 loops that truncate the stream.
 
@@ -131,11 +131,12 @@ loops that truncate the stream.
 total = function(total, element)
 yield total
 
-The *function* argument can be set to :func:`min` for a running
-minimum, :func:`max` for a running maximum, or :func:`operator.mul`
-for a running product.  `Amortization tables
-`_
-can be built by accumulating interest and applying payments:
+To compute a running minimum, set *function* to :func:`min`.
+For a running maximum, set *function* to :func:`max`.
+Or for a running product, set *function* to :func:`operator.mul`.
+To build an `Amortization table
+`_,
+accumulate the interest and apply payments:
 
 .. doctest::
 
@@ -202,10 +203,10 @@ loops that truncate the stream.
 
 .. function:: chain(*iterables)
 
-   Make an iterator that returns elements from the first iterable until it is
-   exhausted, then proceeds to the next iterable, until all of the iterables 
are
-   exhausted.  Used for treating consecutive sequences as a single sequence.
-   Roughly equivalent to::
+   Make an iterator that returns elements from the first iterable until
+   it is exhausted, then proceeds to the next iterable, until all of the
+   iterables are exhausted.  This combines multiple data sources into a
+   single iterator.  Roughly equivalent to::
 
   def chain(*iterables):
   # chain('ABC', 'DEF') → A B C D E F
@@ -353,10 +354,12 @@ loops that truncate the stream.
 
   def cycle(iterable):
   # cycle('ABCD') → A B C D A B C D A B C D ...
+
   saved = []
   for element in iterable:
   yield element
   saved.append(element)
+
   while saved:
   for element in saved:
   yield element
@@ -396,8 +399,10 @@ loops that truncate the stream.
 
   def filterfalse(predicate, iterable):
   # filterfalse(lambda x: x<5, [1,4,6,3,8]) → 6 8
+
   if predicate is None:
   predicate = bool
+
   for x in iterable:
   if not predicate(x):
   yield x
@@ -474,7 +479,7 @@ loops that truncate the stream.
If *start* is zero or ``None``, iteration starts at zero.  Otherwise,
elements from the iterable are skipped until *start* is reached.
 
-   If *stop* is ``None``, iteration continues until the iterable is
+   If *stop* is ``None``, iteration continues until the input is
exhausted, if at all.  Otherwise, it stops at

[Python-checkins] [3.13] Tee of tee was not producing n independent iterators (gh-123884) (gh-125081)

2024-10-08 Thread rhettinger
https://github.com/python/cpython/commit/7bc99dd49ed4cebe4795cc7914c4231209b2aa4b
commit: 7bc99dd49ed4cebe4795cc7914c4231209b2aa4b
branch: 3.13
author: Raymond Hettinger 
committer: rhettinger 
date: 2024-10-08T14:11:43-05:00
summary:

[3.13] Tee of tee was not producing n independent iterators (gh-123884) 
(gh-125081)

files:
A Misc/NEWS.d/next/Library/2024-09-24-22-38-51.gh-issue-123884.iEPTK4.rst
M Doc/library/itertools.rst
M Lib/test/test_itertools.py
M Modules/itertoolsmodule.c

diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index ceaab2f3e72454..8eb8d07b3fb064 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -691,25 +691,36 @@ loops that truncate the stream.
 
 def tee(iterable, n=2):
 if n < 0:
-raise ValueError('n must be >= 0')
-iterator = iter(iterable)
-shared_link = [None, None]
-return tuple(_tee(iterator, shared_link) for _ in range(n))
-
-def _tee(iterator, link):
-try:
-while True:
-if link[1] is None:
-link[0] = next(iterator)
-link[1] = [None, None]
-value, link = link
-yield value
-except StopIteration:
-return
-
-   Once a :func:`tee` has been created, the original *iterable* should not be
-   used anywhere else; otherwise, the *iterable* could get advanced without
-   the tee objects being informed.
+raise ValueError
+if n == 0:
+return ()
+iterator = _tee(iterable)
+result = [iterator]
+for _ in range(n - 1):
+result.append(_tee(iterator))
+return tuple(result)
+
+class _tee:
+
+def __init__(self, iterable):
+it = iter(iterable)
+if isinstance(it, _tee):
+self.iterator = it.iterator
+self.link = it.link
+else:
+self.iterator = it
+self.link = [None, None]
+
+def __iter__(self):
+return self
+
+def __next__(self):
+link = self.link
+if link[1] is None:
+link[0] = next(self.iterator)
+link[1] = [None, None]
+value, self.link = link
+return value
 
When the input *iterable* is already a tee iterator object, all
members of the return tuple are constructed as if they had been
diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py
index 2c8752d215dc69..1e58354b43c14f 100644
--- a/Lib/test/test_itertools.py
+++ b/Lib/test/test_itertools.py
@@ -1642,10 +1642,11 @@ def test_tee(self):
 self.assertEqual(len(result), n)
 self.assertEqual([list(x) for x in result], [list('abc')]*n)
 
-# tee pass-through to copyable iterator
+# tee objects are independent (see bug gh-123884)
 a, b = tee('abc')
 c, d = tee(a)
-self.assertTrue(a is c)
+e, f = tee(c)
+self.assertTrue(len({a, b, c, d, e, f}) == 6)
 
 # test tee_new
 t1, t2 = tee('abc')
@@ -2072,21 +2073,36 @@ def test_tee_recipe(self):
 
 def tee(iterable, n=2):
 if n < 0:
-raise ValueError('n must be >= 0')
-iterator = iter(iterable)
-shared_link = [None, None]
-return tuple(_tee(iterator, shared_link) for _ in range(n))
+raise ValueError
+if n == 0:
+return ()
+iterator = _tee(iterable)
+result = [iterator]
+for _ in range(n - 1):
+result.append(_tee(iterator))
+return tuple(result)
+
+class _tee:
+
+def __init__(self, iterable):
+it = iter(iterable)
+if isinstance(it, _tee):
+self.iterator = it.iterator
+self.link = it.link
+else:
+self.iterator = it
+self.link = [None, None]
 
-def _tee(iterator, link):
-try:
-while True:
-if link[1] is None:
-link[0] = next(iterator)
-link[1] = [None, None]
-value, link = link
-yield value
-except StopIteration:
-return
+def __iter__(self):
+return self
+
+def __next__(self):
+link = self.link
+if link[1] is None:
+link[0] = next(self.iterator)
+link[1] = [None, None]
+value, self.link = link
+return value
 
 # End tee() recipe #
 
@@ -213

[Python-checkins] gh-125063: Emit slices as constants in the bytecode compiler (#125064)

2024-10-08 Thread mdboom
https://github.com/python/cpython/commit/c6127af8685c2a9b416207e46089cee79d028b85
commit: c6127af8685c2a9b416207e46089cee79d028b85
branch: main
author: Michael Droettboom 
committer: mdboom 
date: 2024-10-08T13:18:39-04:00
summary:

gh-125063: Emit slices as constants in the bytecode compiler (#125064)

* Make slices marshallable

* Emit slices as constants

* Update Python/marshal.c

Co-authored-by: Peter Bierma 

* Refactor codegen_slice into two functions so it
always has the same net effect

* Fix for free-threaded builds

* Simplify marshal loading of slices

* Only return SUCCESS/ERROR from codegen_slice

-

Co-authored-by: Mark Shannon 
Co-authored-by: Peter Bierma 

files:
M Include/internal/pycore_magic_number.h
M Lib/test/test_compile.py
M Objects/codeobject.c
M Objects/sliceobject.c
M Python/codegen.c
M Python/marshal.c

diff --git a/Include/internal/pycore_magic_number.h 
b/Include/internal/pycore_magic_number.h
index 2414d25d41bfbf..a88ff2deeba941 100644
--- a/Include/internal/pycore_magic_number.h
+++ b/Include/internal/pycore_magic_number.h
@@ -259,6 +259,7 @@ Known values:
 Python 3.14a1 3605 (Move ENTER_EXECUTOR to opcode 255)
 Python 3.14a1 3606 (Specialize CALL_KW)
 Python 3.14a1 3607 (Add pseudo instructions JUMP_IF_TRUE/FALSE)
+Python 3.14a1 3608 (Add support for slices)
 
 Python 3.15 will start with 3650
 
@@ -271,7 +272,7 @@ PC/launcher.c must also be updated.
 
 */
 
-#define PYC_MAGIC_NUMBER 3607
+#define PYC_MAGIC_NUMBER 3608
 /* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
(little-endian) and then appending b'\r\n'. */
 #define PYC_MAGIC_NUMBER_TOKEN \
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index e6dc7a53189e12..6f838da6018741 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -1386,6 +1386,14 @@ def check_op_count(func, op, expected):
 actual += 1
 self.assertEqual(actual, expected)
 
+def check_consts(func, typ, expected):
+slice_consts = 0
+consts = func.__code__.co_consts
+for instr in dis.Bytecode(func):
+if instr.opname == "LOAD_CONST" and 
isinstance(consts[instr.oparg], typ):
+slice_consts += 1
+self.assertEqual(slice_consts, expected)
+
 def load():
 return x[a:b] + x [a:] + x[:b] + x[:]
 
@@ -1401,15 +1409,30 @@ def long_slice():
 def aug():
 x[a:b] += y
 
-check_op_count(load, "BINARY_SLICE", 4)
+def aug_const():
+x[1:2] += y
+
+def compound_const_slice():
+x[1:2:3, 4:5:6] = y
+
+check_op_count(load, "BINARY_SLICE", 3)
 check_op_count(load, "BUILD_SLICE", 0)
-check_op_count(store, "STORE_SLICE", 4)
+check_consts(load, slice, 1)
+check_op_count(store, "STORE_SLICE", 3)
 check_op_count(store, "BUILD_SLICE", 0)
+check_consts(store, slice, 1)
 check_op_count(long_slice, "BUILD_SLICE", 1)
 check_op_count(long_slice, "BINARY_SLICE", 0)
 check_op_count(aug, "BINARY_SLICE", 1)
 check_op_count(aug, "STORE_SLICE", 1)
 check_op_count(aug, "BUILD_SLICE", 0)
+check_op_count(aug_const, "BINARY_SLICE", 0)
+check_op_count(aug_const, "STORE_SLICE", 0)
+check_consts(aug_const, slice, 1)
+check_op_count(compound_const_slice, "BINARY_SLICE", 0)
+check_op_count(compound_const_slice, "BUILD_SLICE", 0)
+check_consts(compound_const_slice, slice, 0)
+check_consts(compound_const_slice, tuple, 1)
 
 def test_compare_positions(self):
 for opname_prefix, op in [
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index 6f0b3f8b9a3262..8a2f4d32b911d9 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -2336,6 +2336,7 @@ _PyCode_ConstantKey(PyObject *op)
 if (op == Py_None || op == Py_Ellipsis
|| PyLong_CheckExact(op)
|| PyUnicode_CheckExact(op)
+   || PySlice_Check(op)
   /* code_richcompare() uses _PyCode_ConstantKey() internally */
|| PyCode_Check(op))
 {
diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c
index 1b6d35998c2b69..4fef0af93fe095 100644
--- a/Objects/sliceobject.c
+++ b/Objects/sliceobject.c
@@ -343,7 +343,7 @@ Create a slice object.  This is used for extended slicing 
(e.g. a[0:10:2]).");
 static void
 slice_dealloc(PySliceObject *r)
 {
-_PyObject_GC_UNTRACK(r);
+PyObject_GC_UnTrack(r);
 Py_DECREF(r->step);
 Py_DECREF(r->start);
 Py_DECREF(r->stop);
diff --git a/Python/codegen.c b/Python/codegen.c
index 896c30cc14952a..689d2b5124e9d3 100644
--- a/Python/codegen.c
+++ b/Python/codegen.c
@@ -194,6 +194,7 @@ static int codegen_visit_expr(compiler *, expr_ty);
 static int codegen_augassign(compiler *, stmt_ty);
 static int codegen_annassign(compiler *, stmt_ty);
 static int codegen_subscript(compiler *, expr_ty);
+static int codegen_slic

[Python-checkins] Misc improvements to the itertools docs (gh-125147)

2024-10-08 Thread rhettinger
https://github.com/python/cpython/commit/b2a7272408593355c4c8e1d2ce9018cf96691bea
commit: b2a7272408593355c4c8e1d2ce9018cf96691bea
branch: main
author: Raymond Hettinger 
committer: rhettinger 
date: 2024-10-08T14:02:58-05:00
summary:

Misc improvements to the itertools docs (gh-125147)

files:
M Doc/library/itertools.rst

diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index 9a62249816c9bf..c138e903fa5a0f 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -134,7 +134,7 @@ loops that truncate the stream.
 To compute a running minimum, set *function* to :func:`min`.
 For a running maximum, set *function* to :func:`max`.
 Or for a running product, set *function* to :func:`operator.mul`.
-To build an `Amortization table
+To build an `amortization table
 `_,
 accumulate the interest and apply payments:
 
@@ -736,6 +736,26 @@ loops that truncate the stream.
allows nested :func:`tee` calls to share the same underlying data
chain and to have a single update step rather than a chain of calls.
 
+   The flattening property makes tee iterators efficiently peekable:
+
+   .. testcode::
+
+  def lookahead(tee_iterator):
+   "Return the next value without moving the input forward"
+   [forked_iterator] = tee(tee_iterator, 1)
+   return next(forked_iterator)
+
+   .. doctest::
+
+  >>> iterator = iter('abcdef')
+  >>> [iterator] = tee(iterator, 1)   # Make the input peekable
+  >>> next(iterator)  # Move the iterator forward
+  'a'
+  >>> lookahead(iterator) # Check next value
+  'b'
+  >>> next(iterator)  # Continue moving forward
+  'b'
+
``tee`` iterators are not threadsafe. A :exc:`RuntimeError` may be
raised when simultaneously using iterators returned by the same :func:`tee`
call, even if the original *iterable* is threadsafe.
@@ -952,15 +972,6 @@ and :term:`generators ` which incur interpreter 
overhead.
iterators = cycle(islice(iterators, num_active))
yield from map(next, iterators)
 
-   def partition(predicate, iterable):
-   """Partition entries into false entries and true entries.
-
-   If *predicate* is slow, consider wrapping it with functools.lru_cache().
-   """
-   # partition(is_odd, range(10)) → 0 2 4 6 8   and  1 3 5 7 9
-   t1, t2 = tee(iterable)
-   return filterfalse(predicate, t1), filter(predicate, t2)
-
def subslices(seq):
"Return all contiguous non-empty subslices of a sequence."
# subslices('ABCD') → A AB ABC ABCD B BC BCD C CD D
@@ -1178,15 +1189,19 @@ The following recipes have a more mathematical flavor:
 >>> list(it)
 ['d', 'e', 'f']
 
+
 >>> list(prepend(1, [2, 3, 4]))
 [1, 2, 3, 4]
 
+
 >>> list(enumerate('abc'))
 [(0, 'a'), (1, 'b'), (2, 'c')]
 
+
 >>> list(islice(tabulate(lambda x: 2*x), 4))
 [0, 2, 4, 6]
 
+
 >>> list(tail(3, 'ABCDEFG'))
 ['E', 'F', 'G']
 >>> # Verify the input is consumed greedily
@@ -1195,6 +1210,7 @@ The following recipes have a more mathematical flavor:
 >>> list(input_iterator)
 []
 
+
 >>> it = iter(range(10))
 >>> consume(it, 3)
 >>> # Verify the input is consumed lazily
@@ -1205,6 +1221,7 @@ The following recipes have a more mathematical flavor:
 >>> next(it, 'Done')
 'Done'
 
+
 >>> nth('abcde', 3)
 'd'
 >>> nth('abcde', 9) is None
@@ -1216,6 +1233,7 @@ The following recipes have a more mathematical flavor:
 >>> list(it)
 ['d', 'e']
 
+
 >>> [all_equal(s) for s in ('', 'A', '', 'AAAB', 'AAABA')]
 [True, True, True, False, False]
 >>> [all_equal(s, key=str.casefold) for s in ('', 'A', 'AaAa', 'AAAB', 
'AAABA')]
@@ -1229,24 +1247,19 @@ The following recipes have a more mathematical flavor:
 >>> ''.join(it)
 'bbccc'
 
+
 >>> quantify(range(99), lambda x: x%2==0)
 50
-
 >>> quantify([True, False, False, True, True])
 3
-
 >>> quantify(range(12), predicate=lambda x: x%2==1)
 6
 
+
 >>> a = [[1, 2, 3], [4, 5, 6]]
 >>> list(flatten(a))
 [1, 2, 3, 4, 5, 6]
 
->>> list(repeatfunc(pow, 5, 2, 3))
-[8, 8, 8, 8, 8]
-
->>> take(5, map(int, repeatfunc(random.random)))
-[0, 0, 0, 0, 0]
 
 >>> list(ncycles('abc', 3))
 ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c']
@@ -1256,9 +1269,11 @@ The following recipes have a more mathematical flavor:
 >>> list(input_iterator)
 []
 
+
 >>> sum_of_squares([10, 20, 30])
 1400
 
+
 >>> list(reshape([(0, 1), (2, 3), (4, 5)], 3))
 [(0, 1, 2), (3, 4, 5)]
 >>> M = [(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11)]
@@ -1279,6 +1294,7 @@ The following recipes have a more mathematical flavor:
 >>> list(reshape(M, 12))
 [(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)]
 
+
 >>> list(transpose([(1, 2, 3), (11, 22, 33)]))
 [(

[Python-checkins] [3.13] GH-124478: Cleanup argparse documentation (GH-124877) (#125162)

2024-10-08 Thread JelleZijlstra
https://github.com/python/cpython/commit/5f2a5ac9dce719f589ba1d22272b184fb9af2676
commit: 5f2a5ac9dce719f589ba1d22272b184fb9af2676
branch: 3.13
author: Savannah Ostrowski 
committer: JelleZijlstra 
date: 2024-10-08T16:20:01-07:00
summary:

[3.13] GH-124478: Cleanup argparse documentation (GH-124877) (#125162)

(cherry picked from commit 37228bd16e3ef97d32da08848552f7ef016d68ab)

Co-authored-by: Jelle Zijlstra 
Co-authored-by: Tomas R 

files:
A Doc/howto/argparse-optparse.rst
M Doc/library/argparse.rst

diff --git a/Doc/howto/argparse-optparse.rst b/Doc/howto/argparse-optparse.rst
new file mode 100644
index 00..cef2d893b28a62
--- /dev/null
+++ b/Doc/howto/argparse-optparse.rst
@@ -0,0 +1,55 @@
+.. currentmodule:: argparse
+
+.. _upgrading-optparse-code:
+
+==
+Upgrading optparse code
+==
+
+Originally, the :mod:`argparse` module had attempted to maintain compatibility
+with :mod:`optparse`.  However, :mod:`optparse` was difficult to extend
+transparently, particularly with the changes required to support
+``nargs=`` specifiers and better usage messages.  When most everything in
+:mod:`optparse` had either been copy-pasted over or monkey-patched, it no
+longer seemed practical to try to maintain the backwards compatibility.
+
+The :mod:`argparse` module improves on the :mod:`optparse`
+module in a number of ways including:
+
+* Handling positional arguments.
+* Supporting subcommands.
+* Allowing alternative option prefixes like ``+`` and ``/``.
+* Handling zero-or-more and one-or-more style arguments.
+* Producing more informative usage messages.
+* Providing a much simpler interface for custom ``type`` and ``action``.
+
+A partial upgrade path from :mod:`optparse` to :mod:`argparse`:
+
+* Replace all :meth:`optparse.OptionParser.add_option` calls with
+  :meth:`ArgumentParser.add_argument` calls.
+
+* Replace ``(options, args) = parser.parse_args()`` with ``args =
+  parser.parse_args()`` and add additional :meth:`ArgumentParser.add_argument`
+  calls for the positional arguments. Keep in mind that what was previously
+  called ``options``, now in the :mod:`argparse` context is called ``args``.
+
+* Replace :meth:`optparse.OptionParser.disable_interspersed_args`
+  by using :meth:`~ArgumentParser.parse_intermixed_args` instead of
+  :meth:`~ArgumentParser.parse_args`.
+
+* Replace callback actions and the ``callback_*`` keyword arguments with
+  ``type`` or ``action`` arguments.
+
+* Replace string names for ``type`` keyword arguments with the corresponding
+  type objects (e.g. int, float, complex, etc).
+
+* Replace :class:`optparse.Values` with :class:`Namespace` and
+  :exc:`optparse.OptionError` and :exc:`optparse.OptionValueError` with
+  :exc:`ArgumentError`.
+
+* Replace strings with implicit arguments such as ``%default`` or ``%prog`` 
with
+  the standard Python syntax to use dictionaries to format strings, that is,
+  ``%(default)s`` and ``%(prog)s``.
+
+* Replace the OptionParser constructor ``version`` argument with a call to
+  ``parser.add_argument('--version', action='version', version='')``.
diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst
index 95ee7574610001..0014947d019261 100644
--- a/Doc/library/argparse.rst
+++ b/Doc/library/argparse.rst
@@ -1,4 +1,4 @@
-:mod:`!argparse` --- Parser for command-line options, arguments and 
sub-commands
+:mod:`!argparse` --- Parser for command-line options, arguments and subcommands
 

 
 .. module:: argparse
@@ -19,17 +19,13 @@
introduction to Python command-line parsing, have a look at the
:ref:`argparse tutorial `.
 
-The :mod:`argparse` module makes it easy to write user-friendly command-line
-interfaces. The program defines what arguments it requires, and :mod:`argparse`
-will figure out how to parse those out of :data:`sys.argv`.  The 
:mod:`argparse`
+The :mod:`!argparse` module makes it easy to write user-friendly command-line
+interfaces. The program defines what arguments it requires, and 
:mod:`!argparse`
+will figure out how to parse those out of :data:`sys.argv`.  The 
:mod:`!argparse`
 module also automatically generates help and usage messages.  The module
 will also issue errors when users give the program invalid arguments.
 
-
-Core Functionality
---
-
-The :mod:`argparse` module's support for command-line interfaces is built
+The :mod:`!argparse` module's support for command-line interfaces is built
 around an instance of :class:`argparse.ArgumentParser`.  It is a container for
 argument specifications and has options that apply to the parser as whole::
 
@@ -53,133 +49,9 @@ the extracted data in a :class:`argparse.Namespace` object::
args = parser.parse_args()
print(args.filename, args.count, args.verbose)
 
-
-Quick Links for add_argument()
---
-
- 
==

[Python-checkins] [3.12] GH-124478: Cleanup argparse documentation (GH-124877) (#125164)

2024-10-08 Thread JelleZijlstra
https://github.com/python/cpython/commit/0dbad1d8b723e1a6a46784ce720ea474f693127e
commit: 0dbad1d8b723e1a6a46784ce720ea474f693127e
branch: 3.12
author: Savannah Ostrowski 
committer: JelleZijlstra 
date: 2024-10-08T16:20:49-07:00
summary:

[3.12] GH-124478: Cleanup argparse documentation (GH-124877) (#125164)

(cherry picked from commit 37228bd16e3ef97d32da08848552f7ef016d68ab)

Co-authored-by: Jelle Zijlstra 
Co-authored-by: Tomas R 

files:
A Doc/howto/argparse-optparse.rst
M Doc/library/argparse.rst

diff --git a/Doc/howto/argparse-optparse.rst b/Doc/howto/argparse-optparse.rst
new file mode 100644
index 00..cef2d893b28a62
--- /dev/null
+++ b/Doc/howto/argparse-optparse.rst
@@ -0,0 +1,55 @@
+.. currentmodule:: argparse
+
+.. _upgrading-optparse-code:
+
+==
+Upgrading optparse code
+==
+
+Originally, the :mod:`argparse` module had attempted to maintain compatibility
+with :mod:`optparse`.  However, :mod:`optparse` was difficult to extend
+transparently, particularly with the changes required to support
+``nargs=`` specifiers and better usage messages.  When most everything in
+:mod:`optparse` had either been copy-pasted over or monkey-patched, it no
+longer seemed practical to try to maintain the backwards compatibility.
+
+The :mod:`argparse` module improves on the :mod:`optparse`
+module in a number of ways including:
+
+* Handling positional arguments.
+* Supporting subcommands.
+* Allowing alternative option prefixes like ``+`` and ``/``.
+* Handling zero-or-more and one-or-more style arguments.
+* Producing more informative usage messages.
+* Providing a much simpler interface for custom ``type`` and ``action``.
+
+A partial upgrade path from :mod:`optparse` to :mod:`argparse`:
+
+* Replace all :meth:`optparse.OptionParser.add_option` calls with
+  :meth:`ArgumentParser.add_argument` calls.
+
+* Replace ``(options, args) = parser.parse_args()`` with ``args =
+  parser.parse_args()`` and add additional :meth:`ArgumentParser.add_argument`
+  calls for the positional arguments. Keep in mind that what was previously
+  called ``options``, now in the :mod:`argparse` context is called ``args``.
+
+* Replace :meth:`optparse.OptionParser.disable_interspersed_args`
+  by using :meth:`~ArgumentParser.parse_intermixed_args` instead of
+  :meth:`~ArgumentParser.parse_args`.
+
+* Replace callback actions and the ``callback_*`` keyword arguments with
+  ``type`` or ``action`` arguments.
+
+* Replace string names for ``type`` keyword arguments with the corresponding
+  type objects (e.g. int, float, complex, etc).
+
+* Replace :class:`optparse.Values` with :class:`Namespace` and
+  :exc:`optparse.OptionError` and :exc:`optparse.OptionValueError` with
+  :exc:`ArgumentError`.
+
+* Replace strings with implicit arguments such as ``%default`` or ``%prog`` 
with
+  the standard Python syntax to use dictionaries to format strings, that is,
+  ``%(default)s`` and ``%(prog)s``.
+
+* Replace the OptionParser constructor ``version`` argument with a call to
+  ``parser.add_argument('--version', action='version', version='')``.
diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst
index 632185b3f183f5..abe20d7d611695 100644
--- a/Doc/library/argparse.rst
+++ b/Doc/library/argparse.rst
@@ -1,4 +1,4 @@
-:mod:`!argparse` --- Parser for command-line options, arguments and 
sub-commands
+:mod:`!argparse` --- Parser for command-line options, arguments and subcommands
 

 
 .. module:: argparse
@@ -19,17 +19,13 @@
introduction to Python command-line parsing, have a look at the
:ref:`argparse tutorial `.
 
-The :mod:`argparse` module makes it easy to write user-friendly command-line
-interfaces. The program defines what arguments it requires, and :mod:`argparse`
-will figure out how to parse those out of :data:`sys.argv`.  The 
:mod:`argparse`
+The :mod:`!argparse` module makes it easy to write user-friendly command-line
+interfaces. The program defines what arguments it requires, and 
:mod:`!argparse`
+will figure out how to parse those out of :data:`sys.argv`.  The 
:mod:`!argparse`
 module also automatically generates help and usage messages.  The module
 will also issue errors when users give the program invalid arguments.
 
-
-Core Functionality
---
-
-The :mod:`argparse` module's support for command-line interfaces is built
+The :mod:`!argparse` module's support for command-line interfaces is built
 around an instance of :class:`argparse.ArgumentParser`.  It is a container for
 argument specifications and has options that apply to the parser as whole::
 
@@ -53,133 +49,9 @@ the extracted data in a :class:`argparse.Namespace` object::
args = parser.parse_args()
print(args.filename, args.count, args.verbose)
 
-
-Quick Links for add_argument()
---
-
- 
==

[Python-checkins] gh-116110: remove extra processing for the __signature__ attribute (GH-116234)

2024-10-08 Thread ethanfurman
https://github.com/python/cpython/commit/eafd14fbe0fd464b9d700f6d00137415193aa143
commit: eafd14fbe0fd464b9d700f6d00137415193aa143
branch: main
author: Sergey B Kirpichev 
committer: ethanfurman 
date: 2024-10-08T12:36:03-07:00
summary:

gh-116110: remove extra processing for the __signature__ attribute (GH-116234)

This is an alternative to GH-100168.

files:
A Misc/NEWS.d/next/Library/2024-02-27-10-22-15.gh-issue-115937.0cVNur.rst
M Lib/enum.py
M Lib/inspect.py
M Lib/test/test_inspect/test_inspect.py

diff --git a/Lib/enum.py b/Lib/enum.py
index 9d53eb86bc2116..17d72738792982 100644
--- a/Lib/enum.py
+++ b/Lib/enum.py
@@ -1092,6 +1092,21 @@ def _add_member_(cls, name, member):
 # now add to _member_map_ (even aliases)
 cls._member_map_[name] = member
 
+@property
+def __signature__(cls):
+from inspect import Parameter, Signature
+if cls._member_names_:
+return Signature([Parameter('values', Parameter.VAR_POSITIONAL)])
+else:
+return Signature([Parameter('new_class_name', 
Parameter.POSITIONAL_ONLY),
+  Parameter('names', 
Parameter.POSITIONAL_OR_KEYWORD),
+  Parameter('module', Parameter.KEYWORD_ONLY, 
default=None),
+  Parameter('qualname', Parameter.KEYWORD_ONLY, 
default=None),
+  Parameter('type', Parameter.KEYWORD_ONLY, 
default=None),
+  Parameter('start', Parameter.KEYWORD_ONLY, 
default=1),
+  Parameter('boundary', Parameter.KEYWORD_ONLY, 
default=None)])
+
+
 EnumMeta = EnumType # keep EnumMeta name for backwards compatibility
 
 
@@ -1135,13 +1150,6 @@ class Enum(metaclass=EnumType):
 attributes -- see the documentation for details.
 """
 
-@classmethod
-def __signature__(cls):
-if cls._member_names_:
-return '(*values)'
-else:
-return '(new_class_name, /, names, *, module=None, qualname=None, 
type=None, start=1, boundary=None)'
-
 def __new__(cls, value):
 # all enum instances are actually created during class construction
 # without calling this method; this method is called by the metaclass'
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 17314564f35397..1763ef640bbe04 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -2424,18 +2424,10 @@ def _signature_from_callable(obj, *,
 pass
 else:
 if sig is not None:
-# since __text_signature__ is not writable on classes, 
__signature__
-# may contain text (or be a callable that returns text);
-# if so, convert it
-o_sig = sig
-if not isinstance(sig, (Signature, str)) and callable(sig):
-sig = sig()
-if isinstance(sig, str):
-sig = _signature_fromstr(sigcls, obj, sig)
 if not isinstance(sig, Signature):
 raise TypeError(
 'unexpected object {!r} in __signature__ '
-'attribute'.format(o_sig))
+'attribute'.format(sig))
 return sig
 
 try:
diff --git a/Lib/test/test_inspect/test_inspect.py 
b/Lib/test/test_inspect/test_inspect.py
index d2dc9e147d29c2..2ecb7ec1e26e0e 100644
--- a/Lib/test/test_inspect/test_inspect.py
+++ b/Lib/test/test_inspect/test_inspect.py
@@ -4879,38 +4879,6 @@ def foo(): pass
 self.assertEqual(signature_func(foo), inspect.Signature())
 self.assertEqual(inspect.get_annotations(foo), {})
 
-def test_signature_as_str(self):
-self.maxDiff = None
-class S:
-__signature__ = '(a, b=2)'
-
-self.assertEqual(self.signature(S),
- ((('a', ..., ..., 'positional_or_keyword'),
-   ('b', 2, ..., 'positional_or_keyword')),
-  ...))
-
-def test_signature_as_callable(self):
-# __signature__ should be either a staticmethod or a bound classmethod
-class S:
-@classmethod
-def __signature__(cls):
-return '(a, b=2)'
-
-self.assertEqual(self.signature(S),
- ((('a', ..., ..., 'positional_or_keyword'),
-   ('b', 2, ..., 'positional_or_keyword')),
-  ...))
-
-class S:
-@staticmethod
-def __signature__():
-return '(a, b=2)'
-
-self.assertEqual(self.signature(S),
- ((('a', ..., ..., 'positional_or_keyword'),
-   ('b', 2, ..., 'positional_or_keyword')),
-  ...))
-
 def test_signature_on_derived_classes(self):
 # gh-105080: Make sure that signatures are consistent on derived 
classes
 
diff --git 
a/Misc/NEWS.d/next/Library/2024-02-27-10-22-15.gh-issue-115937.0cVNur.rst 
b/Misc/NEWS.d/next/Library/2024-02-27

[Python-checkins] [3.13] Misc improvements to the itertools docs (gh-125147) (gh-125149)

2024-10-08 Thread rhettinger
https://github.com/python/cpython/commit/21a99add4563ccd145f426ceb95f35c3f6c54230
commit: 21a99add4563ccd145f426ceb95f35c3f6c54230
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: rhettinger 
date: 2024-10-08T14:37:13-05:00
summary:

[3.13] Misc improvements to the itertools docs (gh-125147) (gh-125149)

files:
M Doc/library/itertools.rst

diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index e9af6829267fdf..79b729e36f931a 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -134,7 +134,7 @@ loops that truncate the stream.
 To compute a running minimum, set *function* to :func:`min`.
 For a running maximum, set *function* to :func:`max`.
 Or for a running product, set *function* to :func:`operator.mul`.
-To build an `Amortization table
+To build an `amortization table
 `_,
 accumulate the interest and apply payments:
 
@@ -736,6 +736,26 @@ loops that truncate the stream.
allows nested :func:`tee` calls to share the same underlying data
chain and to have a single update step rather than a chain of calls.
 
+   The flattening property makes tee iterators efficiently peekable:
+
+   .. testcode::
+
+  def lookahead(tee_iterator):
+   "Return the next value without moving the input forward"
+   [forked_iterator] = tee(tee_iterator, 1)
+   return next(forked_iterator)
+
+   .. doctest::
+
+  >>> iterator = iter('abcdef')
+  >>> [iterator] = tee(iterator, 1)   # Make the input peekable
+  >>> next(iterator)  # Move the iterator forward
+  'a'
+  >>> lookahead(iterator) # Check next value
+  'b'
+  >>> next(iterator)  # Continue moving forward
+  'b'
+
``tee`` iterators are not threadsafe. A :exc:`RuntimeError` may be
raised when simultaneously using iterators returned by the same :func:`tee`
call, even if the original *iterable* is threadsafe.
@@ -952,15 +972,6 @@ and :term:`generators ` which incur interpreter 
overhead.
iterators = cycle(islice(iterators, num_active))
yield from map(next, iterators)
 
-   def partition(predicate, iterable):
-   """Partition entries into false entries and true entries.
-
-   If *predicate* is slow, consider wrapping it with functools.lru_cache().
-   """
-   # partition(is_odd, range(10)) → 0 2 4 6 8   and  1 3 5 7 9
-   t1, t2 = tee(iterable)
-   return filterfalse(predicate, t1), filter(predicate, t2)
-
def subslices(seq):
"Return all contiguous non-empty subslices of a sequence."
# subslices('ABCD') → A AB ABC ABCD B BC BCD C CD D
@@ -1178,15 +1189,19 @@ The following recipes have a more mathematical flavor:
 >>> list(it)
 ['d', 'e', 'f']
 
+
 >>> list(prepend(1, [2, 3, 4]))
 [1, 2, 3, 4]
 
+
 >>> list(enumerate('abc'))
 [(0, 'a'), (1, 'b'), (2, 'c')]
 
+
 >>> list(islice(tabulate(lambda x: 2*x), 4))
 [0, 2, 4, 6]
 
+
 >>> list(tail(3, 'ABCDEFG'))
 ['E', 'F', 'G']
 >>> # Verify the input is consumed greedily
@@ -1195,6 +1210,7 @@ The following recipes have a more mathematical flavor:
 >>> list(input_iterator)
 []
 
+
 >>> it = iter(range(10))
 >>> consume(it, 3)
 >>> # Verify the input is consumed lazily
@@ -1205,6 +1221,7 @@ The following recipes have a more mathematical flavor:
 >>> next(it, 'Done')
 'Done'
 
+
 >>> nth('abcde', 3)
 'd'
 >>> nth('abcde', 9) is None
@@ -1216,6 +1233,7 @@ The following recipes have a more mathematical flavor:
 >>> list(it)
 ['d', 'e']
 
+
 >>> [all_equal(s) for s in ('', 'A', '', 'AAAB', 'AAABA')]
 [True, True, True, False, False]
 >>> [all_equal(s, key=str.casefold) for s in ('', 'A', 'AaAa', 'AAAB', 
'AAABA')]
@@ -1229,24 +1247,19 @@ The following recipes have a more mathematical flavor:
 >>> ''.join(it)
 'bbccc'
 
+
 >>> quantify(range(99), lambda x: x%2==0)
 50
-
 >>> quantify([True, False, False, True, True])
 3
-
 >>> quantify(range(12), predicate=lambda x: x%2==1)
 6
 
+
 >>> a = [[1, 2, 3], [4, 5, 6]]
 >>> list(flatten(a))
 [1, 2, 3, 4, 5, 6]
 
->>> list(repeatfunc(pow, 5, 2, 3))
-[8, 8, 8, 8, 8]
-
->>> take(5, map(int, repeatfunc(random.random)))
-[0, 0, 0, 0, 0]
 
 >>> list(ncycles('abc', 3))
 ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c']
@@ -1256,9 +1269,11 @@ The following recipes have a more mathematical flavor:
 >>> list(input_iterator)
 []
 
+
 >>> sum_of_squares([10, 20, 30])
 1400
 
+
 >>> list(reshape([(0, 1), (2, 3), (4, 5)], 3))
 [(0, 1, 2), (3, 4, 5)]
 >>> M = [(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11)]
@@ -1279,6 +1294,7 @@ The following recipes have a more mathematical flavor:
 >>> list(reshape(M, 12))
 [(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 

[Python-checkins] [3.12] gh-53203: Improve tests for strptime() (GH-125090) (GH-125093)

2024-10-08 Thread serhiy-storchaka
https://github.com/python/cpython/commit/82257374b9d10bc3df2a466fe4450a5dc576842d
commit: 82257374b9d10bc3df2a466fe4450a5dc576842d
branch: 3.12
author: Serhiy Storchaka 
committer: serhiy-storchaka 
date: 2024-10-08T09:47:37Z
summary:

[3.12] gh-53203: Improve tests for strptime() (GH-125090) (GH-125093)

Run them with different locales and different date and time.

Add the @run_with_locales() decorator to run the test with multiple
locales.

Improve the run_with_locale() context manager/decorator -- it now
catches only expected exceptions and reports the test as skipped if no
appropriate locale is available.
(cherry picked from commit 19984fe024bfd90649f1c36b78c9abf3ed72b27d)

files:
M Lib/test/pickletester.py
M Lib/test/support/__init__.py
M Lib/test/test_codecs.py
M Lib/test/test_decimal.py
M Lib/test/test_float.py
M Lib/test/test_imaplib.py
M Lib/test/test_strptime.py
M Lib/test/test_time.py
M Lib/test/test_types.py
M Lib/test/test_unicode.py

diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py
index b0968afcf683f3..dd7466e2c150d6 100644
--- a/Lib/test/pickletester.py
+++ b/Lib/test/pickletester.py
@@ -26,7 +26,7 @@
 from test import support
 from test.support import os_helper
 from test.support import (
-TestFailed, run_with_locale, no_tracing,
+TestFailed, run_with_locales, no_tracing,
 _2G, _4G, bigmemtest
 )
 from test.support.import_helper import forget
@@ -2591,7 +2591,7 @@ def test_float(self):
 got = self.loads(pickle)
 self.assert_is_copy(value, got)
 
-@run_with_locale('LC_ALL', 'de_DE', 'fr_FR')
+@run_with_locales('LC_ALL', 'de_DE', 'fr_FR', '')
 def test_float_format(self):
 # make sure that floats are formatted locale independent with proto 0
 self.assertEqual(self.dumps(1.2, 0)[0:3], b'F1.')
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index 8519fedf8dbc9d..9c3dcbc1d2bc29 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -854,8 +854,8 @@ def check_sizeof(test, o, size):
 test.assertEqual(result, size, msg)
 
 #===
-# Decorator for running a function in a different locale, correctly resetting
-# it afterwards.
+# Decorator/context manager for running a code in a different locale,
+# correctly resetting it afterwards.
 
 @contextlib.contextmanager
 def run_with_locale(catstr, *locales):
@@ -866,16 +866,21 @@ def run_with_locale(catstr, *locales):
 except AttributeError:
 # if the test author gives us an invalid category string
 raise
-except:
+except Exception:
 # cannot retrieve original locale, so do nothing
 locale = orig_locale = None
+if '' not in locales:
+raise unittest.SkipTest('no locales')
 else:
 for loc in locales:
 try:
 locale.setlocale(category, loc)
 break
-except:
+except locale.Error:
 pass
+else:
+if '' not in locales:
+raise unittest.SkipTest(f'no locales {locales}')
 
 try:
 yield
@@ -883,6 +888,46 @@ def run_with_locale(catstr, *locales):
 if locale and orig_locale:
 locale.setlocale(category, orig_locale)
 
+#===
+# Decorator for running a function in multiple locales (if they are
+# availasble) and resetting the original locale afterwards.
+
+def run_with_locales(catstr, *locales):
+def deco(func):
[email protected](func)
+def wrapper(self, /, *args, **kwargs):
+dry_run = '' in locales
+try:
+import locale
+category = getattr(locale, catstr)
+orig_locale = locale.setlocale(category)
+except AttributeError:
+# if the test author gives us an invalid category string
+raise
+except Exception:
+# cannot retrieve original locale, so do nothing
+pass
+else:
+try:
+for loc in locales:
+with self.subTest(locale=loc):
+try:
+locale.setlocale(category, loc)
+except locale.Error:
+self.skipTest(f'no locale {loc!r}')
+else:
+dry_run = False
+func(self, *args, **kwargs)
+finally:
+locale.setlocale(category, orig_locale)
+if dry_run:
+# no locales available, so just run the test
+# with the current locale
+with self.subTest(locale=None):
+func(self, *args, **kwargs)
+return wrapper
+return deco
+

[Python-checkins] [3.13] gh-123378: fix a crash in `UnicodeError.__str__` (GH-124935) (#125099)

2024-10-08 Thread vstinner
https://github.com/python/cpython/commit/84991153dad8950cd528e23d43c2e77f42002e33
commit: 84991153dad8950cd528e23d43c2e77f42002e33
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: vstinner 
date: 2024-10-08T12:06:57Z
summary:

[3.13] gh-123378: fix a crash in `UnicodeError.__str__` (GH-124935) (#125099)

gh-123378: fix a crash in `UnicodeError.__str__` (GH-124935)
(cherry picked from commit ba14dfafd97d1fd03938ac8ddec4ca5b2f12985d)

Co-authored-by: Bénédikt Tran <[email protected]>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2024-10-03-14-39-41.gh-issue-123378.dCxANf.rst
M Lib/test/test_exceptions.py
M Objects/exceptions.c

diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index 82ac7bea40c398..313345b3b7a526 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -8,6 +8,7 @@
 import weakref
 import errno
 from codecs import BOM_UTF8
+from itertools import product
 from textwrap import dedent
 
 from test.support import (captured_stderr, check_impl_detail,
@@ -1336,6 +1337,29 @@ def test_unicode_errors_no_object(self):
 for klass in klasses:
 self.assertEqual(str(klass.__new__(klass)), "")
 
+def test_unicode_error_str_does_not_crash(self):
+# Test that str(UnicodeError(...)) does not crash.
+# See https://github.com/python/cpython/issues/123378.
+
+for start, end, objlen in product(
+range(-5, 5),
+range(-5, 5),
+range(7),
+):
+obj = 'a' * objlen
+with self.subTest('encode', objlen=objlen, start=start, end=end):
+exc = UnicodeEncodeError('utf-8', obj, start, end, '')
+self.assertIsInstance(str(exc), str)
+
+with self.subTest('translate', objlen=objlen, start=start, 
end=end):
+exc = UnicodeTranslateError(obj, start, end, '')
+self.assertIsInstance(str(exc), str)
+
+encoded = obj.encode()
+with self.subTest('decode', objlen=objlen, start=start, end=end):
+exc = UnicodeDecodeError('utf-8', encoded, start, end, '')
+self.assertIsInstance(str(exc), str)
+
 @no_tracing
 def test_badisinstance(self):
 # Bug #2542: if issubclass(e, MyException) raises an exception,
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-03-14-39-41.gh-issue-123378.dCxANf.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-03-14-39-41.gh-issue-123378.dCxANf.rst
new file mode 100644
index 00..5cd34535d674d3
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-03-14-39-41.gh-issue-123378.dCxANf.rst
@@ -0,0 +1,3 @@
+Fix a crash in the :meth:`~object.__str__` method of :exc:`UnicodeError`
+objects when the :attr:`UnicodeError.start` and :attr:`UnicodeError.end`
+values are invalid or out-of-range. Patch by Bénédikt Tran.
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
index da500c30621da2..b5f3a60baff9a7 100644
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -2959,46 +2959,55 @@ UnicodeEncodeError_init(PyObject *self, PyObject *args, 
PyObject *kwds)
 static PyObject *
 UnicodeEncodeError_str(PyObject *self)
 {
-PyUnicodeErrorObject *uself = (PyUnicodeErrorObject *)self;
+PyUnicodeErrorObject *exc = (PyUnicodeErrorObject *)self;
 PyObject *result = NULL;
 PyObject *reason_str = NULL;
 PyObject *encoding_str = NULL;
 
-if (!uself->object)
+if (exc->object == NULL) {
 /* Not properly initialized. */
 return PyUnicode_FromString("");
+}
 
 /* Get reason and encoding as strings, which they might not be if
they've been modified after we were constructed. */
-reason_str = PyObject_Str(uself->reason);
-if (reason_str == NULL)
+reason_str = PyObject_Str(exc->reason);
+if (reason_str == NULL) {
 goto done;
-encoding_str = PyObject_Str(uself->encoding);
-if (encoding_str == NULL)
+}
+encoding_str = PyObject_Str(exc->encoding);
+if (encoding_str == NULL) {
 goto done;
+}
+
+Py_ssize_t len = PyUnicode_GET_LENGTH(exc->object);
+Py_ssize_t start = exc->start, end = exc->end;
 
-if (uself->start < PyUnicode_GET_LENGTH(uself->object) && uself->end == 
uself->start+1) {
-Py_UCS4 badchar = PyUnicode_ReadChar(uself->object, uself->start);
+if ((start >= 0 && start < len) && (end >= 0 && end <= len) && end == 
start + 1) {
+Py_UCS4 badchar = PyUnicode_ReadChar(exc->object, start);
 const char *fmt;
-if (badchar <= 0xff)
+if (badchar <= 0xff) {
 fmt = "'%U' codec can't encode character '\\x%02x' in position 
%zd: %U";
-else if (badchar <= 0x)
+}
+else if (badchar <= 0x) {
 fmt = "'%U' codec can't encode character '\\u%04x' in position 
%zd: %U";
-else
+}
+else {
 fmt = "'%U

[Python-checkins] gh-121404: typo fix in compile.c: MATADATA -> METADATA (#125101)

2024-10-08 Thread iritkatriel
https://github.com/python/cpython/commit/9a35d053383c87d577ea09951bc9740704f4788d
commit: 9a35d053383c87d577ea09951bc9740704f4788d
branch: main
author: Mikhail Efimov 
committer: iritkatriel <[email protected]>
date: 2024-10-08T12:18:28Z
summary:

gh-121404: typo fix in compile.c: MATADATA -> METADATA (#125101)

files:
M Python/compile.c

diff --git a/Python/compile.c b/Python/compile.c
index 9826d3fbbde976..d463fcde204a05 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -1534,7 +1534,7 @@ _PyCompile_CodeGen(PyObject *ast, PyObject *filename, 
PyCompilerFlags *pflags,
 
 _PyCompile_CodeUnitMetadata *umd = &c->u->u_metadata;
 
-#define SET_MATADATA_INT(key, value) do { \
+#define SET_METADATA_INT(key, value) do { \
 PyObject *v = PyLong_FromLong((long)value); \
 if (v == NULL) goto finally; \
 int res = PyDict_SetItemString(metadata, key, v); \
@@ -1542,10 +1542,10 @@ _PyCompile_CodeGen(PyObject *ast, PyObject *filename, 
PyCompilerFlags *pflags,
 if (res < 0) goto finally; \
 } while (0);
 
-SET_MATADATA_INT("argcount", umd->u_argcount);
-SET_MATADATA_INT("posonlyargcount", umd->u_posonlyargcount);
-SET_MATADATA_INT("kwonlyargcount", umd->u_kwonlyargcount);
-#undef SET_MATADATA_INT
+SET_METADATA_INT("argcount", umd->u_argcount);
+SET_METADATA_INT("posonlyargcount", umd->u_posonlyargcount);
+SET_METADATA_INT("kwonlyargcount", umd->u_kwonlyargcount);
+#undef SET_METADATA_INT
 
 int addNone = mod->kind != Expression_kind;
 if (_PyCodegen_AddReturnAtEnd(c, addNone) < 0) {

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] Doc: Improve description of ``GET_LEN`` opcode (#114583)

2024-10-08 Thread kumaraditya303
https://github.com/python/cpython/commit/e8773e59a835d23b9648271e0eb79c1651581564
commit: e8773e59a835d23b9648271e0eb79c1651581564
branch: main
author: Kirill Podoprigora 
committer: kumaraditya303 
date: 2024-10-08T17:55:40+05:30
summary:

Doc: Improve description of ``GET_LEN`` opcode (#114583)

files:
M Doc/library/dis.rst

diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index 75b84a8d827bc9..1d084a8bf38d98 100644
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -969,7 +969,8 @@ iterations of the loop.
 
 .. opcode:: GET_LEN
 
-   Perform ``STACK.append(len(STACK[-1]))``.
+   Perform ``STACK.append(len(STACK[-1]))``. Used in :keyword:`match` 
statements where
+   comparison with structure of pattern is needed.
 
.. versionadded:: 3.10
 

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.12] Doc: Improve description of ``GET_LEN`` opcode (GH-114583) (#125103)

2024-10-08 Thread kumaraditya303
https://github.com/python/cpython/commit/65272a382b9536e1c53484a58f0f9c392e2ca872
commit: 65272a382b9536e1c53484a58f0f9c392e2ca872
branch: 3.12
author: Miss Islington (bot) <[email protected]>
committer: kumaraditya303 
date: 2024-10-08T12:32:04Z
summary:

[3.12] Doc: Improve description of ``GET_LEN`` opcode (GH-114583) (#125103)

Doc: Improve description of ``GET_LEN`` opcode (GH-114583)
(cherry picked from commit e8773e59a835d23b9648271e0eb79c1651581564)

Co-authored-by: Kirill Podoprigora 

files:
M Doc/library/dis.rst

diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index 82b4aa28857f41..f9f82d25b874e8 100644
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -850,7 +850,8 @@ iterations of the loop.
 
 .. opcode:: GET_LEN
 
-   Perform ``STACK.append(len(STACK[-1]))``.
+   Perform ``STACK.append(len(STACK[-1]))``. Used in :keyword:`match` 
statements where
+   comparison with structure of pattern is needed.
 
.. versionadded:: 3.10
 

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] Doc: Improve description of ``GET_LEN`` opcode (GH-114583) (#125102)

2024-10-08 Thread kumaraditya303
https://github.com/python/cpython/commit/0a63c6603590f339c06e4305e8e6857d702788a9
commit: 0a63c6603590f339c06e4305e8e6857d702788a9
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: kumaraditya303 
date: 2024-10-08T12:30:52Z
summary:

[3.13] Doc: Improve description of ``GET_LEN`` opcode (GH-114583) (#125102)

Doc: Improve description of ``GET_LEN`` opcode (GH-114583)
(cherry picked from commit e8773e59a835d23b9648271e0eb79c1651581564)

Co-authored-by: Kirill Podoprigora 

files:
M Doc/library/dis.rst

diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index ad8ef5dfc76ab8..aed4f38ba29ebe 100644
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -957,7 +957,8 @@ iterations of the loop.
 
 .. opcode:: GET_LEN
 
-   Perform ``STACK.append(len(STACK[-1]))``.
+   Perform ``STACK.append(len(STACK[-1]))``. Used in :keyword:`match` 
statements where
+   comparison with structure of pattern is needed.
 
.. versionadded:: 3.10
 

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] gh-123378: fix a crash in `UnicodeError.__str__` (#124935)

2024-10-08 Thread vstinner
https://github.com/python/cpython/commit/ba14dfafd97d1fd03938ac8ddec4ca5b2f12985d
commit: ba14dfafd97d1fd03938ac8ddec4ca5b2f12985d
branch: main
author: Bénédikt Tran <[email protected]>
committer: vstinner 
date: 2024-10-08T13:37:59+02:00
summary:

gh-123378: fix a crash in `UnicodeError.__str__` (#124935)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2024-10-03-14-39-41.gh-issue-123378.dCxANf.rst
M Lib/test/test_exceptions.py
M Objects/exceptions.c

diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index ba858c49400911..b3c21cd4f3d585 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -8,6 +8,7 @@
 import weakref
 import errno
 from codecs import BOM_UTF8
+from itertools import product
 from textwrap import dedent
 
 from test.support import (captured_stderr, check_impl_detail,
@@ -1336,6 +1337,29 @@ def test_unicode_errors_no_object(self):
 for klass in klasses:
 self.assertEqual(str(klass.__new__(klass)), "")
 
+def test_unicode_error_str_does_not_crash(self):
+# Test that str(UnicodeError(...)) does not crash.
+# See https://github.com/python/cpython/issues/123378.
+
+for start, end, objlen in product(
+range(-5, 5),
+range(-5, 5),
+range(7),
+):
+obj = 'a' * objlen
+with self.subTest('encode', objlen=objlen, start=start, end=end):
+exc = UnicodeEncodeError('utf-8', obj, start, end, '')
+self.assertIsInstance(str(exc), str)
+
+with self.subTest('translate', objlen=objlen, start=start, 
end=end):
+exc = UnicodeTranslateError(obj, start, end, '')
+self.assertIsInstance(str(exc), str)
+
+encoded = obj.encode()
+with self.subTest('decode', objlen=objlen, start=start, end=end):
+exc = UnicodeDecodeError('utf-8', encoded, start, end, '')
+self.assertIsInstance(str(exc), str)
+
 @no_tracing
 def test_badisinstance(self):
 # Bug #2542: if issubclass(e, MyException) raises an exception,
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-03-14-39-41.gh-issue-123378.dCxANf.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-03-14-39-41.gh-issue-123378.dCxANf.rst
new file mode 100644
index 00..5cd34535d674d3
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-03-14-39-41.gh-issue-123378.dCxANf.rst
@@ -0,0 +1,3 @@
+Fix a crash in the :meth:`~object.__str__` method of :exc:`UnicodeError`
+objects when the :attr:`UnicodeError.start` and :attr:`UnicodeError.end`
+values are invalid or out-of-range. Patch by Bénédikt Tran.
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
index b3910855165494..c685481b13a93a 100644
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -2994,46 +2994,55 @@ UnicodeEncodeError_init(PyObject *self, PyObject *args, 
PyObject *kwds)
 static PyObject *
 UnicodeEncodeError_str(PyObject *self)
 {
-PyUnicodeErrorObject *uself = (PyUnicodeErrorObject *)self;
+PyUnicodeErrorObject *exc = (PyUnicodeErrorObject *)self;
 PyObject *result = NULL;
 PyObject *reason_str = NULL;
 PyObject *encoding_str = NULL;
 
-if (!uself->object)
+if (exc->object == NULL) {
 /* Not properly initialized. */
 return PyUnicode_FromString("");
+}
 
 /* Get reason and encoding as strings, which they might not be if
they've been modified after we were constructed. */
-reason_str = PyObject_Str(uself->reason);
-if (reason_str == NULL)
+reason_str = PyObject_Str(exc->reason);
+if (reason_str == NULL) {
 goto done;
-encoding_str = PyObject_Str(uself->encoding);
-if (encoding_str == NULL)
+}
+encoding_str = PyObject_Str(exc->encoding);
+if (encoding_str == NULL) {
 goto done;
+}
+
+Py_ssize_t len = PyUnicode_GET_LENGTH(exc->object);
+Py_ssize_t start = exc->start, end = exc->end;
 
-if (uself->start < PyUnicode_GET_LENGTH(uself->object) && uself->end == 
uself->start+1) {
-Py_UCS4 badchar = PyUnicode_ReadChar(uself->object, uself->start);
+if ((start >= 0 && start < len) && (end >= 0 && end <= len) && end == 
start + 1) {
+Py_UCS4 badchar = PyUnicode_ReadChar(exc->object, start);
 const char *fmt;
-if (badchar <= 0xff)
+if (badchar <= 0xff) {
 fmt = "'%U' codec can't encode character '\\x%02x' in position 
%zd: %U";
-else if (badchar <= 0x)
+}
+else if (badchar <= 0x) {
 fmt = "'%U' codec can't encode character '\\u%04x' in position 
%zd: %U";
-else
+}
+else {
 fmt = "'%U' codec can't encode character '\\U%08x' in position 
%zd: %U";
+}
 result = PyUnicode_FromFormat(
 fmt,
 encoding_str,
 (int)badchar,
-uself->start,
+start,

[Python-checkins] [3.13] gh-118658: Modify cert generation script to extract cert3.pem (GH-124598) (GH-124972)

2024-10-08 Thread encukou
https://github.com/python/cpython/commit/4eab6e8d2963b52c0c3d00895d6711924214ce38
commit: 4eab6e8d2963b52c0c3d00895d6711924214ce38
branch: 3.13
author: Felix Fontein 
committer: encukou 
date: 2024-10-08T13:37:30+02:00
summary:

[3.13] gh-118658: Modify cert generation script to extract cert3.pem 
(GH-124598) (GH-124972)

(cherry picked from commit 480354dc236af9ae9d47b2520aa85fb7293c7b68)

files:
M Lib/test/certdata/cert3.pem
M Lib/test/certdata/make_ssl_certs.py

diff --git a/Lib/test/certdata/cert3.pem b/Lib/test/certdata/cert3.pem
index 034bc43ff1974e..4ab0f5ff133e3f 100644
--- a/Lib/test/certdata/cert3.pem
+++ b/Lib/test/certdata/cert3.pem
@@ -31,4 +31,4 @@ 
zqmtEM65ceSP8lo8Zbrcy+AEkCulFaZ92tyjtbe8oN4wTmTLFw06oFLSZzuiOgDV
 OaphdVKf/pvA6KBpr6izox0KQFIE5z3AAJZfKzMGDDD20xhy7jjQZNMAhjfsT+k4
 SeYB/6KafNxq08uoulj7w4Z4R/EGpkXnU96ZHYHmvGN0RnxwI1cpYHCazG8AjsK/
 anN9brBi5twTGrn+D8LRBqF5Yn+2MKkD0EdXJdtIENHP+32sPQ==
--END CERTIFICATE-
\ No newline at end of file
+-END CERTIFICATE-
diff --git a/Lib/test/certdata/make_ssl_certs.py 
b/Lib/test/certdata/make_ssl_certs.py
index 6626b93976a585..ed2037c1fdff04 100644
--- a/Lib/test/certdata/make_ssl_certs.py
+++ b/Lib/test/certdata/make_ssl_certs.py
@@ -254,6 +254,8 @@ def print_cert(path):
 f.write(key)
 f.write(cert)
 
+check_call(['openssl', 'x509', '-outform', 'pem', '-in', 'keycert3.pem', 
'-out', 'cert3.pem'])
+
 cert, key = make_cert_key('fakehostname', sign=True)
 with open('keycert4.pem', 'w') as f:
 f.write(key)

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] gh-123961: Convert _curses to a multi-phase init module (PEP-489) (#124965)

2024-10-08 Thread vstinner
https://github.com/python/cpython/commit/e4292c041018dd9e831e541b515fec1119f92690
commit: e4292c041018dd9e831e541b515fec1119f92690
branch: main
author: Bénédikt Tran <[email protected]>
committer: vstinner 
date: 2024-10-08T13:42:44+02:00
summary:

gh-123961: Convert _curses to a multi-phase init module (PEP-489) (#124965)

files:
A Misc/NEWS.d/next/Library/2024-10-03-19-16-38.gh-issue-123961.ik1Dgs.rst
M Modules/_cursesmodule.c
M Tools/c-analyzer/cpython/globals-to-fix.tsv

diff --git 
a/Misc/NEWS.d/next/Library/2024-10-03-19-16-38.gh-issue-123961.ik1Dgs.rst 
b/Misc/NEWS.d/next/Library/2024-10-03-19-16-38.gh-issue-123961.ik1Dgs.rst
new file mode 100644
index 00..b637b895d0b803
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-10-03-19-16-38.gh-issue-123961.ik1Dgs.rst
@@ -0,0 +1,2 @@
+Convert :mod:`curses` to multi-phase initialization (:pep:`489`), thereby
+fixing reference leaks at interpreter shutdown. Patch by Bénédikt Tran.
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
index 61b65675375547..27d5df08de933e 100644
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -160,30 +160,31 @@ typedef chtype attr_t;   /* No attr_t type is 
available */
 #define _CURSES_PAIR_CONTENT_FUNC   pair_content
 #endif  /* _NCURSES_EXTENDED_COLOR_FUNCS */
 
-typedef struct _cursesmodule_state {
-PyObject *error;// PyCursesError
-PyTypeObject *window_type;  // PyCursesWindow_Type
-} _cursesmodule_state;
+typedef struct {
+PyObject *error;// curses exception type
+PyTypeObject *window_type;  // exposed by PyCursesWindow_Type
+} cursesmodule_state;
 
-// For now, we keep a global state variable to prepare for PEP 489.
-static _cursesmodule_state curses_global_state;
-
-static inline _cursesmodule_state *
-get_cursesmodule_state(PyObject *Py_UNUSED(module))
+static inline cursesmodule_state *
+get_cursesmodule_state(PyObject *module)
 {
-return &curses_global_state;
+void *state = PyModule_GetState(module);
+assert(state != NULL);
+return (cursesmodule_state *)state;
 }
 
-static inline _cursesmodule_state *
-get_cursesmodule_state_by_cls(PyTypeObject *Py_UNUSED(cls))
+static inline cursesmodule_state *
+get_cursesmodule_state_by_cls(PyTypeObject *cls)
 {
-return &curses_global_state;
+void *state = PyType_GetModuleState(cls);
+assert(state != NULL);
+return (cursesmodule_state *)state;
 }
 
-static inline _cursesmodule_state *
-get_cursesmodule_state_by_win(PyCursesWindowObject *Py_UNUSED(win))
+static inline cursesmodule_state *
+get_cursesmodule_state_by_win(PyCursesWindowObject *win)
 {
-return &curses_global_state;
+return get_cursesmodule_state_by_cls(Py_TYPE(win));
 }
 
 /*[clinic input]
@@ -192,6 +193,9 @@ class _curses.window "PyCursesWindowObject *" 
"clinic_state()->window_type"
 [clinic start generated code]*/
 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=ae6cb623018f2cbc]*/
 
+/* Indicate whether the module has already been loaded or not. */
+static int curses_module_loaded = 0;
+
 /* Tells whether setupterm() has been called to initialise terminfo.  */
 static int curses_setupterm_called = FALSE;
 
@@ -211,8 +215,8 @@ static const char *curses_screen_encoding = NULL;
  * set and this returns 0. Otherwise, this returns 1.
  *
  * Since this function can be called in functions that do not
- * have a direct access to the module's state, the exception
- * type is directly taken from the global state for now.
+ * have a direct access to the module's state, '_curses.error'
+ * is imported on demand.
  */
 static inline int
 _PyCursesCheckFunction(int called, const char *funcname)
@@ -220,7 +224,12 @@ _PyCursesCheckFunction(int called, const char *funcname)
 if (called == TRUE) {
 return 1;
 }
-PyErr_Format(curses_global_state.error, "must call %s() first", funcname);
+PyObject *exc = _PyImport_GetModuleAttrString("_curses", "error");
+if (exc != NULL) {
+PyErr_Format(exc, "must call %s() first", funcname);
+Py_DECREF(exc);
+}
+assert(PyErr_Occurred());
 return 0;
 }
 
@@ -237,7 +246,7 @@ _PyCursesStatefulCheckFunction(PyObject *module, int 
called, const char *funcnam
 if (called == TRUE) {
 return 1;
 }
-_cursesmodule_state *state = get_cursesmodule_state(module);
+cursesmodule_state *state = get_cursesmodule_state(module);
 PyErr_Format(state->error, "must call %s() first", funcname);
 return 0;
 }
@@ -275,7 +284,7 @@ _PyCursesStatefulCheckFunction(PyObject *module, int 
called, const char *funcnam
 /* Utility Functions */
 
 static inline void
-_PyCursesSetError(_cursesmodule_state *state, const char *funcname)
+_PyCursesSetError(cursesmodule_state *state, const char *funcname)
 {
 if (funcname == NULL) {
 PyErr_SetString(state->error, catchall_ERR);
@@ -296,7 +305,7 @@ PyCursesCheckERR(PyObject *module, int code, const char 
*fname)

[Python-checkins] gh-90102: Fix pyio _isatty_open_only() (#125089)

2024-10-08 Thread vstinner
https://github.com/python/cpython/commit/43ad3b51707f51ae4b434e2b5950d2c8bf7cca6e
commit: 43ad3b51707f51ae4b434e2b5950d2c8bf7cca6e
branch: main
author: Cody Maloney 
committer: vstinner 
date: 2024-10-08T11:49:50Z
summary:

gh-90102: Fix pyio _isatty_open_only() (#125089)

Spotted by @ngnpope.

`isatty` returns False to indicate the file is not a TTY. The C
implementation of _io does that (`Py_RETURN_FALSE`) but I got the
bool backwards in the _pyio implementaiton.

files:
M Lib/_pyio.py

diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index 2a1d2a33d02960..7b6d10c008d3cb 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -1806,7 +1806,7 @@ def _isatty_open_only(self):
 """
 if (self._stat_atopen is not None
 and not stat.S_ISCHR(self._stat_atopen.st_mode)):
-return True
+return False
 return os.isatty(self._fd)
 
 @property

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.12] gh-123378: fix a crash in `UnicodeError.__str__` (GH-124935) (#125098)

2024-10-08 Thread vstinner
https://github.com/python/cpython/commit/d3b437cb7816d0163dbc3495889e87ebc22e56a0
commit: d3b437cb7816d0163dbc3495889e87ebc22e56a0
branch: 3.12
author: Miss Islington (bot) <[email protected]>
committer: vstinner 
date: 2024-10-08T11:56:18Z
summary:

[3.12] gh-123378: fix a crash in `UnicodeError.__str__` (GH-124935) (#125098)

gh-123378: fix a crash in `UnicodeError.__str__` (GH-124935)
(cherry picked from commit ba14dfafd97d1fd03938ac8ddec4ca5b2f12985d)

Co-authored-by: Bénédikt Tran <[email protected]>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2024-10-03-14-39-41.gh-issue-123378.dCxANf.rst
M Lib/test/test_exceptions.py
M Objects/exceptions.c

diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index 5b0334f34652d2..c5f4b892efb50f 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -8,6 +8,7 @@
 import weakref
 import errno
 from codecs import BOM_UTF8
+from itertools import product
 from textwrap import dedent
 
 from test.support import (captured_stderr, check_impl_detail,
@@ -1333,6 +1334,29 @@ def test_unicode_errors_no_object(self):
 for klass in klasses:
 self.assertEqual(str(klass.__new__(klass)), "")
 
+def test_unicode_error_str_does_not_crash(self):
+# Test that str(UnicodeError(...)) does not crash.
+# See https://github.com/python/cpython/issues/123378.
+
+for start, end, objlen in product(
+range(-5, 5),
+range(-5, 5),
+range(7),
+):
+obj = 'a' * objlen
+with self.subTest('encode', objlen=objlen, start=start, end=end):
+exc = UnicodeEncodeError('utf-8', obj, start, end, '')
+self.assertIsInstance(str(exc), str)
+
+with self.subTest('translate', objlen=objlen, start=start, 
end=end):
+exc = UnicodeTranslateError(obj, start, end, '')
+self.assertIsInstance(str(exc), str)
+
+encoded = obj.encode()
+with self.subTest('decode', objlen=objlen, start=start, end=end):
+exc = UnicodeDecodeError('utf-8', encoded, start, end, '')
+self.assertIsInstance(str(exc), str)
+
 @no_tracing
 def test_badisinstance(self):
 # Bug #2542: if issubclass(e, MyException) raises an exception,
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-03-14-39-41.gh-issue-123378.dCxANf.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-03-14-39-41.gh-issue-123378.dCxANf.rst
new file mode 100644
index 00..5cd34535d674d3
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-03-14-39-41.gh-issue-123378.dCxANf.rst
@@ -0,0 +1,3 @@
+Fix a crash in the :meth:`~object.__str__` method of :exc:`UnicodeError`
+objects when the :attr:`UnicodeError.start` and :attr:`UnicodeError.end`
+values are invalid or out-of-range. Patch by Bénédikt Tran.
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
index 4f2153b19358d2..c579563db75275 100644
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -2961,46 +2961,55 @@ UnicodeEncodeError_init(PyObject *self, PyObject *args, 
PyObject *kwds)
 static PyObject *
 UnicodeEncodeError_str(PyObject *self)
 {
-PyUnicodeErrorObject *uself = (PyUnicodeErrorObject *)self;
+PyUnicodeErrorObject *exc = (PyUnicodeErrorObject *)self;
 PyObject *result = NULL;
 PyObject *reason_str = NULL;
 PyObject *encoding_str = NULL;
 
-if (!uself->object)
+if (exc->object == NULL) {
 /* Not properly initialized. */
 return PyUnicode_FromString("");
+}
 
 /* Get reason and encoding as strings, which they might not be if
they've been modified after we were constructed. */
-reason_str = PyObject_Str(uself->reason);
-if (reason_str == NULL)
+reason_str = PyObject_Str(exc->reason);
+if (reason_str == NULL) {
 goto done;
-encoding_str = PyObject_Str(uself->encoding);
-if (encoding_str == NULL)
+}
+encoding_str = PyObject_Str(exc->encoding);
+if (encoding_str == NULL) {
 goto done;
+}
+
+Py_ssize_t len = PyUnicode_GET_LENGTH(exc->object);
+Py_ssize_t start = exc->start, end = exc->end;
 
-if (uself->start < PyUnicode_GET_LENGTH(uself->object) && uself->end == 
uself->start+1) {
-Py_UCS4 badchar = PyUnicode_ReadChar(uself->object, uself->start);
+if ((start >= 0 && start < len) && (end >= 0 && end <= len) && end == 
start + 1) {
+Py_UCS4 badchar = PyUnicode_ReadChar(exc->object, start);
 const char *fmt;
-if (badchar <= 0xff)
+if (badchar <= 0xff) {
 fmt = "'%U' codec can't encode character '\\x%02x' in position 
%zd: %U";
-else if (badchar <= 0x)
+}
+else if (badchar <= 0x) {
 fmt = "'%U' codec can't encode character '\\u%04x' in position 
%zd: %U";
-else
+}
+else {
 fmt = "'%U

[Python-checkins] gh-75898: make use of thread more explicit in the "Socket Programming HOWTO" document (#125023)

2024-10-08 Thread kumaraditya303
https://github.com/python/cpython/commit/9047146e546599325cddda266e420f42fb318e4e
commit: 9047146e546599325cddda266e420f42fb318e4e
branch: main
author: Jan Kaliszewski 
committer: kumaraditya303 
date: 2024-10-08T17:43:37+05:30
summary:

gh-75898: make use of thread more explicit in the "Socket Programming HOWTO" 
document (#125023)

files:
M Doc/howto/sockets.rst

diff --git a/Doc/howto/sockets.rst b/Doc/howto/sockets.rst
index 0bbf97da39768d..cbc49d15a0771b 100644
--- a/Doc/howto/sockets.rst
+++ b/Doc/howto/sockets.rst
@@ -100,8 +100,8 @@ mainloop of the web server::
(clientsocket, address) = serversocket.accept()
# now do something with the clientsocket
# in this case, we'll pretend this is a threaded server
-   ct = client_thread(clientsocket)
-   ct.run()
+   ct = make_client_thread(clientsocket)
+   ct.start()
 
 There's actually 3 general ways in which this loop could work - dispatching a
 thread to handle ``clientsocket``, create a new process to handle

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]