Skip to content

Commit

Permalink
Merge branch 'main' into shelve-clear
Browse files Browse the repository at this point in the history
  • Loading branch information
jtcave authored Jul 27, 2023
2 parents 0353d56 + c2b1689 commit 88d48f6
Show file tree
Hide file tree
Showing 25 changed files with 177 additions and 109 deletions.
11 changes: 11 additions & 0 deletions Include/cpython/interpreteridobject.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#ifndef Py_CPYTHON_INTERPRETERIDOBJECT_H
# error "this header file must not be included directly"
#endif

/* Interpreter ID Object */

PyAPI_DATA(PyTypeObject) PyInterpreterID_Type;

PyAPI_FUNC(PyObject *) PyInterpreterID_New(int64_t);
PyAPI_FUNC(PyObject *) PyInterpreterState_GetIDObject(PyInterpreterState *);
PyAPI_FUNC(PyInterpreterState *) PyInterpreterID_LookUp(PyObject *);
4 changes: 4 additions & 0 deletions Include/cpython/pylifecycle.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,7 @@ typedef struct {
PyAPI_FUNC(PyStatus) Py_NewInterpreterFromConfig(
PyThreadState **tstate_p,
const PyInterpreterConfig *config);

typedef void (*atexit_datacallbackfunc)(void *);
PyAPI_FUNC(int) PyUnstable_AtExit(
PyInterpreterState *, atexit_datacallbackfunc, void *);
1 change: 1 addition & 0 deletions Include/cpython/pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
PyAPI_FUNC(int) _PyInterpreterState_RequiresIDRef(PyInterpreterState *);
PyAPI_FUNC(void) _PyInterpreterState_RequireIDRef(PyInterpreterState *, int);

PyAPI_FUNC(PyObject *) PyUnstable_InterpreterState_GetMainModule(PyInterpreterState *);

/* State unique per thread */

Expand Down
3 changes: 0 additions & 3 deletions Include/internal/pycore_interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,6 @@ extern int _PyInterpreterState_IDInitref(PyInterpreterState *);
extern int _PyInterpreterState_IDIncref(PyInterpreterState *);
extern void _PyInterpreterState_IDDecref(PyInterpreterState *);

// Export for '_xxsubinterpreters' shared extension
PyAPI_FUNC(PyObject*) _PyInterpreterState_GetMainModule(PyInterpreterState *);

extern const PyConfig* _PyInterpreterState_GetConfig(PyInterpreterState *interp);

/* Get a copy of the current interpreter configuration.
Expand Down
28 changes: 0 additions & 28 deletions Include/internal/pycore_interp_id.h

This file was deleted.

17 changes: 17 additions & 0 deletions Include/interpreteridobject.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef Py_INTERPRETERIDOBJECT_H
#define Py_INTERPRETERIDOBJECT_H

#ifdef __cplusplus
extern "C" {
#endif

#ifndef Py_LIMITED_API
# define Py_CPYTHON_INTERPRETERIDOBJECT_H
# include "cpython/interpreteridobject.h"
# undef Py_CPYTHON_INTERPRETERIDOBJECT_H
#endif

#ifdef __cplusplus
}
#endif
#endif /* !Py_INTERPRETERIDOBJECT_H */
2 changes: 1 addition & 1 deletion Lib/test/test_capi/check_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def import_singlephase():
try:
import _testsinglephase
except ImportError:
sys.modules.pop('_testsinglephase')
sys.modules.pop('_testsinglephase', None)
return False
else:
del sys.modules['_testsinglephase']
Expand Down
26 changes: 26 additions & 0 deletions Lib/test/test_clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,32 @@ def test_return_converter(self):
""")
self.assertIsInstance(function.return_converter, clinic.int_return_converter)

def test_return_converter_invalid_syntax(self):
stdout = self.parse_function_should_fail("""
module os
os.stat -> invalid syntax
""")
expected_error = "Badly formed annotation for os.stat: 'invalid syntax'"
self.assertIn(expected_error, stdout)

def test_legacy_converter_disallowed_in_return_annotation(self):
stdout = self.parse_function_should_fail("""
module os
os.stat -> "s"
""")
expected_error = "Legacy converter 's' not allowed as a return converter"
self.assertIn(expected_error, stdout)

def test_unknown_return_converter(self):
stdout = self.parse_function_should_fail("""
module os
os.stat -> foooooooooooooooooooooooo
""")
expected_error = (
"No available return converter called 'foooooooooooooooooooooooo'"
)
self.assertIn(expected_error, stdout)

def test_star(self):
function = self.parse_function("""
module os
Expand Down
42 changes: 37 additions & 5 deletions Lib/test/test_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ def require_frozen(module, *, skip=True):
def require_pure_python(module, *, skip=False):
_require_loader(module, SourceFileLoader, skip)


def remove_files(name):
for f in (name + ".py",
name + ".pyc",
Expand Down Expand Up @@ -147,19 +146,34 @@ def _ready_to_import(name=None, source=""):
del sys.modules[name]


def requires_subinterpreters(meth):
"""Decorator to skip a test if subinterpreters are not supported."""
return unittest.skipIf(_interpreters is None,
'subinterpreters required')(meth)
if _testsinglephase is not None:
def restore__testsinglephase(*, _orig=_testsinglephase):
# We started with the module imported and want to restore
# it to its nominal state.
_orig._clear_globals()
_testinternalcapi.clear_extension('_testsinglephase', _orig.__file__)
import _testsinglephase


def requires_singlephase_init(meth):
"""Decorator to skip if single-phase init modules are not supported."""
if not isinstance(meth, type):
def meth(self, _meth=meth):
try:
return _meth(self)
finally:
restore__testsinglephase()
meth = cpython_only(meth)
return unittest.skipIf(_testsinglephase is None,
'test requires _testsinglephase module')(meth)


def requires_subinterpreters(meth):
"""Decorator to skip a test if subinterpreters are not supported."""
return unittest.skipIf(_interpreters is None,
'subinterpreters required')(meth)


class ModuleSnapshot(types.SimpleNamespace):
"""A representation of a module for testing.
Expand Down Expand Up @@ -1962,6 +1976,20 @@ def test_isolated_config(self):
with self.subTest(f'{module}: strict, fresh'):
self.check_compatible_fresh(module, strict=True, isolated=True)

@requires_subinterpreters
@requires_singlephase_init
def test_disallowed_reimport(self):
# See https://github.com/python/cpython/issues/104621.
script = textwrap.dedent('''
import _testsinglephase
print(_testsinglephase)
''')
interpid = _interpreters.create()
with self.assertRaises(_interpreters.RunFailedError):
_interpreters.run_string(interpid, script)
with self.assertRaises(_interpreters.RunFailedError):
_interpreters.run_string(interpid, script)


class TestSinglePhaseSnapshot(ModuleSnapshot):

Expand Down Expand Up @@ -2017,6 +2045,10 @@ def setUpClass(cls):
# Start fresh.
cls.clean_up()

@classmethod
def tearDownClass(cls):
restore__testsinglephase()

def tearDown(self):
# Clean up the module.
self.clean_up()
Expand Down
13 changes: 12 additions & 1 deletion Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,21 @@
from test.support.script_helper import assert_python_ok, assert_python_failure
from test.support import threading_helper
from test.support import import_helper
from test.support import interpreters
try:
from test.support import interpreters
except ImportError:
interpreters = None
import textwrap
import unittest
import warnings


def requires_subinterpreters(meth):
"""Decorator to skip a test if subinterpreters are not supported."""
return unittest.skipIf(interpreters is None,
'subinterpreters required')(meth)


# count the number of test runs, used to create unique
# strings to intern in test_intern()
INTERN_NUMRUNS = 0
Expand Down Expand Up @@ -700,6 +709,7 @@ def __hash__(self):

self.assertRaises(TypeError, sys.intern, S("abc"))

@requires_subinterpreters
def test_subinterp_intern_dynamically_allocated(self):
global INTERN_NUMRUNS
INTERN_NUMRUNS += 1
Expand All @@ -715,6 +725,7 @@ def test_subinterp_intern_dynamically_allocated(self):
assert id(t) != {id(t)}, (id(t), {id(t)})
'''))

@requires_subinterpreters
def test_subinterp_intern_statically_allocated(self):
# See Tools/build/generate_global_objects.py for the list
# of strings that are always statically allocated.
Expand Down
3 changes: 2 additions & 1 deletion Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -1631,6 +1631,7 @@ PYTHON_HEADERS= \
$(srcdir)/Include/floatobject.h \
$(srcdir)/Include/frameobject.h \
$(srcdir)/Include/import.h \
$(srcdir)/Include/interpreteridobject.h \
$(srcdir)/Include/intrcheck.h \
$(srcdir)/Include/iterobject.h \
$(srcdir)/Include/listobject.h \
Expand Down Expand Up @@ -1700,6 +1701,7 @@ PYTHON_HEADERS= \
$(srcdir)/Include/cpython/genobject.h \
$(srcdir)/Include/cpython/import.h \
$(srcdir)/Include/cpython/initconfig.h \
$(srcdir)/Include/cpython/interpreteridobject.h \
$(srcdir)/Include/cpython/listobject.h \
$(srcdir)/Include/cpython/longintrepr.h \
$(srcdir)/Include/cpython/longobject.h \
Expand Down Expand Up @@ -1771,7 +1773,6 @@ PYTHON_HEADERS= \
$(srcdir)/Include/internal/pycore_import.h \
$(srcdir)/Include/internal/pycore_initconfig.h \
$(srcdir)/Include/internal/pycore_interp.h \
$(srcdir)/Include/internal/pycore_interp_id.h \
$(srcdir)/Include/internal/pycore_intrinsics.h \
$(srcdir)/Include/internal/pycore_list.h \
$(srcdir)/Include/internal/pycore_long.h \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Unsupported modules now always fail to be imported.
12 changes: 6 additions & 6 deletions Modules/_testinternalcapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,27 @@
#include "pycore_atomic_funcs.h" // _Py_atomic_int_get()
#include "pycore_bitutils.h" // _Py_bswap32()
#include "pycore_bytesobject.h" // _PyBytes_Find()
#include "pycore_ceval.h" // _PyEval_AddPendingCall
#include "pycore_compile.h" // _PyCompile_CodeGen, _PyCompile_OptimizeCfg, _PyCompile_Assemble, _PyCompile_CleanDoc
#include "pycore_ceval.h" // _PyEval_AddPendingCall
#include "pycore_fileutils.h" // _Py_normpath
#include "pycore_frame.h" // _PyInterpreterFrame
#include "pycore_gc.h" // PyGC_Head
#include "pycore_hashtable.h" // _Py_hashtable_new()
#include "pycore_initconfig.h" // _Py_GetConfigsAsDict()
#include "pycore_interp.h" // _PyInterpreterState_GetConfigCopy()
#include "pycore_interp_id.h" // _PyInterpreterID_LookUp()
#include "pycore_object.h" // _PyObject_IsFreed()
#include "pycore_object.h" // _PyObject_IsFreed()
#include "pycore_pathconfig.h" // _PyPathConfig_ClearGlobal()
#include "pycore_pyerrors.h" // _Py_UTF8_Edit_Cost()
#include "pycore_pystate.h" // _PyThreadState_GET()

#include "frameobject.h"
#include "interpreteridobject.h" // PyInterpreterID_LookUp()
#include "osdefs.h" // MAXPATHLEN

#include "clinic/_testinternalcapi.c.h"

#ifdef MS_WINDOWS
# include <winsock2.h> // struct timeval
# include <winsock2.h> // struct timeval
#endif


Expand Down Expand Up @@ -1083,7 +1083,7 @@ pending_identify(PyObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "O:pending_identify", &interpid)) {
return NULL;
}
PyInterpreterState *interp = _PyInterpreterID_LookUp(interpid);
PyInterpreterState *interp = PyInterpreterID_LookUp(interpid);
if (interp == NULL) {
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_ValueError, "interpreter not found");
Expand Down Expand Up @@ -1433,7 +1433,7 @@ test_atexit(PyObject *self, PyObject *Py_UNUSED(args))
PyThreadState *tstate = Py_NewInterpreter();

struct atexit_data data = {0};
int res = _Py_AtExit(tstate->interp, callback, (void *)&data);
int res = PyUnstable_AtExit(tstate->interp, callback, (void *)&data);
Py_EndInterpreter(tstate);
PyThreadState_Swap(oldts);
if (res < 0) {
Expand Down
11 changes: 3 additions & 8 deletions Modules/_xxinterpchannelsmodule.c
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
/* interpreters module */
/* low-level access to interpreter primitives */

#ifndef Py_BUILD_CORE_BUILTIN
# define Py_BUILD_CORE_MODULE 1
#endif

#include "Python.h"
#include "pycore_atexit.h" // _Py_AtExit()
#include "pycore_interp_id.h" // _PyInterpreterState_GetIDObject()
#include "interpreteridobject.h"


/*
Expand Down Expand Up @@ -2140,7 +2135,7 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds)
goto except;
}
if (res) {
id_obj = _PyInterpreterState_GetIDObject(interp);
id_obj = PyInterpreterState_GetIDObject(interp);
if (id_obj == NULL) {
goto except;
}
Expand Down Expand Up @@ -2407,7 +2402,7 @@ module_exec(PyObject *mod)

// Make sure chnnels drop objects owned by this interpreter
PyInterpreterState *interp = _get_current_interp();
_Py_AtExit(interp, clear_interpreter, (void *)interp);
PyUnstable_AtExit(interp, clear_interpreter, (void *)interp);

return 0;

Expand Down
Loading

0 comments on commit 88d48f6

Please sign in to comment.