pythonic-fp.containers 3.0.0__py3-none-any.whl → 4.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.
@@ -12,21 +12,17 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- """Package of container like data structures.
16
-
17
- +------------------+--------+----------------------------------------------------+
18
- | Description | Class | Package/Module |
19
- +==================+========+====================================================+
20
- | Functional tuple | FTuple | module ``pythonic_fp.containers.functional_tuple`` |
21
- +------------------+--------+----------------------------------------------------+
22
- | Immutable list | IList | module ``pythonic_fp.containers.immutable_list`` |
23
- +------------------+--------+----------------------------------------------------+
24
- | Queues | | package ``pythonic_fp.containers.queues`` |
25
- +------------------+--------+----------------------------------------------------+
26
-
27
- .. note::
15
+ """
16
+ Container with FP interfaces
17
+ ============================
28
18
 
29
- Module ``box`` has been moved to ``pythonic_fp.gadgets.box``.
19
+ +---------------------------------------------+-----------+------------------+
20
+ | module | class | description |
21
+ +=============================================+===========+==================+
22
+ | ``pythonic_fp.containers.functional_tuple`` | FTuple | Functional Tuple |
23
+ +---------------------------------------------+-----------+------------------+
24
+ | ``pythonic_fp.containers.immutable_list`` | IList | Immutable List |
25
+ +---------------------------------------------+-----------+------------------+
30
26
 
31
27
  """
32
28
 
@@ -14,22 +14,19 @@
14
14
 
15
15
  """Pythonic FP - Functional Tuple"""
16
16
 
17
- from __future__ import annotations
18
-
19
17
  from collections.abc import Callable, Iterator
20
- from typing import cast, Never, overload, TypeVar
18
+ from typing import cast, Never, overload, SupportsIndex
21
19
  from pythonic_fp.iterables.folding import accumulate
22
20
  from pythonic_fp.iterables.merging import blend, concat, MergeEnum
23
21
 
24
22
  __all__ = ['FTuple']
25
23
 
26
- D = TypeVar('D', covariant=True)
27
24
 
28
25
  class FTuple[D](tuple[D, ...]):
29
- """Functional Tuple suitable for inheritance
26
+ """Functional Tuple suitable for inheritance.
30
27
 
31
- - Supports both indexing and slicing
32
- - FTuple addition and int multiplication supported
28
+ - supports both indexing and slicing
29
+ - int multiplication and FTuple addition supported
33
30
 
34
31
  - addition concatenates results, resulting in a Union type
35
32
  - both left and right int multiplication supported
@@ -37,9 +34,9 @@ class FTuple[D](tuple[D, ...]):
37
34
  - supports being further inherited from
38
35
  - unslotted
39
36
 
40
- - Since these tuples are homogeneous, their covariance may be quirky
41
-
37
+ Since these tuples are homogeneous, their covariance may be quirky.
42
38
  """
39
+
43
40
  def __reversed__(self) -> Iterator[D]:
44
41
  for ii in range(len(self) - 1, -1, -1):
45
42
  yield (self[ii])
@@ -59,17 +56,14 @@ class FTuple[D](tuple[D, ...]):
59
56
  return False
60
57
  return True
61
58
 
62
- @overload # type:ignore
63
- def __getitem__(self, x: int) -> D: ...
64
59
  @overload
65
- def __getitem__(self, x: slice) -> tuple[D, ...]: ...
66
-
67
- def __getitem__(self, idx: int | slice, /) -> tuple[D, ...] | D:
68
- def newtup(tup: tuple[D, ...]) -> tuple[D, ...]:
69
- return tup[0:-1] + tup[-1:]
60
+ def __getitem__(self, idx: SupportsIndex) -> D: ...
61
+ @overload
62
+ def __getitem__(self, idx: slice) -> tuple[D, ...]: ...
70
63
 
64
+ def __getitem__(self, idx: SupportsIndex | slice) -> tuple[D, ...] | D:
71
65
  if isinstance(idx, slice):
72
- return self.__class__(newtup(super().__getitem__(idx)))
66
+ return self.__class__(super().__getitem__(idx))
73
67
  else:
74
68
  return super().__getitem__(idx)
75
69
 
@@ -79,14 +73,17 @@ class FTuple[D](tuple[D, ...]):
79
73
  /,
80
74
  start: L | None = None,
81
75
  default: L | None = None,
82
- ) -> L | None:
83
- """Fold Left
76
+ ) -> L:
77
+ """Fold Left with optional starting and default values.
84
78
 
85
79
  - fold left with an optional starting value
86
- - first argument of function f is for the accumulated value
87
-
88
- :raises ValueError: when FTuple empty and a start value not given
80
+ - first argument of function ``f`` is for the accumulated value
89
81
 
82
+ :param f: The folding function, first argument is for the accumulated value.
83
+ :param start: An optional starting value.
84
+ :param default: An optional default value if fold does not exist.
85
+ :raises ValueError: When ``FTuple`` empty and a start value not given.
86
+ :returns: The folded value if it exists, otherwise the default value.
90
87
  """
91
88
  it_self = iter(self)
92
89
  if start is not None:
@@ -108,14 +105,14 @@ class FTuple[D](tuple[D, ...]):
108
105
  /,
109
106
  start: R | None = None,
110
107
  default: R | None = None,
111
- ) -> R | None:
112
- """Fold Right
113
-
114
- - fold right with an optional starting value
115
- - second argument of function f is for the accumulated value
108
+ ) -> R:
109
+ """Fold Right with optional starting and default values.
116
110
 
111
+ :param f: The folding function, second argument is for the accumulated value.
112
+ :param start: An optional starting value.
113
+ :param default: An optional default value if fold does not exist.
117
114
  :raises ValueError: when FTuple empty and a start value not given
118
-
115
+ :returns: The folded value if it exists, otherwise the default value.
119
116
  """
120
117
  it_self = reversed(self)
121
118
  if start is not None:
@@ -131,54 +128,58 @@ class FTuple[D](tuple[D, ...]):
131
128
  acc = f(v, acc)
132
129
  return acc
133
130
 
134
- def copy(self) -> FTuple[D]:
135
- """Return a shallow copy of ``FTuple`` in O(1) time & space complexity."""
131
+ def copy(self) -> 'FTuple[D]':
132
+ """Return a shallow copy of ``FTuple`` in O(1) time & space complexity.
133
+
134
+ :returns: New ``FTuple``.
135
+ """
136
136
  return self.__class__(self)
137
137
 
138
138
  def __add__(self, other: object, /) -> tuple[D, ...]:
139
- if not isinstance(other, FTuple):
139
+ if not isinstance(other, tuple):
140
140
  msg = 'FTuple being added to something not an FTuple'
141
141
  raise ValueError(msg)
142
142
  return self.__class__(concat(self, other))
143
143
 
144
- def __mul__(self, num: int, /) -> tuple[D, ...]:
144
+ def __mul__(self, num: SupportsIndex) -> tuple[D, ...]:
145
145
  return self.__class__(super().__mul__(num))
146
146
 
147
- def __rmul__(self, num: int, /) -> tuple[D]:
147
+ def __rmul__(self, num: SupportsIndex) -> tuple[D, ...]:
148
148
  return self.__class__(super().__rmul__(num))
149
149
 
150
150
  def accummulate[L](
151
151
  self, f: Callable[[L, D], L], s: L | None = None, /
152
- ) -> FTuple[L]:
153
- """Accumulate partial folds
152
+ ) -> 'FTuple[L]':
153
+ """Accumulate partial folds.
154
154
 
155
- Accumulate partial fold results in an ``FTuple`` with an optional
156
- starting value.
155
+ Accumulate partial fold with an optional starting value,
156
+ results in an ``FTuple``.
157
157
 
