Skip to content

Adding in a new PyList_Pop C-API #149589

@Vizonex

Description

@Vizonex

Feature or enhancement

Proposal:

So I maintain a python library called FrozenList and I have been trying to improve it's performance in cython significantly but there is still one final bottleneck that remains in my way and that is having a good PyList_Pop() implementation that I can use instead of Cython Generating a more expensive pure python call so I was wondering if I could go in and add a new function in the future to the current C-API that could enable popping an item out of a list object, I have made a full sample using the provided compatibility header file extension for my example here.

#ifndef __LIST_POP_H__
#define __LIST_POP_H__

#ifdef __cplusplus
extern "C" {
#endif

/* A Small File containing a compatible PyList_Pop C-API implementation for
 * Python. */
#include "Python.h"
#include "pythoncapi_compat.h" /* SEE: https://github.com/python/pythoncapi-compat */
#include <assert.h>

/* Inlined version of `valid_index` seen in `listobject.c`
It's just here only for the demonstrational purposes only... */
#define __PYLIST_VALID_INDEX(i, limit) (((size_t)(i)) < ((size_t)(limit)))

/* same api as `list.pop(...)` in python it accepts an index as well as negative
 * numbers. op: the object to pop from. index: the index to take an object out
 * of. returns: The object that has been removed, otherwise this will be NULL 
 * if an error is raised. */

PyObject *PyList_Pop(PyObject *op, Py_ssize_t index) 
{
  	/* Must be defined here due to needing a Critical Section. */
  	PyObject *v;

  	if (!PyList_Check(op)) {
  	  	PyErr_BadInternalCall();
  	  	return NULL;
  	}

  	if (Py_SIZE(op) == 0) {
  	  	PyErr_SetString(PyExc_IndexError, "pop from empty list");
  	  	return NULL;
  	}

  	if (index < 0)
    	index += Py_SIZE(op);

  	if (!__PYLIST_VALID_INDEX(index, Py_SIZE(op))) {
  	  	PyErr_SetString(PyExc_IndexError, "pop index out of range");
  	  	return NULL;
  	}

  /* We're not going to have the exact same access as python
   * has to stuff like ptr_wise_atomic_memmove so we have to
   * use a much different approach to ensure success, will obtain
   * a new ref immediately since both routes below both STEAL
   * a reference! */

    /* Protect from free-threaded manipulation */
    Py_BEGIN_CRITICAL_SECTION(op)

    v = Py_NewRef(PyList_GET_ITEM(op, index));
    if (Py_SIZE(op) == 1) {
      	assert(PyList_Clear(op) < 0);
      	return v;
    }

    /* Really this shouldn't fail... */
    assert((PySequence_DelItem(op, index) < 0));
    Py_END_CRITICAL_SECTION();
    return v;
};

#ifdef __cplusplus
}
#endif

#endif // __LIST_POP_H__

Has this already been discussed elsewhere?

No response given

Links to previous discussion of this feature:

I have looked around to see if this has been discussed before and I have not found anything worthwhile about this at all yet.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions