Skip to content

C API: Remove PyObject_Init() return value #105944

Closed as not planned
Closed as not planned

Description

@vstinner

PyObject_Init(), PyObject_INIT(), PyObject_InitVar() and PyObject_INIT_VAR() return their argument which is technically a borrowed reference. For me, this API is really surprising. Why would someone like to rely on the return value, since the result is already known: it's the input argument?

I propose to remove the return value of these 4 PyObject "INIT" functions.


On PyPI top 5,000 projects (2023-04-13), I found 3 projects using these functions, but none are affected by proposed change:

  • Cython (0.29.34)
  • pyjson5 (1.6.2)
  • boost-python copy by scipy 1.10.1

I searched for PyObject_INIT regex.

Cython is not affected, since it already ignores the result explicitly:

code.putln("(void) PyObject_INIT(o, t);")

There is also boost-python (used by scipy 1.10.1) which defines PyObject_INIT() if it's not defined... for Python 1.x :-) Python 3 is not affected.

#if !defined(PY_MAJOR_VERSION) || PY_MAJOR_VERSION < 2
# define PyObject_INIT(op, typeobj) \
        ( (op)->ob_type = (typeobj), _Py_NewReference((PyObject *)(op)), (op) )
#endif

pyjson5 (1.6.2) declares PyObject_INIT() and PyObject_InitVar() functions in a Cython binding... without using them. So it's not affected neither.

cdef extern from 'Python.h':
    # ...
    object ObjectInit 'PyObject_INIT'(PyObject *obj, type cls)
    PyVarObject *ObjectInitVar 'PyObject_InitVar'(PyVarObject *obj, type cls, Py_ssize_t size)
    object PyLong_FromString(const char *str, char **pend, int base)
    # ...

Activity

  1. vstinner commented on Jun 20, 2023

    @vstinner
    MemberAuthor

    Hum, wait. It's part of the stable ABI, so we cannot just remove the return value without breaking the stable ABI :-(

  2. vstinner commented on Jun 20, 2023

    @vstinner
    MemberAuthor

    Another option is to deprecate this function, but Cython currently uses it to implement a freelist optimization in ModuleNode.generate_new_function() of Cython/Compiler/ModuleNode.py.

  3. vstinner commented on Jun 20, 2023

    @vstinner
    MemberAuthor

    Existing replacement APIs:

    • PyObject_New() (and _PyObject_New()) calls PyObject_Malloc() and PyObject_Init().
    • PyType_GenericAlloc() (and _PyType_AllocNoTrack()) calls PyObject_Malloc(), _PyObject_Init() or _PyObject_InitVar(), and _PyObject_GC_TRACK().
  4. ronaldoussoren commented on Jun 20, 2023

    @ronaldoussoren
    Contributor

    Isn't the borrowed reference harmless here because, as you write, the return value always is the first argument?

    The return value allows you to write return PyObject_Init(someValue, someType); which can lead to slightly more compact code.

  5. erlend-aasland commented on Jun 20, 2023

    @erlend-aasland
    Contributor

    The return value allows you to write return PyObject_Init(someValue, someType); which can lead to slightly more compact code.

    As a data point, memset, strcpy, strcat and company all return their first argument so you can use them for call chaining.

  6. vstinner commented on Jun 23, 2023

    @vstinner
    MemberAuthor

    It seems quite difficult to change, deprecate, and/or remove this function. I don't think that it's worth it to change its return value, so I just close the issue. I missed the fact that it's part of the stable ABI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions