dtools.circular-array 3.12.1__py3-none-any.whl → 3.14.0__py3-none-any.whl

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.
@@ -12,41 +12,51 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- """### Module for an indexable circular array data structure."""
15
+ """### An indexable circular array data structure.
16
+
17
+ An indexable, sliceable, auto-resizing circular array
18
+ data structure with amortized O(1) pushes and pops either end.
19
+
20
+ """
16
21
 
17
22
  from __future__ import annotations
23
+
24
+ __author__ = 'Geoffrey R. Scheller'
25
+ __copyright__ = 'Copyright (c) 2023-2025 Geoffrey R. Scheller'
26
+ __license__ = 'Apache License 2.0'
27
+
18
28
  from collections.abc import Callable, Iterable, Iterator
19
29
  from typing import cast, Never, overload, TypeVar
20
30
 
21
31
  __all__ = ['CA', 'ca']
22
32
 
23
- D = TypeVar('D') # Needed only for pdoc documentation generation. Otherwise,
24
- L = TypeVar('L') # ignored by both MyPy and Python. Makes linters unhappy
25
- R = TypeVar('R') # when these are used on function and method signatures due
26
- U = TypeVar('U') # to "redefined-outer-name" warnings. Function and method
27
- T = TypeVar('T') # signatures do not support variance and bounds constraints.
33
+ D = TypeVar('D')
28
34
 
29
35
 
30
- class CA[D]():
36
+ class CA[D]:
31
37
  """Indexable circular array data structure
32
38
 
33
- - generic, stateful data structure
34
- - lowercase class name chosen to match nomenclature for builtins
35
- - like `list` and `tuple`
39
+ - generic, stateful, invariant data structure
36
40
  - amortized O(1) pushing and popping from either end
37
41
  - O(1) random access any element
38
42
  - will resize itself as needed
39
43
  - sliceable
40
44
  - makes defensive copies of contents for the purposes of iteration
41
- - in boolean context returns true if not empty, false if empty
42
- - in comparisons compare identity before equality (like builtins)
45
+ - in boolean context returns
46
+ - `True` when not empty
47
+ - `False` when empty
48
+ - in comparisons compare identity before equality, like builtins do
43
49
  - raises `IndexError` for out-of-bounds indexing
44
- - raises `ValueError` for popping from or folding an empty `ca`
50
+ - raises `ValueError` for popping from or folding an empty `CA`
45
51
 
46
52
  """
47
53
 
48
54
  __slots__ = '_data', '_cnt', '_cap', '_front', '_rear'
49
55
 
56
+ L = TypeVar('L')
57
+ R = TypeVar('R')
58
+ U = TypeVar('U')
59
+
50
60
  def __init__(self, *dss: Iterable[D]) -> None:
51
61
  if len(dss) < 2:
