pythonic-fp-gadgets 4.0.4__tar.gz → 4.1.0__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.
Files changed (49) hide show
  1. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/.github/workflows/static.yml +2 -2
  2. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/CHANGELOG.rst +10 -3
  3. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/PKG-INFO +2 -2
  4. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/Makefile +2 -2
  5. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/pyproject.toml +2 -2
  6. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/src/pythonic_fp/gadgets/__init__.py +1 -0
  7. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/src/pythonic_fp/gadgets/box.py +43 -27
  8. pythonic_fp_gadgets-4.1.0/src/pythonic_fp/gadgets/wrap.py +270 -0
  9. pythonic_fp_gadgets-4.0.4/src/pythonic_fp/gadgets/wrap.py +0 -219
  10. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/.gitignore +0 -0
  11. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/LICENSE +0 -0
  12. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/README.rst +0 -0
  13. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/gen_conf.py +0 -0
  14. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/requirements.txt +0 -0
  15. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/_static/.gitkeep +0 -0
  16. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/_static/custom.css +0 -0
  17. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/_templates/.gitkeep +0 -0
  18. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/api/box.rst +0 -0
  19. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/api/functions/first_common_ancestor.rst +0 -0
  20. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/api/functions/index.rst +0 -0
  21. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/api/functions/iterate_over_arguments.rst +0 -0
  22. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/api/index.rst +0 -0
  23. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/api/sentinels/flavored.rst +0 -0
  24. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/api/sentinels/index.rst +0 -0
  25. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/api/sentinels/novalue.rst +0 -0
  26. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/api/wrap.rst +0 -0
  27. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/changelog.rst +0 -0
  28. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/description.rst +0 -0
  29. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/index.rst +0 -0
  30. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/releases.rst +0 -0
  31. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/docs/source/usage.rst +0 -0
  32. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/src/pythonic_fp/gadgets/__init__.pyi +0 -0
  33. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/src/pythonic_fp/gadgets/box.pyi +0 -0
  34. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/src/pythonic_fp/gadgets/py.typed +0 -0
  35. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/src/pythonic_fp/gadgets/sentinels/__init__.py +0 -0
  36. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/src/pythonic_fp/gadgets/sentinels/__init__.pyi +0 -0
  37. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/src/pythonic_fp/gadgets/sentinels/flavored.py +0 -0
  38. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/src/pythonic_fp/gadgets/sentinels/flavored.pyi +0 -0
  39. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/src/pythonic_fp/gadgets/sentinels/novalue.py +0 -0
  40. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/src/pythonic_fp/gadgets/sentinels/novalue.pyi +0 -0
  41. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/src/pythonic_fp/gadgets/wrap.pyi +0 -0
  42. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/tests/sentinels/test_novalue.py +0 -0
  43. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/tests/sentinels/test_sentinel_hidden_implemetation_detail.py +0 -0
  44. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/tests/sentinels/test_sentinel_with_functions.py +0 -0
  45. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/tests/test_box.py +0 -0
  46. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/tests/test_first_common_ancestor.py +0 -0
  47. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/tests/test_hwrap.py +0 -0
  48. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/tests/test_iterate_over_arguments.py +0 -0
  49. {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.1.0}/tests/test_wrap.py +0 -0
@@ -6,8 +6,8 @@ on:
6
6
  workflow_dispatch:
7
7
 
8
8
  env:
9
- RELEASE: '4.0.4'
10
- DEVEL: '4.0.4'
9
+ RELEASE: '4.1.0'
10
+ DEVEL: '4.1.1'
11
11
  PYTHON: '3.14'
12
12
 
13
13
  permissions:
@@ -17,17 +17,24 @@ See `Semantic Versioning 2.0.0 <https://semver.org>`_.
17
17
  Releases and Important Milestones
18
18
  ---------------------------------
19
19
 
20
+ PyPI 4.1.0 - 2026-05-12
21
+ ~~~~~~~~~~~~~~~~~~~~~~~
22
+
23
+ - documentation now in maintenance mode
24
+ - development status now "4 - Beta"
25
+ - added a few missing methods
26
+
20
27
  PyPI 4.0.4 - 2026-04-26
21
28
  ~~~~~~~~~~~~~~~~~~~~~~~
22
29
 
23
- Much docstring work for sentinels. Need to push now going into
24
- documentation maintenance to fix broken links on PyPI.
30
+ Much docstring work for sentinels. Need to push now to fix
31
+ broken links on PyPI before going into documentation maintenance.
25
32
 
26
33
  Development Status Reappraisal - 2026-05-05
27
34
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
28
35
 
29
36
  Maintainer appraised the Development Status for
30
- pythonic-fp-gadgets to be ``"3 - Alpha"``.
37
+ pythonic-fp-gadgets to be "3 - Alpha".
31
38
 
32
39
  PyPI 4.0.3 - 2026-04-26
33
40
  ~~~~~~~~~~~~~~~~~~~~~~~
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonic-fp-gadgets
3
- Version: 4.0.4
3
+ Version: 4.1.0
4
4
  Summary: Gadgets
5
5
  Keywords: gadgets
6
6
  Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
7
7
  Requires-Python: >=3.13
8
8
  Description-Content-Type: text/x-rst
9
- Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Development Status :: 4 - Beta
10
10
  Classifier: Framework :: Pytest
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: License :: OSI Approved :: Apache Software License
@@ -5,8 +5,8 @@
5
5
  # - later needs to agree with pyproject.toml
6
6
  PROJECT_NAME = Gadgets
7
7
  PYPI_NAME = gadgets
8
- RELEASE_VERSION = 4.0.4
9
- DEVEL_VERSION = 4.0.4
8
+ RELEASE_VERSION = 4.1.0
9
+ DEVEL_VERSION = 4.1.1
10
10
  CUSTOM_VERSION = 0.0.0
11
11
 
12
12
  SPHINXOPTS ?=
@@ -4,7 +4,7 @@ build-backend = "flit_core.buildapi"
4
4
 
5
5
  [project]
6
6
  name = "pythonic-fp-gadgets"
7
- version = "4.0.4"
7
+ version = "4.1.0"
8
8
  readme = "README.rst"
9
9
  requires-python = ">=3.13"
10
10
  authors = [
@@ -14,7 +14,7 @@ keywords = [
14
14
  "gadgets",
15
15
  ]
16
16
  classifiers = [
17
- "Development Status :: 3 - Alpha",
17
+ "Development Status :: 4 - Beta",
18
18
  "Framework :: Pytest",
19
19
  "Intended Audience :: Developers",
20
20
  "License :: OSI Approved :: Apache Software License",
@@ -59,6 +59,7 @@ def first_common_ancestor(cls1: type, cls2: type) -> type:
59
59
 
60
60
  This happens frequently when the function is given
61
61
  Python builtin types or in multiple inheritance situations.
62
+
62
63
  """
63
64
  if issubclass(cls1, cls2):
64
65
  return cls2
@@ -52,7 +52,7 @@ class Box[T]:
52
52
 
53
53
  Initialize ``Box`` with 0 or 1 items.
54
54
 
55
- :param item: An optional initial ``item`` for the ``Box``.
55
+ :param item: An optional initial item for the ``Box``.
56
56
 
57
57
  """
58
58
  self._item = item
@@ -68,9 +68,9 @@ class Box[T]:
68
68
 
69
69
  def __iter__(self) -> Iterator[T]:
70
70
  """
71
- .. admonition:: Iterability
71
+ .. admonition:: iter
72
72
 
73
- Iterates boxed item.
73
+ :yields: Boxed item.
74
74
 
75
75
  """
76
76
  if self:
@@ -83,7 +83,7 @@ class Box[T]:
83
83
  - 1 if ``Box`` contains an item
84
84
  - 0 if ``Box`` is empty
85
85
 
86
- :returns: The number of items currently in the ``Box``.
86
+ :returns: The number of items currently in the ``Box``.
87
87
 
88
88
  """
89
89
  return 1 if self else 0
@@ -94,10 +94,10 @@ class Box[T]:
94
94
 
95
95
  Efficiently compare ``Box`` to another object.
96
96
 
97
- :param other: The object to be compared.
98
- :returns: ``True`` if ``other`` is another ``Box`` and contains
99
- an object which compares as equal to the object
100
- contained in the ``Box``, otherwise ``False``.
97
+ :param other: The object to be compared.
98
+ :returns: ``True`` if ``other`` is another ``Box`` containing
99
+ an object which compares as equal to the item
100
+ contained in the ``Box``, otherwise ``False``.
101
101
 
102
102
  """
103
103
  if not isinstance(other, type(self)):
@@ -111,19 +111,35 @@ class Box[T]:
111
111
 
112
112
  def __repr__(self) -> str:
113
113
  """
114
- .. admonition:: Representation string
114
+ .. admonition:: repr string
115
115
 
116
116
  Construct string 'Box()' if empty, otherwise 'Box(item_repr)'
117
117
  where ``item_repr = repr(item)`` for the currently contained
118
118
  item.
119
119
 
120
- :returns: A string to reproduce the current state of the ``Box``.
120
+ :returns: A string to reproduce the current state of the ``Box``.
121
121
 
122
122
  """
123
123
  if self:
124
124
  return 'Box(' + repr(self._item) + ')'
125
125
  return 'Box()'
126
126
 
127
+ def __str__(self) -> str:
128
+ """
129
+ .. admonition:: user string
130
+
131
+ Construct string 'Box()' if empty, otherwise 'Box(item_str)'
132
+ where ``item_str = str(item)`` for the currently contained
133
+ item.
134
+
135
+ :returns: A user meaningful string to represent the current
136
+ state of the ``Box``.
137
+
138
+ """
139
+ if self:
140
+ return 'Box(' + str(self._item) + ')'
141
+ return 'Box()'
142
+
127
143
  @overload
128
144
  def get(self) -> T: ...
129
145
  @overload
@@ -136,12 +152,12 @@ class Box[T]:
136
152
  Return the boxed item, if it exists, otherwise
137
153
  an alternate item, if given.
138
154
 
139
- :param alt: An optional item of type ``T`` to return
140
- if the ``Box`` is empty.
141
- :returns: Contents of ``Box`` or an alternate item, if given,
142
- when the ``Box`` is empty.
143
- :raises ValueError: When the ``alt`` item is not provided
144
- but needed.
155
+ :param alt: An optional item of type ``T`` to return
156
+ if the ``Box`` is empty.
157
+ :returns: Contents of ``Box`` or an alternate item, if given,
158
+ when the ``Box`` is empty.
159
+ :raises ValueError: When the ``alt`` item is not provided
160
+ but needed.
145
161
 
146
162
  """
147
163
  if self._item is not _sentinel:
@@ -157,8 +173,8 @@ class Box[T]:
157
173
 
158
174
  Pop item from ``Box`` if not empty.
159
175
 
160
- :returns: The item contained in the ``Box``.
161
- :raises ValueError: If Box is empty.
176
+ :returns: The item contained in the ``Box``.
177
+ :raises ValueError: If Box is empty.
162
178
 
163
179
  """
164
180
  if self._item is _sentinel:
@@ -174,8 +190,8 @@ class Box[T]:
174
190
 
175
191
  Push an item into ``Box`` if empty.
176
192
 
177
- :param item: Item to push into the empty ``Box``.
178
- :raises ValueError: If ``Box`` is not empty.
193
+ :param item: Item to push into the empty ``Box``.
194
+ :raises ValueError: If ``Box`` is not empty.
179
195
 
180
196
  """
181
197
  if self._item is _sentinel:
@@ -200,9 +216,9 @@ class Box[T]:
200
216
 
201
217
  Exchange an item with what is in the Box.
202
218
 
203
- :param ``new_item``: New item to exchange for current item.
204
- :returns: Original contents of the ``Box``.
205
- :raises ValueError: If Box is empty.
219
+ :param ``new_item``: New item to exchange for current item.
220
+ :returns: Original contents of the ``Box``.
221
+ :raises ValueError: If Box is empty.
206
222
 
207
223
  """
208
224
  if self._item is _sentinel:
@@ -219,8 +235,8 @@ class Box[T]:
219
235
  Map function ``f`` over contents. We need to return a new
220
236
  instance since the type of Box can change.
221
237
 
222
- :param f: Mapping function.
223
- :returns: New instance.
238
+ :param f: Mapping function.
239
+ :returns: New instance.
224
240
 
225
241
  """
226
242
  if self._item is _sentinel:
@@ -233,8 +249,8 @@ class Box[T]:
233
249
 
234
250
  Flatmap ``Box`` with function ``f``.
235
251
 
236
- :param f: Binding function.
237
- :returns: New instance.
252
+ :param f: Binding function.
253
+ :returns: New instance.
238
254
 
239
255
  """
240
256
  if self._item is _sentinel:
@@ -0,0 +1,270 @@
1
+ # Copyright 2023-2026 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
+ __all__ = ['Wrap', 'HWrap']
16
+
17
+ from collections.abc import Callable, Iterator, Hashable
18
+
19
+
20
+ class Wrap[T]():
21
+ """
22
+ .. admonition:: Wrap
23
+
24
+ Immutablely wrap exactly one item.
25
+
26
+ .. tip::
27
+
28
+ ``Wrap`` objects are matchable.
29
+
30
+ """
31
+ __slots__ = ('_item',)
32
+ __match_args__ = ('_item',)
33
+
34
+ def __init__(self, item: T) -> None:
35
+ """
36
+ .. admonition:: init
37
+
38
+ Initialize ``Wrap`` with 1 required item.
39
+
40
+ :param item: Item to be wrapped.
41
+
42
+ """
43
+ self._item = item
44
+
45
+ def __bool__(self) -> bool:
46
+ """
47
+ .. admonition:: bool
48
+
49
+ Truthiness same as wrapped object.
50
+
51
+ """
52
+ return bool(self._item)
53
+
54
+ def __len__(self) -> int:
55
+ """
56
+ .. admonition:: len
57
+
58
+ Wrapped items always contain just one item.
59
+
60
+ :returns: 1
61
+
62
+ """
63
+ return 1
64
+
65
+ def __iter__(self) -> Iterator[T]:
66
+ """
67
+ .. admonition:: iter
68
+
69
+ :yields: A reference to the wrapped item.
70
+
71
+ """
72
+ if self:
73
+ yield self._item
74
+
75
+ def __eq__(self, other: object) -> bool:
76
+ """
77
+ .. admonition:: equality comparison
78
+
79
+ Compare ``Wrap`` to another object.
80
+
81
+ :param other: Object to be compared.
82
+ :returns: ``True`` if ``other`` is of type Wrap and wraps
83
+ an item which compares as equal to the wrapped
84
+ item, otherwise ``False``.
85
+
86
+ """
87
+ if not isinstance(other, type(self)):
88
+ return False
89
+
90
+ if self._item is other._item:
91
+ return True
92
+ return self._item == other._item
93
+
94
+ def __repr__(self) -> str:
95
+ """
96
+ .. admonition:: repr string
97
+
98
+ Construct string 'Wrap(item_str)'
99
+ where ``item_str = repr(item)`` for the contained item.
100
+
101
+ :returns: A string to reproduce of the wrapped item.
102
+
103
+ """
104
+ return 'Wrap(' + repr(self._item) + ')'
105
+
106
+ def __str__(self) -> str:
107
+ """
108
+ .. admonition:: user string
109
+
110
+ Construct string 'Wrap(item_str)'
111
+ where ``item_str = str(item)`` for the contained item.
112
+
113
+ :returns: A string meaningful to an end user.
114
+
115
+ """
116
+ return 'Wrap(' + str(self._item) + ')'
117
+
118
+ def map[U](self, f: Callable[[T], U]) -> 'Wrap[U]':
119
+ """
120
+ .. admonition:: map
121
+
122
+ Map function ``f`` over contents.
123
+
124
+ :param f: Mapping function.
125
+ :returns: New instance.
126
+
127
+ """
128
+ return Wrap(f(self._item))
129
+
130
+ def bind[U](self, f: Callable[[T], 'Wrap[U]']) -> 'Wrap[U]':
131
+ """
132
+ .. admonition:: bind
133
+
134
+ Flatmap wrapped object with function ``f``.
135
+
136
+ :param f: Binding function.
137
+ :returns: New instance.
138
+
139
+ """
140
+ return f(self._item)
141
+
142
+
143
+ class HWrap[T: Hashable](Hashable):
144
+ """
145
+ .. admonition:: HWrap
146
+
147
+ Immutablely wrap exactly one hashable item.
148
+
149
+ .. tip::
150
+
151
+ ``HWrap`` objects are hashable and matchable.
152
+
153
+ """
154
+
155
+ __slots__ = ('_item', '_hash')
156
+ __match_args__ = ('_item',)
157
+
158
+ def __init__(self, item: T) -> None:
159
+ """
160
+ .. admonition:: init
161
+
162
+ Initialize ``HWrap`` with 1 required item.
163
+
164
+ :param item: Item to be wrapped.
165
+
166
+ """
167
+ self._item, self._hash = item, hash(item)
168
+
169
+ def __hash__(self) -> int:
170
+ return self._hash
171
+
172
+ def __bool__(self) -> bool:
173
+ """
174
+ .. admonition:: bool
175
+
176
+ Truthiness same as wrapped object.
177
+
178
+ """
179
+ return bool(self._item)
180
+
181
+ def __len__(self) -> int:
182
+ """
183
+ .. admonition:: len
184
+
185
+ HWrapped items always contain just one item.
186
+
187
+ :returns: 1
188
+
189
+ """
190
+ return 1
191
+
192
+ def __iter__(self) -> Iterator[T]:
193
+ """
194
+ .. admonition:: iter
195
+
196
+ :yields: A reference to the wrapped item.
197
+
198
+ """
199
+ if self:
200
+ yield self._item
201
+
202
+ def __eq__(self, other: object) -> bool:
203
+ """
204
+ .. admonition:: equality comparison
205
+
206
+ Compare ``HWrap`` to another object.
207
+
208
+ :param other: Object to be compared.
209
+ :returns: ``True`` if ``other`` is of type HWrap and wraps
210
+ an item which compares as equal to the wrapped
211
+ item, otherwise ``False``.
212
+
213
+ """
214
+ if not isinstance(other, type(self)):
215
+ return False
216
+
217
+ if self._hash != other._hash:
218
+ return False
219
+ if self._item is other._item:
220
+ return True
221
+ return self._item == other._item
222
+
223
+ def __repr__(self) -> str:
224
+ """
225
+ .. admonition:: repr string
226
+
227
+ Construct string 'HWrap(item_str)'
228
+ where ``item_str = repr(item)`` for the contained item.
229
+
230
+ :returns: A string to reproduce of the wrapped object.
231
+
232
+ """
233
+ return 'Wrap(' + repr(self._item) + ')'
234
+
235
+ def __str__(self) -> str:
236
+ """
237
+ .. admonition:: user string
238
+
239
+ Construct string 'HWrap(item_str)'
240
+ where ``item_str = str(item)`` for the contained item.
241
+
242
+ :returns: A string meaningful to an end user.
243
+
244
+ """
245
+ return 'Wrap(' + str(self._item) + ')'
246
+
247
+ def map[U](self, f: Callable[[T], U]) -> 'HWrap[U]':
248
+ """
249
+ .. admonition:: map
250
+
251
+ Map function ``f`` over wrapped the wrapped object
252
+ returning a new ``HWrap`` instance.
253
+
254
+ :param f: Mapping function.
255
+ :returns: New instance.
256
+
257
+ """
258
+ return HWrap(f(self._item))
259
+
260
+ def bind[U](self, f: Callable[[T], 'HWrap[U]']) -> 'HWrap[U]':
261
+ """
262
+ .. admonition:: bind
263
+
264
+ Flatmap ``Box`` with function ``f``.
265
+
266
+ :param f: Binding function.
267
+ :returns: New instance.
268
+
269
+ """
270
+ return f(self._item)
@@ -1,219 +0,0 @@
1
- # Copyright 2023-2026 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
- .. 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
- """
23
-
24
- __all__ = ['Wrap', 'HWrap']
25
-
26
- from collections.abc import Callable, Iterator, Hashable
27
-
28
-
29
- class Wrap[T]():
30
- """
31
- .. admonition:: Wrap object
32
-
33
- Immutablely wrap exactly one value of a
34
- given type. ``Wrap`` objects can be used
35
- in Python match statements.
36
-
37
- """
38
- __slots__ = ('_item',)
39
- __match_args__ = ('_item',)
40
-
41
- def __init__(self, item: T) -> None:
42
- """
43
- .. admonition:: Initializer
44
-
45
- Initialize ``Wrap`` with 1 required item.
46
-
47
- :param item: Item to be wrapped.
48
-
49
- """
50
- self._item = item
51
-
52
- def __bool__(self) -> bool:
53
- """
54
- .. admonition:: Bool
55
-
56
- Truthiness same as wrapped object.
57
-
58
- """
59
- return bool(self._item)
60
-
61
- def __iter__(self) -> Iterator[T]:
62
- """
63
- .. admonition:: Iter
64
-
65
- Iterable, iterates wrapped item.
66
-
67
- """
68
- if self:
69
- yield self._item
70
-
71
- def __str__(self) -> str:
72
- """
73
- .. admonition:: User string
74
-
75
- Construct string 'Box(item_str)'
76
- where ``item_str = str(item)`` for the currently contained
77
- item.
78
-
79
- :returns: A string to reproduce the current state of the ``Box``.
80
-
81
- """
82
- return 'Wrap(' + str(self._item) + ')'
83
-
84
- def __eq__(self, other: object) -> bool:
85
- """
86
- .. admonition:: Equality comparison
87
-
88
- Efficiently compare ``Wrap`` to another object.
89
-
90
- :param other: The object to be compared with,
91
- :returns: ``True`` if ``other`` is of type Wrap and wraps
92
- an object which compares as equal to the wrapped
93
- object, otherwise ``False``.
94
-
95
- """
96
- if not isinstance(other, type(self)):
97
- return False
98
-
99
- if self._item is other._item:
100
- return True
101
- return self._item == other._item
102
-
103
- def map[U](self, f: Callable[[T], U]) -> 'Wrap[U]':
104
- """
105
- .. admonition:: Map
106
-
107
- Map function ``f`` over contents.
108
-
109
- Map function ``f`` over contents.
110
-
111
- :param f: Mapping function.
112
- :returns: New instance.
113
-
114
- """
115
- return Wrap(f(self._item))
116
-
117
- def bind[U](self, f: Callable[[T], 'Wrap[U]']) -> 'Wrap[U]':
118
- """
119
- .. admonition:: Bind
120
-
121
- Flatmap wrapped object with function ``f``.
122
-
123
- :param f: Binding function.
124
- :returns: New instance.
125
-
126
- """
127
- return f(self._item)
128
-
129
-
130
- class HWrap[T: Hashable](Hashable):
131
- """
132
- .. admonition:: Wrap hashable object
133
-
134
- Immutablely wrap exactly one value of a
135
- given hashable type. ``HWrap`` objects can
136
- be used in Python match statements.
137
-
138
- .. tip::
139
-
140
- ``HWrap`` objects are hashable.
141
-
142
- """
143
-
144
- __slots__ = ('_item', '_hash')
145
- __match_args__ = ('_item',)
146
-
147
- def __init__(self, item: T) -> None:
148
- self._item, self._hash = item, hash(item)
149
-
150
- def __hash__(self) -> int:
151
- return self._hash
152
-
153
- def __bool__(self) -> bool:
154
- """
155
- .. admonition:: Bool
156
-
157
- Truthiness same as wrapped object.
158
-
159
- """
160
- return bool(self._item)
161
-
162
- def __iter__(self) -> Iterator[T]:
163
- """
164
- .. admonition:: Iter
165
-
166
- Iterable, iterates wrapped item.
167
-
168
- """
169
- if self:
170
- yield self._item
171
-
172
- def __repr__(self) -> str:
173
- return 'Wrap(' + repr(self._item) + ')'
174
-
175
- def __eq__(self, other: object) -> bool:
176
- """
177
- .. admonition:: Equality comparison
178
-
179
- Efficiently compare to another object.
180
-
181
- :param other: Object to be compared
182
- :returns: ``True`` if ``other`` is of type HWrap and wraps
183
- an object which compares as equal to the wrapped
184
- object, otherwise ``False``.
185
-
186
- """
187
- if not isinstance(other, type(self)):
188
- return False
189
-
190
- if self._hash != other._hash:
191
- return False
192
- if self._item is other._item:
193
- return True
194
- return self._item == other._item
195
-
196
- def map[U](self, f: Callable[[T], U]) -> 'HWrap[U]':
197
- """
198
- .. admonition:: Map
199
-
200
- Map function ``f`` over wrapped the wrapped object
201
- returning a new ``HWrap`` instance.
202
-
203
- :param f: Mapping function.
204
- :returns: New instance.
205
-
206
- """
207
- return HWrap(f(self._item))
208
-
209
- def bind[U](self, f: Callable[[T], 'HWrap[U]']) -> 'HWrap[U]':
210
- """
211
- .. admonition:: Bind
212
-
213
- Flatmap ``Box`` with function ``f``.
214
-
215
- :param f: Binding function.
216
- :returns: New instance.
217
-
218
- """
219
- return f(self._item)