Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
9f7bbaf
gh-129813, PEP 782: Optimize byteswriter_resize() (#139101)
vstinner Sep 18, 2025
db68bfc
gh-138720: Make Buffered closed check match flush (GH-138724)
cmaloney Sep 18, 2025
0ac377f
Make Android streams respect the unbuffered (`-u`) option (#138806)
mhsmith Sep 18, 2025
8247e1b
gh-137838: Add CI for no-opt JIT (#139081)
corona10 Sep 18, 2025
70ad1b3
gh-138998: `expat/refresh.sh`: Fail if Step 3 is not completed (GH-13…
StanFromIreland Sep 18, 2025
49f1c30
gh-139098: Use multiphase initialization in `_testcapi` (GH-139102)
ZeroIntensity Sep 18, 2025
36ec60f
gh-138970: Adjust tests for pegen rule flags (#139107)
encukou Sep 18, 2025
571210b
gh-135729: Store reference to globals in `Interpreter._decref` (GH-13…
ZeroIntensity Sep 18, 2025
89ff88b
Document `Py_AddPendingCall()` change with subinterpreters in 3.12 (G…
ZeroIntensity Sep 18, 2025
d6a6fe2
gh-129813, PEP 782: Use PyBytesWriter in ssl.MemoryBIO (#139113)
vstinner Sep 18, 2025
2191497
gh-136003: Execute pre-finalization callbacks in a loop (GH-136004)
ZeroIntensity Sep 18, 2025
6504f20
gh-135755: Make Py_TAIL_CALL_INTERP macro private (#138981)
vstinner Sep 18, 2025
243d599
gh-129813, PEP 782: Use PyBytesWriter in _sqlite (#138956)
vstinner Sep 18, 2025
a269e69
gh-139109: Dynamic opcode targets (GH-139111)
Fidget-Spinner Sep 18, 2025
594bdde
gh-137242: Mention Android binary releases in documentation (#138305)
mhsmith Sep 18, 2025
1ebd726
gh-64490: Argument Clinic: Add support for ``**kwds`` (#138344)
AA-Turner Sep 18, 2025
b0a8073
gh-73487: Convert `_decimal` to use Argument Clinic (part 7) (#138221)
skirpichev Sep 18, 2025
446587c
gh-129813, PEP 782: Use PyBytesWriter in _ssl (#138929)
vstinner Sep 18, 2025
9b35f7c
gh-129813, PEP 782: Use PyBytesWriter in bufferedio.c (#139121)
vstinner Sep 18, 2025
e163fbd
fixes gh-139090: add os.RWF_DONTCACHE (#139091)
benjaminp Sep 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/jit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,33 @@ jobs:
make all --jobs 4
./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3

no-opt-jit:
name: JIT without optimizations (Debug)
needs: interpreter
runs-on: ubuntu-24.04
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
llvm:
- 19
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Build with JIT
run: |
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }}
export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH"
./configure --enable-experimental-jit --with-pydebug
make all --jobs 4
- name: Run tests without optimizations
run: |
PYTHON_UOPS_OPTIMIZE=0 ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3

# XXX: GH-133171
# jit-with-disabled-gil:
# name: Free-Threaded (Debug)
Expand Down
4 changes: 4 additions & 0 deletions Doc/c-api/init.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1834,6 +1834,10 @@ pointer and a void pointer argument.
called from the main interpreter. Each subinterpreter now has its own
list of scheduled calls.

.. versionchanged:: 3.12
This function now always schedules *func* to be run in the main
interpreter.

.. _profiling:

Profiling and Tracing
Expand Down
11 changes: 11 additions & 0 deletions Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1501,6 +1501,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo

- :data:`RWF_HIPRI`
- :data:`RWF_NOWAIT`
- :data:`RWF_DONTCACHE`

Return the total number of bytes actually read which can be less than the
total capacity of all the objects.
Expand Down Expand Up @@ -1546,6 +1547,15 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
.. versionadded:: 3.7


.. data:: RWF_DONTCACHE

Use uncached buffered IO.

.. availability:: Linux >= 6.14

.. versionadded:: next


.. function:: ptsname(fd, /)

Return the name of the slave pseudo-terminal device associated with the
Expand Down Expand Up @@ -1587,6 +1597,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
- :data:`RWF_DSYNC`
- :data:`RWF_SYNC`
- :data:`RWF_APPEND`
- :data:`RWF_DONTCACHE`

Return the total number of bytes actually written.

Expand Down
11 changes: 9 additions & 2 deletions Doc/using/android.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,15 @@ If you're sure you want to do all of this manually, read on. You can use the
:source:`testbed app <Android/testbed>` as a guide; each step below contains a
link to the relevant file.

* Build Python by following the instructions in :source:`Android/README.md`.
This will create the directory ``cross-build/HOST/prefix``.
* First, acquire a build of Python for Android:

* The easiest way is to download an Android release from `python.org
<https://www.python.org/downloads/android/>`__. The ``prefix`` directory
mentioned below is at the top level of the package.

* Or if you want to build it yourself, follow the instructions in
:source:`Android/README.md`. The ``prefix`` directory will be created under
:samp:`cross-build/{HOST}`.

* Add code to your :source:`build.gradle <Android/testbed/app/build.gradle.kts>`
file to copy the following items into your project. All except your own Python
Expand Down
1 change: 1 addition & 0 deletions Include/cpython/pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ struct _ts {
# define _PyThreadState_WHENCE_THREADING 3
# define _PyThreadState_WHENCE_GILSTATE 4
# define _PyThreadState_WHENCE_EXEC 5
# define _PyThreadState_WHENCE_THREADING_DAEMON 6
#endif

/* Currently holds the GIL. Must be its own field to avoid data races */
Expand Down
16 changes: 10 additions & 6 deletions Lib/_android_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,19 @@ def init_streams(android_log_write, stdout_prio, stderr_prio):

global logcat
logcat = Logcat(android_log_write)

sys.stdout = TextLogStream(
stdout_prio, "python.stdout", sys.stdout.fileno())
sys.stderr = TextLogStream(
stderr_prio, "python.stderr", sys.stderr.fileno())
sys.stdout = TextLogStream(stdout_prio, "python.stdout", sys.stdout)
sys.stderr = TextLogStream(stderr_prio, "python.stderr", sys.stderr)


class TextLogStream(io.TextIOWrapper):
def __init__(self, prio, tag, fileno=None, **kwargs):
def __init__(self, prio, tag, original=None, **kwargs):
# Respect the -u option.
if original:
kwargs.setdefault("write_through", original.write_through)
fileno = original.fileno()
else:
fileno = None

# The default is surrogateescape for stdout and backslashreplace for
# stderr, but in the context of an Android log, readability is more
# important than reversibility.
Expand Down
9 changes: 7 additions & 2 deletions Lib/concurrent/interpreters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,17 @@ def __del__(self):
def __reduce__(self):
return (type(self), (self._id,))

def _decref(self):
# gh-135729: Globals might be destroyed by the time this is called, so we
# need to keep references ourself
def _decref(self, *,
InterpreterNotFoundError=InterpreterNotFoundError,
_interp_decref=_interpreters.decref,
):
if not self._ownsref:
return
self._ownsref = False
try:
_interpreters.decref(self._id)
_interp_decref(self._id)
except InterpreterNotFoundError:
pass

Expand Down
32 changes: 18 additions & 14 deletions Lib/test/test_android.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,34 +91,38 @@ def tearDown(self):
self.logcat_thread = None

@contextmanager
def unbuffered(self, stream):
stream.reconfigure(write_through=True)
def reconfigure(self, stream, **settings):
original_settings = {key: getattr(stream, key, None) for key in settings.keys()}
stream.reconfigure(**settings)
try:
yield
finally:
stream.reconfigure(write_through=False)
stream.reconfigure(**original_settings)

# In --verbose3 mode, sys.stdout and sys.stderr are captured, so we can't
# test them directly. Detect this mode and use some temporary streams with
# the same properties.
def stream_context(self, stream_name, level):
# https://developer.android.com/ndk/reference/group/logging
prio = {"I": 4, "W": 5}[level]

stack = ExitStack()
stack.enter_context(self.subTest(stream_name))

# In --verbose3 mode, sys.stdout and sys.stderr are captured, so we can't
# test them directly. Detect this mode and use some temporary streams with
# the same properties.
stream = getattr(sys, stream_name)
native_stream = getattr(sys, f"__{stream_name}__")
if isinstance(stream, io.StringIO):
# https://developer.android.com/ndk/reference/group/logging
prio = {"I": 4, "W": 5}[level]
stack.enter_context(
patch(
f"sys.{stream_name}",
TextLogStream(
prio, f"python.{stream_name}", native_stream.fileno(),
errors="backslashreplace"
stream := TextLogStream(
prio, f"python.{stream_name}", native_stream,
),
)
)

# The tests assume the stream is initially buffered.
stack.enter_context(self.reconfigure(stream, write_through=False))

return stack

def test_str(self):
Expand All @@ -145,7 +149,7 @@ def write(s, lines=None, *, write_len=None):
self.assert_logs(level, tag, lines)

# Single-line messages,
with self.unbuffered(stream):
with self.reconfigure(stream, write_through=True):
write("", [])

write("a")
Expand Down Expand Up @@ -192,7 +196,7 @@ def write(s, lines=None, *, write_len=None):

# However, buffering can be turned off completely if you want a
# flush after every write.
with self.unbuffered(stream):
with self.reconfigure(stream, write_through=True):
write("\nx", ["", "x"])
write("\na\n", ["", "a"])
write("\n", [""])
Expand Down
56 changes: 56 additions & 0 deletions Lib/test/test_atexit.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,62 @@ def thready():
# want them to affect the rest of the tests.
script_helper.assert_python_ok("-c", textwrap.dedent(source))

@threading_helper.requires_working_threading()
def test_thread_created_in_atexit(self):
source = """if True:
import atexit
import threading
import time


def run():
print(24)
time.sleep(1)
print(42)

@atexit.register
def start_thread():
threading.Thread(target=run).start()
"""
return_code, stdout, stderr = script_helper.assert_python_ok("-c", source)
self.assertEqual(return_code, 0)
self.assertEqual(stdout, f"24{os.linesep}42{os.linesep}".encode("utf-8"))
self.assertEqual(stderr, b"")

@threading_helper.requires_working_threading()
@unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()")
def test_thread_created_in_atexit_subinterpreter(self):
try:
from concurrent import interpreters
except ImportError:
self.skipTest("subinterpreters are not available")

read, write = os.pipe()
source = f"""if True:
import atexit
import threading
import time
import os

def run():
os.write({write}, b'spanish')
time.sleep(1)
os.write({write}, b'inquisition')

@atexit.register
def start_thread():
threading.Thread(target=run).start()
"""
interp = interpreters.create()
try:
interp.exec(source)

# Close the interpreter to invoke atexit callbacks
interp.close()
self.assertEqual(os.read(read, 100), b"spanishinquisition")
finally:
os.close(read)
os.close(write)

@support.cpython_only
class SubinterpreterTest(unittest.TestCase):
Expand Down
66 changes: 66 additions & 0 deletions Lib/test/test_capi/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from test import support
from test.support import MISSING_C_DOCSTRINGS
from test.support import import_helper
from test.support import script_helper
from test.support import threading_helper
from test.support import warnings_helper
from test.support import requires_limited_api
Expand Down Expand Up @@ -1641,6 +1642,36 @@ def subthread():

self.assertEqual(actual, int(interpid))

@threading_helper.requires_working_threading()
def test_pending_call_creates_thread(self):
source = """
import _testinternalcapi
import threading
import time


def output():
print(24)
time.sleep(1)
print(42)


def callback():
threading.Thread(target=output).start()


def create_pending_call():
time.sleep(1)
_testinternalcapi.simple_pending_call(callback)


threading.Thread(target=create_pending_call).start()
"""
return_code, stdout, stderr = script_helper.assert_python_ok('-c', textwrap.dedent(source))
self.assertEqual(return_code, 0)
self.assertEqual(stdout, f"24{os.linesep}42{os.linesep}".encode("utf-8"))
self.assertEqual(stderr, b"")


class SubinterpreterTest(unittest.TestCase):

Expand Down Expand Up @@ -1949,6 +1980,41 @@ def test_module_state_shared_in_global(self):
subinterp_attr_id = os.read(r, 100)
self.assertEqual(main_attr_id, subinterp_attr_id)

@threading_helper.requires_working_threading()
@unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()")
@requires_subinterpreters
def test_pending_call_creates_thread_subinterpreter(self):
interpreters = import_helper.import_module("concurrent.interpreters")
r, w = os.pipe()
source = f"""if True:
import _testinternalcapi
import threading
import time
import os


def output():
time.sleep(1)
os.write({w}, b"x")


def callback():
threading.Thread(target=output).start()


def create_pending_call():
time.sleep(1)
_testinternalcapi.simple_pending_call(callback)


threading.Thread(target=create_pending_call).start()
"""
interp = interpreters.create()
interp.exec(source)
interp.close()
data = os.read(r, 1)
self.assertEqual(data, b"x")


@requires_subinterpreters
class InterpreterConfigTests(unittest.TestCase):
Expand Down
Loading
Loading