52
62
  self._data: list[D | None] = (
@@ -77,7 +87,6 @@ class CA[D]():
77
87
  self._front, self._cap = self._front + self._cap, 2 * self._cap
78
88
 
79
89
  def _compact_storage_capacity(self) -> None:
80
- """Compact the CA."""
81
90
  match self._cnt:
82
91
  case 0:
83
92
  self._cap, self._front, self._rear, self._data = 2, 0, 1, [None, None]
@@ -211,11 +220,11 @@ class CA[D]():
211
220
  raise IndexError(msg1 + msg2 + msg3)
212
221
 
213
222
  @overload
214
- def __delitem__(self, idx: int) -> None: ...
223
+ def __delitem__(self, idx: int, /) -> None: ...
215
224
  @overload
216
- def __delitem__(self, idx: slice) -> None: ...
225
+ def __delitem__(self, idx: slice, /) -> None: ...
217
226
 
218
- def __delitem__(self, idx: int | slice) -> None:
227
+ def __delitem__(self, idx: int | slice, /) -> None:
219
228
  data = list(self)
220
229
  del data[idx]
221
230
  _ca = CA(data)
@@ -259,7 +268,11 @@ class CA[D]():
259
268
  return True
260
269
 
261
270
  def pushl(self, *ds: D) -> None:
262
- """Push data from the left onto the CA."""
271
+ """Push left.
272
+
273
+ - push data from the left onto the CA
274
+
275
+ """
263
276
  for d in ds:
264
277
  if self._cnt == self._cap:
265
278
  self._double_storage_capacity()
@@ -267,7 +280,11 @@ class CA[D]():
267
280
  self._data[self._front], self._cnt = d, self._cnt + 1
268
281
 
269
282
  def pushr(self, *ds: D) -> None:
270
- """Push data from the right onto the CA."""
283
+ """Push right.
284
+
285
+ - push data from the right onto the CA
286
+
287
+ """
271
288
  for d in ds:
272
289
  if self._cnt == self._cap:
273
290
  self._double_storage_capacity()
@@ -275,9 +292,10 @@ class CA[D]():
275
292
  self._data[self._rear], self._cnt = d, self._cnt + 1
276
293
 
277
294
  def popl(self) -> D | Never:
278
- """Pop one value off the left side of the CA.
295
+ """Pop left.
279
296
 
280
- Raises `ValueError` when called on an empty CA.
297
+ - pop one value off the left side of the `CA`
298
+ - raises `ValueError` when called on an empty `CA`
281
299
 
282
300
  """
283
301
  if self._cnt > 1:
@@ -301,9 +319,10 @@ class CA[D]():
301
319
  return cast(D, d)
302
320
 
303
321
  def popr(self) -> D | Never:
304
- """Pop one value off the right side of the CA.
322
+ """Pop right
305
323
 
306
- Raises `ValueError` when called on an empty CA.
324
+ - pop one value off the right side of the `CA`
325
+ - raises `ValueError` when called on an empty `CA`
307
326
 
308
327
  """
309
328
  if self._cnt > 1:
@@ -330,7 +349,7 @@ class CA[D]():
330
349
  """Pop one value from left, provide a mandatory default value.
331
350
 
332
351
  - safe version of popl
333
- - returns a default value in the event the `CA` is empty
352
+ - returns the default value if `CA` is empty
334
353
 
335
354
  """
336
355
  try:
@@ -342,7 +361,7 @@ class CA[D]():
342
361
  """Pop one value from right, provide a mandatory default value.
343
362
 
344
363
  - safe version of popr
345
- - returns a default value in the event the `CA` is empty
364
+ - returns the default value if `CA` is empty
346
365
 
347
366
  """
348
367
  try:
@@ -353,9 +372,8 @@ class CA[D]():
353
372
  def poplt(self, maximum: int, /) -> tuple[D, ...]:
354
373
  """Pop multiple values from left side of `CA`.
355
374
 
356
- - returns the results in a tuple of type `tuple[~D, ...]`
357
- - returns an empty tuple if `CA` is empty
358
- - pop no more that `max` values
375
+ - returns the results in a tuple of type
376
+ - pop no more that `maximum` values
359
377
  - will pop less if `CA` becomes empty
360
378
 
361
379
  """
@@ -374,9 +392,8 @@ class CA[D]():
374
392
  def poprt(self, maximum: int, /) -> tuple[D, ...]:
375
393
  """Pop multiple values from right side of `CA`.
376
394
 
377
- - returns the results in a tuple of type `tuple[~D, ...]`
378
- - returns an empty tuple if `CA` is empty
379
- - pop no more that `max` values
395
+ - returns the results in a tuple
396
+ - pop no more that `maximum` values
380
397
  - will pop less if `CA` becomes empty
381
398
 
382
399
  """
@@ -392,34 +409,31 @@ class CA[D]():
392
409
  return tuple(ds)
393
410
 
394
411
  def rotl(self, n: int = 1, /) -> None:
395
- """Rotate `CA` arguments left n times."""
412
+ """Rotate `CA` components to the left n times."""
396
413
  if self._cnt < 2:
397
414
  return
398
415
  for _ in range(n, 0, -1):
399
416
  self.pushr(self.popl())
400
417
 
401
418
  def rotr(self, n: int = 1, /) -> None:
402
- """Rotate `CA` arguments right n times."""
419
+ """Rotate `CA` components to the right n times."""
403
420
  if self._cnt < 2:
404
421
  return
405
422
  for _ in range(n, 0, -1):
406
423
  self.pushl(self.popr())
407
424
 
408
425
  def map[U](self, f: Callable[[D], U], /) -> CA[U]:
409
- """Apply function f over contents, returns new `CA` instance.
426
+ """Apply function `f` over the `CA` contents,
410
427
 
411
- - parameter `f` function of type `f[~D, ~U] -> CA[~U]`
412
- - returns a new instance of type `CA[~U]`
428
+ - returns a new `CA` instance
413
429
 
414
430
  """
415
431
  return CA(map(f, self))
416
432
 
417
- def foldl[L](self, f: Callable[[L, D], L], /, initial: L | None = None) -> L:
418
- """Left fold `CA` via function and optional initial value.
433
+ def foldl[L](self, f: Callable[[L, D], L], initial: L | None = None, /) -> L:
434
+ """Left fold `CA` with function `f` and an optional `initial` value.
419
435
 
420
- - parameter `f` function of type `f[~L, ~D] -> ~L`
421
- - the first argument to `f` is for the accumulated value.
422
- - parameter `initial` is an optional initial value
436
+ - first argument to `f` is for the accumulated value
423
437
  - returns the reduced value of type `~L`
424
438
  - note that `~L` and `~D` can be the same type
425
439
  - if an initial value is not given then by necessity `~L = ~D`
@@ -443,12 +457,10 @@ class CA[D]():
443
457
  acc = f(acc, d)
444
458
  return acc
445
459
 
446
- def foldr[R](self, f: Callable[[D, R], R], /, initial: R | None = None) -> R:
447
- """Right fold `CA` via function and optional initial value.
460
+ def foldr[R](self, f: Callable[[D, R], R], initial: R | None = None, /) -> R:
461
+ """Right fold `CA` with function `f` and an optional `initial` value.
448
462
 
449
- - parameter `f` function of type `f[~D, ~R] -> ~R`
450
- - the second argument to f is for the accumulated value
451
- - parameter `initial` is an optional initial value
463
+ - second argument to f is for the accumulated value
452
464
  - returns the reduced value of type `~R`
453
465
  - note that `~R` and `~D` can be the same type
454
466
  - if an initial value is not given then by necessity `~R = ~D`
@@ -497,6 +509,6 @@ class CA[D]():
497
509
  self._front, self._rear = 0, self._cap - 1
498
510
 
499
511
 
500
- def ca[T](*ds: T) -> CA[T]:
512
+ def ca[D](*ds: D) -> CA[D]:
501
513
  """Function to produce a `CA` array from a variable number of arguments."""
502
514
  return CA(ds)
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dtools.circular-array
3
- Version: 3.12.1
4
- Summary: ### Developer Tools - Circular Array Data Structure
5
- Keywords: circular array,circle array,CA,dequeue,dqueue,FIFO,LIFO,pop,push,indexable,auto-resizing,auto resizing,resizing
3
+ Version: 3.14.0
4
+ Summary: ### An indexable circular array data structure.
5
+ Keywords: circular array,dequeue,pop,push,indexable,auto resizing
6
6
  Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
7
7
  Requires-Python: >=3.12
8
8
  Description-Content-Type: text/markdown
@@ -16,7 +16,8 @@ Classifier: Typing :: Typed
16
16
  License-File: LICENSE
17
17
  Requires-Dist: pytest >=8.3.5 ; extra == "test"
18
18
  Project-URL: Changelog, https://github.com/grscheller/dtools-circular-array/blob/main/CHANGELOG.md
19
- Project-URL: Documentation, https://grscheller.github.io/dtools-docs/circular-array
19
+ Project-URL: Documentation, https://grscheller.github.io/dtools-namespace-projects/circular-array
20
+ Project-URL: Homepage, https://github.com/grscheller/dtools-namespace-projects/blob/main/README.md
20
21
  Project-URL: Source, https://github.com/grscheller/dtools-circular-array
21
22
  Provides-Extra: test
22
23
 
@@ -31,37 +32,41 @@ structure,
31
32
  - **Detailed documentation**
32
33
  - [Detailed API documentation][3] on *GH-Pages*
33
34
 
34
- This project is part of the [Developer Tools for Python][4] **dtools.**
35
- namespace project.
35
+ This project is part of the [Developer Tools for Python][4] **dtools**
36
+ namespace projects.
36
37
 
37
38
  ## Overview
38
39
 
39
40
  - O(1) amortized pushes and pops either end.
40
41
  - O(1) indexing
41
42
  - fully supports slicing
42
- - safely mutates while iterating over copies of previous state
43
+ - safely mutates over previous state
43
44
 
44
45
  ### Module circular_array
45
46
 
46
- A full featured, auto resizing circular array. Python package containing
47
- a module implementing a full featured, indexable, sliceable, double
48
- sided, auto-resizing circular array data structure.
47
+ A full featured auto resizing circular array data structure. Double
48
+ sided, indexable, sliceable, and iterable. When iterated, uses cached
49
+ copies of its present state so that the circular array itself can safely
50
+ be mutated.
49
51
 
50
- Useful either if used directly as an improved version of a Python List
51
- or in a "has-a" relationship when implementing other data structures.
52
+ Useful either if used directly like a Python list, or in a "has-a"
53
+ relationship when implementing other data structures.
52
54
 
53
55
  - *module* dtools.circular_array
54
- - *class* ca: circular array data structure
55
- - *function* CA: factory function to produce a ca from data
56
+ - *class* `CA:` circular array data structure
57
+ - initializer takes 1 or 0 iterators
58
+ - like `list` or `set`
59
+ - *function* `ca`: produces a `CA` from function's arguments
60
+ - similar use case as syntactic constructs `[]` or `{}`
56
61
 
57
- Above nomenclature modeled after of a builtin data type like `list`, where
58
- `ca` takes an optional iterator as an argument and CA is all caps to represent
59
- syntactic sugar like `[]` or `{}`.
62
+ Above nomenclature modeled after builtin data types like `list`, where
63
+ `CA` and `ca` correspond respectfully to `list` and `[]` in their use
64
+ cases.
60
65
 
61
66
  #### Usage
62
67
 
63
68
  ```python
64
- from dtools.circular_array.ca import CA, ca
69
+ from dtools.circular_array import CA, ca
65
70
 
66
71
  ca1 = ca(1, 2, 3)
67
72
  assert ca1.popl() == 1
@@ -79,8 +84,8 @@ syntactic sugar like `[]` or `{}`.
79
84
  tup4 = ca2.poprt(4)
80
85
  assert tup3 == (1, 2, 3)
81
86
  assert tup4 == (10, 9, 8, 7)
82
- assert ca2 == CA(4, 5, 6)
83
- four, *rest = ca.popft(1000)
87
+ assert ca2 == ca(4, 5, 6)
88
+ four, *rest = ca2.poplt(1000)
84
89
  assert four == 4
85
90
  assert rest == [5, 6]
86
91
  assert len(ca2) == 0
@@ -94,10 +99,8 @@ syntactic sugar like `[]` or `{}`.
94
99
  assert len(ca2) == 0
95
100
  ```
96
101
 
97
- ______________________________________________________________________
98
-
99
102
  [1]: https://pypi.org/project/dtools.circular-array
100
103
  [2]: https://github.com/grscheller/dtools-circular-array
101
- [3]: https://grscheller.github.io/dtools-docs/circular-array
102
- [4]: https://github.com/grscheller/dtools-docs
104
+ [3]: https://grscheller.github.io/dtools-namespace-projects/circular-array
105
+ [4]: https://github.com/grscheller/dtools-namespace-projects/blob/main/README.md
103
106
 
@@ -0,0 +1,5 @@
1
+ dtools/circular_array.py,sha256=GJn30Xub6oSd2Ltiy76ccUytiNpVS43RJNU4Oax6EkM,16184
2
+ dtools_circular_array-3.14.0.dist-info/licenses/LICENSE,sha256=csqbZRvA3Nyuav1aszWvswE8CZtaKr-hMjjjcKqms7w,10774
3
+ dtools_circular_array-3.14.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
4
+ dtools_circular_array-3.14.0.dist-info/METADATA,sha256=PPfMaC0yPeiMRUp9rsXxFtEmw7mUVkJK-DAtffjKjhI,3597
5
+ dtools_circular_array-3.14.0.dist-info/RECORD,,
@@ -1,33 +0,0 @@
1
- # Copyright 2023-2025 Geoffrey R. Scheller
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- """### Developer Tools - Circular Array Data Structure
16
-
17
- Package for an indexable, sliceable, auto-resizing circular array
18
- data structure with amortized O(1) pushes and pops either end.
19
-
20
- Circular array data structure.
21
-
22
- - *module* dtools.circular_array
23
- - *class* dtools.circular_array.ca.CA
24
- - initializer takes up to 1 iterable
25
- - *function* dtools.circular_array.ca.ca
26
- - factory function taking a variable number of arguments
27
-
28
- """
29
-
30
- __version__ = '3.12.1'
31
- __author__ = 'Geoffrey R. Scheller'
32
- __copyright__ = 'Copyright (c) 2023-2025 Geoffrey R. Scheller'
33
- __license__ = 'Apache License 2.0'
File without changes
@@ -1,7 +0,0 @@
1
- dtools/circular_array/__init__.py,sha256=2CvFEORjHTaRYTteVH-Ifo7THhbttgif71VigBM-3-Y,1189
2
- dtools/circular_array/ca.py,sha256=PRt-AOvR9WqwS6EdKFWXde7VxBc1yOD6kuCK_lXuWFQ,16683
3
- dtools/circular_array/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- dtools_circular_array-3.12.1.dist-info/licenses/LICENSE,sha256=csqbZRvA3Nyuav1aszWvswE8CZtaKr-hMjjjcKqms7w,10774
5
- dtools_circular_array-3.12.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
6
- dtools_circular_array-3.12.1.dist-info/METADATA,sha256=9HGyzkYTReGc9LUbP_X1qbWEK-EQKVwA_gAaZdI_5cI,3495
7
- dtools_circular_array-3.12.1.dist-info/RECORD,,