Skip to content

Commit f55d6b1

Browse files
committed
Drop gc/weakref support
1 parent 3c9e01e commit f55d6b1

File tree

2 files changed

+53
-79
lines changed

2 files changed

+53
-79
lines changed

arraydeque.c

+53-64
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,25 @@
44
#include <stddef.h> /* for offsetof */
55

66
#ifndef ARRAYDEQUE_VERSION
7-
#define ARRAYDEQUE_VERSION "1.2.3"
7+
#define ARRAYDEQUE_VERSION "1.2.4"
88
#endif
99

10-
/* The ArrayDeque object structure.
11-
A new field weakreflist is added to support weak references */
10+
/* The ArrayDeque object structure. */
1211
typedef struct {
1312
PyObject_HEAD
14-
PyObject *weakreflist; /* For weak reference support */
15-
PyObject **array; /* Pointer to array of PyObject* */
16-
Py_ssize_t capacity; /* Allocated length of array */
17-
Py_ssize_t size; /* Number of elements stored */
18-
Py_ssize_t head; /* Index of first element */
19-
Py_ssize_t tail; /* Index one past the last element */
20-
Py_ssize_t maxlen; /* Maximum allowed size (if < 0 then unbounded) */
13+
PyObject **array; /* pointer to array of PyObject* */
14+
Py_ssize_t capacity; /* allocated length of array */
15+
Py_ssize_t size; /* number of elements stored */
16+
Py_ssize_t head; /* index of first element */
17+
Py_ssize_t tail; /* index one past the last element */
18+
Py_ssize_t maxlen; /* maximum allowed size (if < 0 then unbounded) */
2119
} ArrayDequeObject;
2220

2321
/* Forward declaration of type for iterator */
2422
typedef struct {
2523
PyObject_HEAD
26-
ArrayDequeObject *deque; /* Reference to the deque */
27-
Py_ssize_t index; /* Current index into the deque (0 .. size) */
24+
ArrayDequeObject *deque; /* reference to the deque */
25+
Py_ssize_t index; /* current index into the deque (0 .. size) */
2826
} ArrayDequeIter;
2927

3028
/* Resize the backing array to new_capacity and recenter the data.
@@ -61,16 +59,21 @@ arraydeque_resize(ArrayDequeObject *self, Py_ssize_t new_capacity)
6159
static PyObject *
6260
ArrayDeque_append(ArrayDequeObject *self, PyObject *arg)
6361
{
62+
/* If maxlen is 0, do nothing. */
6463
if (self->maxlen == 0) {
6564
Py_RETURN_NONE;
6665
}
66+
67+
/* If bounded and full, drop the leftmost element. */
6768
if (self->maxlen >= 0 && self->size == self->maxlen) {
6869
PyObject *old = self->array[self->head];
6970
Py_DECREF(old);
7071
self->array[self->head] = NULL;
7172
self->head++;
7273
self->size--;
7374
}
75+
76+
/* Grow the internal array if needed */
7477
if (self->tail >= self->capacity) {
7578
if (arraydeque_resize(self, self->capacity * 2) < 0)
7679
return NULL;
@@ -92,12 +95,16 @@ ArrayDeque_appendleft(ArrayDequeObject *self, PyObject *arg)
9295
if (self->maxlen == 0) {
9396
Py_RETURN_NONE;
9497
}
98+
99+
/* If bounded and full, drop the rightmost element */
95100
if (self->maxlen >= 0 && self->size == self->maxlen) {
96101
self->tail--;
97102
Py_DECREF(self->array[self->tail]);
98103
self->array[self->tail] = NULL;
99104
self->size--;
100105
}
106+
107+
/* Grow the internal array if necessary */
101108
if (self->head <= 0) {
102109
if (arraydeque_resize(self, self->capacity * 2) < 0)
103110
return NULL;
@@ -146,17 +153,19 @@ ArrayDeque_popleft(ArrayDequeObject *self, PyObject *Py_UNUSED(ignored))
146153
static PyObject *
147154
ArrayDeque_clear(ArrayDequeObject *self, PyObject *Py_UNUSED(ignored))
148155
{
149-
for (Py_ssize_t i = self->head; i < self->tail; i++) {
156+
Py_ssize_t i;
157+
for (i = self->head; i < self->tail; i++) {
150158
Py_CLEAR(self->array[i]);
151159
}
152160
self->size = 0;
161+
/* Reset head and tail to the center of the current allocation */
153162
self->head = self->capacity / 2;
154163
self->tail = self->head;
155164
Py_RETURN_NONE;
156165
}
157166

158167
/* Method: extend(iterable)
159-
Extend the right side with elements from an iterable. */
168+
Extend the right side of the deque by appending elements from the iterable. */
160169
static PyObject *
161170
ArrayDeque_extend(ArrayDequeObject *self, PyObject *iterable)
162171
{
@@ -179,8 +188,8 @@ ArrayDeque_extend(ArrayDequeObject *self, PyObject *iterable)
179188
}
180189

181190
/* Method: extendleft(iterable)
182-
Extend the left side with elements from an iterable.
183-
Note the order is reversed relative to the input. */
191+
Extend the left side of the deque by appending elements from the iterable.
192+
Note that left appends reverse the order relative to the input. */
184193
static PyObject *
185194
ArrayDeque_extendleft(ArrayDequeObject *self, PyObject *iterable)
186195
{
@@ -191,6 +200,8 @@ ArrayDeque_extendleft(ArrayDequeObject *self, PyObject *iterable)
191200
if (list == NULL)
192201
return NULL;
193202
len = PyList_Size(list);
203+
/* Iterate in forward order: each call to appendleft will
204+
insert the item before the previous ones, thus reversing the input order. */
194205
for (i = 0; i < len; i++) {
195206
PyObject *item = PyList_GET_ITEM(list, i);
196207
if (ArrayDeque_appendleft(self, item) == NULL) {
@@ -203,8 +214,9 @@ ArrayDeque_extendleft(ArrayDequeObject *self, PyObject *iterable)
203214
}
204215

205216
/* Method: rotate(n=1)
206-
Rotate the deque n steps to the right (n negative rotates left).
207-
Each rotation uses pop/popleft and append/appendleft. */
217+
Rotate the deque n steps to the right. If n is negative, rotate left.
218+
Each individual rotation is implemented using pop/popleft and append/appendleft.
219+
*/
208220
static PyObject *
209221
ArrayDeque_rotate(ArrayDequeObject *self, PyObject *args)
210222
{
@@ -244,7 +256,8 @@ ArrayDeque_rotate(ArrayDequeObject *self, PyObject *args)
244256
}
245257

246258
/* Method: remove(value)
247-
Remove the first occurrence of value. */
259+
Remove the first occurrence of value.
260+
*/
248261
static PyObject *
249262
ArrayDeque_remove(ArrayDequeObject *self, PyObject *value)
250263
{
@@ -262,7 +275,7 @@ ArrayDeque_remove(ArrayDequeObject *self, PyObject *value)
262275
}
263276
Py_DECREF(self->array[i]);
264277
for (Py_ssize_t j = i; j < self->tail - 1; j++) {
265-
self->array[j] = self->array[j + 1];
278+
self->array[j] = self->array[j+1];
266279
}
267280
self->array[self->tail - 1] = NULL;
268281
self->tail--;
@@ -271,7 +284,8 @@ ArrayDeque_remove(ArrayDequeObject *self, PyObject *value)
271284
}
272285

273286
/* Method: count(value)
274-
Count the number of occurrences of value. */
287+
Count the number of occurrences of value.
288+
*/
275289
static PyObject *
276290
ArrayDeque_count(ArrayDequeObject *self, PyObject *value)
277291
{
@@ -293,7 +307,7 @@ ArrayDeque_length(ArrayDequeObject *self)
293307
return self->size;
294308
}
295309

296-
/* Sequence protocol: __getitem__ (only integer indices) */
310+
/* Sequence protocol: __getitem__ support (only for integer indices) */
297311
static PyObject *
298312
ArrayDeque_seq_getitem(ArrayDequeObject *self, Py_ssize_t index)
299313
{
@@ -308,7 +322,7 @@ ArrayDeque_seq_getitem(ArrayDequeObject *self, Py_ssize_t index)
308322
return item;
309323
}
310324

311-
/* Mapping protocol: __getitem__ (same as sequence) */
325+
/* Mapping protocol: __getitem__ support (same as sequence) */
312326
static PyObject *
313327
ArrayDeque_getitem(ArrayDequeObject *self, PyObject *key)
314328
{
@@ -322,10 +336,11 @@ ArrayDeque_getitem(ArrayDequeObject *self, PyObject *key)
322336
return ArrayDeque_seq_getitem(self, index);
323337
}
324338

325-
/* Sequence protocol: __setitem__ (only integer indices) */
339+
/* Sequence protocol: __setitem__ support (only for integer indices) */
326340
static int
327341
ArrayDeque_seq_setitem(ArrayDequeObject *self, Py_ssize_t index, PyObject *value)
328342
{
343+
/* If value is NULL, this signals deletion which is not supported. */
329344
if (value == NULL) {
330345
PyErr_SetString(PyExc_TypeError, "deque deletion not supported");
331346
return -1;
@@ -343,7 +358,7 @@ ArrayDeque_seq_setitem(ArrayDequeObject *self, Py_ssize_t index, PyObject *value
343358
return 0;
344359
}
345360

346-
/* Mapping protocol: __setitem__ */
361+
/* Mapping protocol: __setitem__ support */
347362
static int
348363
ArrayDeque_setitem(ArrayDequeObject *self, PyObject *key, PyObject *value)
349364
{
@@ -371,7 +386,7 @@ ArrayDeque_contains(ArrayDequeObject *self, PyObject *value)
371386
return 0;
372387
}
373388

374-
/* Rich comparison support: only equality and inequality */
389+
/* Rich comparison support: only equality and inequality are implemented */
375390
static PyObject *
376391
ArrayDeque_richcompare(PyObject *self, PyObject *other, int op)
377392
{
@@ -454,6 +469,7 @@ ArrayDequeIter_next(ArrayDequeIter *it)
454469
Py_INCREF(item);
455470
return item;
456471
}
472+
/* End of iteration */
457473
return NULL;
458474
}
459475

@@ -468,7 +484,7 @@ static PyTypeObject ArrayDequeIter_Type = {
468484
.tp_iternext = (iternextfunc)ArrayDequeIter_next,
469485
};
470486

471-
/* __iter__ method for ArrayDeque */
487+
/* __iter__ method for ArrayDeque: return a new iterator */
472488
static PyObject *
473489
ArrayDeque_iter(ArrayDequeObject *self)
474490
{
@@ -482,19 +498,17 @@ ArrayDeque_iter(ArrayDequeObject *self)
482498
return (PyObject *)it;
483499
}
484500

485-
/* __new__ method: allocate a new ArrayDeque.
486-
This version uses PyObject_GC_New to ensure that the object
487-
is not already tracked by the garbage collector (fixing issues on macOS). */
501+
/* __new__ method: allocate a new ArrayDeque */
488502
static PyObject *
489503
ArrayDeque_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
490504
{
491505
ArrayDequeObject *self;
492-
self = PyObject_GC_New(ArrayDequeObject, type);
506+
self = (ArrayDequeObject *)type->tp_alloc(type, 0);
493507
if (self == NULL)
494508
return NULL;
495-
self->weakreflist = NULL;
496509
self->capacity = 8; /* initial capacity */
497510
self->size = 0;
511+
/* Start in the middle so that both ends have some space */
498512
self->head = self->capacity / 2;
499513
self->tail = self->head;
500514
self->array = PyMem_New(PyObject *, self->capacity);
@@ -508,13 +522,12 @@ ArrayDeque_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
508522
}
509523
/* Default: unbounded deque */
510524
self->maxlen = -1;
511-
PyObject_GC_Track((PyObject *)self);
512525
return (PyObject *)self;
513526
}
514527

515528
/* __init__ method: optionally initialize the deque with an iterable and a maxlen.
516529
Signature: ArrayDeque([iterable[, maxlen]])
517-
If maxlen is provided (and not None) it must be a non-negative integer.
530+
If maxlen is provided and not None, it must be a non-negative integer.
518531
When iterable is longer than maxlen, only the rightmost elements are retained.
519532
*/
520533
static int
@@ -563,35 +576,13 @@ ArrayDeque_init(ArrayDequeObject *self, PyObject *args, PyObject *kwds)
563576
static void
564577
ArrayDeque_dealloc(ArrayDequeObject *self)
565578
{
566-
PyObject_GC_UnTrack(self);
567-
PyObject_ClearWeakRefs((PyObject *)self);
568579
for (Py_ssize_t i = self->head; i < self->tail; i++) {
569-
Py_CLEAR(self->array[i]);
580+
Py_XDECREF(self->array[i]);
570581
}
571582
PyMem_Free(self->array);
572583
Py_TYPE(self)->tp_free((PyObject *)self);
573584
}
574585

575-
/* GC traverse function */
576-
static int
577-
ArrayDeque_traverse(ArrayDequeObject *self, visitproc visit, void *arg)
578-
{
579-
for (Py_ssize_t i = self->head; i < self->tail; i++) {
580-
Py_VISIT(self->array[i]);
581-
}
582-
return 0;
583-
}
584-
585-
/* GC clear function */
586-
static int
587-
ArrayDeque_clear_func(ArrayDequeObject *self)
588-
{
589-
for (Py_ssize_t i = self->head; i < self->tail; i++) {
590-
Py_CLEAR(self->array[i]);
591-
}
592-
return 0;
593-
}
594-
595586
/* Getter for the maxlen attribute.
596587
Returns None if unbounded; otherwise a Python integer. */
597588
static PyObject *
@@ -625,10 +616,11 @@ ArrayDeque_reduce(ArrayDequeObject *self)
625616
return NULL;
626617
}
627618
}
619+
/* Build args tuple as (iterable, maxlen) */
628620
PyObject *args = Py_BuildValue("(OO)", list, maxlen);
629621
Py_DECREF(list);
630622
Py_DECREF(maxlen);
631-
Py_INCREF(Py_TYPE(self));
623+
/* Return a two-tuple: (constructor, args) */
632624
return Py_BuildValue("OO", Py_TYPE(self), args);
633625
}
634626

@@ -666,7 +658,7 @@ static PyMethodDef ArrayDeque_methods[] = {
666658
{NULL} /* Sentinel */
667659
};
668660

669-
/* Sequence methods */
661+
/* Define sequence methods (supporting __len__, __getitem__, __setitem__, and __contains__) */
670662
static PySequenceMethods ArrayDeque_as_sequence = {
671663
(lenfunc)ArrayDeque_length, /* sq_length */
672664
0, /* sq_concat */
@@ -680,7 +672,7 @@ static PySequenceMethods ArrayDeque_as_sequence = {
680672
0 /* sq_inplace_repeat */
681673
};
682674

683-
/* Mapping methods */
675+
/* Define mapping methods so that deque[index] works as expected. */
684676
static PyMappingMethods ArrayDeque_as_mapping = {
685677
(lenfunc)ArrayDeque_length, /* mp_length */
686678
(binaryfunc)ArrayDeque_getitem, /* mp_subscript */
@@ -695,7 +687,7 @@ static PyTypeObject ArrayDequeType = {
695687
.tp_basicsize = sizeof(ArrayDequeObject),
696688
.tp_itemsize = 0,
697689
.tp_dealloc = (destructor)ArrayDeque_dealloc,
698-
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
690+
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
699691
.tp_new = ArrayDeque_new,
700692
.tp_init = (initproc)ArrayDeque_init,
701693
.tp_iter = (getiterfunc)ArrayDeque_iter,
@@ -706,9 +698,6 @@ static PyTypeObject ArrayDequeType = {
706698
.tp_str = (reprfunc)ArrayDeque_str,
707699
.tp_repr = (reprfunc)ArrayDeque_repr,
708700
.tp_richcompare = ArrayDeque_richcompare,
709-
.tp_weaklistoffset = offsetof(ArrayDequeObject, weakreflist),
710-
.tp_traverse = (traverseproc)ArrayDeque_traverse,
711-
.tp_clear = (inquiry)ArrayDeque_clear_func,
712701
};
713702

714703
/* Module definition */

test_arraydeque.py

-15
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
import unittest
1616
import pickle
1717
import copy
18-
import weakref
19-
import gc
2018

2119
from arraydeque import ArrayDeque
2220
from collections import deque # for reference comparisons
@@ -310,19 +308,6 @@ def test_copy(self):
310308
self.assertNotEqual(list(d), list(d2))
311309

312310

313-
# ---------------------------
314-
# Weak Reference Testing
315-
# ---------------------------
316-
class TestArrayDequeWeakref(unittest.TestCase):
317-
def test_weakref(self):
318-
d = ArrayDeque('abc')
319-
ref = weakref.ref(d)
320-
self.assertIsNotNone(ref())
321-
del d
322-
gc.collect()
323-
self.assertIsNone(ref())
324-
325-
326311
# ---------------------------
327312
# Subclassing Testing
328313
# ---------------------------

0 commit comments

Comments
 (0)