158
+ :param f: Folding function used to produce partial folds.
159
+ :param s: Optional starting value.
160
+ :returns: New ``FTuple`` of the partial folds.
158
161
  """
159
162
  if s is None:
160
163
  return FTuple(accumulate(self, f))
161
164
  return FTuple(accumulate(self, f, s))
162
165
 
163
- def map[U](self, f: Callable[[D], U], /) -> FTuple[U]:
166
+ def map[U](self, f: Callable[[D], U], /) -> 'FTuple[U]':
164
167
  return FTuple(map(f, self))
165
168
 
166
169
  def bind[U](
167
- self, f: Callable[[D], FTuple[U]],
168
- merge_enum: MergeEnum = MergeEnum.Concat,
169
- yield_partials: bool = False,
170
- ) -> FTuple[U] | Never:
170
+ self,
171
+ f: 'Callable[[D], FTuple[U]]',
172
+ merge_type: MergeEnum = MergeEnum.Concat,
173
+ yield_partials: bool = False,
174
+ ) -> 'FTuple[U] | Never':
171
175
  """Bind function ``f`` to the ``FTuple``.
172
176
 
173
- :param ds: values to instantiate FTuple
174
- :return: resulting FTuple
175
- :raises ValueError: if given unknown merge_type
176
-
177
+ :param f: Function ``D -> FTuple[U]``
178
+ :param merge_type: ``MergeEnum`` to determine how to merge the result.
179
+ :param yield_partials: Yield unmatched values if ``MergeEnum`` given as merge type.
180
+ :return: Resulting ``FTuple``.
181
+ :raises ValueError: If given an unknown merge enumeration.
177
182
  """
178
183
  return FTuple(
179
- blend(
180
- *map(f, self),
181
- merge_enum=merge_enum,
182
- yield_partials=yield_partials
183
- )
184
+ blend(*map(f, self), merge_enum=merge_type, yield_partials=yield_partials)
184
185
  )
@@ -0,0 +1,22 @@
1
+ from collections.abc import Callable, Iterator
2
+ from pythonic_fp.iterables.merging import MergeEnum
3
+ from typing import Never, SupportsIndex, overload
4
+
5
+ __all__ = ['FTuple']
6
+
7
+ class FTuple[D](tuple[D, ...]):
8
+ def __reversed__(self) -> Iterator[D]: ...
9
+ def __eq__(self, other: object) -> bool: ...
10
+ @overload
11
+ def __getitem__(self, idx: SupportsIndex) -> D: ...
12
+ @overload
13
+ def __getitem__(self, idx: slice) -> tuple[D, ...]: ...
14
+ def foldl[L](self, f: Callable[[L, D], L], /, start: L | None = None, default: L | None = None) -> L: ...
15
+ def foldr[R](self, f: Callable[[D, R], R], /, start: R | None = None, default: R | None = None) -> R: ...
16
+ def copy(self) -> FTuple[D]: ...
17
+ def __add__(self, other: object) -> tuple[D, ...]: ...
18
+ def __mul__(self, num: SupportsIndex) -> tuple[D, ...]: ...
19
+ def __rmul__(self, num: SupportsIndex) -> tuple[D, ...]: ...
20
+ def accummulate[L](self, f: Callable[[L, D], L], s: L | None = None, /) -> FTuple[L]: ...
21
+ def map[U](self, f: Callable[[D], U], /) -> FTuple[U]: ...
22
+ def bind[U](self, f: Callable[[D], FTuple[U]], merge_type: MergeEnum = ..., yield_partials: bool = False) -> FTuple[U] | Never: ...
@@ -12,47 +12,36 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- """Pythonic FP - Immutable guaranteed hashable lists
16
-
17
- - hashable if elements are hashable
18
- - declared covariant in its generic datatype
19
- - hashability should be enforced by LSP tooling
20
- - hashability will be enforced at runtime
21
- - IList addition supported via concatenation
22
- - IList integer multiplication supported
23
-
24
- """
25
-
26
- from __future__ import annotations
15
+ """Pythonic FP - Immutable guaranteed hashable lists."""
27
16
 
28
17
  from collections.abc import Callable, Iterable, Iterator, Hashable
29
- from typing import cast, Never, overload, TypeVar
18
+ from typing import cast, Never, overload
30
19
  from pythonic_fp.iterables.folding import accumulate
31
20
  from pythonic_fp.iterables.merging import blend, concat, MergeEnum
32
21
 
33
22
  __all__ = ['IList']
34
23
 
35
- D = TypeVar('D', covariant=True)
36
- T = TypeVar('T')
37
24
 
38
25
  class IList[D](Hashable):
39
26
  """Immutable List like data structure.
40
27
 
28
+ - hashability should be enforced by LSP tooling
29
+ - hashability will be enforced at runtime
41
30
  - its method type parameters are also covariant
42
- - hashability will be enforced by LSP tooling
43
31
  - supports both indexing and slicing
44
- - addition concatenates results, resulting type a Union type
45
- - both left and right int multiplication supported
32
+ - addition and left & right ``int`` multiplication supported
33
+
34
+ - addition is concatenation resulting in a union type
46
35
 
47
36
  """
37
+
48
38
  __slots__ = ('_ds', '_len', '_hash')
49
39
  __match_args__ = ('_ds', '_len')
50
40
 
51
- L = TypeVar('L')
52
- R = TypeVar('R')
53
- U = TypeVar('U')
54
-
55
41
  def __init__(self, *dss: Iterable[D]) -> None:
42
+ """
43
+ :param dss: 0 or 1 iterables
44
+ """
56
45
  if (size := len(dss)) > 1:
57
46
  msg = f'IList expects at most 1 iterable argument, got {size}'
58
47
  raise ValueError(msg)
@@ -98,9 +87,9 @@ class IList[D](Hashable):
98
87
  @overload
99
88
  def __getitem__(self, idx: int, /) -> D: ...
100
89
  @overload
101
- def __getitem__(self, idx: slice, /) -> IList[D]: ...
90
+ def __getitem__(self, idx: slice, /) -> 'IList[D]': ...
102
91
 
103
- def __getitem__(self, idx: slice | int, /) -> IList[D] | D:
92
+ def __getitem__(self, idx: slice | int, /) -> 'IList[D] | D':
104
93
  if isinstance(idx, slice):
105
94
  return IList(self._ds[idx])
106
95
  return self._ds[idx]
@@ -112,13 +101,13 @@ class IList[D](Hashable):
112
101
  start: L | None = None,
113
102
  default: L | None = None,
114
103
  ) -> L | None:
115
- """Fold Left
116
-
117
- - fold left with an optional starting value
118
- - first argument of function ``f`` is for the accumulated value
119
-
120
- "raises ValueError: when empty and a start value not given
104
+ """Fold Left.
121
105
 
106
+ :param f: Folding function, first argument is for the accumulated value.
107
+ :param start: Optional starting value.
108
+ :param default: Optional default value if fold does not exist.
109
+ :returns: Folded value.
110
+ :raises ValueError: When empty and a start value not given.
122
111
  """
123
112
  it = iter(self._ds)
124
113
  if start is not None:
@@ -143,13 +132,13 @@ class IList[D](Hashable):
143
132
  start: R | None = None,
144
133
  default: R | None = None,
145
134
  ) -> R | None:
146
- """Fold Right
147
-
148
- - fold right with an optional starting value
149
- - second argument of function ``f`` is for the accumulated value
150
-
151
- "raises ValueError: when empty and a start value not given
135
+ """Fold Right.
152
136
 
