Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-104614: Make Sure ob_type is Always Set Correctly by PyType_Ready()#105122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Show file tree
Hide file tree
Changes from1 commit
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
PrevPrevious commit
Fix the test.
  • Loading branch information
@ericsnowcurrently
ericsnowcurrently committedJun 1, 2023
commit80d20c650d5d758a65c7b824c729e55d7f2693bb
27 changes: 17 additions & 10 deletionsLib/test/test_capi/test_misc.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1629,27 +1629,34 @@ def test_tp_mro_is_set(self):

class TestStaticTypes(unittest.TestCase):

_has_run = False

@classmethod
def setUpClass(cls):
# The tests here don't play nice with our approach to refleak
# detection, so we bail out in that case.
if cls._has_run:
raise unittest.SkipTest('these tests do not support re-running')
cls._has_run = True
Comment on lines +1634 to +1640
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This is nice. IMO, we should use a similar approach intest_import instead of the hack suggested ingh-104796.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Perhaps alsogh-105085 should have been solved like this.

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I'm glad you appreciate the solution 😄, but actually am a bit torn about this. On the one hand, it gets us what we want in a simple, clear way (IMHO). On the other hand, this approach means theremay be code paths that don't get checked for ref leaks, which we'd want to avoid.

If we use this pattern elsewhere, we'll need to be vigilant about making sure we don't miss any ref leak coverage. I'm not sure how easy that would be generally, though it's unlikely that there are more than a handful of tests where we'd want to skip refleak detection. FWIW, in this case I don't think ref leaks are much of a concern.

erlend-aasland reacted with thumbs up emoji
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

we should use a similar approach

Wouldn't this skip the wholeclass Test instead of a smallerdef test_...?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Yes, so you make sure to collect all your "ref leak unsafe" tests in a single unittest class.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Alternatively, create a decorator for it.


@contextlib.contextmanager
def basic_static_type(self, *args):
cls = _testcapi.get_basic_static_type(*args)
yield cls

def test_pytype_ready_always_sets_tp_type(self):
# The point of this test is to prevent something like
# https://github.com/python/cpython/issues/104614
# from happening again.

@contextlib.contextmanager
def basic_static_type(*args):
cls = _testcapi.get_basic_static_type(*args)
try:
yield cls
finally:
_testcapi.clear_basic_static_type(cls)

# First check when tp_base/tp_bases is *not* set before PyType_Ready().
with basic_static_type() as cls:
withself.basic_static_type() as cls:
self.assertIs(cls.__base__, object);
self.assertEqual(cls.__bases__, (object,));
self.assertIs(type(cls), type(object));

# Then check when we *do* set tp_base/tp_bases first.
with basic_static_type(object) as cls:
withself.basic_static_type(object) as cls:
self.assertIs(cls.__base__, object);
self.assertEqual(cls.__bases__, (object,));
self.assertIs(type(cls), type(object));
Expand Down
47 changes: 18 additions & 29 deletionsModules/_testcapimodule.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2627,13 +2627,20 @@ type_get_tp_mro(PyObject *self, PyObject *type)
}


static PyTypeObject BasicStaticType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "BasicStaticType",
.tp_basicsize = sizeof(PyObject),
/* We only use 2 in test_capi/test_misc.py. */
#define NUM_BASIC_STATIC_TYPES 2
static PyTypeObject BasicStaticTypes[NUM_BASIC_STATIC_TYPES] = {
#define INIT_BASIC_STATIC_TYPE \
{ \
PyVarObject_HEAD_INIT(NULL, 0) \
.tp_name = "BasicStaticType", \
.tp_basicsize = sizeof(PyObject), \
}
INIT_BASIC_STATIC_TYPE,
INIT_BASIC_STATIC_TYPE,
#undef INIT_BASIC_STATIC_TYPE
};

static PyObject * clear_basic_static_type(PyObject *, PyObject *);
static int num_basic_static_types_used = 0;

static PyObject *
get_basic_static_type(PyObject *self, PyObject *args)
Expand All@@ -2644,42 +2651,25 @@ get_basic_static_type(PyObject *self, PyObject *args)
}
assert(base == NULL || PyType_Check(base));

PyTypeObject *cls = &BasicStaticType;
assert(!(cls->tp_flags & Py_TPFLAGS_READY));
if(num_basic_static_types_used >= NUM_BASIC_STATIC_TYPES) {
PyErr_SetString(PyExc_RuntimeError, "no more available basic static types");
return NULL;
}
PyTypeObject *cls = &BasicStaticTypes[num_basic_static_types_used++];

if (base != NULL) {
cls->tp_base = (PyTypeObject *)Py_NewRef(base);
cls->tp_bases = Py_BuildValue("(O)", base);
if (cls->tp_bases == NULL) {
clear_basic_static_type(self, (PyObject *)cls);
return NULL;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Should decreftp_base andtp_bases before returning.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Openedgh-105225.

ericsnowcurrently reacted with thumbs up emoji
Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Thanks for following up after-the-fact!

}
}
if (PyType_Ready(cls) < 0) {
clear_basic_static_type(self, (PyObject *)cls);
return NULL;
}
Py_INCREF(cls);
return (PyObject *)cls;
}

static PyObject *
clear_basic_static_type(PyObject *self, PyObject *clsobj)
{
// Reset it back to the statically initialized state.
PyTypeObject *cls = (PyTypeObject *)clsobj;
Py_CLEAR(cls->ob_base.ob_base.ob_type);
Py_CLEAR(cls->tp_base);
Py_CLEAR(cls->tp_bases);
Py_CLEAR(cls->tp_mro);
Py_CLEAR(cls->tp_subclasses);
Py_CLEAR(cls->tp_dict);
cls->tp_flags &= ~Py_TPFLAGS_READY;
cls->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
cls->tp_version_tag = 0;
Py_RETURN_NONE;
}


// Test PyThreadState C API
static PyObject *
Expand DownExpand Up@@ -3439,7 +3429,6 @@ static PyMethodDef TestMethods[] = {
{"type_get_tp_bases", type_get_tp_bases, METH_O},
{"type_get_tp_mro", type_get_tp_mro, METH_O},
{"get_basic_static_type", get_basic_static_type, METH_VARARGS, NULL},
{"clear_basic_static_type", clear_basic_static_type, METH_O, NULL},
{"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL},
{"frame_getlocals", frame_getlocals, METH_O, NULL},
{"frame_getglobals", frame_getglobals, METH_O, NULL},
Expand Down
3 changes: 2 additions & 1 deletionTools/c-analyzer/cpython/ignored.tsv
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -420,7 +420,8 @@ Modules/_testcapi/watchers.c-num_code_object_destroyed_events-
Modules/_testcapi/watchers.c-pyfunc_watchers-
Modules/_testcapi/watchers.c-func_watcher_ids-
Modules/_testcapi/watchers.c-func_watcher_callbacks-
Modules/_testcapimodule.c-BasicStaticType-
Modules/_testcapimodule.c-BasicStaticTypes-
Modules/_testcapimodule.c-num_basic_static_types_used-
Modules/_testcapimodule.c-ContainerNoGC_members-
Modules/_testcapimodule.c-ContainerNoGC_type-
Modules/_testcapimodule.c-FmData-
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp