Skip to content
Closed
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
4 changes: 4 additions & 0 deletions Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ typedef struct _heaptypeobject {
PyBufferProcs as_buffer;
PyObject *ht_name, *ht_slots, *ht_qualname;
struct _dictkeysobject *ht_cached_keys;
PyObject *ht_module;
/* here are optional user slots, followed by the members. */
} PyHeapTypeObject;

Expand All @@ -297,6 +298,9 @@ PyAPI_FUNC(PyTypeObject *) _PyType_CalculateMetaclass(PyTypeObject *, PyObject *
PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *, const char *);
PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *, const char *);

PyAPI_FUNC(PyObject *) PyType_GetModule(PyTypeObject *);
PyAPI_FUNC(void *) PyType_GetModuleState(PyTypeObject *);

struct _Py_Identifier;
PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
PyAPI_FUNC(void) _Py_BreakPoint(void);
Expand Down
33 changes: 32 additions & 1 deletion Include/methodobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ extern "C" {
for the latter. */

PyAPI_DATA(PyTypeObject) PyCFunction_Type;
PyAPI_DATA(PyTypeObject) PyCMethod_Type;

#define PyCFunction_Check(op) (Py_TYPE(op) == &PyCFunction_Type)
#define PyCFunction_Check(op) ((Py_TYPE(op) == &PyCFunction_Type) || (PyType_IsSubtype(Py_TYPE(op), &PyCFunction_Type)))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we use the existing PyCFunction_Type for methods?
The METH_METHOD flag should signal that the underlying struct is PyCMethodObject; you'll also want to set tp_itemsize and the use PyObject_GC_NewVar.


typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
typedef PyObject *(*_PyCFunctionFast) (PyObject *, PyObject *const *, Py_ssize_t);
Expand All @@ -22,6 +23,8 @@ typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *, PyObject *,
typedef PyObject *(*_PyCFunctionFastWithKeywords) (PyObject *,
PyObject *const *, Py_ssize_t,
PyObject *);
typedef PyObject *(*PyCMethod)(PyObject *, PyTypeObject *, PyObject *, PyObject *);

PyAPI_FUNC(PyCFunction) PyCFunction_GetFunction(PyObject *);
PyAPI_FUNC(PyObject *) PyCFunction_GetSelf(PyObject *);
PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *);
Expand All @@ -36,6 +39,10 @@ PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *);
NULL : ((PyCFunctionObject *)func) -> m_self)
#define PyCFunction_GET_FLAGS(func) \
(((PyCFunctionObject *)func) -> m_ml -> ml_flags)
#define PyCFunction_GET_CLASS(func) \
(((PyCFunctionObject *)func) -> m_ml -> ml_flags & METH_METHOD ? \
((PyCMethodObject *)func) -> mm_class : NULL)

#endif
Py_DEPRECATED(3.9) PyAPI_FUNC(PyObject *) PyCFunction_Call(PyObject *, PyObject *, PyObject *);

Expand All @@ -52,6 +59,13 @@ typedef struct PyMethodDef PyMethodDef;
PyAPI_FUNC(PyObject *) PyCFunction_NewEx(PyMethodDef *, PyObject *,
PyObject *);

#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03080000
#define PyCFunction_NewEx(ML, SELF, MOD) PyCMethod_New((ML), (SELF), (MOD), NULL)
PyAPI_FUNC(PyObject *) PyCMethod_New(PyMethodDef *, PyObject *,
PyObject *, PyTypeObject *);
#endif


/* Flag passed to newmethodobject */
/* #define METH_OLDARGS 0x0000 -- unsupported now */
#define METH_VARARGS 0x0001
Expand Down Expand Up @@ -84,6 +98,16 @@ PyAPI_FUNC(PyObject *) PyCFunction_NewEx(PyMethodDef *, PyObject *,
#define METH_STACKLESS 0x0000
#endif

/* METH_METHOD means the function stores an
* additional reference to the class that defines it;
* both self and class are passed to it.
* It uses PyCMethodObject instead of PyCFunctionObject.
* May not be combined with METH_NOARGS, METH_O, METH_CLASS or METH_STATIC.
*/

#define METH_METHOD 0x0200


#ifndef Py_LIMITED_API
typedef struct {
PyObject_HEAD
Expand All @@ -93,6 +117,13 @@ typedef struct {
PyObject *m_weakreflist; /* List of weak references */
vectorcallfunc vectorcall;
} PyCFunctionObject;

typedef struct {
PyCFunctionObject func;
PyTypeObject *mm_class; /* Class that defines this method */
} PyCMethodObject;


#endif

#ifdef __cplusplus
Expand Down
3 changes: 3 additions & 0 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ PyAPI_FUNC(PyObject*) PyType_FromSpecWithBases(PyType_Spec*, PyObject*);
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03040000
PyAPI_FUNC(void*) PyType_GetSlot(struct _typeobject*, int);
#endif
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03080000
PyAPI_FUNC(PyObject*) PyType_FromModuleAndSpec(PyObject *, PyType_Spec *, PyObject *);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oops, this is missing from the PEP's summary.

#endif

/* Generic type check */
PyAPI_FUNC(int) PyType_IsSubtype(struct _typeobject *, struct _typeobject *);
Expand Down
47 changes: 47 additions & 0 deletions Lib/test/test_importlib/extension/test_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,53 @@ def test_bad_traverse(self):
assert_python_failure("-c", script)


def test_subclass_get_module(self):
testmultiphase = self.load_module_by_name("_testmultiphase_meth_state_access")

class StateAccessType_Subclass(testmultiphase.StateAccessType):
pass

ex = StateAccessType_Subclass()

self.assertIs(ex.get_defining_module(), testmultiphase)

def test_subclass_get_module_with_super(self):
testmultiphase = self.load_module_by_name("_testmultiphase_meth_state_access")

class StateAccessType_Subclass(testmultiphase.StateAccessType):
def get_defining_module(self):
return super().get_defining_module()

ex = StateAccessType_Subclass()

self.assertIs(ex.get_defining_module(), testmultiphase)

def test_state_counter(self):
testmultiphase = self.load_module_by_name("_testmultiphase_meth_state_access")

a = testmultiphase.StateAccessType()
b = testmultiphase.StateAccessType()

self.assertEquals(a.get_count(), b.get_count())
self.assertEquals(a.get_count(), 0)

a.increment_count()
self.assertEquals(a.get_count(), b.get_count())
self.assertEquals(a.get_count(), 1)

b.increment_count()
self.assertEquals(a.get_count(), b.get_count())
self.assertEquals(a.get_count(), 2)

a.decrement_count()
self.assertEquals(a.get_count(), b.get_count())
self.assertEquals(a.get_count(), 1)

b.decrement_count()
self.assertEquals(a.get_count(), b.get_count())
self.assertEquals(a.get_count(), 0)


(Frozen_MultiPhaseExtensionModuleTests,
Source_MultiPhaseExtensionModuleTests
) = util.test_both(MultiPhaseExtensionModuleTests, machinery=machinery)
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -3498,6 +3498,7 @@ def test_create_at_shutdown_without_encoding(self):
if err:
# Can error out with a RuntimeError if the module state
# isn't found.
# self.assertIn(self.shutdown_error, err.decode())
self.assertIn(self.shutdown_error, err.decode())
else:
self.assertEqual("ok", out.decode().strip())
Expand Down Expand Up @@ -3685,6 +3686,7 @@ def _to_memoryview(buf):

class CTextIOWrapperTest(TextIOWrapperTest):
io = io

shutdown_error = "RuntimeError: could not find io module state"

def test_initialization(self):
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -1306,7 +1306,7 @@ def delx(self): del self.__x
'3P' # PyMappingMethods
'10P' # PySequenceMethods
'2P' # PyBufferProcs
'4P')
'5P')
class newstyleclass(object): pass
# Separate block for PyDictKeysObject with 8 keys and 5 entries
check(newstyleclass, s + calcsize("2nP2n0P") + 8 + 5*calcsize("n2P"))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Module C state is now accessible from C-defined heap type methods. (PEP-573)
Loading