pythonic-fp-gadgets 4.0.2__py3-none-any.whl → 4.0.3__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/gadgets/__init__.py +16 -13
- pythonic_fp/gadgets/box.py +62 -28
- pythonic_fp/gadgets/sentinels/__init__.py +2 -2
- pythonic_fp/gadgets/sentinels/flavored.py +17 -9
- pythonic_fp/gadgets/sentinels/novalue.py +8 -3
- pythonic_fp/gadgets/wrap.py +78 -18
- {pythonic_fp_gadgets-4.0.2.dist-info → pythonic_fp_gadgets-4.0.3.dist-info}/METADATA +11 -9
- pythonic_fp_gadgets-4.0.3.dist-info/RECORD +17 -0
- pythonic_fp_gadgets-4.0.2.dist-info/RECORD +0 -17
- {pythonic_fp_gadgets-4.0.2.dist-info → pythonic_fp_gadgets-4.0.3.dist-info}/WHEEL +0 -0
- {pythonic_fp_gadgets-4.0.2.dist-info → pythonic_fp_gadgets-4.0.3.dist-info}/licenses/LICENSE +0 -0
pythonic_fp/gadgets/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2023-
|
|
1
|
+
# Copyright 2023-2026 Geoffrey R. Scheller
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -21,7 +21,7 @@ Gadgets
|
|
|
21
21
|
- Functions and classes which could go multiple places or have
|
|
22
22
|
no good place to go.
|
|
23
23
|
- Self-contained with minimal dependencies.
|
|
24
|
-
- No pythonic_fp dependencies.
|
|
24
|
+
- No pythonic_fp dependencies at all.
|
|
25
25
|
|
|
26
26
|
"""
|
|
27
27
|
|
|
@@ -31,13 +31,16 @@ from inspect import getmro
|
|
|
31
31
|
__all__ = ['first_common_ancestor', 'iterate_over_arguments']
|
|
32
32
|
|
|
33
33
|
__author__ = 'Geoffrey R. Scheller'
|
|
34
|
-
__copyright__ = 'Copyright (c) 2023-
|
|
34
|
+
__copyright__ = 'Copyright (c) 2023-2026 Geoffrey R. Scheller'
|
|
35
35
|
__license__ = 'Apache License 2.0'
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
def first_common_ancestor(cls1: type, cls2: type) -> type:
|
|
39
|
-
"""
|
|
40
|
-
|
|
39
|
+
"""
|
|
40
|
+
.. admonition:: Least upper bound
|
|
41
|
+
|
|
42
|
+
Find the least upper bound in the inheritance graph
|
|
43
|
+
of two classes.
|
|
41
44
|
|
|
42
45
|
.. warning::
|
|
43
46
|
|
|
@@ -52,9 +55,9 @@ def first_common_ancestor(cls1: type, cls2: type) -> type:
|
|
|
52
55
|
|
|
53
56
|
:param cls1: A class in the inheritance hierarchy.
|
|
54
57
|
:param cls2: A class in the inheritance hierarchy.
|
|
55
|
-
:returns: First common ancestor based on getmro order.
|
|
56
|
-
:raises TypeError: Raised when no common ancestor or
|
|
57
|
-
caught when raised by ``inspect.getmro``.
|
|
58
|
+
:returns: First common ancestor based on ``getmro`` order.
|
|
59
|
+
:raises TypeError: Raised when no common ancestor exists, or when
|
|
60
|
+
not caught when raised by ``inspect.getmro``.
|
|
58
61
|
|
|
59
62
|
"""
|
|
60
63
|
if issubclass(cls1, cls2):
|
|
@@ -69,14 +72,14 @@ def first_common_ancestor(cls1: type, cls2: type) -> type:
|
|
|
69
72
|
|
|
70
73
|
|
|
71
74
|
def iterate_over_arguments[A](*args: A) -> Iterator[A]:
|
|
72
|
-
"""
|
|
75
|
+
"""
|
|
76
|
+
.. admonition:: Iterate over arguments.
|
|
73
77
|
|
|
74
|
-
|
|
78
|
+
Function returning an iterator over its arguments.
|
|
75
79
|
|
|
76
|
-
|
|
80
|
+
.. note::
|
|
77
81
|
|
|
78
|
-
|
|
79
|
-
- maybe in the C world
|
|
82
|
+
Does not create a Python object to iterate over.
|
|
80
83
|
|
|
81
84
|
:param args: Objects to iterate over.
|
|
82
85
|
:returns: An iterator of the functions arguments.
|
pythonic_fp/gadgets/box.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2023-
|
|
1
|
+
# Copyright 2023-2026 Geoffrey R. Scheller
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -11,6 +11,22 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
+
"""
|
|
15
|
+
.. admonition:: Module box
|
|
16
|
+
|
|
17
|
+
Containers holding at most one item of a given type.
|
|
18
|
+
|
|
19
|
+
.. tip::
|
|
20
|
+
|
|
21
|
+
Objects of the ``Box`` type
|
|
22
|
+
|
|
23
|
+
- Are truthy if not empty.
|
|
24
|
+
- Can be combined with other iterators before being filled.
|
|
25
|
+
- Can be used like a promise.
|
|
26
|
+
- Threadsafe
|
|
27
|
+
- Can be used in Python match statements.
|
|
28
|
+
|
|
29
|
+
"""
|
|
14
30
|
|
|
15
31
|
__all__ = ['Box']
|
|
16
32
|
|
|
@@ -22,19 +38,10 @@ _sentinel: Final[_Sentinel] = object()
|
|
|
22
38
|
|
|
23
39
|
|
|
24
40
|
class Box[T]:
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
.. note::
|
|
28
|
-
|
|
29
|
-
- ``Box(item: T)``: contains at one item of type ``T``
|
|
30
|
-
- ``Box[T]()``: creates empty container
|
|
31
|
-
|
|
32
|
-
Where type ``T`` is some definite type, which
|
|
33
|
-
could be ``None`` or even ``Never``.
|
|
34
|
-
|
|
35
|
-
.. tip ::
|
|
41
|
+
"""
|
|
42
|
+
.. admonition:: Box
|
|
36
43
|
|
|
37
|
-
|
|
44
|
+
Container holding at most one item of a given type.
|
|
38
45
|
|
|
39
46
|
"""
|
|
40
47
|
__slots__ = ('_item',)
|
|
@@ -47,8 +54,8 @@ class Box[T]:
|
|
|
47
54
|
|
|
48
55
|
def __init__(self, item: T | _Sentinel = _sentinel) -> None:
|
|
49
56
|
"""
|
|
50
|
-
:param item: An optional initial
|
|
51
|
-
|
|
57
|
+
:param item: An optional initial ``item`` for the ``Box``.
|
|
58
|
+
|
|
52
59
|
"""
|
|
53
60
|
self._item = item
|
|
54
61
|
|
|
@@ -69,12 +76,15 @@ class Box[T]:
|
|
|
69
76
|
|
|
70
77
|
def __eq__(self, other: object) -> bool:
|
|
71
78
|
"""
|
|
72
|
-
|
|
79
|
+
.. admonition:: Equality comparison
|
|
80
|
+
|
|
81
|
+
Efficiently compare ``Box`` to another object.
|
|
73
82
|
|
|
74
83
|
:param other: The object to be compared with,
|
|
75
84
|
:returns: ``True`` if ``other`` is of type Box and contains
|
|
76
85
|
an object which compares as equal to the object
|
|
77
86
|
contained in the ``Box``, otherwise ``False``.
|
|
87
|
+
|
|
78
88
|
"""
|
|
79
89
|
if not isinstance(other, type(self)):
|
|
80
90
|
return False
|
|
@@ -91,10 +101,13 @@ class Box[T]:
|
|
|
91
101
|
def get(self, alt: T) -> T: ...
|
|
92
102
|
|
|
93
103
|
def get(self, alt: T | _Sentinel = _sentinel) -> T:
|
|
94
|
-
"""
|
|
95
|
-
|
|
104
|
+
"""
|
|
105
|
+
.. admonition:: Get
|
|
106
|
+
|
|
107
|
+
Return the boxed item, if it exists, otherwise
|
|
108
|
+
an alternate item, if given.
|
|
96
109
|
|
|
97
|
-
:param alt: An optional item of type ``T`` to return
|
|
110
|
+
:param alt: An optional item of type ``T`` to return
|
|
98
111
|
if the ``Box`` is empty.
|
|
99
112
|
:returns: Contents of ``Box`` or an alternate item, if given,
|
|
100
113
|
when the ``Box`` is empty.
|
|
@@ -110,7 +123,10 @@ class Box[T]:
|
|
|
110
123
|
return cast(T, alt)
|
|
111
124
|
|
|
112
125
|
def pop(self) -> T:
|
|
113
|
-
"""
|
|
126
|
+
"""
|
|
127
|
+
.. admonition:: Pop
|
|
128
|
+
|
|
129
|
+
Pop item from ``Box`` if not empty.
|
|
114
130
|
|
|
115
131
|
:returns: The item contained in the ``Box``.
|
|
116
132
|
:raises ValueError: If Box is empty.
|
|
@@ -124,7 +140,10 @@ class Box[T]:
|
|
|
124
140
|
return popped
|
|
125
141
|
|
|
126
142
|
def push(self, item: T) -> None:
|
|
127
|
-
"""
|
|
143
|
+
"""
|
|
144
|
+
.. admonition:: Push
|
|
145
|
+
|
|
146
|
+
Push an item into ``Box`` if empty.
|
|
128
147
|
|
|
129
148
|
:param item: Item to push into the empty ``Box``.
|
|
130
149
|
:raises ValueError: If ``Box`` is not empty.
|
|
@@ -138,15 +157,24 @@ class Box[T]:
|
|
|
138
157
|
return None
|
|
139
158
|
|
|
140
159
|
def put(self, item: T) -> None:
|
|
141
|
-
"""
|
|
160
|
+
"""
|
|
161
|
+
.. admonition:: Put
|
|
162
|
+
|
|
163
|
+
Put an item in the Box. Discard any previous contents.
|
|
164
|
+
|
|
165
|
+
"""
|
|
142
166
|
self._item = item
|
|
143
167
|
|
|
144
168
|
def exchange(self, new_item: T) -> T:
|
|
145
|
-
"""
|
|
169
|
+
"""
|
|
170
|
+
.. admonition:: Exchange
|
|
171
|
+
|
|
172
|
+
Exchange an item with what is in the Box.
|
|
146
173
|
|
|
147
174
|
:param ``new_item``: New item to exchange for current item.
|
|
148
175
|
:returns: Original contents of the ``Box``.
|
|
149
176
|
:raises ValueError: If Box is empty.
|
|
177
|
+
|
|
150
178
|
"""
|
|
151
179
|
if self._item is _sentinel:
|
|
152
180
|
msg = 'Box: Trying to exchange items from an empty Box'
|
|
@@ -156,11 +184,14 @@ class Box[T]:
|
|
|
156
184
|
return popped
|
|
157
185
|
|
|
158
186
|
def map[U](self, f: Callable[[T], U]) -> 'Box[U]':
|
|
159
|
-
"""
|
|
160
|
-
|
|
187
|
+
"""
|
|
188
|
+
.. admonition:: Map
|
|
189
|
+
|
|
190
|
+
Map function ``f`` over contents. We need to return a new
|
|
191
|
+
instance since the type of Box can change.
|
|
161
192
|
|
|
162
193
|
:param f: Mapping function.
|
|
163
|
-
:returns:
|
|
194
|
+
:returns: New instance.
|
|
164
195
|
|
|
165
196
|
"""
|
|
166
197
|
if self._item is _sentinel:
|
|
@@ -168,10 +199,13 @@ class Box[T]:
|
|
|
168
199
|
return Box(f(cast(T, self._item)))
|
|
169
200
|
|
|
170
201
|
def bind[U](self, f: Callable[[T], 'Box[U]']) -> 'Box[U]':
|
|
171
|
-
"""
|
|
202
|
+
"""
|
|
203
|
+
.. admonition:: Bind
|
|
204
|
+
|
|
205
|
+
Flatmap ``Box`` with function ``f``.
|
|
172
206
|
|
|
173
207
|
:param f: Binding function.
|
|
174
|
-
:returns:
|
|
208
|
+
:returns: New instance.
|
|
175
209
|
|
|
176
210
|
"""
|
|
177
211
|
if self._item is _sentinel:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2023-
|
|
1
|
+
# Copyright 2023-2026 Geoffrey R. Scheller
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -13,5 +13,5 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
__author__ = 'Geoffrey R. Scheller'
|
|
16
|
-
__copyright__ = 'Copyright (c) 2023-
|
|
16
|
+
__copyright__ = 'Copyright (c) 2023-2026 Geoffrey R. Scheller'
|
|
17
17
|
__license__ = 'Apache License 2.0'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2023-
|
|
1
|
+
# Copyright 2023-2026 Geoffrey R. Scheller
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -13,26 +13,27 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
"""
|
|
16
|
-
.. admonition::
|
|
16
|
+
.. admonition:: When different flavors of the truth are needed.
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Sentinel values labeled by different (hashable) flavors.
|
|
19
|
+
Threadsafe.
|
|
19
20
|
|
|
20
|
-
..
|
|
21
|
+
.. tip::
|
|
21
22
|
|
|
22
23
|
Can be compared using ``==`` and ``!=``. A flavored sentinel
|
|
23
24
|
value always equals itself and never equals anything else,
|
|
24
25
|
especially other flavored sentinel values.
|
|
25
26
|
|
|
27
|
+
.. tip::
|
|
28
|
+
|
|
26
29
|
Useful for union types where ``Sentinel[H]`` is one of the
|
|
27
30
|
types making up the union.
|
|
28
31
|
|
|
32
|
+
.. tip::
|
|
33
|
+
|
|
29
34
|
To ensure that reference equality is used, put the known
|
|
30
35
|
sentinel value first in the comparison.
|
|
31
36
|
|
|
32
|
-
.. note::
|
|
33
|
-
|
|
34
|
-
Threadsafe.
|
|
35
|
-
|
|
36
37
|
"""
|
|
37
38
|
|
|
38
39
|
import threading
|
|
@@ -60,6 +61,7 @@ class Sentinel[H: Hashable]:
|
|
|
60
61
|
:param flavor: Some Hashable value of generic type ``H``.
|
|
61
62
|
:returns: The ``Sentinel`` singleton instance with flavor ``flavor``.
|
|
62
63
|
:rtype: ``Sentinel[H]`` where ``H`` is a subtype of Hashable.
|
|
64
|
+
|
|
63
65
|
"""
|
|
64
66
|
if not hasattr(self, '_flavor'):
|
|
65
67
|
self._flavor = flavor
|
|
@@ -69,6 +71,12 @@ class Sentinel[H: Hashable]:
|
|
|
69
71
|
|
|
70
72
|
def flavor(self) -> H:
|
|
71
73
|
"""
|
|
72
|
-
|
|
74
|
+
.. admonition:: Get flavor
|
|
75
|
+
|
|
76
|
+
Get the sentinel's flavor, a hashable
|
|
77
|
+
value of type ``H``.
|
|
78
|
+
|
|
79
|
+
:returns: The sentinel's flavor.
|
|
80
|
+
|
|
73
81
|
"""
|
|
74
82
|
return self._flavor
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2023-
|
|
1
|
+
# Copyright 2023-2026 Geoffrey R. Scheller
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
"""
|
|
16
|
-
.. admonition::
|
|
17
|
-
|
|
16
|
+
.. admonition:: Missing value.
|
|
17
|
+
|
|
18
|
+
Singleton class representing an actually,
|
|
19
|
+
not potentially, missing value.
|
|
18
20
|
|
|
19
21
|
``NoValue()`` is a singleton object representing a missing value.
|
|
20
22
|
|
|
@@ -100,6 +102,7 @@ class NoValue():
|
|
|
100
102
|
def __new__(cls) -> 'NoValue':
|
|
101
103
|
"""
|
|
102
104
|
:returns: The ``NoValue`` singleton instance.
|
|
105
|
+
|
|
103
106
|
"""
|
|
104
107
|
if cls._instance is None:
|
|
105
108
|
with cls._lock:
|
|
@@ -113,11 +116,13 @@ class NoValue():
|
|
|
113
116
|
def __eq__(self, other: object) -> bool:
|
|
114
117
|
"""
|
|
115
118
|
:returns: ``False``
|
|
119
|
+
|
|
116
120
|
"""
|
|
117
121
|
return False
|
|
118
122
|
|
|
119
123
|
def __ne__(self, other: object) -> bool:
|
|
120
124
|
"""
|
|
121
125
|
:returns: ``False``
|
|
126
|
+
|
|
122
127
|
"""
|
|
123
128
|
return False
|
pythonic_fp/gadgets/wrap.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2023-
|
|
1
|
+
# Copyright 2023-2026 Geoffrey R. Scheller
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -11,6 +11,15 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
+
"""
|
|
15
|
+
.. admonition:: Module wrap
|
|
16
|
+
|
|
17
|
+
Wrap objects in ways to make them "immutable."
|
|
18
|
+
|
|
19
|
+
- Class ``Wrap``: wrap an object
|
|
20
|
+
- Class ``HWrap``: wrap a hashable object
|
|
21
|
+
|
|
22
|
+
"""
|
|
14
23
|
|
|
15
24
|
__all__ = ['Wrap', 'HWrap']
|
|
16
25
|
|
|
@@ -18,23 +27,38 @@ from collections.abc import Callable, Iterator, Hashable
|
|
|
18
27
|
|
|
19
28
|
|
|
20
29
|
class Wrap[T]():
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
.. tip::
|
|
30
|
+
"""
|
|
31
|
+
.. admonition:: Wrap object
|
|
24
32
|
|
|
25
|
-
|
|
33
|
+
Immutablely wrap exactly one value of a given type.
|
|
26
34
|
|
|
27
35
|
"""
|
|
28
36
|
__slots__ = ('_item',)
|
|
29
37
|
__match_args__ = ('_item',)
|
|
30
38
|
|
|
31
39
|
def __init__(self, item: T) -> None:
|
|
40
|
+
"""
|
|
41
|
+
:param item: Item to be wrapped.
|
|
42
|
+
|
|
43
|
+
"""
|
|
32
44
|
self._item = item
|
|
33
45
|
|
|
34
46
|
def __bool__(self) -> bool:
|
|
47
|
+
"""
|
|
48
|
+
.. admonition:: Bool
|
|
49
|
+
|
|
50
|
+
Truthiness same as wrapped object.
|
|
51
|
+
|
|
52
|
+
"""
|
|
35
53
|
return bool(self._item)
|
|
36
54
|
|
|
37
55
|
def __iter__(self) -> Iterator[T]:
|
|
56
|
+
"""
|
|
57
|
+
.. admonition:: Iter
|
|
58
|
+
|
|
59
|
+
Iterable, iterates wrapped item.
|
|
60
|
+
|
|
61
|
+
"""
|
|
38
62
|
if self:
|
|
39
63
|
yield self._item
|
|
40
64
|
|
|
@@ -43,12 +67,15 @@ class Wrap[T]():
|
|
|
43
67
|
|
|
44
68
|
def __eq__(self, other: object) -> bool:
|
|
45
69
|
"""
|
|
46
|
-
|
|
70
|
+
.. admonition:: Equality comparison
|
|
71
|
+
|
|
72
|
+
Efficiently compare ``Wrap`` to another object.
|
|
47
73
|
|
|
48
74
|
:param other: The object to be compared with,
|
|
49
75
|
:returns: ``True`` if ``other`` is of type Wrap and wraps
|
|
50
76
|
an object which compares as equal to the wrapped
|
|
51
77
|
object, otherwise ``False``.
|
|
78
|
+
|
|
52
79
|
"""
|
|
53
80
|
if not isinstance(other, type(self)):
|
|
54
81
|
return False
|
|
@@ -58,26 +85,37 @@ class Wrap[T]():
|
|
|
58
85
|
return self._item == other._item
|
|
59
86
|
|
|
60
87
|
def map[U](self, f: Callable[[T], U]) -> 'Wrap[U]':
|
|
61
|
-
"""
|
|
88
|
+
"""
|
|
89
|
+
.. admonition:: Map
|
|
90
|
+
|
|
91
|
+
Map function ``f`` over contents.
|
|
92
|
+
|
|
93
|
+
Map function ``f`` over contents.
|
|
62
94
|
|
|
63
95
|
:param f: Mapping function.
|
|
64
|
-
:returns:
|
|
96
|
+
:returns: New instance.
|
|
65
97
|
|
|
66
98
|
"""
|
|
67
99
|
return Wrap(f(self._item))
|
|
68
100
|
|
|
69
101
|
def bind[U](self, f: Callable[[T], 'Wrap[U]']) -> 'Wrap[U]':
|
|
70
|
-
"""
|
|
102
|
+
"""
|
|
103
|
+
.. admonition:: Bind
|
|
104
|
+
|
|
105
|
+
Flatmap wrapped object with function ``f``.
|
|
71
106
|
|
|
72
107
|
:param f: Binding function.
|
|
73
|
-
:returns:
|
|
108
|
+
:returns: New instance.
|
|
74
109
|
|
|
75
110
|
"""
|
|
76
111
|
return f(self._item)
|
|
77
112
|
|
|
78
113
|
|
|
79
114
|
class HWrap[T: Hashable](Hashable):
|
|
80
|
-
"""
|
|
115
|
+
"""
|
|
116
|
+
.. admonition:: Wrap hashable object
|
|
117
|
+
|
|
118
|
+
Immutablely wrap exactly one value of a given hashable type.
|
|
81
119
|
|
|
82
120
|
.. tip::
|
|
83
121
|
|
|
@@ -85,7 +123,7 @@ class HWrap[T: Hashable](Hashable):
|
|
|
85
123
|
|
|
86
124
|
.. tip::
|
|
87
125
|
|
|
88
|
-
``HWrap`` objects are hashable
|
|
126
|
+
``HWrap`` objects are hashable.
|
|
89
127
|
|
|
90
128
|
"""
|
|
91
129
|
__slots__ = ('_item', '_hash')
|
|
@@ -98,9 +136,21 @@ class HWrap[T: Hashable](Hashable):
|
|
|
98
136
|
return self._hash
|
|
99
137
|
|
|
100
138
|
def __bool__(self) -> bool:
|
|
139
|
+
"""
|
|
140
|
+
.. admonition:: Bool
|
|
141
|
+
|
|
142
|
+
Truthiness same as wrapped object.
|
|
143
|
+
|
|
144
|
+
"""
|
|
101
145
|
return bool(self._item)
|
|
102
146
|
|
|
103
147
|
def __iter__(self) -> Iterator[T]:
|
|
148
|
+
"""
|
|
149
|
+
.. admonition:: Iter
|
|
150
|
+
|
|
151
|
+
Iterable, iterates wrapped item.
|
|
152
|
+
|
|
153
|
+
"""
|
|
104
154
|
if self:
|
|
105
155
|
yield self._item
|
|
106
156
|
|
|
@@ -109,12 +159,15 @@ class HWrap[T: Hashable](Hashable):
|
|
|
109
159
|
|
|
110
160
|
def __eq__(self, other: object) -> bool:
|
|
111
161
|
"""
|
|
112
|
-
|
|
162
|
+
.. admonition:: Equality comparison
|
|
113
163
|
|
|
114
|
-
|
|
164
|
+
Efficiently compare to another object.
|
|
165
|
+
|
|
166
|
+
:param other: Object to be compared
|
|
115
167
|
:returns: ``True`` if ``other`` is of type HWrap and wraps
|
|
116
168
|
an object which compares as equal to the wrapped
|
|
117
169
|
object, otherwise ``False``.
|
|
170
|
+
|
|
118
171
|
"""
|
|
119
172
|
if not isinstance(other, type(self)):
|
|
120
173
|
return False
|
|
@@ -126,19 +179,26 @@ class HWrap[T: Hashable](Hashable):
|
|
|
126
179
|
return self._item == other._item
|
|
127
180
|
|
|
128
181
|
def map[U](self, f: Callable[[T], U]) -> 'HWrap[U]':
|
|
129
|
-
"""
|
|
182
|
+
"""
|
|
183
|
+
.. admonition:: Map
|
|
184
|
+
|
|
185
|
+
Map function ``f`` over wrapped the wrapped object
|
|
186
|
+
returning a new ``HWrap`` instance.
|
|
130
187
|
|
|
131
188
|
:param f: Mapping function.
|
|
132
|
-
:returns:
|
|
189
|
+
:returns: New instance.
|
|
133
190
|
|
|
134
191
|
"""
|
|
135
192
|
return HWrap(f(self._item))
|
|
136
193
|
|
|
137
194
|
def bind[U](self, f: Callable[[T], 'HWrap[U]']) -> 'HWrap[U]':
|
|
138
|
-
"""
|
|
195
|
+
"""
|
|
196
|
+
.. admonition:: Bind
|
|
197
|
+
|
|
198
|
+
Flatmap ``Box`` with function ``f``.
|
|
139
199
|
|
|
140
200
|
:param f: Binding function.
|
|
141
|
-
:returns:
|
|
201
|
+
:returns: New instance.
|
|
142
202
|
|
|
143
203
|
"""
|
|
144
204
|
return f(self._item)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonic-fp-gadgets
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.3
|
|
4
4
|
Summary: Gadgets
|
|
5
5
|
Keywords: gadgets
|
|
6
6
|
Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
|
|
@@ -11,14 +11,14 @@ Classifier: Framework :: Pytest
|
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
13
|
Classifier: Operating System :: OS Independent
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
15
|
Classifier: Typing :: Typed
|
|
16
16
|
License-File: LICENSE
|
|
17
17
|
Requires-Dist: pytest>=8.4.1 ; extra == "test"
|
|
18
|
-
Requires-Dist: pythonic-fp-circulararray>=6.0.
|
|
18
|
+
Requires-Dist: pythonic-fp-circulararray>=6.0.4 ; extra == "test"
|
|
19
19
|
Project-URL: Changelog, https://github.com/grscheller/pythonic-fp-gadgets/blob/main/CHANGELOG.rst
|
|
20
|
-
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp
|
|
21
|
-
Project-URL: Homepage, https://grscheller.github.io/pythonic-fp/homepage/
|
|
20
|
+
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp-gadgets/
|
|
21
|
+
Project-URL: Homepage, https://grscheller.github.io/pythonic-fp/homepage/html/
|
|
22
22
|
Project-URL: Source, https://github.com/grscheller/pythonic-fp-gadgets
|
|
23
23
|
Provides-Extra: test
|
|
24
24
|
|
|
@@ -27,7 +27,7 @@ Pythonic FP - Gadgets
|
|
|
27
27
|
|
|
28
28
|
PyPI project
|
|
29
29
|
`pythonic-fp-gadgets
|
|
30
|
-
<https://pypi.org/project/pythonic-fp>`_.
|
|
30
|
+
<https://pypi.org/project/pythonic-fp-gadgets>`_.
|
|
31
31
|
|
|
32
32
|
Library of simple, but useful, classes and functions with no dependencies
|
|
33
33
|
outside the Python Standard Library.
|
|
@@ -35,12 +35,14 @@ outside the Python Standard Library.
|
|
|
35
35
|
- Gadgets
|
|
36
36
|
|
|
37
37
|
- single item box
|
|
38
|
+
- wrapped (hashable) references
|
|
38
39
|
- function returning iterator of its arguments
|
|
39
40
|
- function to find the latest common ancestor of two classes
|
|
41
|
+
- sentinel values
|
|
40
42
|
|
|
41
43
|
Part of the
|
|
42
44
|
`pythonic-fp
|
|
43
|
-
<https://grscheller.github.io/pythonic-fp
|
|
45
|
+
<https://grscheller.github.io/pythonic-fp/homepage/html/>`_
|
|
44
46
|
PyPI projects.
|
|
45
47
|
|
|
46
48
|
Documentation
|
|
@@ -48,11 +50,11 @@ Documentation
|
|
|
48
50
|
|
|
49
51
|
Documentation for this project is hosted on
|
|
50
52
|
`GitHub Pages
|
|
51
|
-
<https://grscheller.github.io/pythonic-fp
|
|
53
|
+
<https://grscheller.github.io/pythonic-fp-gadgets>`_.
|
|
52
54
|
|
|
53
55
|
Copyright and License
|
|
54
56
|
---------------------
|
|
55
57
|
|
|
56
|
-
Copyright (c) 2025 Geoffrey R. Scheller. Licensed under the Apache
|
|
58
|
+
Copyright (c) 2025-2026 Geoffrey R. Scheller. Licensed under the Apache
|
|
57
59
|
License, Version 2.0. See the LICENSE file for details.
|
|
58
60
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
pythonic_fp/gadgets/__init__.py,sha256=29fNYn8LLhCUfvZ_vcofSoGhs9_Hw95cPF3sd0G1z9M,2739
|
|
2
|
+
pythonic_fp/gadgets/__init__.pyi,sha256=cAf10WX2ZHMs-Tf93mImmDdZw7lNEmwjorkiVF04s_E,224
|
|
3
|
+
pythonic_fp/gadgets/box.py,sha256=2PL0wkimRwgsL15aTkB2QTo4B40IY6G0lCo0Esa0CTU,5860
|
|
4
|
+
pythonic_fp/gadgets/box.pyi,sha256=UkJBY8Xiv4oe70Q3bhJL1jGHwYmFWwkTIdxvf2zR3v4,837
|
|
5
|
+
pythonic_fp/gadgets/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
pythonic_fp/gadgets/wrap.py,sha256=EVhp115DcokaBa-0xN3CN3Qlr4vkHvEZQIPuMYYxHas,4950
|
|
7
|
+
pythonic_fp/gadgets/wrap.pyi,sha256=TRbBph_517VNCKchwU-pdtPCn73c09gq8EchDFM7Uyo,868
|
|
8
|
+
pythonic_fp/gadgets/sentinels/__init__.py,sha256=WsGXds578j1y7FsLEGer59P1c8EItUBTp60cuA0T56E,724
|
|
9
|
+
pythonic_fp/gadgets/sentinels/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
pythonic_fp/gadgets/sentinels/flavored.py,sha256=W33020r3U8jxIDdJDaa-xE7o4cI1NY0bLZi8puIUBqA,2353
|
|
11
|
+
pythonic_fp/gadgets/sentinels/flavored.pyi,sha256=K3YSyXLyylPII2ahRP0olhzLJruOeR73TFJDC6rCUwk,212
|
|
12
|
+
pythonic_fp/gadgets/sentinels/novalue.py,sha256=9wya9NBDQzfr2rxwrCd7uh74Trm4iVsK53UAUZo5Iyk,3613
|
|
13
|
+
pythonic_fp/gadgets/sentinels/novalue.pyi,sha256=zhZdePpWoQ3ZXOq7xQWGR0A4W_XzasdBQKcoywXZEZI,173
|
|
14
|
+
pythonic_fp_gadgets-4.0.3.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
|
15
|
+
pythonic_fp_gadgets-4.0.3.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
16
|
+
pythonic_fp_gadgets-4.0.3.dist-info/METADATA,sha256=z6LP-dUh7AgGHq-TTbn3qS0QyIDc-5GNohGM23QXMzU,1868
|
|
17
|
+
pythonic_fp_gadgets-4.0.3.dist-info/RECORD,,
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
pythonic_fp/gadgets/__init__.py,sha256=iEm1j8DIchgQvH4Tom6IyMeYz8i3LSAXBBBtz_gPykE,2673
|
|
2
|
-
pythonic_fp/gadgets/__init__.pyi,sha256=cAf10WX2ZHMs-Tf93mImmDdZw7lNEmwjorkiVF04s_E,224
|
|
3
|
-
pythonic_fp/gadgets/box.py,sha256=cmHe2kZhEMNtH51xlpdyd68SV-bBXxC6gRH8N0W6TqE,5443
|
|
4
|
-
pythonic_fp/gadgets/box.pyi,sha256=UkJBY8Xiv4oe70Q3bhJL1jGHwYmFWwkTIdxvf2zR3v4,837
|
|
5
|
-
pythonic_fp/gadgets/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
pythonic_fp/gadgets/wrap.py,sha256=x3mvawiRh9G3vMq3ub5Va6qBJgKcvkCKvWeR6pWrJBw,3929
|
|
7
|
-
pythonic_fp/gadgets/wrap.pyi,sha256=TRbBph_517VNCKchwU-pdtPCn73c09gq8EchDFM7Uyo,868
|
|
8
|
-
pythonic_fp/gadgets/sentinels/__init__.py,sha256=81T-HGQ9BgXj57PO_pduo8MpSQpRgZwT8LIXwODhHkI,724
|
|
9
|
-
pythonic_fp/gadgets/sentinels/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
pythonic_fp/gadgets/sentinels/flavored.py,sha256=sXgjhBPHEulH9C3Yc09PwAPDscfRH2vKj5JldgS6Sak,2260
|
|
11
|
-
pythonic_fp/gadgets/sentinels/flavored.pyi,sha256=K3YSyXLyylPII2ahRP0olhzLJruOeR73TFJDC6rCUwk,212
|
|
12
|
-
pythonic_fp/gadgets/sentinels/novalue.py,sha256=UbyQ4AVTdM2flLmF2WE0sh7_dErPQiTeM8U6GdWaHN4,3602
|
|
13
|
-
pythonic_fp/gadgets/sentinels/novalue.pyi,sha256=zhZdePpWoQ3ZXOq7xQWGR0A4W_XzasdBQKcoywXZEZI,173
|
|
14
|
-
pythonic_fp_gadgets-4.0.2.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
|
15
|
-
pythonic_fp_gadgets-4.0.2.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
16
|
-
pythonic_fp_gadgets-4.0.2.dist-info/METADATA,sha256=GsyqIcxiofODEI34GKxzySI-FBv-pkwxaEhnTfZzcOs,1815
|
|
17
|
-
pythonic_fp_gadgets-4.0.2.dist-info/RECORD,,
|
|
File without changes
|
{pythonic_fp_gadgets-4.0.2.dist-info → pythonic_fp_gadgets-4.0.3.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|