Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions Include/cpython/pythonrun.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ PyAPI_FUNC(PyObject *) PyRun_FileExFlags(
PyCompilerFlags *flags);


PyAPI_FUNC(PyObject *) Py_CompileStringFlags(
const char *str,
const char *filename,
int start,
PyCompilerFlags *flags);
PyAPI_FUNC(PyObject *) Py_CompileStringExFlags(
const char *str,
const char *filename, /* decoded from the filesystem encoding */
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_pythonrun.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ extern const char* _Py_SourceAsString(
PyCompilerFlags *cf,
PyObject **cmd_copy);

extern PyObject * _Py_CompileStringObjectWithModule(
extern PyObject * _Py_CompileString(
const char *str,
PyObject *filename, int start,
PyCompilerFlags *flags, int optimize,
Expand Down
6 changes: 6 additions & 0 deletions Include/internal/pycore_sysmodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ extern int _PySys_SetIntMaxStrDigits(int maxdigits);

extern int _PySysRemoteDebug_SendExec(int pid, int tid, const char *debugger_script_path);

extern void _PySys_FormatV(
PyObject *key,
FILE *fp,
const char *format,
va_list va);

#ifdef __cplusplus
}
#endif
Expand Down
74 changes: 73 additions & 1 deletion Lib/test/test_capi/test_run.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import ast
import os
import sys
import tempfile
import textwrap
import unittest
from collections import UserDict
from test import support
from test.support import import_helper
from test.support.os_helper import unlink, TESTFN, TESTFN_ASCII, TESTFN_UNDECODABLE

_testcapi = import_helper.import_module('_testcapi')
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')


NULL = None
_testcapi = import_helper.import_module('_testcapi')
Py_single_input = _testcapi.Py_single_input
Py_file_input = _testcapi.Py_file_input
Py_eval_input = _testcapi.Py_eval_input
INVALID_START = Py_single_input - 1
STDIN = '<stdin>'
STDERR_FD = 2
PyCF_ONLY_AST = _testcapi.PyCF_ONLY_AST
PyCF_IGNORE_COOKIE = _testcapi.PyCF_IGNORE_COOKIE

# Code raising a SyntaxError
SYNTAX_ERROR = 'True = 1'
Expand Down Expand Up @@ -68,6 +75,9 @@ def run(s, *args):
run(b'raise ValueError("BUG")', {})
self.assertEqual(str(cm.exception), 'BUG')

with self.assertRaises(ValueError):
func(b'x = 1', INVALID_START, {})

self.assertIsNone(run(b'a\n', dict(a=1)))
self.assertIsNone(run(b'a\n', dict(a=1), {}))
self.assertIsNone(run(b'a\n', {}, dict(a=1)))
Expand Down Expand Up @@ -118,6 +128,9 @@ def run(*args):
closeit = 1
self.assertIsNone(run(dict(a=1), {}, closeit))

with self.assertRaises(ValueError):
func(filename, INVALID_START, {})

self.assertRaises(NameError, run, {})
self.assertRaises(NameError, run, {}, {})
self.assertRaises(TypeError, run, dict(a=1), [])
Expand Down Expand Up @@ -357,6 +370,65 @@ def test_run_simplestringflags(self):
# Test PyRun_SimpleStringFlags()
self.check_run_simplestring(_testcapi.run_simplestringflags)

def check_compilestring(self, compilestring, has_flags, encode_filename=True):
filename_str = TESTFN
if encode_filename:
filename = os.fsencode(filename_str)
else:
filename = filename_str

def check_code(co, name, value):
ns = {}
exec(co, ns, ns)
self.assertEqual(ns[name], value)

co = compilestring(b'x = 1', filename, Py_file_input)
self.assertEqual(co.co_filename, filename_str)
check_code(co, 'x', 1)

if has_flags:
code = textwrap.dedent("""
# encoding: latin1
x = 'a\xe9'
""")
co = compilestring(code.encode(), filename, Py_file_input, PyCF_IGNORE_COOKIE)
self.assertEqual(co.co_filename, filename_str)
check_code(co, 'x', 'a\xe9')

co = compilestring(code.encode(), filename, Py_file_input)
self.assertEqual(co.co_filename, filename_str)
check_code(co, 'x', 'a\xc3\xa9')

tree = compilestring(b'x = 1', filename, Py_file_input, PyCF_ONLY_AST)
self.assertIsInstance(tree, ast.AST)

co = compilestring(b'raise ValueError("BUG")', filename, Py_file_input)
with self.assertRaises(ValueError):
exec(co, {})

with self.assertRaises(SyntaxError) as cm:
compilestring(SYNTAX_ERROR.encode(), filename, Py_file_input)

with self.assertRaises(ValueError):
compilestring(b'x = 1', filename, INVALID_START)

def test_compilestring(self):
# Test Py_CompileString()
self.check_compilestring(_testlimitedcapi.run_compilestring, False)

def test_compilestringflags(self):
# Test Py_CompileStringFlags()
self.check_compilestring(_testcapi.run_compilestringflags, True)

def test_compilestringexflags(self):
# Test Py_CompileStringExFlags()
self.check_compilestring(_testcapi.run_compilestringexflags, True)

def test_compilestringobject(self):
# Test Py_CompileStringObject()
self.check_compilestring(_testcapi.run_compilestringobject, True,
encode_filename=False)


if __name__ == '__main__':
unittest.main()
2 changes: 1 addition & 1 deletion Lib/test/test_cmd_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ def test_undecodable_code(self):
# decodable from ASCII) and run_command() failed on
# PyUnicode_AsUTF8String(). This is the expected behaviour on
# Linux.
pattern = b"Unable to decode the command from the command line:"
pattern = b"python: Unable to decode the command from the command line:"
elif p.returncode == 0:
# _Py_char2wchar() decoded b'\xff' as '\xff' even if the locale is
# C and the locale encoding is ASCII. It occurs on FreeBSD, Solaris
Expand Down
88 changes: 80 additions & 8 deletions Lib/test/test_free_threading/test_itertools.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import unittest
from itertools import accumulate, batched, chain, combinations_with_replacement, cycle, permutations, zip_longest
from itertools import (
accumulate,
batched,
chain,
combinations_with_replacement,
cycle,
permutations,
tee,
zip_longest,
)
from test.support import threading_helper


Expand All @@ -15,20 +24,23 @@ def work_iterator(it):


class ItertoolsThreading(unittest.TestCase):

@threading_helper.reap_threads
def test_accumulate(self):
number_of_iterations = 10
for _ in range(number_of_iterations):
it = accumulate(tuple(range(40)))
threading_helper.run_concurrently(work_iterator, nthreads=10, args=[it])
threading_helper.run_concurrently(
work_iterator, nthreads=10, args=[it]
)

@threading_helper.reap_threads
def test_batched(self):
number_of_iterations = 10
for _ in range(number_of_iterations):
it = batched(tuple(range(1000)), 2)
threading_helper.run_concurrently(work_iterator, nthreads=10, args=[it])
threading_helper.run_concurrently(
work_iterator, nthreads=10, args=[it]
)

@threading_helper.reap_threads
def test_cycle(self):
Expand All @@ -46,28 +58,88 @@ def test_chain(self):
number_of_iterations = 10
for _ in range(number_of_iterations):
it = chain(*[(1,)] * 200)
threading_helper.run_concurrently(work_iterator, nthreads=6, args=[it])
threading_helper.run_concurrently(
work_iterator, nthreads=6, args=[it]
)

@threading_helper.reap_threads
def test_combinations_with_replacement(self):
number_of_iterations = 6
for _ in range(number_of_iterations):
it = combinations_with_replacement(tuple(range(2)), 2)
threading_helper.run_concurrently(work_iterator, nthreads=6, args=[it])
threading_helper.run_concurrently(
work_iterator, nthreads=6, args=[it]
)

@threading_helper.reap_threads
def test_permutations(self):
number_of_iterations = 6
for _ in range(number_of_iterations):
it = permutations(tuple(range(4)), 2)
threading_helper.run_concurrently(work_iterator, nthreads=6, args=[it])
threading_helper.run_concurrently(
work_iterator, nthreads=6, args=[it]
)

@threading_helper.reap_threads
def test_zip_longest(self):
number_of_iterations = 10
for _ in range(number_of_iterations):
it = zip_longest(list(range(4)), list(range(8)), fillvalue=0)
threading_helper.run_concurrently(work_iterator, nthreads=10, args=[it])
threading_helper.run_concurrently(
work_iterator, nthreads=10, args=[it]
)


class TestTeeConcurrent(unittest.TestCase):
# itertools.tee branches share a linked list of internal data cells.
# Concurrent iteration must not corrupt that shared state or crash the
# free-threaded build. A crash shows up as the interpreter dying (not as a
# caught exception); tee is documented as not thread-safe, so a
# ``RuntimeError`` from the re-entrancy guard is an allowed outcome and is
# tolerated here.

def test_same_branch(self):
# Many threads consume the same tee branch.
errors = []

def consume(it):
try:
for _ in it:
pass
except RuntimeError:
pass
except Exception as e:
errors.append(e)

for _ in range(100):
a, _ = tee(iter(range(2000)), 2)
threading_helper.run_concurrently(consume, nthreads=8, args=(a,))

self.assertEqual(errors, [], msg=f"unexpected errors: {errors}")

def test_sibling_branches(self):
# Each thread consumes a different sibling branch of the same tee.
errors = []

def make_worker(it):
def consume():
try:
for _ in it:
pass
except RuntimeError:
pass
except Exception as e:
errors.append(e)

return consume

for _ in range(100):
branches = tee(iter(range(4000)), 8)
threading_helper.run_concurrently(
[make_worker(it) for it in branches]
)

self.assertEqual(errors, [], msg=f"unexpected errors: {errors}")


if __name__ == "__main__":
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a crash when concurrently iterating an :func:`itertools.tee` iterator on
the free-threaded build.
2 changes: 1 addition & 1 deletion Modules/Setup.stdlib.in
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c _testinternalcapi/complex.c _testinternalcapi/interpreter.c _testinternalcapi/tuple.c
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/modsupport.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c _testcapi/type.c _testcapi/function.c _testcapi/module.c _testcapi/weakref.c
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/slots.c _testlimitedcapi/sys.c _testlimitedcapi/threadstate.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c _testlimitedcapi/weakref.c
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/slots.c _testlimitedcapi/sys.c _testlimitedcapi/threadstate.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c _testlimitedcapi/weakref.c _testlimitedcapi/run.c
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c

Expand Down
Loading
Loading