pythonic-fp-gadgets 4.0.4__tar.gz → 4.2.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.
- {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.2.0}/PKG-INFO +22 -31
- pythonic_fp_gadgets-4.0.4/README.rst → pythonic_fp_gadgets-4.2.0/README.md +13 -21
- {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.2.0}/pyproject.toml +30 -25
- {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.2.0}/src/pythonic_fp/gadgets/__init__.py +1 -0
- {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.2.0}/src/pythonic_fp/gadgets/box.py +43 -27
- pythonic_fp_gadgets-4.2.0/src/pythonic_fp/gadgets/wrap.py +270 -0
- pythonic_fp_gadgets-4.0.4/.github/workflows/static.yml +0 -64
- pythonic_fp_gadgets-4.0.4/.gitignore +0 -9
- pythonic_fp_gadgets-4.0.4/CHANGELOG.rst +0 -110
- pythonic_fp_gadgets-4.0.4/docs/Makefile +0 -78
- pythonic_fp_gadgets-4.0.4/docs/gen_conf.py +0 -110
- pythonic_fp_gadgets-4.0.4/docs/requirements.txt +0 -2
- pythonic_fp_gadgets-4.0.4/docs/source/_static/.gitkeep +0 -0
- pythonic_fp_gadgets-4.0.4/docs/source/_static/custom.css +0 -3
- pythonic_fp_gadgets-4.0.4/docs/source/_templates/.gitkeep +0 -0
- pythonic_fp_gadgets-4.0.4/docs/source/api/box.rst +0 -6
- pythonic_fp_gadgets-4.0.4/docs/source/api/functions/first_common_ancestor.rst +0 -4
- pythonic_fp_gadgets-4.0.4/docs/source/api/functions/index.rst +0 -8
- pythonic_fp_gadgets-4.0.4/docs/source/api/functions/iterate_over_arguments.rst +0 -4
- pythonic_fp_gadgets-4.0.4/docs/source/api/index.rst +0 -31
- pythonic_fp_gadgets-4.0.4/docs/source/api/sentinels/flavored.rst +0 -8
- pythonic_fp_gadgets-4.0.4/docs/source/api/sentinels/index.rst +0 -14
- pythonic_fp_gadgets-4.0.4/docs/source/api/sentinels/novalue.rst +0 -11
- pythonic_fp_gadgets-4.0.4/docs/source/api/wrap.rst +0 -6
- pythonic_fp_gadgets-4.0.4/docs/source/changelog.rst +0 -7
- pythonic_fp_gadgets-4.0.4/docs/source/description.rst +0 -8
- pythonic_fp_gadgets-4.0.4/docs/source/index.rst +0 -30
- pythonic_fp_gadgets-4.0.4/docs/source/releases.rst +0 -28
- pythonic_fp_gadgets-4.0.4/docs/source/usage.rst +0 -160
- pythonic_fp_gadgets-4.0.4/src/pythonic_fp/gadgets/wrap.py +0 -219
- pythonic_fp_gadgets-4.0.4/tests/sentinels/test_novalue.py +0 -83
- pythonic_fp_gadgets-4.0.4/tests/sentinels/test_sentinel_hidden_implemetation_detail.py +0 -70
- pythonic_fp_gadgets-4.0.4/tests/sentinels/test_sentinel_with_functions.py +0 -31
- pythonic_fp_gadgets-4.0.4/tests/test_box.py +0 -95
- pythonic_fp_gadgets-4.0.4/tests/test_first_common_ancestor.py +0 -115
- pythonic_fp_gadgets-4.0.4/tests/test_hwrap.py +0 -76
- pythonic_fp_gadgets-4.0.4/tests/test_iterate_over_arguments.py +0 -46
- pythonic_fp_gadgets-4.0.4/tests/test_wrap.py +0 -71
- {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.2.0}/LICENSE +0 -0
- {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.2.0}/src/pythonic_fp/gadgets/__init__.pyi +0 -0
- {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.2.0}/src/pythonic_fp/gadgets/box.pyi +0 -0
- {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.2.0}/src/pythonic_fp/gadgets/py.typed +0 -0
- {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.2.0}/src/pythonic_fp/gadgets/sentinels/__init__.py +0 -0
- {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.2.0}/src/pythonic_fp/gadgets/sentinels/__init__.pyi +0 -0
- {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.2.0}/src/pythonic_fp/gadgets/sentinels/flavored.py +0 -0
- {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.2.0}/src/pythonic_fp/gadgets/sentinels/flavored.pyi +0 -0
- {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.2.0}/src/pythonic_fp/gadgets/sentinels/novalue.py +0 -0
- {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.2.0}/src/pythonic_fp/gadgets/sentinels/novalue.pyi +0 -0
- {pythonic_fp_gadgets-4.0.4 → pythonic_fp_gadgets-4.2.0}/src/pythonic_fp/gadgets/wrap.pyi +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: pythonic-fp-gadgets
|
|
3
|
-
Version: 4.0
|
|
4
|
-
Summary: Gadgets
|
|
3
|
+
Version: 4.2.0
|
|
4
|
+
Summary: Gadgets without external Pythonic FP dependencies.
|
|
5
5
|
Keywords: gadgets
|
|
6
6
|
Author-email: "Geoffrey R. Scheller" <geoffrey@scheller.com>
|
|
7
7
|
Requires-Python: >=3.13
|
|
8
|
-
Description-Content-Type: text/
|
|
9
|
-
Classifier: Development Status ::
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
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
|
|
@@ -14,46 +14,37 @@ Classifier: Operating System :: OS Independent
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.14
|
|
15
15
|
Classifier: Typing :: Typed
|
|
16
16
|
License-File: LICENSE
|
|
17
|
-
|
|
18
|
-
|
|
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/projects/gadgets.html
|
|
17
|
+
Project-URL: Changelog, https://github.com/grscheller/pythonic-fp-gadgets/blob/main/CHANGELOG.md
|
|
18
|
+
Project-URL: Documentation, https://grscheller.github.io/pythonic-fp-gadgets/release/html/
|
|
21
19
|
Project-URL: Homepage, https://grscheller.github.io/pythonic-fp/
|
|
22
20
|
Project-URL: Source, https://github.com/grscheller/pythonic-fp-gadgets
|
|
23
|
-
|
|
21
|
+
Import-Name: pythonic_fp.gadgets
|
|
22
|
+
Import-Namespace: pythonic_fp
|
|
24
23
|
|
|
25
|
-
Pythonic FP - Gadgets
|
|
26
|
-
=====================
|
|
27
|
-
|
|
28
|
-
PyPI project
|
|
29
|
-
`pythonic-fp-gadgets
|
|
30
|
-
<https://pypi.org/project/pythonic-fp-gadgets>`_.
|
|
24
|
+
# Pythonic FP - Gadgets
|
|
31
25
|
|
|
32
26
|
Library of simple, but useful, classes and functions with no dependencies
|
|
33
27
|
outside the Python Standard Library.
|
|
34
28
|
|
|
35
|
-
|
|
29
|
+
PyPI project
|
|
30
|
+
[pythonic-fp-gadgets](https://pypi.org/project/pythonic-fp-gadgets).
|
|
31
|
+
Part of the
|
|
32
|
+
[pythonic-fp](https://grscheller.github.io/pythonic-fp/)
|
|
33
|
+
PyPI projects.
|
|
34
|
+
|
|
35
|
+
## Documentation
|
|
36
|
+
|
|
37
|
+
Documentation and other links for this project are hosted on
|
|
38
|
+
[GitHub Pages](https://grscheller.github.io/pythonic-fp/projects/gadgets.html).
|
|
36
39
|
|
|
40
|
+
- Gadgets
|
|
37
41
|
- single item box
|
|
38
42
|
- immutable wrapped (hashable) references
|
|
39
43
|
- function returning iterator of its arguments
|
|
40
44
|
- function to find the latest common ancestor of two classes
|
|
41
45
|
- sentinel values
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
`pythonic-fp
|
|
45
|
-
<https://grscheller.github.io/pythonic-fp/>`_
|
|
46
|
-
PyPI projects.
|
|
47
|
-
|
|
48
|
-
Documentation
|
|
49
|
-
-------------
|
|
50
|
-
|
|
51
|
-
Documentation and other links for this project are hosted on
|
|
52
|
-
`GitHub Pages
|
|
53
|
-
<https://grscheller.github.io/pythonic-fp/projects/gadgets.html>`_.
|
|
54
|
-
|
|
55
|
-
Copyright and License
|
|
56
|
-
---------------------
|
|
47
|
+
## Copyright and License
|
|
57
48
|
|
|
58
49
|
Copyright (c) 2025-2026 Geoffrey R. Scheller. Licensed under the Apache
|
|
59
50
|
License, Version 2.0. See the LICENSE file for details.
|
|
@@ -1,35 +1,27 @@
|
|
|
1
|
-
Pythonic FP - Gadgets
|
|
2
|
-
=====================
|
|
3
|
-
|
|
4
|
-
PyPI project
|
|
5
|
-
`pythonic-fp-gadgets
|
|
6
|
-
<https://pypi.org/project/pythonic-fp-gadgets>`_.
|
|
1
|
+
# Pythonic FP - Gadgets
|
|
7
2
|
|
|
8
3
|
Library of simple, but useful, classes and functions with no dependencies
|
|
9
4
|
outside the Python Standard Library.
|
|
10
5
|
|
|
11
|
-
|
|
6
|
+
PyPI project
|
|
7
|
+
[pythonic-fp-gadgets](https://pypi.org/project/pythonic-fp-gadgets).
|
|
8
|
+
Part of the
|
|
9
|
+
[pythonic-fp](https://grscheller.github.io/pythonic-fp/)
|
|
10
|
+
PyPI projects.
|
|
11
|
+
|
|
12
|
+
## Documentation
|
|
13
|
+
|
|
14
|
+
Documentation and other links for this project are hosted on
|
|
15
|
+
[GitHub Pages](https://grscheller.github.io/pythonic-fp/projects/gadgets.html).
|
|
12
16
|
|
|
17
|
+
- Gadgets
|
|
13
18
|
- single item box
|
|
14
19
|
- immutable wrapped (hashable) references
|
|
15
20
|
- function returning iterator of its arguments
|
|
16
21
|
- function to find the latest common ancestor of two classes
|
|
17
22
|
- sentinel values
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
`pythonic-fp
|
|
21
|
-
<https://grscheller.github.io/pythonic-fp/>`_
|
|
22
|
-
PyPI projects.
|
|
23
|
-
|
|
24
|
-
Documentation
|
|
25
|
-
-------------
|
|
26
|
-
|
|
27
|
-
Documentation and other links for this project are hosted on
|
|
28
|
-
`GitHub Pages
|
|
29
|
-
<https://grscheller.github.io/pythonic-fp/projects/gadgets.html>`_.
|
|
30
|
-
|
|
31
|
-
Copyright and License
|
|
32
|
-
---------------------
|
|
24
|
+
## Copyright and License
|
|
33
25
|
|
|
34
26
|
Copyright (c) 2025-2026 Geoffrey R. Scheller. Licensed under the Apache
|
|
35
27
|
License, Version 2.0. See the LICENSE file for details.
|
|
@@ -4,9 +4,10 @@ build-backend = "flit_core.buildapi"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pythonic-fp-gadgets"
|
|
7
|
-
version = "4.0
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
version = "4.2.0"
|
|
8
|
+
description = "Gadgets without external Pythonic FP dependencies."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.13" # syntactic floor: known to SyntaxError below this
|
|
10
11
|
authors = [
|
|
11
12
|
{ name = "Geoffrey R. Scheller", email = "geoffrey@scheller.com" },
|
|
12
13
|
]
|
|
@@ -14,32 +15,48 @@ keywords = [
|
|
|
14
15
|
"gadgets",
|
|
15
16
|
]
|
|
16
17
|
classifiers = [
|
|
17
|
-
"Development Status ::
|
|
18
|
+
"Development Status :: 4 - Beta",
|
|
18
19
|
"Framework :: Pytest",
|
|
19
20
|
"Intended Audience :: Developers",
|
|
20
21
|
"License :: OSI Approved :: Apache Software License",
|
|
21
22
|
"Operating System :: OS Independent",
|
|
22
|
-
"Programming Language :: Python :: 3.14",
|
|
23
|
+
"Programming Language :: Python :: 3.14", # tested: pytest test suite successful
|
|
23
24
|
"Typing :: Typed",
|
|
24
25
|
]
|
|
25
26
|
dependencies = []
|
|
26
|
-
dynamic = ["description"]
|
|
27
27
|
|
|
28
28
|
[project.urls]
|
|
29
|
-
Changelog = "https://github.com/grscheller/pythonic-fp-gadgets/blob/main/CHANGELOG.
|
|
30
|
-
Documentation =
|
|
29
|
+
Changelog = "https://github.com/grscheller/pythonic-fp-gadgets/blob/main/CHANGELOG.md"
|
|
30
|
+
Documentation ="https://grscheller.github.io/pythonic-fp-gadgets/release/html/"
|
|
31
31
|
Homepage = "https://grscheller.github.io/pythonic-fp/"
|
|
32
32
|
Source = "https://github.com/grscheller/pythonic-fp-gadgets"
|
|
33
33
|
|
|
34
|
-
[
|
|
34
|
+
[dependency-groups]
|
|
35
|
+
docs = [
|
|
36
|
+
"furo>=2025.12.19",
|
|
37
|
+
"sphinx>=9.1",
|
|
38
|
+
]
|
|
39
|
+
pub = [
|
|
40
|
+
"flit",
|
|
41
|
+
]
|
|
35
42
|
test = [
|
|
36
43
|
"pytest>=8.4.1",
|
|
37
|
-
"pythonic-fp-circulararray>=6.0.4",
|
|
38
44
|
]
|
|
39
45
|
|
|
40
46
|
[tool.flit.module]
|
|
41
47
|
name = "pythonic_fp.gadgets"
|
|
42
48
|
|
|
49
|
+
[tool.flit.sdist]
|
|
50
|
+
exclude = [
|
|
51
|
+
"docs/",
|
|
52
|
+
"tests/",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[tool.pytest]
|
|
56
|
+
addopts = ["-ra"]
|
|
57
|
+
consider_namespace_packages = true
|
|
58
|
+
testpaths = ["tests/"]
|
|
59
|
+
|
|
43
60
|
[tool.mypy]
|
|
44
61
|
explicit_package_bases = true
|
|
45
62
|
local_partial_types = true
|
|
@@ -48,23 +65,12 @@ warn_redundant_casts = true
|
|
|
48
65
|
warn_return_any = true
|
|
49
66
|
warn_unused_configs = true
|
|
50
67
|
|
|
51
|
-
[tool.pylsp-mypy]
|
|
52
|
-
enabled = true
|
|
53
|
-
live-mode = true
|
|
54
|
-
dmypy = false
|
|
55
|
-
strict = true
|
|
56
|
-
report_progress = true
|
|
57
|
-
|
|
58
|
-
[tool.pytest]
|
|
59
|
-
addopts = [
|
|
60
|
-
"-ra",
|
|
61
|
-
]
|
|
62
|
-
consider_namespace_packages = true
|
|
63
|
-
testpaths = ["tests/"]
|
|
64
|
-
|
|
65
68
|
[tool.ruff]
|
|
66
69
|
target-version = "py314"
|
|
67
70
|
|
|
71
|
+
[tool.ruff.lint]
|
|
72
|
+
ignore = ["RUF022", "E741"]
|
|
73
|
+
|
|
68
74
|
[tool.ruff.lint.flake8-quotes]
|
|
69
75
|
docstring-quotes = "double"
|
|
70
76
|
|
|
@@ -73,7 +79,6 @@ docstring-quotes = "double"
|
|
|
73
79
|
"E731",
|
|
74
80
|
"C901",
|
|
75
81
|
]
|
|
76
|
-
"**/*.py" = ["E741"]
|
|
77
82
|
|
|
78
83
|
[tool.ruff.format]
|
|
79
84
|
quote-style = "single"
|
|
@@ -52,7 +52,7 @@ class Box[T]:
|
|
|
52
52
|
|
|
53
53
|
Initialize ``Box`` with 0 or 1 items.
|
|
54
54
|
|
|
55
|
-
|
|
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::
|
|
71
|
+
.. admonition:: iter
|
|
72
72
|
|
|
73
|
-
|
|
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
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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::
|
|
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
|
-
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
161
|
-
|
|
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
|
-
|
|
178
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
223
|
-
|
|
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
|
-
|
|
237
|
-
|
|
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)
|