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
3 changes: 3 additions & 0 deletions Doc/data/stable_abi.dat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

96 changes: 55 additions & 41 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,12 @@ struct _object {
# endif
};
#else
Py_ssize_t ob_refcnt;
Py_ssize_t ob_refcnt; // part of stable ABI; do not change
#endif
_Py_ALIGNED_DEF(_PyObject_MIN_ALIGNMENT, char) _aligner;
};

PyTypeObject *ob_type;
PyTypeObject *ob_type; // part of stable ABI; do not change
};
#else
// Objects that are not owned by any thread use a thread id (tid) of zero.
Expand Down Expand Up @@ -173,7 +173,7 @@ struct _object {
#ifndef _Py_OPAQUE_PYOBJECT
struct PyVarObject {
PyObject ob_base;
Py_ssize_t ob_size; /* Number of items in variable part */
Py_ssize_t ob_size; // Number of items in variable part. Part of stable ABI
};
#endif
typedef struct PyVarObject PyVarObject;
Expand Down Expand Up @@ -265,56 +265,72 @@ _Py_IsOwnedByCurrentThread(PyObject *ob)
}
#endif

// Py_TYPE() implementation for the stable ABI
PyAPI_DATA(PyTypeObject) PyLong_Type;
PyAPI_DATA(PyTypeObject) PyBool_Type;

/* Definitions for the stable ABI */
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 14)
PyAPI_FUNC(PyTypeObject*) Py_TYPE(PyObject *ob);
#endif
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)
PyAPI_FUNC(Py_ssize_t) Py_SIZE(PyObject *ob);
PyAPI_FUNC(int) Py_IS_TYPE(PyObject *ob, PyTypeObject *type);
PyAPI_FUNC(void) Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size);
#endif
Comment on lines +275 to +279
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm just checking PyO3's FFI definitions against 3.15b1 and it seems to me that this PR has changed these symbols to always be exported functions on 3.15.

That seems to differ with the issue description, which apparently intended to keep the static inline functions for the non-limited API?

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.

Extract of object.c in the 3.15 branch:

static inline int
_Py_IS_TYPE_impl(PyObject *ob, PyTypeObject *type)
{
    return Py_TYPE(ob) == type;
}

#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 11)
#  define Py_IS_TYPE(ob, type) _Py_IS_TYPE_impl(_PyObject_CAST(ob), (type))
#elif Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 15)
#  define Py_IS_TYPE(ob, type) _Py_IS_TYPE_impl((ob), (type))
#else
// Limited API 3.15+: use function calls
#endif

If Py_LIMITED_API is not defined or if it's defined to Python 3.14 or older, Py_IS_TYPE() is implemented as a static inline function. The code has been modified since this PR.

Would you mind to elaborate which API is implemented as a function call? Using the limited C API or not? Which limited C API version?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You're right, sorry for the confusion. I think the tooling I was using to check had a bug, and then I saw this diff and got myself confused. 🤦

Even looking at this diff again, the #define macros just below starting on 292 will implement as a static inline function.

Sorry for the noise!

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.

No problem. Thanks for double checking.


#ifndef _Py_OPAQUE_PYOBJECT

#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030e0000
// Stable ABI implements Py_TYPE() as a function call
// on limited C API version 3.14 and newer.
static inline void
Py_SET_TYPE(PyObject *ob, PyTypeObject *type)
{
ob->ob_type = type;
}