137
+ :param f: Folding function, second argument is for the accumulated value.
138
+ :param start: Optional starting value.
139
+ :param default: Optional default value if fold does not exist.
140
+ :returns: Folded value.
141
+ :raises ValueError: When empty and a start value not given.
153
142
  """
154
143
  it = reversed(self._ds)
155
144
  if start is not None:
@@ -167,52 +156,59 @@ class IList[D](Hashable):
167
156
  acc = f(v, acc)
168
157
  return acc
169
158
 
170
- def __add__(self, other: IList[D], /) -> IList[D]:
159
+ def __add__(self, other: 'IList[D]', /) -> 'IList[D]':
171
160
  if not isinstance(other, IList):
172
- msg = 'IList being added to something not a IList'
161
+ msg = 'IList being added to something not an IList'
173
162
  raise ValueError(msg)
174
163
 
175
164
  return IList(concat(self, other))
176
165
 
177
- def __mul__(self, num: int, /) -> IList[D]:
166
+ def __mul__(self, num: int, /) -> 'IList[D]':
178
167
  return IList(self._ds.__mul__(num if num > 0 else 0))
179
168
 
180
- def __rmul__(self, num: int, /) -> IList[D]:
169
+ def __rmul__(self, num: int, /) -> 'IList[D]':
181
170
  return IList(self._ds.__mul__(num if num > 0 else 0))
182
171
 
183
172
  def accummulate[L](
184
173
  self, f: Callable[[L, D], L], s: L | None = None, /
185
- ) -> IList[L]:
186
- """Accumulate partial folds
174
+ ) -> 'IList[L]':
175
+ """Accumulate partial folds.
187
176
 
188
- Accumulate partial fold results in an IList with
189
- an optional starting value.
177
+ Accumulate partial fold with an optional starting value,
178
+ results in an ``IList``.
190
179
 
180
+ :param f: Folding function used to produce partial folds.
181
+ :param s: Optional starting value.
182
+ :returns: New ``FTuple`` of the partial folds.
191
183
  """
192
184
  if s is None:
193
185
  return IList(accumulate(self, f))
194
186
  return IList(accumulate(self, f, s))
195
187
 
196
- def map[U](self, f: Callable[[D], U], /) -> IList[U]:
188
+ def map[U](self, f: Callable[[D], U], /) -> 'IList[U]':
197
189
  return IList(map(f, self))
198
190
 
199
191
  def bind[U](
200
- self,
201
- f: Callable[[D], IList[U]],
202
- merge_enum: MergeEnum = MergeEnum.Concat,
203
- yield_partials: bool = False,
204
- ) -> IList[U] | Never:
192
+ self,
193
+ f: 'Callable[[D], IList[U]]',
194
+ merge_enum: MergeEnum = MergeEnum.Concat,
195
+ yield_partials: bool = False,
196
+ ) -> 'IList[U] | Never':
205
197
  """Bind function `f` to the `IList`.
206
198
 
207
199
  :param ds: values to instantiate IList
208
200
  :return: resulting IList
209
201
  :raises ValueError: if given unknown merge_type
210
202
 
203
+ """
204
+ """Bind function ``f`` to the ``IList``.
205
+
206
+ :param f: Function ``D -> IList[U]``
207
+ :param merge_type: ``MergeEnum`` to determine how to merge the result.
208
+ :param yield_partials: Yield unmatched values if ``MergeEnum`` given as merge type.
209
+ :return: Resulting ``IList``.
210
+ :raises ValueError: If given an unknown merge enumeration.
211
211
  """
212
212
  return IList(
213
- blend(
214
- *map(f, self),
215
- merge_enum=merge_enum,
216
- yield_partials=yield_partials
217
- )
213
+ blend(*map(f, self), merge_enum=merge_enum, yield_partials=yield_partials)
218
214
  )
@@ -0,0 +1,28 @@
1
+ from _typeshed import Incomplete
2
+ from collections.abc import Callable, Hashable, Iterable, Iterator
3
+ from pythonic_fp.iterables.merging import MergeEnum
4
+ from typing import Never, overload
5
+
6
+ __all__ = ['IList']
7
+
8
+ class IList[D](Hashable):
9
+ __match_args__: Incomplete
10
+ def __init__(self, *dss: Iterable[D]) -> None: ...
11
+ def __hash__(self) -> int: ...
12
+ def __iter__(self) -> Iterator[D]: ...
13
+ def __reversed__(self) -> Iterator[D]: ...
14
+ def __bool__(self) -> bool: ...
15
+ def __len__(self) -> int: ...
16
+ def __eq__(self, other: object) -> bool: ...
17
+ @overload
18
+ def __getitem__(self, idx: int) -> D: ...
19
+ @overload
20
+ def __getitem__(self, idx: slice) -> IList[D]: ...
21
+ def foldl[L](self, f: Callable[[L, D], L], /, start: L | None = None, default: L | None = None) -> L | None: ...
22
+ def foldr[R](self, f: Callable[[D, R], R], /, start: R | None = None, default: R | None = None) -> R | None: ...
23
+ def __add__(self, other: IList[D]) -> IList[D]: ...
24
+ def __mul__(self, num: int) -> IList[D]: ...
25
+ def __rmul__(self, num: int) -> IList[D]: ...
26
+ def accummulate[L](self, f: Callable[[L, D], L], s: L | None = None, /) -> IList[L]: ...
27
+ def map[U](self, f: Callable[[D], U], /) -> IList[U]: ...
28
+ def bind[U](self, f: Callable[[D], IList[U]], merge_enum: MergeEnum = ..., yield_partials: bool = False) -> IList[U] | Never: ...
@@ -0,0 +1,62 @@
1
+ Metadata-Version: 2.4
2
+ Name: pythonic-fp-containers
3
+ Version: 4.0.0
4
+ Summary: Container with FP interfaces
5
+ Keywords: functional_containers,functional_tuple,immutable_list
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 :: 3 - Alpha
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>=6.0.0
18
+ Requires-Dist: pythonic-fp-fptools>=5.1.2
19
+ Requires-Dist: pythonic-fp-iterables>=5.1.2
20
+ Requires-Dist: pytest>=8.4.1 ; extra == "test"
21
+ Project-URL: Changelog, https://github.com/grscheller/pythonic-fp-containers/blob/main/CHANGELOG.rst
22
+ Project-URL: Documentation, https://grscheller.github.io/pythonic-fp/containers/development/build/html/
23
+ Project-URL: Homepage, https://grscheller.github.io/pythonic-fp/homepage/build/html/
24
+ Project-URL: Source, https://github.com/grscheller/pythonic-fp-containers
25
+ Provides-Extra: test
26
+
27
+ Pythonic FP - Containers
28
+ ========================
29
+
30
+ PyPI project
31
+ `pythonic-fp.containers
32
+ <https://pypi.org/project/pythonic-fp.containers>`_.
33
+
34
+ Python package of container like data structures with functional
35
+ programming inspired tweaks.
36
+
37
+ +---------------------------------------------+-----------+------------------+
38
+ | module | class | name |
39
+ +=============================================+===========+==================+
40
+ | ``pythonic_fp.containers.functional_tuple`` | FTuple | Functional Tuple |
41
+ +---------------------------------------------+-----------+------------------+
42
+ | ``pythonic_fp.containers.immutable_list`` | IList | Immutable List |
43
+ +---------------------------------------------+-----------+------------------+
44
+
45
+ Part of the
46
+ `pythonic-fp
47
+ <https://grscheller.github.io/pythonic-fp/homepage/build/html/index.html>`_
48
+ PyPI projects.
49
+
50
+ Documentation
51
+ -------------
52
+
53
+ Documentation for this project is hosted on
54
+ `GitHub Pages
55
+ <https://grscheller.github.io/pythonic-fp/containers/development/build/html>`_.
56
+
57
+ Copyright and License
58
+ ---------------------
59
+
60
+ Copyright (c) 2023-2025 Geoffrey R. Scheller. Licensed under the Apache
61
+ License, Version 2.0. See the LICENSE file for details.
62
+
@@ -0,0 +1,11 @@
1
+ pythonic_fp/containers/__init__.py,sha256=48lsc3kU2Yf5IKjeExlOtO2ISwZv_1SWyL-e9OtP3VI,1346
2
+ pythonic_fp/containers/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ pythonic_fp/containers/functional_tuple.py,sha256=aSFAnuAinT6XgLsRwh2JX1J6a6fFOJYS5N3TAUH5u0s,6518
4
+ pythonic_fp/containers/functional_tuple.pyi,sha256=g8IvlIhPxecEsetEexZ7WmN9Ifdh-vAeK0z9SwLiCGE,1182
5
+ pythonic_fp/containers/immutable_list.py,sha256=SoPYqIRgXbhfVC5dP9x1xYOIZqUrcIPH317A5bIPPos,7065
6
+ pythonic_fp/containers/immutable_list.pyi,sha256=HRm0Updbx9UJpl5RI0prOwimSHDUJtM-3t2hEmYz6kw,1372
7
+ pythonic_fp/containers/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ pythonic_fp_containers-4.0.0.dist-info/licenses/LICENSE,sha256=IJMkVWQUVy-SonK6SMaMotDQ6Zb-lVi2bUTiGp1GUO8,10774
9
+ pythonic_fp_containers-4.0.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
10
+ pythonic_fp_containers-4.0.0.dist-info/METADATA,sha256=sTGVrVvUqjf7KyG-FzkvpdcMrzuaWLomCDKoRjwbPDE,2438
11
+ pythonic_fp_containers-4.0.0.dist-info/RECORD,,
@@ -1,31 +0,0 @@
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
- """Three types of queues.
16
-
17
- +------------------------------------+-----------+--------------------------+
18
- | module | Class | description |
19
- +====================================+===========+==========================+
20
- | pythonic_fp.containers.queues.fifo | FIFOQueue | First-In-First-Out queue |
21
- +------------------------------------+-----------+--------------------------+
22
- | pythonic_fp.containers.queues.lifo | LIFOQueue | Last-In-First-Out queue |
23
- +------------------------------------+-----------+--------------------------+
24
- | pythonic_fp.containers.queues.de | DEQueue | Double-Ended queue |
25
- +------------------------------------+-----------+--------------------------+
26
-
27
- """
28
-
29
- __author__ = 'Geoffrey R. Scheller'
30
- __copyright__ = 'Copyright (c) 2023-2025 Geoffrey R. Scheller'
31
- __license__ = 'Apache License 2.0'
@@ -1,180 +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 Double-Ended (DE) Queue data structure.
16
-
17
- - O(1) pops each end
18
- - O(1) amortized pushes each end
19
- - O(1) length determination
20
- - will automatically increase storage capacity when needed
21
- - in a Boolean context, true if not empty, false if empty
22
- - neither indexable nor sliceable by design
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__ = ['DEQueue', 'de_queue']
33
-
34
- D = TypeVar('D')
35
-
36
-
37
- class DEQueue[D]:
38
-
39
- __slots__ = ('_ca',)
40
-
41
- def __init__(self, *dss: Iterable[D]) -> None:
42
- """Initial data in FIFO order, newest to oldest, as if
43
- pushed on from the right side.
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'DEQueue expects at most 1 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, DEQueue):
62
- return False
63
- return self._ca == other._ca
64
-
65
- def __iter__(self) -> Iterator[D]:
66
- return iter(list(self._ca))
67
-
68
- def __reversed__(self) -> Iterator[D]:
69
- return reversed(list(self._ca))
70
-
71
- def __repr__(self) -> str:
72
- if len(self) == 0:
73
- return 'DEQueue()'
74
- return 'DEQueue(' + ', '.join(map(repr, self._ca)) + ')'
75
-
76
- def __str__(self) -> str:
77
- return '>< ' + ' | '.join(map(str, self)) + ' ><'
78
-
79
- def copy(self) -> DEQueue[D]:
80
- """Shallow copy.
81
-
82
- :return: shallow copy of the DEQueue
83
-
84
- """
85
- return DEQueue(self._ca)
86
-
87
- def pushl(self, *ds: D) -> None:
88
- """Push data onto left side of DEQueue.
89
-
90
- :param ds: items to be pushed onto DEQueue from the left
91
-
92
- """
93
- self._ca.pushl(*ds)
94
-
95
- def pushr(self, *ds: D) -> None:
96
- """Push data onto right side of DEQueue.
97
-
98
- :param ds: items to be pushed onto DEQueue from the right
99
-
100
- """
101
- self._ca.pushr(*ds)
102
-
103
- def popl(self) -> MayBe[D]:
104
- """Pop next data item from left side DEQueue, if it exists.
105
-
106
- :return: MayBe of popped item if queue was not empty, empty MayBe otherwise
107
-
108
- """
109
- if self._ca:
110
- return MayBe(self._ca.popl())
111
- return MayBe()
112
-
113
- def popr(self) -> MayBe[D]:
114
- """Pop next item off right side DEQueue.
115
-
116
- :return: MayBe of popped item if queue was not empty, empty MayBe otherwise
117
-
118
- """
119
- if self._ca:
120
- return MayBe(self._ca.popr())
121
- return MayBe()
122
-
123
- def peakl(self) -> MayBe[D]:
124
- """Peak at data on left side of DEQueue.
125
-
126
- :return: MayBe of leftmost data if queue not empty, empty MayBe otherwise
127
-
128
- """
129
- if self._ca:
130
- return MayBe(self._ca[0])
131
- return MayBe()
132
-
133
- def peakr(self) -> MayBe[D]:
134
- """Peak at right side of DEQueue. Does not consume item.
135
-
136
- :return: MayBe of rightmost data if queue not empty, empty MayBe otherwise
137
-
138
- """
139
- if self._ca:
140
- return MayBe(self._ca[-1])
141
- return MayBe()
142
-
143
- def foldl[L](self, f: Callable[[L, D], L], start: L | None = None) -> MayBe[L]:
144
- """Reduces DEQueue left to right.
145
-
146
- :param f: reducing function, first argument is for accumulator
147
- :param start: optional starting value
148
- :return: MayBe of reduced value with f, empty MayBe if queue empty and no starting value given
149
-
150
- """
151
- if start is None:
152
- if not self._ca:
153
- return MayBe()
154
- return MayBe(self._ca.foldl(f, start))
155
-
156
- def foldr[R](self, f: Callable[[D, R], R], start: R | None = None) -> MayBe[R]:
157
- """Reduces DEQueue right to left.
158
-
159
- :param f: reducing function, second argument is for accumulator
160
- :param start: optional starting value
161
- :return: MayBe of reduced value with f, empty MayBe if queue empty and no starting value given
162
-
163
- """
164
- if start is None:
165
- if not self._ca:
166
- return MayBe()
167
- return MayBe(self._ca.foldr(f, start))
168
-
169
- def map[U](self, f: Callable[[D], U]) -> DEQueue[U]:
170
- """Map f over the DEQueue left to right, retain original order.
171
-
172
- :return: new DEQueue instance
173
-
174
- """
175
- return DEQueue(map(f, self._ca))
176
-
177
-
178
- def de_queue[D](*ds: D) -> DEQueue[D]:
179
- """DEQueue factory function."""
180
- return DEQueue(ds)
@@ -1,145 +0,0 @@
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)
@@ -1,136 +0,0 @@
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)
@@ -1,63 +0,0 @@
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
-
@@ -1,13 +0,0 @@
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,,