fastqueue-lib 0.0.17__tar.gz → 0.0.18__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {fastqueue-lib-0.0.17/fastqueue_lib.egg-info → fastqueue_lib-0.0.18}/PKG-INFO +4 -6
  2. {fastqueue-lib-0.0.17 → fastqueue_lib-0.0.18}/README.md +1 -4
  3. fastqueue_lib-0.0.18/fastqueue/__init__.py +3 -0
  4. fastqueue_lib-0.0.18/fastqueue/__init__.pyi +39 -0
  5. {fastqueue-lib-0.0.17 → fastqueue_lib-0.0.18}/fastqueue/prototypes.py +21 -28
  6. fastqueue_lib-0.0.18/fastqueue/py.typed +0 -0
  7. {fastqueue-lib-0.0.17 → fastqueue_lib-0.0.18/fastqueue_lib.egg-info}/PKG-INFO +4 -6
  8. {fastqueue-lib-0.0.17 → fastqueue_lib-0.0.18}/fastqueue_lib.egg-info/SOURCES.txt +3 -1
  9. {fastqueue-lib-0.0.17 → fastqueue_lib-0.0.18}/pyproject.toml +1 -1
  10. fastqueue_lib-0.0.18/src/ccqueue.cpp +131 -0
  11. {fastqueue-lib-0.0.17 → fastqueue_lib-0.0.18}/src/cllqueue.cpp +28 -12
  12. {fastqueue-lib-0.0.17 → fastqueue_lib-0.0.18}/src/fastqueue.c +324 -383
  13. fastqueue_lib-0.0.18/tests/test_memory.py +1030 -0
  14. fastqueue_lib-0.0.18/tests/test_queue.py +673 -0
  15. fastqueue-lib-0.0.17/fastqueue/__init__.py +0 -3
  16. fastqueue-lib-0.0.17/fastqueue.py +0 -144
  17. fastqueue-lib-0.0.17/src/ccqueue.cpp +0 -89
  18. fastqueue-lib-0.0.17/tests/test_queue.py +0 -248
  19. {fastqueue-lib-0.0.17 → fastqueue_lib-0.0.18}/LICENSE +0 -0
  20. {fastqueue-lib-0.0.17 → fastqueue_lib-0.0.18}/MANIFEST.in +0 -0
  21. {fastqueue-lib-0.0.17 → fastqueue_lib-0.0.18}/fastqueue_lib.egg-info/dependency_links.txt +0 -0
  22. {fastqueue-lib-0.0.17 → fastqueue_lib-0.0.18}/fastqueue_lib.egg-info/requires.txt +0 -0
  23. {fastqueue-lib-0.0.17 → fastqueue_lib-0.0.18}/fastqueue_lib.egg-info/top_level.txt +0 -0
  24. {fastqueue-lib-0.0.17 → fastqueue_lib-0.0.18}/setup.cfg +0 -0
  25. {fastqueue-lib-0.0.17 → fastqueue_lib-0.0.18}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: fastqueue-lib
3
- Version: 0.0.17
3
+ Version: 0.0.18
4
4
  Summary: Fast single ended queue library for python
5
5
  Author-email: Matthew Taylor <matthew.taylor.andre@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/MatthewAndreTaylor/fastqueue
@@ -26,6 +26,7 @@ Requires-Dist: numpy; extra == "examples"
26
26
  Requires-Dist: pandas; extra == "examples"
27
27
  Provides-Extra: tests
28
28
  Requires-Dist: pytest; extra == "tests"
29
+ Dynamic: license-file
29
30
 
30
31
  # fastqueue
31
32
 
@@ -43,7 +44,7 @@ Single-ended fast queues built in C tuned for Python.
43
44
 
44
45
  ## Installation
45
46
 