#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 11)
// Non-limited API & limited API 3.11 & below: use static inline functions and
// use _PyObject_CAST so that users don't need their own casts
# define Py_TYPE(ob) _Py_TYPE_impl(_PyObject_CAST(ob))
# define Py_SIZE(ob) _Py_SIZE_impl(_PyObject_CAST(ob))
# define Py_IS_TYPE(ob, type) _Py_IS_TYPE_impl(_PyObject_CAST(ob), (type))
# define Py_SET_SIZE(ob, size) _Py_SET_SIZE_impl(_PyVarObject_CAST(ob), (size))
# define Py_SET_TYPE(ob, type) Py_SET_TYPE(_PyObject_CAST(ob), type)
#elif Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 15)
// Limited API 3.11-3.14: use static inline functions, without casts
# define Py_SIZE(ob) _Py_SIZE_impl(ob)
# define Py_IS_TYPE(ob, type) _Py_IS_TYPE_impl((ob), (type))
# define Py_SET_SIZE(ob, size) _Py_SET_SIZE_impl((ob), (size))
# if Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 14)
// Py_TYPE() is static inline only on Limited API 3.13 and below
# define Py_TYPE(ob) _Py_TYPE_impl(ob)
# endif
#else
static inline PyTypeObject* _Py_TYPE(PyObject *ob)
{
return ob->ob_type;
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
# define Py_TYPE(ob) _Py_TYPE(_PyObject_CAST(ob))
#else
# define Py_TYPE(ob) _Py_TYPE(ob)
#endif
// Limited API 3.15+: use function calls
#endif

PyAPI_DATA(PyTypeObject) PyLong_Type;
PyAPI_DATA(PyTypeObject) PyBool_Type;
static inline
PyTypeObject* _Py_TYPE_impl(PyObject *ob)
{
return ob->ob_type;
}

#ifndef _Py_OPAQUE_PYOBJECT
// bpo-39573: The Py_SET_SIZE() function must be used to set an object size.
static inline Py_ssize_t Py_SIZE(PyObject *ob) {
static inline Py_ssize_t
_Py_SIZE_impl(PyObject *ob)
{
assert(Py_TYPE(ob) != &PyLong_Type);
assert(Py_TYPE(ob) != &PyBool_Type);
return _PyVarObject_CAST(ob)->ob_size;
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
# define Py_SIZE(ob) Py_SIZE(_PyObject_CAST(ob))
#endif
#endif // !defined(_Py_OPAQUE_PYOBJECT)

static inline int Py_IS_TYPE(PyObject *ob, PyTypeObject *type) {
static inline int
_Py_IS_TYPE_impl(PyObject *ob, PyTypeObject *type)
{
return Py_TYPE(ob) == type;
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
# define Py_IS_TYPE(ob, type) Py_IS_TYPE(_PyObject_CAST(ob), (type))
#endif


#ifndef _Py_OPAQUE_PYOBJECT
static inline void Py_SET_TYPE(PyObject *ob, PyTypeObject *type) {
ob->ob_type = type;
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
# define Py_SET_TYPE(ob, type) Py_SET_TYPE(_PyObject_CAST(ob), type)
#endif

static inline void Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) {
static inline void
_Py_SET_SIZE_impl(PyVarObject *ob, Py_ssize_t size)
{
assert(Py_TYPE(_PyObject_CAST(ob)) != &PyLong_Type);
assert(Py_TYPE(_PyObject_CAST(ob)) != &PyBool_Type);
#ifdef Py_GIL_DISABLED
Expand All @@ -323,9 +339,7 @@ static inline void Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) {
ob->ob_size = size;
#endif
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
# define Py_SET_SIZE(ob, size) Py_SET_SIZE(_PyVarObject_CAST(ob), (size))
#endif

#endif // !defined(_Py_OPAQUE_PYOBJECT)


Expand Down
3 changes: 3 additions & 0 deletions Lib/test/test_stable_abi_ctypes.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Expose the functions :c:func:`Py_SIZE`, :c:func:`Py_IS_TYPE` and
:c:func:`Py_SET_SIZE` in the Stable ABI.
11 changes: 11 additions & 0 deletions Misc/stable_abi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2524,8 +2524,10 @@
added = '3.13'

[function.Py_TYPE]
# Before 3.14, this was a macro that accessed the PyObject member
added = '3.14'
[function.Py_REFCNT]
# Before 3.14, this was a macro that accessed the PyObject member
added = '3.14'
[function.PyIter_NextItem]
added = '3.14'
Expand Down Expand Up @@ -2641,3 +2643,12 @@
added = '3.15'
[function.PyDict_SetDefaultRef]
added = '3.15'
[function.Py_SIZE]
# Before 3.15, this was a macro that accessed the PyObject member
added = '3.15'
[function.Py_IS_TYPE]
# Before 3.15, this was a macro that accessed the PyObject member
added = '3.15'
[function.Py_SET_SIZE]
# Before 3.15, this was a macro that accessed the PyObject member
added = '3.15'
31 changes: 13 additions & 18 deletions Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -3361,24 +3361,6 @@ Py_GetConstantBorrowed(unsigned int constant_id)
return Py_GetConstant(constant_id);
}


// Py_TYPE() implementation for the stable ABI
#undef Py_TYPE
PyTypeObject*
Py_TYPE(PyObject *ob)
{
return _Py_TYPE(ob);
}


// Py_REFCNT() implementation for the stable ABI
#undef Py_REFCNT
Py_ssize_t
Py_REFCNT(PyObject *ob)
{
return _Py_REFCNT(ob);
}

int
PyUnstable_IsImmortal(PyObject *op)
{
Expand All @@ -3405,3 +3387,16 @@ _PyObject_VisitType(PyObject *op, visitproc visit, void *arg)
Py_VISIT(tp);
return 0;
}

// Implementations for the stable ABI
// Keep these at the end.
#undef Py_TYPE
#undef Py_REFCNT
#undef Py_SIZE
#undef Py_IS_TYPE
#undef Py_SET_SIZE
PyTypeObject* Py_TYPE(PyObject *ob) { return _Py_TYPE_impl(ob); }
Py_ssize_t Py_REFCNT(PyObject *ob) { return _Py_REFCNT(ob); }
Py_ssize_t Py_SIZE(PyObject *o) { return _Py_SIZE_impl(o); }
int Py_IS_TYPE(PyObject *o, PyTypeObject *t) { return _Py_IS_TYPE_impl(o, t); }
void Py_SET_SIZE(PyVarObject *o, Py_ssize_t s) { _Py_SET_SIZE_impl(o, s); }
Comment thread
encukou marked this conversation as resolved.
3 changes: 3 additions & 0 deletions PC/python3dll.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading