pythonic-fp.containers 2.0.0__py3-none-any.whl → 3.0.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.
- pythonic_fp/containers/__init__.py +17 -1
- pythonic_fp/containers/functional_tuple.py +51 -56
- pythonic_fp/containers/immutable_list.py +50 -54
- pythonic_fp/containers/queues/__init__.py +31 -0
- pythonic_fp/containers/queues/de.py +180 -0
- pythonic_fp/containers/queues/fifo.py +145 -0
- pythonic_fp/containers/queues/lifo.py +136 -0
- pythonic_fp/containers/queues/py.typed +0 -0
- pythonic_fp_containers-3.0.0.dist-info/METADATA +63 -0
- pythonic_fp_containers-3.0.0.dist-info/RECORD +13 -0
- pythonic_fp/containers/box.py +0 -159
- pythonic_fp/containers/maybe.py +0 -226
- pythonic_fp/containers/xor.py +0 -314
- pythonic_fp_containers-2.0.0.dist-info/METADATA +0 -50
- pythonic_fp_containers-2.0.0.dist-info/RECORD +0 -11
- {pythonic_fp_containers-2.0.0.dist-info → pythonic_fp_containers-3.0.0.dist-info}/WHEEL +0 -0
- {pythonic_fp_containers-2.0.0.dist-info → pythonic_fp_containers-3.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Copyright 2023-2024 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
|
+
"""Stateful First-In-First-Out (FIFO) Queue data structure.
|
|
16
|
+
|
|
17
|
+
- O(1) pops
|
|
18
|
+
- O(1) amortized pushes
|
|
19
|
+
- in a Boolean context, true if not empty, false if empty
|
|
20
|
+
- will automatically increase storage capacity when needed
|
|
21
|
+
- neither indexable nor sliceable by design
|
|
22
|
+
- O(1) length determination
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from collections.abc import Callable, Iterable, Iterator
|
|
28
|
+
from typing import TypeVar
|
|
29
|
+
from pythonic_fp.circulararray.auto import CA
|
|
30
|
+
from pythonic_fp.fptools.maybe import MayBe
|
|
31
|
+
|
|
32
|
+
__all__ = ['FIFOQueue', 'fifo_queue']
|
|
33
|
+
|
|
34
|
+
D = TypeVar('D')
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class FIFOQueue[D]:
|
|
38
|
+
|
|
39
|
+
__slots__ = ('_ca',)
|
|
40
|
+
|
|
41
|
+
def __init__(self, *dss: Iterable[D]) -> None:
|
|
42
|
+
"""Initial data in natural FIFO order, newest to oldest.
|
|
43
|
+
|
|
44
|
+
:param dss: takes up to one iterable
|
|
45
|
+
:raises ValueError: if more than 1 iterable is given
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
if (size := len(dss)) > 1:
|
|
49
|
+
msg = f'FIFOQueue expects at most 1 iterable argument, got {size}'
|
|
50
|
+
raise ValueError(msg)
|
|
51
|
+
self._ca = CA(dss[0]) if size == 1 else CA()
|
|
52
|
+
|
|
53
|
+
def __bool__(self) -> bool:
|
|
54
|
+
return len(self._ca) > 0
|
|
55
|
+
|
|
56
|
+
def __len__(self) -> int:
|
|
57
|
+
return len(self._ca)
|
|
58
|
+
|
|
59
|
+
def __eq__(self, other: object) -> bool:
|
|
60
|
+
if not isinstance(other, FIFOQueue):
|
|
61
|
+
return False
|
|
62
|
+
return self._ca == other._ca
|
|
63
|
+
|
|
64
|
+
def __iter__(self) -> Iterator[D]:
|
|
65
|
+
return iter(list(self._ca))
|
|
66
|
+
|
|
67
|
+
def __repr__(self) -> str:
|
|
68
|
+
if len(self) == 0:
|
|
69
|
+
return 'FIFOQueue()'
|
|
70
|
+
return 'FIFOQueue(' + ', '.join(map(repr, self._ca)) + ')'
|
|
71
|
+
|
|
72
|
+
def __str__(self) -> str:
|
|
73
|
+
return '<< ' + ' < '.join(map(str, self)) + ' <<'
|
|
74
|
+
|
|
75
|
+
def copy(self) -> FIFOQueue[D]:
|
|
76
|
+
"""Shallow copy.
|
|
77
|
+
|
|
78
|
+
:return: shallow copy of the FIFOQueue
|
|
79
|
+
|
|
80
|
+
"""
|
|
81
|
+
return FIFOQueue(self._ca)
|
|
82
|
+
|
|
83
|
+
def push(self, *ds: D) -> None:
|
|
84
|
+
"""Push data onto FIFOQueue.
|
|
85
|
+
|
|
86
|
+
:param ds: data items to be pushed onto FIFOQueue
|
|
87
|
+
|
|
88
|
+
"""
|
|
89
|
+
self._ca.pushr(*ds)
|
|
90
|
+
|
|
91
|
+
def pop(self) -> MayBe[D]:
|
|
92
|
+
"""Pop oldest data item off of FIFOQueue.
|
|
93
|
+
|
|
94
|
+
:return: MayBe of popped data item if queue was not empty, empty MayBe otherwise
|
|
95
|
+
|
|
96
|
+
"""
|
|
97
|
+
if self._ca:
|
|
98
|
+
return MayBe(self._ca.popl())
|
|
99
|
+
return MayBe()
|
|
100
|
+
|
|
101
|
+
def peak_last_in(self) -> MayBe[D]:
|
|
102
|
+
"""Peak at newest data item on queue.
|
|
103
|
+
|
|
104
|
+
:return: MayBe of newest data item on queue, empty MayBe if queue empty
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
if self._ca:
|
|
108
|
+
return MayBe(self._ca[-1])
|
|
109
|
+
return MayBe()
|
|
110
|
+
|
|
111
|
+
def peak_next_out(self) -> MayBe[D]:
|
|
112
|
+
"""Peak at oldest data item on queue.
|
|
113
|
+
|
|
114
|
+
:return: MayBe of oldest data item on queue, empty MayBe if queue empty
|
|
115
|
+
|
|
116
|
+
"""
|
|
117
|
+
if self._ca:
|
|
118
|
+
return MayBe(self._ca[0])
|
|
119
|
+
return MayBe()
|
|
120
|
+
|
|
121
|
+
def fold[T](self, f: Callable[[T, D], T], start: T | None = None) -> MayBe[T]:
|
|
122
|
+
"""Reduces FIFOQueue in natural FIFO Order, oldest to newest.
|
|
123
|
+
|
|
124
|
+
:param f: reducing function, first argument is for accumulator
|
|
125
|
+
:param start: optional starting value
|
|
126
|
+
:return: MayBe of reduced value with f, empty MayBe if queue empty and no starting value given
|
|
127
|
+
|
|
128
|
+
"""
|
|
129
|
+
if start is None:
|
|
130
|
+
if not self._ca:
|
|
131
|
+
return MayBe()
|
|
132
|
+
return MayBe(self._ca.foldl(f, start))
|
|
133
|
+
|
|
134
|
+
def map[U](self, f: Callable[[D], U]) -> FIFOQueue[U]:
|
|
135
|
+
"""Map f over the FIFOQueue, retain original order.
|
|
136
|
+
|
|
137
|
+
:return: new FIFOQueue instance
|
|
138
|
+
|
|
139
|
+
"""
|
|
140
|
+
return FIFOQueue(map(f, self._ca))
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def fifo_queue[D](*ds: D) -> FIFOQueue[D]:
|
|
144
|
+
"""FIFOQueue factory function."""
|
|
145
|
+
return FIFOQueue(ds)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Copyright 2023-2024 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
|
+
"""Stateful Last-In-First-Out (LIFO) Queue data structure.
|
|
16
|
+
|
|
17
|
+
- O(1) pops
|
|
18
|
+
- O(1) amortized pushes
|
|
19
|
+
- in a Boolean context, true if not empty, false if empty
|
|
20
|
+
- will automatically increase storage capacity when needed
|
|
21
|
+
- neither indexable nor sliceable by design
|
|
22
|
+
- O(1) length determination
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from collections.abc import Callable, Iterable, Iterator
|
|
28
|
+
from typing import TypeVar
|
|
29
|
+
from pythonic_fp.circulararray.auto import CA
|
|
30
|
+
from pythonic_fp.fptools.function import swap
|
|
31
|
+
from pythonic_fp.fptools.maybe import MayBe
|
|
32
|
+
|
|
33
|
+
__all__ = ['LIFOQueue', 'lifo_queue']
|
|
34
|
+
|
|
35
|
+
D = TypeVar('D')
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class LIFOQueue[D]:
|
|
39
|
+
|
|
40
|
+
__slots__ = ('_ca',)
|
|
41
|
+
|
|
42
|
+
def __init__(self, *dss: Iterable[D]) -> None:
|
|
43
|
+
"""Initial data in natural LIFO order, oldest to newest.
|
|
44
|
+
|
|
45
|
+
:param dss: takes up to one iterable
|
|
46
|
+
:raises ValueError: if more than 1 iterable is given
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
if (size := len(dss)) > 1:
|
|
50
|
+
msg = f'LIFOQueue expects at most 1 iterable argument, got {size}'
|
|
51
|
+
raise ValueError(msg)
|
|
52
|
+
self._ca = CA(dss[0]) if size == 1 else CA()
|
|
53
|
+
|
|
54
|
+
def __bool__(self) -> bool:
|
|
55
|
+
return len(self._ca) > 0
|
|
56
|
+
|
|
57
|
+
def __len__(self) -> int:
|
|
58
|
+
return len(self._ca)
|
|
59
|
+
|
|
60
|
+
def __eq__(self, other: object) -> bool:
|
|
61
|
+
if not isinstance(other, LIFOQueue):
|
|
62
|
+
return False
|
|
63
|
+
return self._ca == other._ca
|
|
64
|
+
|
|
65
|
+
def __iter__(self) -> Iterator[D]:
|
|
66
|
+
return reversed(list(self._ca))
|
|
67
|
+
|
|
68
|
+
def __repr__(self) -> str:
|
|
69
|
+
if len(self) == 0:
|
|
70
|
+
return 'LIFOQueue()'
|
|
71
|
+
return 'LIFOQueue(' + ', '.join(map(repr, self._ca)) + ')'
|
|
72
|
+
|
|
73
|
+
def __str__(self) -> str:
|
|
74
|
+
return '|| ' + ' > '.join(map(str, self)) + ' ><'
|
|
75
|
+
|
|
76
|
+
def copy(self) -> LIFOQueue[D]:
|
|
77
|
+
"""Shallow copy.
|
|
78
|
+
|
|
79
|
+
:return: shallow copy of the LIFOQueue
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
return LIFOQueue(reversed(self._ca))
|
|
83
|
+
|
|
84
|
+
def push(self, *ds: D) -> None:
|
|
85
|
+
"""Push data onto LIFOQueue.
|
|
86
|
+
|
|
87
|
+
:param ds: data items to be pushed onto LIFOQueue
|
|
88
|
+
|
|
89
|
+
"""
|
|
90
|
+
self._ca.pushr(*ds)
|
|
91
|
+
|
|
92
|
+
def pop(self) -> MayBe[D]:
|
|
93
|
+
"""Pop newest data item off of LIFOQueue.
|
|
94
|
+
|
|
95
|
+
:return: MayBe of popped data item if queue was not empty, empty MayBe otherwise
|
|
96
|
+
|
|
97
|
+
"""
|
|
98
|
+
if self._ca:
|
|
99
|
+
return MayBe(self._ca.popr())
|
|
100
|
+
return MayBe()
|
|
101
|
+
|
|
102
|
+
def peak(self) -> MayBe[D]:
|
|
103
|
+
"""Peak at newest data item on queue.
|
|
104
|
+
|
|
105
|
+
:return: MayBe of newest data item on queue, empty MayBe if queue empty
|
|
106
|
+
|
|
107
|
+
"""
|
|
108
|
+
if self._ca:
|
|
109
|
+
return MayBe(self._ca[-1])
|
|
110
|
+
return MayBe()
|
|
111
|
+
|
|
112
|
+
def fold[T](self, f: Callable[[T, D], T], start: T | None = None) -> MayBe[T]:
|
|
113
|
+
"""Reduces LIFOQUEUE in natural LIFO Order, newest to oldest.
|
|
114
|
+
|
|
115
|
+
:param f: reducing function, first argument is for accumulator
|
|
116
|
+
:param start: optional starting value
|
|
117
|
+
:return: MayBe of reduced value with f, empty MayBe if queue empty and no starting value given
|
|
118
|
+
|
|
119
|
+
"""
|
|
120
|
+
if start is None:
|
|
121
|
+
if not self._ca:
|
|
122
|
+
return MayBe()
|
|
123
|
+
return MayBe(self._ca.foldr(swap(f), start))
|
|
124
|
+
|
|
125
|
+
def map[U](self, f: Callable[[D], U]) -> LIFOQueue[U]:
|
|
126
|
+
"""Map f over the LIFOQueue, retain original order.
|
|
127
|
+
|
|
128
|
+
:return: new LIFOQueue instance
|
|
129
|
+
|
|
130
|
+
"""
|
|
131
|
+
return LIFOQueue(reversed(CA(map(f, reversed(self._ca)))))
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def lifo_queue[D](*ds: D) -> LIFOQueue[D]:
|
|
135
|
+
"""LIFOQueue factory function."""
|
|
136
|
+
return LIFOQueue(ds)
|
|
File without changes
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pythonic-fp-containers
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: Package of container like data structures.
|
|
5
|
+
Keywords: boxes,functional_tuple,immutable_list,queue,fifo,lifo,dqueue,dequeue
|
|
6
|
+
Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Description-Content-Type: text/x-rst
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Framework :: Pytest
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: pythonic-fp-circulararray>= 5.3.0
|
|
18
|
+
Requires-Dist: pythonic-fp-fptools>=5.0.0
|
|
19
|
+
Requires-Dist: pythonic-fp-iterables>=5.0.0
|
|
20
|
+
Requires-Dist: pythonic-fp-singletons>=1.0.0
|
|
21
|
+
Requires-Dist: pytest>=8.4.1 ; extra == "test"
|
|
22
|
+
Requires-Dist: pythonic-fp-splitends>=1.0.0 ; extra == "test"
|
|
23
|
+
Project-URL: Changelog, https://github.com/grscheller/pythonic-fp-containers/blob/main/CHANGELOG.rst
|
|
24
|
+
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp/containers/development/build/html/releases.html
|
|
25
|
+
Project-URL: Homepage, https://github.com/grscheller/pythonic-fp/blob/main/README.md
|
|
26
|
+
Project-URL: Source, https://github.com/grscheller/pythonic-fp-containers
|
|
27
|
+
Provides-Extra: test
|
|
28
|
+
|
|
29
|
+
Pythonic FP - Containers
|
|
30
|
+
========================
|
|
31
|
+
|
|
32
|
+
PyPI project
|
|
33
|
+
`pythonic-fp.containers <https://pypi.org/project/pythonic-fp.containers>`_.
|
|
34
|
+
|
|
35
|
+
Python package of container like data structures.
|
|
36
|
+
|
|
37
|
+
+------------------+------------------------------------------------------+
|
|
38
|
+
| Description | Module |
|
|
39
|
+
+==================+======================================================+
|
|
40
|
+
| Single item box | *module* ``pythonic_fp.containers.box`` |
|
|
41
|
+
| Functional tuple | *module* ``pythonic_fp.containers.functional_tuple`` |
|
|
42
|
+
| Immutable list | *module* ``pythonic_fp.containers.immutable_list`` |
|
|
43
|
+
+------------------+------------------------------------------------------+
|
|
44
|
+
|
|
45
|
+
**DEPRECATION WARNING:** ``pythonic_fp.fptools.box`` as been deprecated.
|
|
46
|
+
Use ``pythonic_fp.box`` instead.
|
|
47
|
+
|
|
48
|
+
This PyPI project is part of of the grscheller
|
|
49
|
+
`pythonic-fp namespace projects <https://github.com/grscheller/pythonic-fp/blob/main/README.md>`_.
|
|
50
|
+
|
|
51
|
+
Documentation
|
|
52
|
+
-------------
|
|
53
|
+
|
|
54
|
+
Documentation for this project is hosted on
|
|
55
|
+
`GitHub Pages
|
|
56
|
+
<https://grscheller.github.io/pythonic-fp/containers/development/build/html>`_.
|
|
57
|
+
|
|
58
|
+
Copyright and License
|
|
59
|
+
---------------------
|
|
60
|
+
|
|
61
|
+
Copyright (c) 2023-2025 Geoffrey R. Scheller. Licensed under the Apache
|
|
62
|
+
License, Version 2.0. See the LICENSE file for details.
|
|
63
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
pythonic_fp/containers/__init__.py,sha256=N5-djJadhpE6h8bg31AtUrqami8RPjsiUYwlwrPpYnI,1603
|
|
2
|
+
pythonic_fp/containers/functional_tuple.py,sha256=AffGbovvg0WpnImZwzhD47djLps27Ns29eKzFosXOC4,5799
|
|
3
|
+
pythonic_fp/containers/immutable_list.py,sha256=SGfGKRyDyBylrNn9qvpoKog531RKAusRsxhtnVa5Rps,6677
|
|
4
|
+
pythonic_fp/containers/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
pythonic_fp/containers/queues/__init__.py,sha256=GJdzodYt32pADih2UHOiR9U4jfZ6SQSw6PsCYZ2r1wg,1459
|
|
6
|
+
pythonic_fp/containers/queues/de.py,sha256=lBoZE8SRe6__NEFpNiqKhumDtdIVvm7S7HMojEUAL1s,5288
|
|
7
|
+
pythonic_fp/containers/queues/fifo.py,sha256=Q9luMfZzSJzGU3aJwnJ5OIeT6hs-6ieh70b5wYGodI4,4221
|
|
8
|
+
pythonic_fp/containers/queues/lifo.py,sha256=xudvLpEvqv9bl3bCqTiwlC2IDZ9A5kHVl-aIjNAKyeA,4031
|
|
9
|
+
pythonic_fp/containers/queues/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
pythonic_fp_containers-3.0.0.dist-info/licenses/LICENSE,sha256=IJMkVWQUVy-SonK6SMaMotDQ6Zb-lVi2bUTiGp1GUO8,10774
|
|
11
|
+
pythonic_fp_containers-3.0.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
12
|
+
pythonic_fp_containers-3.0.0.dist-info/METADATA,sha256=UeoEZi26JqOUc0AYxcwpr8FrxsNEA7XOu5IMySlm_jk,2672
|
|
13
|
+
pythonic_fp_containers-3.0.0.dist-info/RECORD,,
|
pythonic_fp/containers/box.py
DELETED
|
@@ -1,159 +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
|
-
"""Stateful container holds at most one item of a given type."""
|
|
16
|
-
|
|
17
|
-
from __future__ import annotations
|
|
18
|
-
|
|
19
|
-
__all__ = ['Box']
|
|
20
|
-
|
|
21
|
-
from collections.abc import Callable, Iterator
|
|
22
|
-
from typing import cast, Final, Never, overload, TypeVar
|
|
23
|
-
from pythonic_fp.fptools.singletons import Sentinel
|
|
24
|
-
|
|
25
|
-
D = TypeVar('D')
|
|
26
|
-
|
|
27
|
-
_sentinel: Final[Sentinel] = Sentinel('Box')
|
|
28
|
-
|
|
29
|
-
class Box[D]:
|
|
30
|
-
"""Container holding at most one item of a given type
|
|
31
|
-
|
|
32
|
-
- where ``Box(item)`` contains at most one item of type ``~D``
|
|
33
|
-
|
|
34
|
-
- ``Box()`` creates an empty container
|
|
35
|
-
- can store any item of any type, including ``None``
|
|
36
|
-
"""
|
|
37
|
-
|
|
38
|
-
__slots__ = ('_item',)
|
|
39
|
-
__match_args__ = ('_item',)
|
|
40
|
-
|
|
41
|
-
T = TypeVar('T')
|
|
42
|
-
|
|
43
|
-
@overload
|
|
44
|
-
def __init__(self) -> None: ...
|
|
45
|
-
@overload
|
|
46
|
-
def __init__(self, value: D) -> None: ...
|
|
47
|
-
|
|
48
|
-
def __init__(self, value: D | Sentinel = Sentinel('Box')) -> None:
|
|
49
|
-
"""Initialize Box with an "optional" initial value.
|
|
50
|
-
|
|
51
|
-
:param value: "optional" initial value
|
|
52
|
-
:type value: ~D
|
|
53
|
-
"""
|
|
54
|
-
self._item: D | Sentinel = value
|
|
55
|
-
|
|
56
|
-
def __bool__(self) -> bool:
|
|
57
|
-
return self._item is not Sentinel('Box')
|
|
58
|
-
|
|
59
|
-
def __iter__(self) -> Iterator[D]:
|
|
60
|
-
if self:
|
|
61
|
-
yield cast(D, self._item)
|
|
62
|
-
|
|
63
|
-
def __repr__(self) -> str:
|
|
64
|
-
if self:
|
|
65
|
-
return 'Box(' + repr(self._item) + ')'
|
|
66
|
-
return 'Box()'
|
|
67
|
-
|
|
68
|
-
def __len__(self) -> int:
|
|
69
|
-
return 1 if self else 0
|
|
70
|
-
|
|
71
|
-
def __eq__(self, other: object) -> bool:
|
|
72
|
-
if not isinstance(other, type(self)):
|
|
73
|
-
return False
|
|
74
|
-
|
|
75
|
-
if self._item is other._item:
|
|
76
|
-
return True
|
|
77
|
-
if self._item == other._item:
|
|
78
|
-
return True
|
|
79
|
-
return False
|
|
80
|
-
|
|
81
|
-
@overload
|
|
82
|
-
def get(self) -> D | Never: ...
|
|
83
|
-
@overload
|
|
84
|
-
def get(self, alt: D) -> D: ...
|
|
85
|
-
@overload
|
|
86
|
-
def get(self, alt: Sentinel) -> D | Never: ...
|
|
87
|
-
|
|
88
|
-
def get(self, alt: D | Sentinel = Sentinel('Box')) -> D | Never:
|
|
89
|
-
"""Return the contained value if it exists, otherwise an alternate value.
|
|
90
|
-
|
|
91
|
-
:rtype: ~D
|
|
92
|
-
:raises ValueError: when an alternate value is not provided but needed
|
|
93
|
-
"""
|
|
94
|
-
if self._item is not _sentinel:
|
|
95
|
-
return cast(D, self._item)
|
|
96
|
-
if alt is _sentinel:
|
|
97
|
-
msg = 'Box: get from empty Box with no alt return value provided'
|
|
98
|
-
raise ValueError(msg)
|
|
99
|
-
return cast(D, alt)
|
|
100
|
-
|
|
101
|
-
def pop(self) -> D | Never:
|
|
102
|
-
"""Pop the value if Box is not empty.
|
|
103
|
-
|
|
104
|
-
:rtype: ~D
|
|
105
|
-
:raises ValueError: if box is empty
|
|
106
|
-
"""
|
|
107
|
-
if self._item is _sentinel:
|
|
108
|
-
msg = 'Box: Trying to pop an item from an empty Box'
|
|
109
|
-
raise ValueError(msg)
|
|
110
|
-
popped = cast(D, self._item)
|
|
111
|
-
self._item = _sentinel
|
|
112
|
-
return popped
|
|
113
|
-
|
|
114
|
-
def push(self, item: D) -> None | Never:
|
|
115
|
-
"""Push an item in an empty Box.
|
|
116
|
-
|
|
117
|
-
:raises ValueError: if box is not empty
|
|
118
|
-
"""
|
|
119
|
-
if self._item is Sentinel('Box'):
|
|
120
|
-
self._item = item
|
|
121
|
-
else:
|
|
122
|
-
msg = 'Box: Trying to push an item in an empty Box'
|
|
123
|
-
raise ValueError(msg)
|
|
124
|
-
return None
|
|
125
|
-
|
|
126
|
-
def put(self, item: D) -> None:
|
|
127
|
-
"""Put an item in the Box. Discard any previous contents."""
|
|
128
|
-
self._item = item
|
|
129
|
-
|
|
130
|
-
def exchange(self, new_item: D) -> D | Never:
|
|
131
|
-
"""Exchange an item with what is in the Box.
|
|
132
|
-
|
|
133
|
-
:rtype: ~D
|
|
134
|
-
:raises ValueError: if box is empty
|
|
135
|
-
"""
|
|
136
|
-
if self._item is _sentinel:
|
|
137
|
-
msg = 'Box: Trying to exchange items using an empty Box'
|
|
138
|
-
raise ValueError(msg)
|
|
139
|
-
popped = cast(D, self._item)
|
|
140
|
-
self._item = new_item
|
|
141
|
-
return popped
|
|
142
|
-
|
|
143
|
-
def map[T](self, f: Callable[[D], T]) -> Box[T]:
|
|
144
|
-
"""Map function ``f`` over contents.
|
|
145
|
-
|
|
146
|
-
:return: a new instance
|
|
147
|
-
"""
|
|
148
|
-
if self._item is Sentinel('Box'):
|
|
149
|
-
return Box()
|
|
150
|
-
return Box(f(cast(D, self._item)))
|
|
151
|
-
|
|
152
|
-
def bind[T](self, f: Callable[[D], Box[T]]) -> Box[T]:
|
|
153
|
-
"""Flatmap ``Box`` with function ``f``.
|
|
154
|
-
|
|
155
|
-
:return: a new instance
|
|
156
|
-
"""
|
|
157
|
-
if self._item is _sentinel:
|
|
158
|
-
return Box()
|
|
159
|
-
return f(cast(D, self._item))
|