46
- To install fastqueue, using pip:
47
+ To install fastqueue, using [pip](https://pypi.org/project/fastqueue-lib):
47
48
 
48
49
  ```bash
49
50
  pip install fastqueue-lib
@@ -97,9 +98,6 @@ True
97
98
  ['🚒', '🛴', '🚅', '🚗']
98
99
  ```
99
100
 
100
- Another alternative is `fastqueue.LockQueue()` which supports all queue operations.
101
- `fastqueue.LockQueue()` is built as a thread-safe alternative to the other queue types.
102
-
103
101
  ## Example Benchmarks
104
102
 
105
103
  ### Queue operations
@@ -14,7 +14,7 @@ Single-ended fast queues built in C tuned for Python.
14
14
 
15
15
  ## Installation
16
16
 
17
- To install fastqueue, using pip:
17
+ To install fastqueue, using [pip](https://pypi.org/project/fastqueue-lib):
18
18
 
19
19
  ```bash
20
20
  pip install fastqueue-lib
@@ -68,9 +68,6 @@ True
68
68
  ['🚒', '🛴', '🚅', '🚗']
69
69
  ```
70
70
 
71
- Another alternative is `fastqueue.LockQueue()` which supports all queue operations.
72
- `fastqueue.LockQueue()` is built as a thread-safe alternative to the other queue types.
73
-
74
71
  ## Example Benchmarks
75
72
 
76
73
  ### Queue operations
@@ -0,0 +1,3 @@
1
+ __all__ = "Queue", "QueueC"
2
+
3
+ from _fastqueue import Queue, QueueC
@@ -0,0 +1,39 @@
1
+ """
2
+ fastqueue
3
+ =====
4
+
5
+ * Queue
6
+ * QueueC
7
+
8
+ """
9
+ from typing import Any, Optional
10
+ from collections.abc import Iterable
11
+ from typing_extensions import Self
12
+
13
+ __all__ = "Queue", "QueueC"
14
+
15
+ class Queue:
16
+ def __init__(self, iterable: Optional[Iterable] = None) -> None: ...
17
+ def enqueue(self, item: Any) -> None: ...
18
+ def dequeue(self) -> Any: ...
19
+ def extend(self, items: Iterable[Any]) -> None: ...
20
+ def clear(self) -> None: ...
21
+ def __len__(self) -> int: ...
22
+ def __getitem__(self, index: int) -> Any: ...
23
+ def __setitem__(self, index: int, item: Any) -> None: ...
24
+ def __contains__(self, item: Any) -> bool: ...
25
+ def copy(self) -> Self: ...
26
+ def __copy__(self) -> Self: ...
27
+
28
+ class QueueC:
29
+ def __init__(self, iterable: Optional[Iterable] = None) -> None: ...
30
+ def enqueue(self, item: Any) -> None: ...
31
+ def dequeue(self) -> Any: ...
32
+ def extend(self, items: Iterable[Any]) -> None: ...
33
+ def clear(self) -> None: ...
34
+ def __len__(self) -> int: ...
35
+ def __getitem__(self, index: int) -> Any: ...
36
+ def __setitem__(self, index: int, item: Any) -> None: ...
37
+ def __contains__(self, item: Any) -> bool: ...
38
+ def copy(self) -> Self: ...
39
+ def __copy__(self) -> Self: ...
@@ -27,20 +27,21 @@ class LLNodeQueue(Structure):
27
27
 
28
28
 
29
29
  __libfile = Path(__file__).parent / "cllqueue.so"
30
- llqueue_lib = CDLL(str(__libfile))
30
+ # Native calls own Python references and must keep the GIL.
31
+ llqueue_lib = PyDLL(str(__libfile))
31
32
 
32
33
  llqueue_lib.new_node_queue.argtypes = []
33
34
  llqueue_lib.new_node_queue.restype = POINTER(LLNodeQueue)
34
35
 
35
- llqueue_lib.is_empty.argtypes = [POINTER(LLNodeQueue)]
36
- llqueue_lib.is_empty.restype = c_bool
37
-
38
36
  llqueue_lib.enqueue.argtypes = [POINTER(LLNodeQueue), py_object]
39
37
  llqueue_lib.enqueue.restype = None
40
38
 
41
39
  llqueue_lib.dequeue.argtypes = [POINTER(LLNodeQueue)]
42
40
  llqueue_lib.dequeue.restype = py_object
43
41
 
42
+ llqueue_lib.clear_q.argtypes = [POINTER(LLNodeQueue)]
43
+ llqueue_lib.clear_q.restype = None
44
+
44
45
  llqueue_lib.free_q.argtypes = [POINTER(LLNodeQueue)]
45
46
  llqueue_lib.free_q.restype = None
46
47
 
@@ -52,9 +53,6 @@ class LLQueue:
52
53
  def __init__(self):
53
54
  self.queue = llqueue_lib.new_node_queue()
54
55
 
55
- def is_empty(self) -> bool:
56
- return llqueue_lib.is_empty(self.queue)
57
-
58
56
  def __len__(self) -> int:
59
57
  return self.queue.contents.length
60
58
 
@@ -62,44 +60,43 @@ class LLQueue:
62
60
  llqueue_lib.enqueue(self.queue, item)
63
61
 
64
62
  def dequeue(self) -> Any:
65
- if self.is_empty():
66
- raise IndexError
67
63
  return llqueue_lib.dequeue(self.queue)
68
64
 
69
65
  def clear(self):
70
- llqueue_lib.free_q(self.queue)
71
- self.queue = llqueue_lib.new__queue()
66
+ llqueue_lib.clear_q(self.queue)
72
67
 
73
68
  def __del__(self):
74
- llqueue_lib.free_q(self.queue)
69
+ queue = getattr(self, "queue", None)
70
+ self.queue = None
71
+ llqueue_lib.free_q(queue)
75
72
 
76
73
 
77
74
  # Matt's Contiguous Queue
78
75
  class ContiguousQueue(Structure):
79
76
  _fields_ = [
80
77
  ("objects", POINTER(py_object)),
81
- ("length", c_int),
82
- ("capacity", c_int),
83
- ("front", c_int),
84
- ("back", c_int),
78
+ ("length", c_size_t),
79
+ ("capacity", c_size_t),
80
+ ("front", c_size_t),
81
+ ("back", c_size_t),
85
82
  ]
86
83
 
87
84
 
88
85
  __libfile = Path(__file__).parent / "ccqueue.so"
89
- cqueue_lib = CDLL(str(__libfile))
86
+ cqueue_lib = PyDLL(str(__libfile))
90
87
 
91
88
  cqueue_lib.new__queue.argtypes = []
92
89
  cqueue_lib.new__queue.restype = POINTER(ContiguousQueue)
93
90
 
94
- cqueue_lib.is_empty.argtypes = [POINTER(ContiguousQueue)]
95
- cqueue_lib.is_empty.restype = c_bool
96
-
97
91
  cqueue_lib.enqueue.argtypes = [POINTER(ContiguousQueue), py_object]
98
92
  cqueue_lib.enqueue.restype = None
99
93
 
100
94
  cqueue_lib.dequeue.argtypes = [POINTER(ContiguousQueue)]
101
95
  cqueue_lib.dequeue.restype = py_object
102
96
 
97
+ cqueue_lib.clear_q.argtypes = [POINTER(ContiguousQueue)]
98
+ cqueue_lib.clear_q.restype = None
99
+
103
100
  cqueue_lib.free_q.argtypes = [POINTER(ContiguousQueue)]
104
101
  cqueue_lib.free_q.restype = None
105
102
 
@@ -111,9 +108,6 @@ class ContQueue:
111
108
  def __init__(self):
112
109
  self.queue = cqueue_lib.new__queue()
113
110
 
114
- def is_empty(self) -> bool:
115
- return cqueue_lib.is_empty(self.queue)
116
-
117
111
  def __len__(self) -> int:
118
112
  return self.queue.contents.length
119
113
 
@@ -121,13 +115,12 @@ class ContQueue:
121
115
  cqueue_lib.enqueue(self.queue, item)
122
116
 
123
117
  def dequeue(self) -> Any:
124
- if self.is_empty():
125
- raise IndexError
126
118
  return cqueue_lib.dequeue(self.queue)
127
119
 
128
120
  def clear(self):
129
- cqueue_lib.free_q(self.queue)
130
- self.queue = cqueue_lib.new__queue()
121
+ cqueue_lib.clear_q(self.queue)
131
122
 
132
123
  def __del__(self):
133
- cqueue_lib.free_q(self.queue)
124
+ queue = getattr(self, "queue", None)
125
+ self.queue = None
126
+ cqueue_lib.free_q(queue)
File without changes
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: fastqueue-lib
3
- Version: 0.0.17
3
+ Version: 0.0.18
4
4
  Summary: Fast single ended queue library for python
5
5
  Author-email: Matthew Taylor <matthew.taylor.andre@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/MatthewAndreTaylor/fastqueue
@@ -26,6 +26,7 @@ Requires-Dist: numpy; extra == "examples"
26
26
  Requires-Dist: pandas; extra == "examples"
27
27
  Provides-Extra: tests
28
28
  Requires-Dist: pytest; extra == "tests"
29
+ Dynamic: license-file
29
30
 
30
31
  # fastqueue
31
32
 
@@ -43,7 +44,7 @@ Single-ended fast queues built in C tuned for Python.
43
44
 
44
45
  ## Installation
45
46
 
46
- To install fastqueue, using pip:
47
+ To install fastqueue, using [pip](https://pypi.org/project/fastqueue-lib):
47
48
 
48
49
  ```bash
49
50
  pip install fastqueue-lib
@@ -97,9 +98,6 @@ True
97
98
  ['🚒', '🛴', '🚅', '🚗']
98
99
  ```
99
100
 
100
- Another alternative is `fastqueue.LockQueue()` which supports all queue operations.
101
- `fastqueue.LockQueue()` is built as a thread-safe alternative to the other queue types.
102
-
103
101
  ## Example Benchmarks
104
102
 
105
103
  ### Queue operations
@@ -1,11 +1,12 @@
1
1
  LICENSE
2
2
  MANIFEST.in
3
3
  README.md
4
- fastqueue.py
5
4
  pyproject.toml
6
5
  setup.py
7
6
  fastqueue/__init__.py
7
+ fastqueue/__init__.pyi
8
8
  fastqueue/prototypes.py
9
+ fastqueue/py.typed
9
10
  fastqueue_lib.egg-info/PKG-INFO
10
11
  fastqueue_lib.egg-info/SOURCES.txt
11
12
  fastqueue_lib.egg-info/dependency_links.txt
@@ -14,4 +15,5 @@ fastqueue_lib.egg-info/top_level.txt
14
15
  src/ccqueue.cpp
15
16
  src/cllqueue.cpp
16
17
  src/fastqueue.c
18
+ tests/test_memory.py
17
19
  tests/test_queue.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fastqueue-lib"
7
- version = "0.0.17"
7
+ version = "0.0.18"
8
8
  description="Fast single ended queue library for python"
9
9
  authors = [
10
10
  {name = "Matthew Taylor", email = "matthew.taylor.andre@gmail.com"},
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Copyright (c) 2023 Matthew Andre Taylor
3
+ */
4
+ #ifdef _WIN32
5
+ #define QUEUE_LIBRARY_API extern "C" __declspec(dllexport)
6
+ #else
7
+ #define QUEUE_LIBRARY_API extern "C"
8
+ #endif
9
+
10
+ #include <Python.h>
11
+ #include <stdlib.h>
12
+
13
+ // Matt's C Contiguous Queue
14
+ QUEUE_LIBRARY_API typedef struct queue {
15
+ PyObject** objects;
16
+ size_t length;
17
+ size_t capacity;
18
+ size_t front;
19
+ size_t back;
20
+ } queue_t;
21
+
22
+ QUEUE_LIBRARY_API queue_t* new__queue() {
23
+ queue_t* queue = (queue_t*)malloc(sizeof(queue_t));
24
+ if (queue == NULL) {
25
+ PyErr_NoMemory();
26
+ return NULL;
27
+ }
28
+ queue->length = 0;
29
+ queue->capacity = 256;
30
+ queue->front = 0;
31
+ queue->back = queue->capacity - 1;
32
+ queue->objects = (PyObject**)malloc(256 * sizeof(PyObject*));
33
+ if (queue->objects == NULL) {
34
+ free(queue);
35
+ PyErr_NoMemory();
36
+ return NULL;
37
+ }
38
+ return queue;
39
+ }
40
+
41
+ int resize(queue_t* queue, size_t newCapacity) {
42
+ if (newCapacity > PY_SSIZE_T_MAX / sizeof(PyObject*)) {
43
+ PyErr_NoMemory();
44
+ return -1;
45
+ }
46
+ PyObject** newObjects = (PyObject**)malloc(newCapacity * sizeof(PyObject*));
47
+ if (newObjects == NULL) {
48
+ PyErr_NoMemory();
49
+ return -1;
50
+ }
51
+ for (size_t i = 0; i < queue->length; ++i) {
52
+ newObjects[i] = queue->objects[(queue->front + i) % queue->capacity];
53
+ }
54
+
55
+ free(queue->objects);
56
+ queue->objects = newObjects;
57
+ queue->front = 0;
58
+ queue->back = (queue->length + newCapacity - 1) % newCapacity;
59
+ queue->capacity = newCapacity;
60
+ return 0;
61
+ }
62
+
63
+ QUEUE_LIBRARY_API void enqueue(queue_t* queue, PyObject* object) {
64
+ if (queue == NULL) {
65
+ PyErr_SetString(PyExc_RuntimeError, "Queue is not allocated");
66
+ return;
67
+ }
68
+ if (object == Py_None)
69
+ return;
70
+
71
+ if (queue->length == queue->capacity) {
72
+ if (queue->capacity > PY_SSIZE_T_MAX / sizeof(PyObject*) / 2) {
73
+ PyErr_NoMemory();
74
+ return;
75
+ }
76
+ if (resize(queue, queue->capacity ? queue->capacity * 2 : 256) < 0)
77
+ return;
78
+ }
79
+
80
+ Py_INCREF(object);
81
+ queue->front = (queue->front + queue->capacity - 1) % queue->capacity;
82
+ queue->objects[queue->front] = object;
83
+ queue->length++;
84
+ }
85
+
86
+ QUEUE_LIBRARY_API PyObject* dequeue(queue_t* queue) {
87
+ if (queue == NULL || queue->length == 0) {
88
+ PyErr_SetString(PyExc_IndexError, "dequeue from an empty queue");
89
+ return NULL;
90
+ }
91
+
92
+ PyObject* object = queue->objects[queue->back];
93
+ queue->back = (queue->back + queue->capacity - 1) % queue->capacity;
94
+ queue->length--;
95
+ return object;
96
+ }
97
+
98
+ QUEUE_LIBRARY_API void clear_q(queue_t* queue) {
99
+ if (queue == NULL || queue->length == 0) {
100
+ return;
101
+ }
102
+
103
+ PyObject** objects = queue->objects;
104
+ size_t length = queue->length;
105
+ size_t capacity = queue->capacity;
106
+ size_t front = queue->front;
107
+ // Detach before decrefs so callbacks cannot overwrite the old payloads.
108
+ queue->objects = NULL;
109
+ queue->length = queue->capacity = queue->front = queue->back = 0;
110
+ for (size_t i = 0; i < length; ++i) {
111
+ Py_DECREF(objects[(front + i) % capacity]);
112
+ }
113
+ if (queue->objects == NULL) {
114
+ queue->objects = objects;
115
+ queue->capacity = capacity;
116
+ queue->front = 0;
117
+ queue->back = capacity - 1;
118
+ } else {
119
+ free(objects);
120
+ }
121
+ }
122
+
123
+ QUEUE_LIBRARY_API void free_q(queue_t* queue) {
124
+ if (queue == NULL) {
125
+ return;
126
+ }
127
+
128
+ clear_q(queue);
129
+ free(queue->objects);
130
+ free(queue);
131
+ }
@@ -8,7 +8,6 @@
8
8
  #endif
9
9
 
10
10
  #include <Python.h>
11
- #include <stdio.h>
12
11
  #include <stdlib.h>
13
12
 
14
13
  // Matt's C Linked List Queue
@@ -26,8 +25,10 @@ QUEUE_LIBRARY_API typedef struct queue {
26
25
 
27
26
  node_t* new_node(PyObject* object) {
28
27
  node_t* node = (node_t*)malloc(sizeof(node_t));
29
- if (node == NULL)
28
+ if (node == NULL) {
29
+ PyErr_NoMemory();
30
30
  return NULL;
31
+ }
31
32
 
32
33
  Py_INCREF(object);
33
34
  node->object = object;
@@ -37,25 +38,27 @@ node_t* new_node(PyObject* object) {
37
38
 
38
39
  QUEUE_LIBRARY_API queue_t* new_node_queue() {
39
40
  queue_t* queue = (queue_t*)malloc(sizeof(queue_t));
41
+ if (queue == NULL) {
42
+ PyErr_NoMemory();
43
+ return NULL;
44
+ }
40
45
  queue->front = NULL;
41
46
  queue->back = NULL;
42
47
  queue->length = 0;
43
48
  return queue;
44
49
  }
45
50
 
46
- QUEUE_LIBRARY_API int is_empty(queue_t* queue) {
51
+ QUEUE_LIBRARY_API void enqueue(queue_t* queue, PyObject* object) {
47
52
  if (queue == NULL) {
48
- perror("Queue not allocated");
49
- return 1;
53
+ PyErr_SetString(PyExc_RuntimeError, "Queue is not allocated");
54
+ return;
50
55
  }
51
- return queue->length == 0;
52
- }
53
-
54
- QUEUE_LIBRARY_API void enqueue(queue_t* queue, PyObject* object) {
55
56
  if (object == Py_None)
56
57
  return;
57
58
 
58
59
  node_t* node = new_node(object);
60
+ if (node == NULL)
61
+ return;
59
62
  if (queue->back == NULL) {
60
63
  queue->front = node;
61
64
  queue->back = node;
@@ -67,8 +70,10 @@ QUEUE_LIBRARY_API void enqueue(queue_t* queue, PyObject* object) {
67
70
  }
68
71
 
69
72
  QUEUE_LIBRARY_API PyObject* dequeue(queue_t* queue) {
70
- if (is_empty(queue))
73
+ if (queue == NULL || queue->length == 0) {
74
+ PyErr_SetString(PyExc_IndexError, "dequeue from an empty queue");
71
75
  return NULL;
76
+ }
72
77
 
73
78
  node_t* temp = queue->front;
74
79
  PyObject* object = temp->object;
@@ -80,13 +85,16 @@ QUEUE_LIBRARY_API PyObject* dequeue(queue_t* queue) {
80
85
  return object;
81
86
  }
82
87
 
83
- QUEUE_LIBRARY_API void free_q(queue_t* queue) {
88
+ QUEUE_LIBRARY_API void clear_q(queue_t* queue) {
84
89
  if (queue == NULL) {
85
- perror("Queue not allocated");
86
90
  return;
87
91
  }
88
92
 
89
93
  node_t* current = queue->front;
94
+ // Detach before decrefs so finalizers can safely reuse this queue.
95
+ queue->front = NULL;
96
+ queue->back = NULL;
97
+ queue->length = 0;
90
98
  node_t* next;
91
99
  while (current != NULL) {
92
100
  next = current->next;
@@ -94,5 +102,13 @@ QUEUE_LIBRARY_API void free_q(queue_t* queue) {
94
102
  free(current);
95
103
  current = next;
96
104
  }
105
+ }
106
+
107
+ QUEUE_LIBRARY_API void free_q(queue_t* queue) {
108
+ if (queue == NULL) {
109
+ return;
110
+ }
111
+
112
+ clear_q(queue);
97
113
  free(queue);
98
114
  }