pythonic-fp-circulararray 5.4.0__tar.gz → 6.0.1__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.
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/CHANGELOG.rst +23 -5
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/PKG-INFO +6 -6
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/README.rst +2 -2
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/pyproject.toml +4 -4
- pythonic_fp_circulararray-6.0.1/src/pythonic_fp/circulararray/__init__.py +37 -0
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/src/pythonic_fp/circulararray/auto.py +47 -37
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/src/pythonic_fp/circulararray/auto.pyi +1 -4
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/src/pythonic_fp/circulararray/fixed.py +67 -63
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/src/pythonic_fp/circulararray/fixed.pyi +2 -7
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/tests/auto/test_ca.py +10 -0
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/tests/fixed/test_caf.py +21 -10
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/tests/fixed/test_caf_capacity.py +3 -3
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/tests/fixed/test_repr_str_fixed.py +3 -3
- pythonic_fp_circulararray-5.4.0/src/pythonic_fp/circulararray/__init__.py +0 -36
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/.gitignore +0 -0
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/LICENSE +0 -0
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/src/pythonic_fp/circulararray/__init__.pyi +0 -0
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/src/pythonic_fp/circulararray/py.typed +0 -0
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/tests/auto/test_ca_capacity.py +0 -0
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/tests/auto/test_ca_fold_with_none.py +0 -0
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/tests/auto/test_repr_str_auto.py +0 -0
- {pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/tests/fixed/test_caf_fold_with_none.py +0 -0
|
@@ -17,12 +17,26 @@ See `Semantic Versioning 2.0.0 <https://semver.org>`_.
|
|
|
17
17
|
Releases and Important Milestones
|
|
18
18
|
---------------------------------
|
|
19
19
|
|
|
20
|
-
PyPI
|
|
21
|
-
|
|
20
|
+
PyPI 6.0.1 - 2026-01-TBD
|
|
21
|
+
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Docstring improvements based on lessons learned from my PyPI
|
|
24
|
+
boring-math-abstract-algebra project.
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
PyPI 6.0.0 - 2025-09-26
|
|
27
|
+
~~~~~~~~~~~~~~~~~~~~~~~
|
|
28
|
+
|
|
29
|
+
Changed how "optional" iterator in initializer signature is handled.
|
|
30
|
+
|
|
31
|
+
- same trick as is done in queues project.
|
|
32
|
+
- no longer need to use a sentinel in the signature
|
|
33
|
+
- less typing boilerplate
|
|
34
|
+
- required a bump in major version number
|
|
35
|
+
|
|
36
|
+
PyPI 5.4.0 - 2025-09-25
|
|
37
|
+
~~~~~~~~~~~~~~~~~~~~~~~
|
|
38
|
+
|
|
39
|
+
Gave both circular arrays the ability to store None as a value.
|
|
26
40
|
|
|
27
41
|
- realized that both circular array types cannot store None as a value
|
|
28
42
|
|
|
@@ -30,10 +44,14 @@ PyPI 5.4.0 - TBD
|
|
|
30
44
|
|
|
31
45
|
- typing for foldl & foldr a bit quirky (mypy bug?)
|
|
32
46
|
|
|
33
|
-
- bumping minor version number
|
|
47
|
+
- bumping minor version number to 5.4.0
|
|
34
48
|
|
|
35
49
|
- more than a bug fix but not really an API change either
|
|
36
50
|
|
|
51
|
+
- updated README.rst for last two PyPI releases
|
|
52
|
+
|
|
53
|
+
- previous version never fully updated for two circular array types
|
|
54
|
+
|
|
37
55
|
PyPI 5.3.3 - 2025-09-21
|
|
38
56
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
39
57
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonic-fp-circulararray
|
|
3
|
-
Version:
|
|
3
|
+
Version: 6.0.1
|
|
4
4
|
Summary: Circular Array
|
|
5
5
|
Keywords: auto resizing,circular array,circulararray,dequeue,indexable,pop,push
|
|
6
6
|
Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
|
|
@@ -14,11 +14,11 @@ Classifier: Operating System :: OS Independent
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
15
15
|
Classifier: Typing :: Typed
|
|
16
16
|
License-File: LICENSE
|
|
17
|
-
Requires-Dist: pythonic-fp-gadgets>=
|
|
17
|
+
Requires-Dist: pythonic-fp-gadgets>=4.0.2
|
|
18
18
|
Requires-Dist: pytest>=8.4.1 ; extra == "test"
|
|
19
19
|
Project-URL: Changelog, https://github.com/grscheller/pythonic-fp-circulararray/blob/main/CHANGELOG.rst
|
|
20
|
-
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp/circulararray/development/build/html/
|
|
21
|
-
Project-URL: Homepage, https://github.
|
|
20
|
+
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp/circulararray/development/build/html/
|
|
21
|
+
Project-URL: Homepage, https://grscheller.github.io/pythonic-fp/homepage/build/html/
|
|
22
22
|
Project-URL: Source, https://github.com/grscheller/pythonic-fp-circulararray
|
|
23
23
|
Provides-Extra: test
|
|
24
24
|
|
|
@@ -54,7 +54,7 @@ Python module implementing stateful circular array data structures.
|
|
|
54
54
|
|
|
55
55
|
Part of the
|
|
56
56
|
`pythonic-fp
|
|
57
|
-
<https://grscheller.github.io/pythonic-fp
|
|
57
|
+
<https://grscheller.github.io/pythonic-fp>`_
|
|
58
58
|
PyPI projects.
|
|
59
59
|
|
|
60
60
|
Documentation
|
|
@@ -62,7 +62,7 @@ Documentation
|
|
|
62
62
|
|
|
63
63
|
Documentation for this project is hosted on
|
|
64
64
|
`GitHub Pages
|
|
65
|
-
<https://grscheller.github.io/pythonic-fp/circulararray
|
|
65
|
+
<https://grscheller.github.io/pythonic-fp/circulararray>`_.
|
|
66
66
|
|
|
67
67
|
Copyright and License
|
|
68
68
|
---------------------
|
|
@@ -30,7 +30,7 @@ Python module implementing stateful circular array data structures.
|
|
|
30
30
|
|
|
31
31
|
Part of the
|
|
32
32
|
`pythonic-fp
|
|
33
|
-
<https://grscheller.github.io/pythonic-fp
|
|
33
|
+
<https://grscheller.github.io/pythonic-fp>`_
|
|
34
34
|
PyPI projects.
|
|
35
35
|
|
|
36
36
|
Documentation
|
|
@@ -38,7 +38,7 @@ Documentation
|
|
|
38
38
|
|
|
39
39
|
Documentation for this project is hosted on
|
|
40
40
|
`GitHub Pages
|
|
41
|
-
<https://grscheller.github.io/pythonic-fp/circulararray
|
|
41
|
+
<https://grscheller.github.io/pythonic-fp/circulararray>`_.
|
|
42
42
|
|
|
43
43
|
Copyright and License
|
|
44
44
|
---------------------
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pythonic-fp-circulararray"
|
|
3
|
-
version = "
|
|
3
|
+
version = "6.0.1"
|
|
4
4
|
readme = "README.rst"
|
|
5
5
|
requires-python = ">=3.13"
|
|
6
6
|
license = { file = "LICENSE" }
|
|
@@ -24,14 +24,14 @@ classifiers = [
|
|
|
24
24
|
"Typing :: Typed",
|
|
25
25
|
]
|
|
26
26
|
dependencies = [
|
|
27
|
-
"pythonic-fp-gadgets>=
|
|
27
|
+
"pythonic-fp-gadgets>=4.0.2",
|
|
28
28
|
]
|
|
29
29
|
dynamic = ["description"]
|
|
30
30
|
|
|
31
31
|
[project.urls]
|
|
32
32
|
Changelog = "https://github.com/grscheller/pythonic-fp-circulararray/blob/main/CHANGELOG.rst"
|
|
33
|
-
Documentation = "https://grscheller.github.io/pythonic-fp/circulararray/development/build/html/
|
|
34
|
-
Homepage = "https://github.
|
|
33
|
+
Documentation = "https://grscheller.github.io/pythonic-fp/circulararray/development/build/html/"
|
|
34
|
+
Homepage = "https://grscheller.github.io/pythonic-fp/homepage/build/html/"
|
|
35
35
|
Source = "https://github.com/grscheller/pythonic-fp-circulararray"
|
|
36
36
|
|
|
37
37
|
[project.optional-dependencies]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Copyright 2024-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
|
+
"""
|
|
16
|
+
Circular Array
|
|
17
|
+
--------------
|
|
18
|
+
|
|
19
|
+
.. admonition:: Stateful circular array data structures.
|
|
20
|
+
|
|
21
|
+
+-------------------------------------+-------------------------------------------+
|
|
22
|
+
| Module | Description |
|
|
23
|
+
+=====================================+===========================================+
|
|
24
|
+
| **pythonic_fp.circulararray.auto** | Variable storage capacity circular array. |
|
|
25
|
+
+-------------------------------------+-------------------------------------------+
|
|
26
|
+
| **pythonic_fp.circulararray.fixed** | Fixed storage capacity circular array. |
|
|
27
|
+
+-------------------------------------+-------------------------------------------+
|
|
28
|
+
|
|
29
|
+
- O(1) pops and pushes either end
|
|
30
|
+
- O(1) size determination
|
|
31
|
+
- O(1) indexing
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
__author__ = 'Geoffrey R. Scheller'
|
|
36
|
+
__copyright__ = 'Copyright (c) 2023-2026 Geoffrey R. Scheller'
|
|
37
|
+
__license__ = 'Apache License 2.0'
|
|
@@ -13,19 +13,16 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
"""
|
|
16
|
-
Variable
|
|
17
|
-
=========================
|
|
16
|
+
.. admonition:: Variable storage capacity circular array.**
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- O(1)
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
- in boolean context, falsy when empty, otherwise truthy
|
|
28
|
-
- function ``ca`` produces auto-resizing circular array from arguments
|
|
18
|
+
- O(1) pops either end
|
|
19
|
+
- O(1) amortized pushes either end
|
|
20
|
+
- O(1) indexing, fully supports slicing
|
|
21
|
+
- auto-resizing more storage capacity when necessary, manually compatible
|
|
22
|
+
- iterable, safely mutates while iterators iterating over previous state
|
|
23
|
+
- comparisons compare identity before equality, like builtins
|
|
24
|
+
- in boolean context, falsy when empty, otherwise truthy
|
|
25
|
+
- function ``ca`` produces auto-resizing circular array from arguments
|
|
29
26
|
|
|
30
27
|
"""
|
|
31
28
|
|
|
@@ -41,22 +38,22 @@ nada: Final[NoValue] = NoValue()
|
|
|
41
38
|
class CA[I]:
|
|
42
39
|
__slots__ = '_items', '_cnt', '_cap', '_front', '_rear'
|
|
43
40
|
|
|
44
|
-
|
|
45
|
-
def __init__(self) -> None: ...
|
|
46
|
-
@overload
|
|
47
|
-
def __init__(self, items: Iterable[I]) -> None: ...
|
|
48
|
-
|
|
49
|
-
def __init__(self, items: Iterable[I | NoValue] | NoValue = nada) -> None:
|
|
41
|
+
def __init__(self, *items: Iterable[I]) -> None:
|
|
50
42
|
"""
|
|
51
|
-
:param items:
|
|
52
|
-
|
|
43
|
+
:param items: Optionally takes a single iterable to
|
|
44
|
+
initially populate the circular array.
|
|
45
|
+
:raises TypeError: When ``items[0]`` not iterable.
|
|
46
|
+
:raises ValueError: If more than 1 iterable is given.
|
|
53
47
|
"""
|
|
54
|
-
if items
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
48
|
+
if (size := len(items)) > 1:
|
|
49
|
+
msg = f'CA expects at most 1 argument, got {size}'
|
|
50
|
+
raise ValueError(msg)
|
|
51
|
+
if size:
|
|
52
|
+
values: list[I | NoValue] = list(cast(Iterable[I | NoValue], items[0]))
|
|
58
53
|
self._items = [nada] + values + [nada]
|
|
59
|
-
|
|
54
|
+
else:
|
|
55
|
+
self._items = [nada, nada]
|
|
56
|
+
self._cap = (cap := len(self._items))
|
|
60
57
|
self._cnt = cap - 2
|
|
61
58
|
if cap == 2:
|
|
62
59
|
self._front = 0
|
|
@@ -285,6 +282,12 @@ class CA[I]:
|
|
|
285
282
|
del _ca
|
|
286
283
|
|
|
287
284
|
def __eq__(self, other: object) -> bool:
|
|
285
|
+
"""
|
|
286
|
+
:param other: The object to be compared to.
|
|
287
|
+
:returns: ``True`` if object is another CA whose items compare
|
|
288
|
+
as equal to the corresponding items in the CA,
|
|
289
|
+
otherwise ``False``.
|
|
290
|
+
"""
|
|
288
291
|
if self is other:
|
|
289
292
|
return True
|
|
290
293
|
if not isinstance(other, type(self)):
|
|
@@ -292,10 +295,10 @@ class CA[I]:
|
|
|
292
295
|
|
|
293
296
|
(
|
|
294
297
|
front1,
|
|
295
|
-
|
|
298
|
+
cnt1,
|
|
296
299
|
capacity1,
|
|
297
300
|
front2,
|
|
298
|
-
|
|
301
|
+
cnt2,
|
|
299
302
|
capacity2,
|
|
300
303
|
) = (
|
|
301
304
|
self._front,
|
|
@@ -306,10 +309,10 @@ class CA[I]:
|
|
|
306
309
|
other._cap,
|
|
307
310
|
)
|
|
308
311
|
|
|
309
|
-
if
|
|
312
|
+
if cnt1 != cnt2:
|
|
310
313
|
return False
|
|
311
314
|
|
|
312
|
-
for nn in range(
|
|
315
|
+
for nn in range(cnt1):
|
|
313
316
|
if (
|
|
314
317
|
self._items[(front1 + nn) % capacity1]
|
|
315
318
|
is other._items[(front2 + nn) % capacity2]
|
|
@@ -459,7 +462,8 @@ class CA[I]:
|
|
|
459
462
|
def poplt(self, maximum: int) -> tuple[I, ...]:
|
|
460
463
|
"""Pop multiple items from left side of circular array.
|
|
461
464
|
|
|
462
|
-
:param maximum: Maximum number of items to pop, may pop less
|
|
465
|
+
:param maximum: Maximum number of items to pop, may pop less
|
|
466
|
+
if not enough items.
|
|
463
467
|
:returns: Tuple of items in the order popped, left to right.
|
|
464
468
|
"""
|
|
465
469
|
item_list: list[I] = []
|
|
@@ -477,7 +481,8 @@ class CA[I]:
|
|
|
477
481
|
def poprt(self, maximum: int) -> tuple[I, ...]:
|
|
478
482
|
"""Pop multiple items from right side of circular array.
|
|
479
483
|
|
|
480
|
-
:param maximum: Maximum number of items to pop, may pop less
|
|
484
|
+
:param maximum: Maximum number of items to pop, may pop less
|
|
485
|
+
if not enough items.
|
|
481
486
|
:returns: Tuple of items in the order popped, right to left.
|
|
482
487
|
"""
|
|
483
488
|
item_list: list[I] = []
|
|
@@ -526,10 +531,12 @@ class CA[I]:
|
|
|
526
531
|
def foldl[L](self, f: Callable[[L, I], L], start: L | NoValue = NoValue()) -> L:
|
|
527
532
|
"""Fold left with a function and optional starting item.
|
|
528
533
|
|
|
529
|
-
:param f: Folding function, first argument to ``f`` is for
|
|
534
|
+
:param f: Folding function, first argument to ``f`` is for
|
|
535
|
+
the accumulator.
|
|
530
536
|
:param start: "Optional" starting item.
|
|
531
537
|
:returns: Reduced value produced by the left fold.
|
|
532
|
-
:raises ValueError: When circular array empty and no starting
|
|
538
|
+
:raises ValueError: When circular array empty and no starting
|
|
539
|
+
item given.
|
|
533
540
|
"""
|
|
534
541
|
if self._cnt == 0:
|
|
535
542
|
if start is nada:
|
|
@@ -556,10 +563,12 @@ class CA[I]:
|
|
|
556
563
|
def foldr[R](self, f: Callable[[I, R], R], start: R | NoValue = nada) -> R:
|
|
557
564
|
"""Fold right with a function and an optional starting item.
|
|
558
565
|
|
|
559
|
-
:param f: Folding function, second argument to ``f`` is for
|
|
560
|
-
|
|
566
|
+
:param f: Folding function, second argument to ``f`` is for
|
|
567
|
+
the accumulator.
|
|
568
|
+
:param start: Optional starting item.
|
|
561
569
|
:returns: Reduced value produced by the right fold.
|
|
562
|
-
:raises ValueError: When circular array empty and no starting
|
|
570
|
+
:raises ValueError: When circular array empty and no starting
|
|
571
|
+
item given.
|
|
563
572
|
"""
|
|
564
573
|
if self._cnt == 0:
|
|
565
574
|
if start is nada:
|
|
@@ -611,7 +620,8 @@ class CA[I]:
|
|
|
611
620
|
storage capacity. To just compact the circular array, do not
|
|
612
621
|
provide ``minimum_capacity``.
|
|
613
622
|
|
|
614
|
-
:param minimum_capacity: Minimum storage capacity to compact
|
|
623
|
+
:param minimum_capacity: Minimum storage capacity to compact
|
|
624
|
+
the circular array.
|
|
615
625
|
"""
|
|
616
626
|
self._compact_storage_capacity()
|
|
617
627
|
if (min_cap := minimum_capacity) > self._cap:
|
|
@@ -4,10 +4,7 @@ from typing import overload
|
|
|
4
4
|
__all__ = ['CA', 'ca']
|
|
5
5
|
|
|
6
6
|
class CA[I]:
|
|
7
|
-
|
|
8
|
-
def __init__(self) -> None: ...
|
|
9
|
-
@overload
|
|
10
|
-
def __init__(self, items: Iterable[I]) -> None: ...
|
|
7
|
+
def __init__(self, *items: Iterable[I]) -> None: ...
|
|
11
8
|
def __iter__(self) -> Iterator[I]: ...
|
|
12
9
|
def __reversed__(self) -> Iterator[I]: ...
|
|
13
10
|
def __bool__(self) -> bool: ...
|
|
@@ -13,18 +13,15 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
"""
|
|
16
|
-
Fixed
|
|
17
|
-
======================
|
|
16
|
+
.. admonition:: Fixed storage capacity circular array.**
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
- in boolean context, falsy when either empty or full, otherwise truthy
|
|
27
|
-
- function ``caf`` produces fixed capacity circular array from arguments
|
|
18
|
+
- O(1) pops and pushes either end
|
|
19
|
+
- O(1) indexing, does not support slicing
|
|
20
|
+
- fixed total storage capacity
|
|
21
|
+
- iterable, safely mutates while iterators iterating over previous state
|
|
22
|
+
- comparisons compare identity before equality, like builtins
|
|
23
|
+
- in boolean context, falsy when either empty or full, otherwise truthy
|
|
24
|
+
- function ``caf`` produces fixed capacity circular array from arguments
|
|
28
25
|
|
|
29
26
|
"""
|
|
30
27
|
|
|
@@ -40,44 +37,39 @@ nada: Final[NoValue] = NoValue()
|
|
|
40
37
|
class CAF[I]:
|
|
41
38
|
__slots__ = '_items', '_cnt', '_cap', '_front', '_rear'
|
|
42
39
|
|
|
43
|
-
|
|
44
|
-
def __init__(self) -> None: ...
|
|
45
|
-
@overload
|
|
46
|
-
def __init__(self, items: Iterable[I]) -> None: ...
|
|
47
|
-
@overload
|
|
48
|
-
def __init__(self, items: Iterable[I], capacity: int) -> None: ...
|
|
49
|
-
|
|
50
|
-
def __init__(
|
|
51
|
-
self, items: Iterable[I | NoValue] | NoValue = nada, capacity: int = 2
|
|
52
|
-
) -> None:
|
|
40
|
+
def __init__(self, *items: Iterable[I], cap: int = 2) -> None:
|
|
53
41
|
"""
|
|
54
|
-
:param items:
|
|
55
|
-
|
|
56
|
-
:
|
|
42
|
+
:param items: Optionally takes a single iterable to
|
|
43
|
+
initially populate the circular array.
|
|
44
|
+
:param cap: Minimum fixed storage capacity of circular array.
|
|
45
|
+
:raises TypeError: When ``items[0]`` not iterable,
|
|
46
|
+
:raises ValueError: If more than 1 iterable is given.
|
|
57
47
|
"""
|
|
58
|
-
|
|
59
|
-
if items
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
cap = max(2, cap)
|
|
49
|
+
if (size := len(items)) > 1:
|
|
50
|
+
msg = f'CAF expects at most 1 argument, got {size}'
|
|
51
|
+
raise ValueError(msg)
|
|
52
|
+
if size:
|
|
53
|
+
values: list[I | NoValue] = list(cast(Iterable[I | NoValue], items[0]))
|
|
54
|
+
cnt = len(values)
|
|
55
|
+
cap = max(cnt, cap)
|
|
56
|
+
self._items = values + [nada] * (cap - cnt)
|
|
62
57
|
else:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
self._cap: Final[int] = capacity
|
|
69
|
-
self._cnt = count
|
|
70
|
-
if count == 0:
|
|
58
|
+
self._items = [nada] * cap
|
|
59
|
+
cnt = 0
|
|
60
|
+
self._cap: Final[int] = cap
|
|
61
|
+
self._cnt = cnt
|
|
62
|
+
if cnt == 0:
|
|
71
63
|
self._front = 0
|
|
72
|
-
self._rear =
|
|
64
|
+
self._rear = cap - 1
|
|
73
65
|
else:
|
|
74
66
|
self._front = 0
|
|
75
|
-
self._rear =
|
|
67
|
+
self._rear = cnt - 1
|
|
76
68
|
|
|
77
69
|
def __iter__(self) -> Iterator[I]:
|
|
78
70
|
if self._cnt > 0:
|
|
79
71
|
(
|
|
80
|
-
|
|
72
|
+
cap,
|
|
81
73
|
rear,
|
|
82
74
|
position,
|
|
83
75
|
current_state,
|
|
@@ -90,13 +82,13 @@ class CAF[I]:
|
|
|
90
82
|
|
|
91
83
|
while position != rear:
|
|
92
84
|
yield cast(I, current_state[position])
|
|
93
|
-
position = (position + 1) %
|
|
85
|
+
position = (position + 1) % cap
|
|
94
86
|
yield cast(I, current_state[position])
|
|
95
87
|
|
|
96
88
|
def __reversed__(self) -> Iterator[I]:
|
|
97
89
|
if self._cnt > 0:
|
|
98
90
|
(
|
|
99
|
-
|
|
91
|
+
cap,
|
|
100
92
|
front,
|
|
101
93
|
position,
|
|
102
94
|
current_state,
|
|
@@ -109,7 +101,7 @@ class CAF[I]:
|
|
|
109
101
|
|
|
110
102
|
while position != front:
|
|
111
103
|
yield cast(I, current_state[position])
|
|
112
|
-
position = (position - 1) %
|
|
104
|
+
position = (position - 1) % cap
|
|
113
105
|
yield cast(I, current_state[position])
|
|
114
106
|
|
|
115
107
|
def __repr__(self) -> str:
|
|
@@ -159,7 +151,7 @@ class CAF[I]:
|
|
|
159
151
|
def __delitem__(self, idx: int) -> None:
|
|
160
152
|
item_list = list(self)
|
|
161
153
|
del item_list[idx]
|
|
162
|
-
_ca = CAF(item_list, self._cap)
|
|
154
|
+
_ca = CAF(item_list, cap = self._cap)
|
|
163
155
|
(
|
|
164
156
|
self._items,
|
|
165
157
|
self._cnt,
|
|
@@ -174,6 +166,12 @@ class CAF[I]:
|
|
|
174
166
|
del _ca
|
|
175
167
|
|
|
176
168
|
def __eq__(self, other: object) -> bool:
|
|
169
|
+
"""
|
|
170
|
+
:param other: The object to be compared to.
|
|
171
|
+
:returns: ``True`` if object is another CAF whose items compare
|
|
172
|
+
as equal to the corresponding items in the CAF,
|
|
173
|
+
otherwise ``False``.
|
|
174
|
+
"""
|
|
177
175
|
if self is other:
|
|
178
176
|
return True
|
|
179
177
|
if not isinstance(other, type(self)):
|
|
@@ -181,11 +179,11 @@ class CAF[I]:
|
|
|
181
179
|
|
|
182
180
|
(
|
|
183
181
|
front1,
|
|
184
|
-
|
|
185
|
-
|
|
182
|
+
cnt1,
|
|
183
|
+
cap1,
|
|
186
184
|
front2,
|
|
187
|
-
|
|
188
|
-
|
|
185
|
+
cnt2,
|
|
186
|
+
cap2,
|
|
189
187
|
) = (
|
|
190
188
|
self._front,
|
|
191
189
|
self._cnt,
|
|
@@ -195,18 +193,18 @@ class CAF[I]:
|
|
|
195
193
|
other._cap,
|
|
196
194
|
)
|
|
197
195
|
|
|
198
|
-
if
|
|
196
|
+
if cnt1 != cnt2:
|
|
199
197
|
return False
|
|
200
198
|
|
|
201
|
-
for nn in range(
|
|
199
|
+
for nn in range(cnt1):
|
|
202
200
|
if (
|
|
203
|
-
self._items[(front1 + nn) %
|
|
204
|
-
is other._items[(front2 + nn) %
|
|
201
|
+
self._items[(front1 + nn) % cap1]
|
|
202
|
+
is other._items[(front2 + nn) % cap2]
|
|
205
203
|
):
|
|
206
204
|
continue
|
|
207
205
|
if (
|
|
208
|
-
self._items[(front1 + nn) %
|
|
209
|
-
!= other._items[(front2 + nn) %
|
|
206
|
+
self._items[(front1 + nn) % cap1]
|
|
207
|
+
!= other._items[(front2 + nn) % cap2]
|
|
210
208
|
):
|
|
211
209
|
return False
|
|
212
210
|
return True
|
|
@@ -352,7 +350,8 @@ class CAF[I]:
|
|
|
352
350
|
def poplt(self, maximum: int) -> tuple[I, ...]:
|
|
353
351
|
"""Pop multiple items from left side of circular array.
|
|
354
352
|
|
|
355
|
-
:param maximum: Maximum number of items to pop, may pop less
|
|
353
|
+
:param maximum: Maximum number of items to pop, may pop less
|
|
354
|
+
if not enough items.
|
|
356
355
|
:returns: Tuple of items in the order popped, left to right.
|
|
357
356
|
"""
|
|
358
357
|
item_list: list[I] = []
|
|
@@ -370,7 +369,8 @@ class CAF[I]:
|
|
|
370
369
|
def poprt(self, maximum: int) -> tuple[I, ...]:
|
|
371
370
|
"""Pop multiple items from right side of circular array.
|
|
372
371
|
|
|
373
|
-
:param maximum: Maximum number of items to pop, may pop less
|
|
372
|
+
:param maximum: Maximum number of items to pop, may pop less
|
|
373
|
+
if not enough items.
|
|
374
374
|
:returns: Tuple of items in the order popped, right to left.
|
|
375
375
|
"""
|
|
376
376
|
item_list: list[I] = []
|
|
@@ -409,7 +409,7 @@ class CAF[I]:
|
|
|
409
409
|
:param f: Callable from type ``I`` to type ``U``.
|
|
410
410
|
:returns: New fixed capacity circular array instance.
|
|
411
411
|
"""
|
|
412
|
-
return CAF(map(f, self), self._cap)
|
|
412
|
+
return CAF(map(f, self), cap = self._cap)
|
|
413
413
|
|
|
414
414
|
@overload
|
|
415
415
|
def foldl[L](self, f: Callable[[I, I], I]) -> I: ...
|
|
@@ -419,10 +419,12 @@ class CAF[I]:
|
|
|
419
419
|
def foldl[L](self, f: Callable[[L, I], L], start: L | NoValue = nada) -> L:
|
|
420
420
|
"""Fold left with a function and optional stating item.
|
|
421
421
|
|
|
422
|
-
:param f: Folding function, first argument to ``f`` is for
|
|
422
|
+
:param f: Folding function, first argument to ``f`` is for
|
|
423
|
+
the accumulator.
|
|
423
424
|
:param start: Optional starting item.
|
|
424
425
|
:returns: Reduced value produced by the left fold.
|
|
425
|
-
:raises ValueError: When circular array empty and no starting
|
|
426
|
+
:raises ValueError: When circular array empty and no starting
|
|
427
|
+
item given.
|
|
426
428
|
"""
|
|
427
429
|
if self._cnt == 0:
|
|
428
430
|
if start is nada:
|
|
@@ -449,10 +451,12 @@ class CAF[I]:
|
|
|
449
451
|
def foldr[R](self, f: Callable[[I, R], R], start: R | NoValue = nada) -> R:
|
|
450
452
|
"""Fold right with a function and an optional starting item.
|
|
451
453
|
|
|
452
|
-
:param f: Folding function, second argument to ``f`` is for
|
|
454
|
+
:param f: Folding function, second argument to ``f`` is for
|
|
455
|
+
the accumulator.
|
|
453
456
|
:param start: Optional starting item.
|
|
454
457
|
:returns: Reduced value produced by the right fold.
|
|
455
|
-
:raises ValueError: When circular array empty and no starting
|
|
458
|
+
:raises ValueError: When circular array empty and no starting
|
|
459
|
+
item given.
|
|
456
460
|
"""
|
|
457
461
|
if self._cnt == 0:
|
|
458
462
|
if start is nada:
|
|
@@ -500,11 +504,11 @@ class CAF[I]:
|
|
|
500
504
|
return self._cnt / self._cap
|
|
501
505
|
|
|
502
506
|
|
|
503
|
-
def caf[T](*items: T,
|
|
507
|
+
def caf[T](*items: T, cap: int = 2) -> CAF[T]:
|
|
504
508
|
"""Produce a circular array from a variable number of arguments.
|
|
505
509
|
|
|
506
510
|
:param items: Initial items for a new fixed capacity :circular array.
|
|
507
|
-
:param
|
|
511
|
+
:param cap: The minimum storage capacity to set.
|
|
508
512
|
:returns: New fixed storage capacity circular array.
|
|
509
513
|
"""
|
|
510
|
-
return CAF(items,
|
|
514
|
+
return CAF(items, cap=cap)
|
|
@@ -4,12 +4,7 @@ from typing import overload
|
|
|
4
4
|
__all__ = ['CAF', 'caf']
|
|
5
5
|
|
|
6
6
|
class CAF[I]:
|
|
7
|
-
|
|
8
|
-
def __init__(self) -> None: ...
|
|
9
|
-
@overload
|
|
10
|
-
def __init__(self, items: Iterable[I]) -> None: ...
|
|
11
|
-
@overload
|
|
12
|
-
def __init__(self, items: Iterable[I], capacity: int) -> None: ...
|
|
7
|
+
def __init__(self, *items: Iterable[I], cap: int = 2) -> None: ...
|
|
13
8
|
def __iter__(self) -> Iterator[I]: ...
|
|
14
9
|
def __reversed__(self) -> Iterator[I]: ...
|
|
15
10
|
def __bool__(self) -> bool: ...
|
|
@@ -41,4 +36,4 @@ class CAF[I]:
|
|
|
41
36
|
def empty(self) -> None: ...
|
|
42
37
|
def fraction_filled(self) -> float: ...
|
|
43
38
|
|
|
44
|
-
def caf[T](*items: T,
|
|
39
|
+
def caf[T](*items: T, cap: int = 2) -> CAF[T]: ...
|
|
@@ -485,3 +485,13 @@ class TestCircularArrayResizing:
|
|
|
485
485
|
assert baz == ca(3, 2, 1, 3, 4, 0, 6, 1, 8, 2, 10)
|
|
486
486
|
del baz[6:10:2]
|
|
487
487
|
assert baz == ca(3, 2, 1, 3, 4, 0, 1, 2, 10)
|
|
488
|
+
|
|
489
|
+
def test_store_none(self) -> None:
|
|
490
|
+
ca1 = ca(1, 'a', None, (), 42)
|
|
491
|
+
assert ca1.popl() == 1
|
|
492
|
+
assert ca1.popr() == 42
|
|
493
|
+
assert ca1
|
|
494
|
+
assert ca1.popl() == 'a'
|
|
495
|
+
assert ca1.popl() is None
|
|
496
|
+
assert ca1.popr() == ()
|
|
497
|
+
assert not ca1
|
|
@@ -20,7 +20,7 @@ class TestCircularArrayFixed:
|
|
|
20
20
|
|
|
21
21
|
def test_mutate_returns_none(self) -> None:
|
|
22
22
|
"""Test for builtin behaviors"""
|
|
23
|
-
ca1: CAF[int] = caf(
|
|
23
|
+
ca1: CAF[int] = caf(cap=4)
|
|
24
24
|
assert not ca1
|
|
25
25
|
assert ca1.pushl(1) is None # type: ignore[func-returns-value]
|
|
26
26
|
assert ca1
|
|
@@ -63,7 +63,7 @@ class TestCircularArrayFixed:
|
|
|
63
63
|
|
|
64
64
|
def test_push_then_pop(self) -> None:
|
|
65
65
|
"""Functionality test"""
|
|
66
|
-
ca0: CAF[str] = caf(
|
|
66
|
+
ca0: CAF[str] = caf(cap=42)
|
|
67
67
|
pushed1 = '42'
|
|
68
68
|
ca0.pushl(pushed1)
|
|
69
69
|
popped1 = ca0.popl()
|
|
@@ -156,8 +156,8 @@ class TestCircularArrayFixed:
|
|
|
156
156
|
|
|
157
157
|
def test_equality(self) -> None:
|
|
158
158
|
"""Functionality test"""
|
|
159
|
-
c1: CAF[object] = caf(1, 2, 3, 'Forty-Two', (7, 11, 'foobar'),
|
|
160
|
-
c2: CAF[object] = caf(2, 3, 'Forty-Two',
|
|
159
|
+
c1: CAF[object] = caf(1, 2, 3, 'Forty-Two', (7, 11, 'foobar'), cap=7)
|
|
160
|
+
c2: CAF[object] = caf(2, 3, 'Forty-Two', cap=7)
|
|
161
161
|
c2.pushl(1)
|
|
162
162
|
c2.pushr((7, 11, 'foobar'))
|
|
163
163
|
assert c1 == c2
|
|
@@ -175,7 +175,7 @@ class TestCircularArrayFixed:
|
|
|
175
175
|
assert c1 == c2
|
|
176
176
|
|
|
177
177
|
hold_a = c1.popl()
|
|
178
|
-
c1 = CAF(c1, 42)
|
|
178
|
+
c1 = CAF(c1, cap=42)
|
|
179
179
|
hold_b = c1.popl()
|
|
180
180
|
hold_c = c1.popr()
|
|
181
181
|
c1.pushl(hold_b)
|
|
@@ -312,7 +312,7 @@ class TestCircularArrayFixed:
|
|
|
312
312
|
|
|
313
313
|
def test_readme(self) -> None:
|
|
314
314
|
"""Functionality test"""
|
|
315
|
-
ca0 = caf(1, 2, 3,
|
|
315
|
+
ca0 = caf(1, 2, 3, cap=10)
|
|
316
316
|
assert ca0.popl() == 1
|
|
317
317
|
assert ca0.popr() == 3
|
|
318
318
|
ca0.pushr(42)
|
|
@@ -340,7 +340,7 @@ class TestCircularArrayFixed:
|
|
|
340
340
|
|
|
341
341
|
def test_pop(self) -> None:
|
|
342
342
|
"""Functionality test"""
|
|
343
|
-
ca1 = caf(1, 2, 3,
|
|
343
|
+
ca1 = caf(1, 2, 3, cap=4)
|
|
344
344
|
assert ca1.popld(42) == 1
|
|
345
345
|
assert ca1.poprd(42) == 3
|
|
346
346
|
assert ca1.popld(42) == 2
|
|
@@ -348,7 +348,7 @@ class TestCircularArrayFixed:
|
|
|
348
348
|
assert ca1.popld(42) == 42
|
|
349
349
|
assert len(ca1) == 0
|
|
350
350
|
|
|
351
|
-
ca2: CAF[int] = caf(0, 1, 2, 3, 4, 5, 6,
|
|
351
|
+
ca2: CAF[int] = caf(0, 1, 2, 3, 4, 5, 6, cap=10)
|
|
352
352
|
assert ca2.popl() == 0
|
|
353
353
|
assert ca2.popr() == 6
|
|
354
354
|
assert ca2 == caf(1, 2, 3, 4, 5)
|
|
@@ -437,8 +437,8 @@ class TestCircularArrayFixed:
|
|
|
437
437
|
(4, 1),
|
|
438
438
|
(4, 3),
|
|
439
439
|
)
|
|
440
|
-
foo = caf(0, 1, 2, 3, 4,
|
|
441
|
-
bar = CAF[tuple[int, int]]((),
|
|
440
|
+
foo = caf(0, 1, 2, 3, 4, cap=10)
|
|
441
|
+
bar = CAF[tuple[int, int]]((), cap=100)
|
|
442
442
|
|
|
443
443
|
for ii in foo:
|
|
444
444
|
if ii % 2 == 1:
|
|
@@ -489,3 +489,14 @@ class TestCircularArrayFixed:
|
|
|
489
489
|
assert False
|
|
490
490
|
else:
|
|
491
491
|
assert bar == 0
|
|
492
|
+
|
|
493
|
+
def test_store_none(self) -> None:
|
|
494
|
+
caf1 = caf(1, 'a', None, (), 42)
|
|
495
|
+
assert not caf1
|
|
496
|
+
assert caf1.popl() == 1
|
|
497
|
+
assert caf1.popr() == 42
|
|
498
|
+
assert caf1
|
|
499
|
+
assert caf1.popl() == 'a'
|
|
500
|
+
assert caf1.popl() is None
|
|
501
|
+
assert caf1.popr() == ()
|
|
502
|
+
assert not caf1
|
{pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/tests/fixed/test_caf_capacity.py
RENAMED
|
@@ -22,7 +22,7 @@ class TestCapacityFixed:
|
|
|
22
22
|
ca0: CAF[int] = CAF()
|
|
23
23
|
assert ca0.capacity() == 2
|
|
24
24
|
|
|
25
|
-
ca0 = caf(1, 2,
|
|
25
|
+
ca0 = caf(1, 2, cap=8)
|
|
26
26
|
assert ca0.fraction_filled() == 2 / 8
|
|
27
27
|
|
|
28
28
|
ca0.pushl(0)
|
|
@@ -49,7 +49,7 @@ class TestCapacityFixed:
|
|
|
49
49
|
|
|
50
50
|
def test_empty(self) -> None:
|
|
51
51
|
"""Functionality test"""
|
|
52
|
-
c: CAF[int] = caf(
|
|
52
|
+
c: CAF[int] = caf(cap=401)
|
|
53
53
|
assert c == caf()
|
|
54
54
|
assert len(c) == 0
|
|
55
55
|
for ii in 1, 2, 3, 4, 5:
|
|
@@ -73,7 +73,7 @@ class TestCapacityFixed:
|
|
|
73
73
|
|
|
74
74
|
def test_one(self) -> None:
|
|
75
75
|
"""Functionality test"""
|
|
76
|
-
c = caf(42,
|
|
76
|
+
c = caf(42, cap=5)
|
|
77
77
|
assert c.capacity() == 5
|
|
78
78
|
assert len(c) == 1
|
|
79
79
|
popped = c.popld(0)
|
|
@@ -25,7 +25,7 @@ class TestReprFixed:
|
|
|
25
25
|
bar: CAF[int] = eval(repr(ca0))
|
|
26
26
|
assert foo == bar
|
|
27
27
|
|
|
28
|
-
ca1: CAF[str|int] = CAF((),
|
|
28
|
+
ca1: CAF[str|int] = CAF((), cap=10)
|
|
29
29
|
assert repr(ca1) == 'caf()'
|
|
30
30
|
ca2: CAF[int|str] = eval(repr(ca1))
|
|
31
31
|
assert ca2 == ca1
|
|
@@ -51,7 +51,7 @@ class TestReprFixed:
|
|
|
51
51
|
assert ca2 == ca1
|
|
52
52
|
assert ca2 is not ca1
|
|
53
53
|
|
|
54
|
-
ca3: CAF[int] = caf(1, 10, 0, 42,
|
|
54
|
+
ca3: CAF[int] = caf(1, 10, 0, 42, cap=10)
|
|
55
55
|
ca3.pushr(2)
|
|
56
56
|
ca3.pushr(100)
|
|
57
57
|
ca3.pushr(3)
|
|
@@ -65,7 +65,7 @@ class TestReprFixed:
|
|
|
65
65
|
ca3.pushr(3)
|
|
66
66
|
assert repr(ca3) == 'caf(8, 9, 10, 0, 42, 1, 2, 3)'
|
|
67
67
|
|
|
68
|
-
ca4: CAF[int] = CAF([1, 10, 0, 42],
|
|
68
|
+
ca4: CAF[int] = CAF([1, 10, 0, 42], cap=100)
|
|
69
69
|
ca4.pushr(2)
|
|
70
70
|
ca4.pushr(100)
|
|
71
71
|
ca4.pushr(3)
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# Copyright 2024-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
|
-
"""
|
|
16
|
-
Circular Array
|
|
17
|
-
--------------
|
|
18
|
-
|
|
19
|
-
Package implementing stateful circular array data structures.
|
|
20
|
-
|
|
21
|
-
- O(1) pops and pushes either end
|
|
22
|
-
- O(1) indexing and size determination
|
|
23
|
-
- O(1) size determination
|
|
24
|
-
|
|
25
|
-
+-------------------------------------+-------------------------------------------+
|
|
26
|
-
| Module | Description |
|
|
27
|
-
+=====================================+===========================================+
|
|
28
|
-
| **pythonic_fp.circulararray.auto** | Variable storage capacity circular array. |
|
|
29
|
-
+-------------------------------------+-------------------------------------------+
|
|
30
|
-
| **pythonic_fp.circulararray.fixed** | Fixed storage capacity circular array. |
|
|
31
|
-
+-------------------------------------+-------------------------------------------+
|
|
32
|
-
"""
|
|
33
|
-
|
|
34
|
-
__author__ = 'Geoffrey R. Scheller'
|
|
35
|
-
__copyright__ = 'Copyright (c) 2023-2025 Geoffrey R. Scheller'
|
|
36
|
-
__license__ = 'Apache License 2.0'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/tests/auto/test_ca_capacity.py
RENAMED
|
File without changes
|
|
File without changes
|
{pythonic_fp_circulararray-5.4.0 → pythonic_fp_circulararray-6.0.1}/tests/auto/test_repr_str_auto.py
RENAMED
|
File without changes
|
|
File without changes
|