pythonic-fp-booleans 1.1.2__tar.gz → 2.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 (27) hide show
  1. pythonic_fp_booleans-2.1.0/CHANGELOG.rst +82 -0
  2. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/PKG-INFO +11 -11
  3. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/README.rst +5 -4
  4. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/pyproject.toml +5 -6
  5. {pythonic_fp_booleans-1.1.2/src/pythonic_fp/booleans/subtypes → pythonic_fp_booleans-2.1.0/src/pythonic_fp/booleans}/__init__.py +19 -13
  6. {pythonic_fp_booleans-1.1.2/src/pythonic_fp/booleans/subtypes → pythonic_fp_booleans-2.1.0/src/pythonic_fp/booleans}/flavored.py +38 -46
  7. {pythonic_fp_booleans-1.1.2/src/pythonic_fp/booleans/subtypes → pythonic_fp_booleans-2.1.0/src/pythonic_fp/booleans}/flavored.pyi +1 -1
  8. pythonic_fp_booleans-2.1.0/src/pythonic_fp/booleans/py.typed +0 -0
  9. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/src/pythonic_fp/booleans/subtypable.py +47 -26
  10. {pythonic_fp_booleans-1.1.2/src/pythonic_fp/booleans/subtypes → pythonic_fp_booleans-2.1.0/src/pythonic_fp/booleans}/truthy_falsy.py +21 -32
  11. {pythonic_fp_booleans-1.1.2/src/pythonic_fp/booleans/subtypes → pythonic_fp_booleans-2.1.0/src/pythonic_fp/booleans}/truthy_falsy.pyi +1 -1
  12. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/tests/test_bool_vs_fbool.py +1 -1
  13. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/tests/test_flavored.py +1 -1
  14. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/tests/test_flavored_non_shortcut_logic.py +1 -1
  15. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/tests/test_flavored_sbool.py +1 -1
  16. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/tests/test_true_false.py +1 -1
  17. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/tests/test_true_false_non_shortcut_logic.py +2 -2
  18. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/tests/test_typing.py +2 -2
  19. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/tests/test_variance.py +2 -2
  20. pythonic_fp_booleans-1.1.2/CHANGELOG.rst +0 -46
  21. pythonic_fp_booleans-1.1.2/src/pythonic_fp/booleans/__init__.py +0 -99
  22. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/.gitignore +0 -0
  23. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/LICENSE +0 -0
  24. /pythonic_fp_booleans-1.1.2/src/pythonic_fp/booleans/py.typed → /pythonic_fp_booleans-2.1.0/src/pythonic_fp/booleans/__init__.pyi +0 -0
  25. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/src/pythonic_fp/booleans/subtypable.pyi +0 -0
  26. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/tests/test_bool_vs_sbool.py +0 -0
  27. {pythonic_fp_booleans-1.1.2 → pythonic_fp_booleans-2.1.0}/tests/test_subtypable_non_shortcut_logic.py +0 -0
@@ -0,0 +1,82 @@
1
+ CHANGELOG
2
+ =========
3
+
4
+ PyPI pythonic-fp-booleans project.
5
+
6
+ Semantic Versioning
7
+ -------------------
8
+
9
+ Strict 3 digit semantic versioning adopted 2025-05-19.
10
+
11
+ - **MAJOR** version incremented for incompatible API changes
12
+ - **MINOR** version incremented for backward compatible added functionality
13
+ - **PATCH** version incremented for backward compatible bug fixes
14
+
15
+ See `Semantic Versioning 2.0.0 <https://semver.org>`_.
16
+
17
+ Releases and Important Milestones
18
+ ---------------------------------
19
+
20
+ PyPI 2.1.0 - 2025-12-02
21
+ ~~~~~~~~~~~~~~~~~~~~~~~
22
+
23
+ Now requires pythonic-fp-gadgets 4.0.1 to be installed.
24
+
25
+
26
+ PyPI 2.0.0 - 2025-09-27
27
+ ~~~~~~~~~~~~~~~~~~~~~~~
28
+
29
+ Released to get the next consistent set of Pythonic FP out (v3.2.0) out.
30
+
31
+ - PyPI documentation link now goes to root, not releases
32
+ - decided to flatten directory structure
33
+
34
+ - makes more sense from an end user perspective
35
+ - will help Sphinx docs be more homogeneous across repos
36
+
37
+ Warning: API not fully locked down, major version number change may be needed.
38
+
39
+ TODO:
40
+
41
+ - bitwise operators not symmetric when used with ``SBool`` types and ``int`` or ``bool``
42
+
43
+ - may need to define reversed bit-wise operators
44
+
45
+ - ``TS_Boolean`` has a code smell to it
46
+
47
+ - may be just a hack to get get around sub-optimal type usage
48
+ - Using ``TS_Bool`` as a variable type may be enough
49
+
50
+ PyPI 1.1.2 - 2025-09-09
51
+ ~~~~~~~~~~~~~~~~~~~~~~~
52
+
53
+ Fixed pyproject.toml dependency issues.
54
+
55
+ PyPI 1.1.1 - 2025-09-03
56
+ ~~~~~~~~~~~~~~~~~~~~~~~
57
+
58
+ Only change was to give README.rst a final edit. Missed this on
59
+ the v1.1.0 release.
60
+
61
+
62
+ PyPI 1.1.0 - 2025-09-02
63
+ ~~~~~~~~~~~~~~~~~~~~~~~
64
+
65
+ First PyPI release as pythonic-fp-booleans.
66
+
67
+ - module booleans.subtypable
68
+ - package booleans.subtypes
69
+
70
+ - module booleans.subtypes.flavored
71
+ - module booleans.subtypes.truthy_falsy
72
+
73
+ Needs
74
+
75
+ - "pythonic-fp>=3.0.0" for gadgets
76
+ - "pythonic-fp-sentinels>=2.1.0",
77
+
78
+ Created pythonic-fp-boolean repo - 2025-08-06
79
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
80
+
81
+ - created grscheller/pythonic-fp-boolean GitHub repo
82
+ - moved pythonic_fp.singletons.sbool to pythonic_fp.booleans.sbool
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonic-fp-booleans
3
- Version: 1.1.2
4
- Summary: Subtypable Boolean like classes
3
+ Version: 2.1.0
4
+ Summary: Subtypable Boolean Class Hierarchy
5
5
  Keywords: booleans,subtypable
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
@@ -14,12 +14,11 @@ Classifier: Operating System :: OS Independent
14
14
  Classifier: Programming Language :: Python :: 3.13
15
15
  Classifier: Typing :: Typed
16
16
  License-File: LICENSE
17
- Requires-Dist: pythonic-fp-gadgets>=3.0.0
18
- Requires-Dist: pythonic-fp-sentinels>=2.1.0
17
+ Requires-Dist: pythonic-fp-gadgets>=4.0.1
19
18
  Requires-Dist: pytest>=8.4.1 ; extra == "test"
20
19
  Project-URL: Changelog, https://github.com/grscheller/pythonic-fp-booleans/blob/main/CHANGELOG.rst
21
- Project-URL: Documentation, https://grscheller.github.io/pythonic-fp/booleans/development/build/html/releases.html
22
- Project-URL: Homepage, https://github.com/grscheller/pythonic-fp/blob/main/README.md
20
+ Project-URL: Documentation, https://grscheller.github.io/pythonic-fp/booleans/development/build/html/
21
+ Project-URL: Homepage, https://grscheller.github.io/pythonic-fp/homepage/build/html/
23
22
  Project-URL: Source, https://github.com/grscheller/pythonic-fp-booleans
24
23
  Provides-Extra: test
25
24
 
@@ -35,16 +34,17 @@ Subtypable Boolean like classes.
35
34
  While still compatible with Python shortcut logic, these classes can be non-shortcut logically
36
35
  composed with Python’s bitwise operators.
37
36
 
38
- This PyPI project is part of of the grscheller
39
- `pythonic-fp namespace projects
40
- <https://github.com/grscheller/pythonic-fp/blob/main/README.md>`_
37
+ Part of the
38
+ `pythonic-fp
39
+ <https://grscheller.github.io/pythonic-fp>`_
40
+ PyPI projects.
41
41
 
42
42
  Documentation
43
43
  -------------
44
44
 
45
45
  Documentation for this project is hosted on
46
46
  `GitHub Pages
47
- <https://grscheller.github.io/pythonic-fp/booleans/development/build/html>`_.
47
+ <https://grscheller.github.io/pythonic-fp/booleans>`_.
48
48
 
49
49
  Copyright and License
50
50
  ---------------------
@@ -10,16 +10,17 @@ Subtypable Boolean like classes.
10
10
  While still compatible with Python shortcut logic, these classes can be non-shortcut logically
11
11
  composed with Python’s bitwise operators.
12
12
 
13
- This PyPI project is part of of the grscheller
14
- `pythonic-fp namespace projects
15
- <https://github.com/grscheller/pythonic-fp/blob/main/README.md>`_
13
+ Part of the
14
+ `pythonic-fp
15
+ <https://grscheller.github.io/pythonic-fp>`_
16
+ PyPI projects.
16
17
 
17
18
  Documentation
18
19
  -------------
19
20
 
20
21
  Documentation for this project is hosted on
21
22
  `GitHub Pages
22
- <https://grscheller.github.io/pythonic-fp/booleans/development/build/html>`_.
23
+ <https://grscheller.github.io/pythonic-fp/booleans>`_.
23
24
 
24
25
  Copyright and License
25
26
  ---------------------
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pythonic-fp-booleans"
3
- version = "1.1.2"
3
+ version = "2.1.0"
4
4
  readme = "README.rst"
5
5
  requires-python = ">=3.13"
6
6
  license = { file = "LICENSE" }
@@ -10,7 +10,7 @@ keywords = [
10
10
  "subtypable",
11
11
  ]
12
12
  classifiers = [
13
- "Development Status :: 3 - Alpha",
13
+ "Development Status :: 4 - Beta",
14
14
  "Framework :: Pytest",
15
15
  "Intended Audience :: Developers",
16
16
  "License :: OSI Approved :: Apache Software License",
@@ -19,15 +19,14 @@ classifiers = [
19
19
  "Typing :: Typed",
20
20
  ]
21
21
  dependencies = [
22
- "pythonic-fp-gadgets>=3.0.0",
23
- "pythonic-fp-sentinels>=2.1.0",
22
+ "pythonic-fp-gadgets>=4.0.1",
24
23
  ]
25
24
  dynamic = ["description"]
26
25
 
27
26
  [project.urls]
28
27
  Changelog = "https://github.com/grscheller/pythonic-fp-booleans/blob/main/CHANGELOG.rst"
29
- Documentation = "https://grscheller.github.io/pythonic-fp/booleans/development/build/html/releases.html"
30
- Homepage = "https://github.com/grscheller/pythonic-fp/blob/main/README.md"
28
+ Documentation = "https://grscheller.github.io/pythonic-fp/booleans/development/build/html/"
29
+ Homepage = "https://grscheller.github.io/pythonic-fp/homepage/build/html/"
31
30
  Source = "https://github.com/grscheller/pythonic-fp-booleans"
32
31
 
33
32
  [project.optional-dependencies]
@@ -13,23 +13,29 @@
13
13
  # limitations under the License.
14
14
 
15
15
  """
16
- Module subtypes
17
- ===============
16
+ Subtypable Boolean Class Hierarchy
17
+ ==================================
18
18
 
19
- Module flavored
20
- ---------------
19
+ .. graphviz::
21
20
 
22
- - when different "flavors" of the truth matter
23
- - the constructor ``FBool(witness: object, flavor: Hashable)`` produces invariant types
21
+ digraph Booleans {
22
+ bgcolor="#957fb8";
23
+ node [style=filled, fillcolor="#181616", fontcolor="#dcd7ba"];
24
+ edge [color="#181616", fontcolor="#dcd7ba"];
25
+ int -> bool;
26
+ int -> SBool;
27
+ SBool -> "FBool(h1)";
28
+ SBool -> "FBool(h2)";
29
+ SBool -> "FBool(h3)";
30
+ SBool -> TF_Bool;
31
+ TF_Bool -> T_Bool;
32
+ TF_Bool -> F_Bool;
33
+ }
24
34
 
25
- Module truthy_falsy
26
- -------------------
35
+ While still compatible with Python shortcut logic, ``SBool`` and its
36
+ subclasses can be non-shortcut logically composed with Python's bitwise
37
+ operators.
27
38
 
28
- - where truthy and falsy are typeable
29
- - class ``TF_Bool`` consists of two disjoint subclasses
30
-
31
- - ``T_Bool`` instances are always truthy
32
- - ``F_Bool`` instances are always falsy
33
39
  """
34
40
 
35
41
  __author__ = 'Geoffrey R. Scheller'
@@ -12,42 +12,46 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
-
16
15
  """
17
- Flavored Booleans
18
- =================
16
+ **Class F_Bool.**
17
+
18
+ When different flavors of the truth matter. Each ``FBool`` is
19
+ an ``SBool`` subtype corresponds to a hashable value
20
+ called its flavor.
21
+
22
+ .. warning::
23
+
24
+ Combining ``FBool`` instances of different flavors
25
+ with bitwise operators will just result in an ``SBool``.
26
+
27
+ ----
28
+
29
+ **Function truthy(flavor: Hashable)**
19
30
 
20
- When different flavors of the truth are needed. Each "flavor" is
21
- "indexed" by a hashable value, not "subtyped" by it.
31
+ Returns the truthy ``FBool`` of a particular ``flavor``.
32
+
33
+ ----
34
+
35
+ **Function falsy(flavor: Hashable)**
36
+
37
+ Returns the falsy ``FBool`` of a particular ``flavor``.
22
38
  """
23
39
 
24
40
  import threading
25
41
  from collections.abc import Hashable
26
42
  from typing import ClassVar, final
27
- from ..subtypable import SBool
43
+ from .subtypable import SBool
28
44
 
29
45
  __all__ = ['FBool', 'truthy', 'falsy']
30
46
 
31
47
 
32
48
  @final
33
49
  class FBool(SBool):
34
- """
35
- Flavored Boolean
36
- ----------------
50
+ _falsy_dict: 'ClassVar[dict[Hashable, FBool]]' = {}
51
+ _falsy_dict_lock: ClassVar[threading.Lock] = threading.Lock()
37
52
 
38
- When different flavors of the truth are needed. Each "flavor" is
39
- "indexed" by a hashable value, not "subtyped" by it.
40
-
41
- This type can also do (non-shortcut) Boolean logic using Python
42
- bitwise operators. Combining ``FBool`` instances of different
43
- flavors in this way will just result in an ``SBool``.
44
- """
45
-
46
- _truthy: 'ClassVar[dict[Hashable, FBool]]' = {}
47
- _truthy_lock: ClassVar[threading.Lock] = threading.Lock()
48
-
49
- _falsy: 'ClassVar[dict[Hashable, FBool]]' = {}
50
- _falsy_lock: ClassVar[threading.Lock] = threading.Lock()
53
+ _truthy_dict: 'ClassVar[dict[Hashable, FBool]]' = {}
54
+ _truthy_dict_lock: ClassVar[threading.Lock] = threading.Lock()
51
55
 
52
56
  def __new__(cls, witness: object, flavor: Hashable) -> 'FBool':
53
57
  """
@@ -56,22 +60,21 @@ class FBool(SBool):
56
60
  :returns: The truthy or falsy ``FBool`` instance of a particular ``flavor``.
57
61
  """
58
62
  if witness:
59
- if flavor not in cls._truthy:
63
+ if flavor not in cls._truthy_dict:
60
64
  with cls._truthy_lock:
61
- if flavor not in cls._truthy:
62
- cls._truthy[flavor] = super(SBool, cls).__new__(cls, True)
63
- return cls._truthy[flavor]
65
+ if flavor not in cls._truthy_dict:
66
+ cls._truthy_dict[flavor] = super(SBool, cls).__new__(cls, True)
67
+ return cls._truthy_dict[flavor]
64
68
  else:
65
- if flavor not in cls._falsy:
66
- with cls._falsy_lock:
67
- if flavor not in cls._falsy:
68
- cls._falsy[flavor] = super(SBool, cls).__new__(cls, False)
69
- return cls._falsy[flavor]
69
+ if flavor not in cls._falsy_dict:
70
+ with cls._falsy_dict_lock:
71
+ if flavor not in cls._falsy_dict:
72
+ cls._falsy_dict[flavor] = super(SBool, cls).__new__(cls, False)
73
+ return cls._falsy_dict[flavor]
70
74
 
71
75
  def __init__(self, witness: object, flavor: Hashable) -> None:
72
76
  self._flavor = flavor
73
77
 
74
- # override in derived classes
75
78
  def __repr__(self) -> str:
76
79
  if self:
77
80
  return f'FBool(True, {repr(self._flavor)})'
@@ -110,34 +113,23 @@ class FBool(SBool):
110
113
  return self ^ other
111
114
 
112
115
  def flavor(self) -> Hashable:
113
- """t
116
+ """
114
117
  :returns: The flavor of the FBool.
115
-
116
118
  """
117
119
  return self._flavor
118
120
 
119
121
 
120
122
  def truthy(flavor: Hashable) -> FBool:
121
123
  """
122
- Get truthy FBool
123
- ----------------
124
-
125
- Get the truthy ``FBool`` of a particular ``flavor``.
126
-
127
124
  :param flavor: Hashable value to determine which singleton ``flavor`` to return.
128
- :returns: The truthy singleton of a particular ``flavor``.
125
+ :returns: The truthy singleton of a particular ``flavor``.
129
126
  """
130
127
  return FBool(True, flavor)
131
128
 
132
129
 
133
130
  def falsy(flavor: Hashable) -> FBool:
134
131
  """
135
- Get falsy FBool
136
- ---------------
137
-
138
- Get the falsy ``FBool`` of a particular ``flavor``.
139
-
140
132
  :param flavor: Hashable value to determine which singleton ``flavor`` to return.
141
- :returns: The falsy singleton of a particular ``flavor``.
133
+ :returns: The falsy singleton of a particular ``flavor``.
142
134
  """
143
135
  return FBool(False, flavor)
@@ -1,4 +1,4 @@
1
- from ..subtypable import SBool
1
+ from .subtypable import SBool
2
2
  from collections.abc import Hashable
3
3
 
4
4
  __all__ = ['FBool', 'truthy', 'falsy']
@@ -12,14 +12,52 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ """
16
+ **Subtypable Boolean.**
15
17
 
16
- """Subtypable Booleans."""
18
+ Like Python's built in bool, class ``SBool`` is a subclass of int.
19
+ Unlike bool, this version can be further subclassed.
20
+
21
+ This type and its subtypes can also do (non-shortcut) Boolean logic
22
+ using Python bitwise operators.
23
+
24
+ +-------------------+--------+------------+
25
+ | Boolean operation | symbol | dunder |
26
+ +===================+========+============+
27
+ | not | ``~`` | __invert__ |
28
+ +-------------------+--------+------------+
29
+ | and | ``&`` | __and__ |
30
+ +-------------------+--------+------------+
31
+ | or | ``|`` | __or__ |
32
+ +-------------------+--------+------------+
33
+ | xor | ``^`` | __xor__ |
34
+ +-------------------+--------+------------+
35
+
36
+ .. note::
37
+
38
+ These operators are contravariant, that is they will return the
39
+ instance of the latest common ancestor of their arguments. More
40
+ specifically, the instance returned will have the type of the
41
+ least upper bound in the inheritance graph of the classes of
42
+ the two arguments.
43
+
44
+ While compatible with Python short-cut logic. Unfortunately, the
45
+ ``not`` operator always returns a ``bool``. Use the bitwise ``~``
46
+ operator to return an opposite ``SBool`` class or subclass.
47
+
48
+ .. warning::
49
+
50
+ The "bitwise" operators can raise ``TypeError`` exceptions
51
+ when applied against an ``SBool`` and objects not descended
52
+ from ``int``.
53
+
54
+ """
17
55
 
18
56
  import threading
19
57
  from collections.abc import Hashable
20
58
  from typing import cast, ClassVar, Final, overload
21
- from pythonic_fp.gadgets.latest_common_ancestor import lca
22
- from pythonic_fp.sentinels.novalue import NoValue
59
+ from pythonic_fp.gadgets import first_common_ancestor as fca
60
+ from pythonic_fp.gadgets.sentinels.novalue import NoValue
23
61
 
24
62
  __all__ = ['SBool', 'TRUTH', 'LIE']
25
63
 
@@ -27,17 +65,6 @@ _novalue = NoValue()
27
65
 
28
66
 
29
67
  class SBool(int):
30
- """Subtypable Booleans.
31
-
32
- Like Python's built in bool, class SBool is a subclass of int.
33
- Unlike bool, this version can be further subclassed. It can be
34
- used with Python ``and`` and ``or`` short-cut logic. The ``not``
35
- operator will just return a ``bool``. Use the ``snot`` function
36
- to return an ``SBool`` or ``SBool`` subclass.
37
-
38
- This type and its subtypes can also do (non-shortcut) Boolean logic
39
- using Python bitwise operators.
40
- """
41
68
  _falsy: 'ClassVar[SBool | NoValue]' = _novalue
42
69
  _falsy_lock: ClassVar[threading.Lock] = threading.Lock()
43
70
 
@@ -83,14 +110,12 @@ class SBool(int):
83
110
 
84
111
  def __and__(self, other: int) -> int:
85
112
  try:
86
- base_class = lca(type(self), type(other))
113
+ base_class = fca(type(self), type(other))
87
114
  except TypeError:
88
115
  if type(other) is bool:
89
116
  base_class = int
90
117
  else:
91
- msg = (
92
- f"unsupported operand type(s) for &: '{type(self)}' and '{type(other)}'"
93
- )
118
+ msg = f"unsupported operand type(s) for &: '{type(self)}' and '{type(other)}'"
94
119
  raise TypeError(msg)
95
120
 
96
121
  if issubclass(base_class, SBool):
@@ -102,14 +127,12 @@ class SBool(int):
102
127
 
103
128
  def __or__(self, other: int) -> int:
104
129
  try:
105
- base_class = lca(type(self), type(other))
130
+ base_class = fca(type(self), type(other))
106
131
  except TypeError:
107
132
  if type(other) is bool:
108
133
  base_class = int
109
134
  else:
110
- msg = (
111
- f"unsupported operand type(s) for |: '{type(self)}' and '{type(other)}'"
112
- )
135
+ msg = f"unsupported operand type(s) for |: '{type(self)}' and '{type(other)}'"
113
136
  raise TypeError(msg)
114
137
 
115
138
  if issubclass(base_class, SBool):
@@ -121,14 +144,12 @@ class SBool(int):
121
144
 
122
145
  def __xor__(self, other: int) -> int:
123
146
  try:
124
- base_class = lca(type(self), type(other))
147
+ base_class = fca(type(self), type(other))
125
148
  except TypeError:
126
149
  if type(other) is bool:
127
150
  base_class = int
128
151
  else:
129
- msg = (
130
- f"unsupported operand type(s) for ^: '{type(self)}' and '{type(other)}'"
131
- )
152
+ msg = f"unsupported operand type(s) for ^: '{type(self)}' and '{type(other)}'"
132
153
  raise TypeError(msg)
133
154
 
134
155
  if issubclass(base_class, SBool):
@@ -12,19 +12,33 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
-
16
15
  """
17
- Truthy-Falsy Booleans
18
- ---------------------
16
+ **Class TF_Bool**
17
+
18
+ Subclass of ``SBool`` whose truthy values and falsy values are
19
+ different distinct singleton subtypes.
20
+
21
+ This type can also do (non-shortcut) Boolean logic using Python
22
+ bitwise operators.
23
+
24
+ ----
19
25
 
26
+ **Class T_Bool**
20
27
 
21
- Booleans whose "truthy" and "falsy" instances are distinct subtypes."""
28
+ The subtype of ``TF_Bool`` which is always truthy.
29
+
30
+ ----
31
+
32
+ **Class F_Bool**
33
+
34
+ The subtype of ``TF_Bool`` which is always falsy.
35
+ """
22
36
 
23
37
  import threading
24
38
  from collections.abc import Hashable
25
39
  from typing import cast, ClassVar, Final, final
26
- from pythonic_fp.sentinels.novalue import NoValue
27
- from ..subtypable import SBool
40
+ from pythonic_fp.gadgets.sentinels.novalue import NoValue
41
+ from .subtypable import SBool
28
42
 
29
43
  __all__ = [
30
44
  'TF_Bool',
@@ -39,22 +53,11 @@ _novalue = NoValue()
39
53
 
40
54
 
41
55
  class TF_Bool(SBool):
42
- """
43
- Truthy-Falsy Boolean
44
- --------------------
45
-
46
- Subclass of ``SBool`` whose truthy values and falsy values are
47
- different singleton subtypes.
48
-
49
- This type can also do (non-shortcut) Boolean logic using Python
50
- bitwise operators.
51
- """
52
-
53
56
  def __new__(cls, witness: object, flavor: Hashable = NoValue()) -> 'TF_Bool':
54
57
  """
55
58
  :param witness: Determines which subtype, ``T_Bool`` or ``F_Bool`` is returned.
56
59
  :param flavor: Ignored parameter, only two flavors, one truthy and one falsy.
57
- :returns: either The singleton truthy or singleton falsy subtypes.
60
+ :returns: Either The singleton truthy or singleton falsy subtypes.
58
61
  """
59
62
  if witness:
60
63
  return T_Bool()
@@ -94,13 +97,6 @@ class TF_Bool(SBool):
94
97
 
95
98
  @final
96
99
  class T_Bool(TF_Bool):
97
- """
98
- Truthy TF_Bool
99
- --------------
100
-
101
- The subtype of ``TF_Bool`` which is always truthy.
102
- """
103
-
104
100
  _truthy: 'ClassVar[T_Bool | NoValue]' = _novalue
105
101
  _lock: ClassVar[threading.Lock] = threading.Lock()
106
102
 
@@ -126,13 +122,6 @@ class T_Bool(TF_Bool):
126
122
 
127
123
  @final
128
124
  class F_Bool(TF_Bool):
129
- """
130
- Falsy TF_Bool
131
- -------------
132
-
133
- The subtype of ``TF_Bool`` which is always falsy.
134
- """
135
-
136
125
  _falsy: 'ClassVar[F_Bool | NoValue]' = _novalue
137
126
  _lock: ClassVar[threading.Lock] = threading.Lock()
138
127
 
@@ -1,4 +1,4 @@
1
- from ..subtypable import SBool
1
+ from .subtypable import SBool
2
2
  from collections.abc import Hashable
3
3
  from typing import Final
4
4
 
@@ -13,7 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  from pythonic_fp.booleans.subtypable import SBool, TRUTH, LIE
16
- from pythonic_fp.booleans.subtypes.flavored import FBool, truthy, falsy
16
+ from pythonic_fp.booleans.flavored import FBool, truthy, falsy
17
17
 
18
18
  class TestBooleanBehaviors():
19
19
  def test_bool(self) -> None:
@@ -12,7 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from pythonic_fp.booleans.subtypes.flavored import FBool, truthy, falsy
15
+ from pythonic_fp.booleans.flavored import FBool, truthy, falsy
16
16
  from pythonic_fp.booleans.subtypable import TRUTH, LIE
17
17
 
18
18
  t0_1 = FBool(1 == 1, 0)
@@ -13,7 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  from pythonic_fp.booleans.subtypable import TRUTH, LIE
16
- from pythonic_fp.booleans.subtypes.flavored import FBool, truthy, falsy
16
+ from pythonic_fp.booleans.flavored import FBool, truthy, falsy
17
17
 
18
18
 
19
19
  class TestBitwiseOperations():
@@ -12,7 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from pythonic_fp.booleans.subtypes.flavored import FBool, truthy, falsy
15
+ from pythonic_fp.booleans.flavored import FBool, truthy, falsy
16
16
  from pythonic_fp.booleans.subtypable import SBool, TRUTH, LIE
17
17
 
18
18
  fbt0_1 = FBool(1 == 1, 0)
@@ -13,7 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  from pythonic_fp.booleans.subtypable import SBool, TRUTH, LIE
16
- from pythonic_fp.booleans.subtypes.truthy_falsy import (
16
+ from pythonic_fp.booleans.truthy_falsy import (
17
17
  TF_Boolean,
18
18
  T_Bool,
19
19
  F_Bool,
@@ -13,8 +13,8 @@
13
13
  # limitations under the License.
14
14
 
15
15
  from pythonic_fp.booleans.subtypable import TRUTH, LIE
16
- from pythonic_fp.booleans.subtypes.truthy_falsy import TF_Boolean, TF_Bool, T_Bool, F_Bool
17
- from pythonic_fp.booleans.subtypes.truthy_falsy import ALWAYS, NEVER
16
+ from pythonic_fp.booleans.truthy_falsy import TF_Boolean, TF_Bool, T_Bool, F_Bool
17
+ from pythonic_fp.booleans.truthy_falsy import ALWAYS, NEVER
18
18
 
19
19
  false0: TF_Boolean = F_Bool('ignored', 'also ignored')
20
20
  false1: TF_Boolean = TF_Bool(1 > 42)
@@ -13,8 +13,8 @@
13
13
  # limitations under the License.
14
14
 
15
15
  from pythonic_fp.booleans.subtypable import SBool
16
- from pythonic_fp.booleans.subtypes.flavored import FBool, truthy, falsy
17
- from pythonic_fp.booleans.subtypes.truthy_falsy import TF_Bool, T_Bool, F_Bool
16
+ from pythonic_fp.booleans.flavored import FBool, truthy, falsy
17
+ from pythonic_fp.booleans.truthy_falsy import TF_Bool, T_Bool, F_Bool
18
18
 
19
19
 
20
20
  class TestInvert:
@@ -12,8 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from pythonic_fp.booleans.subtypes.truthy_falsy import TF_Bool, TF_Boolean, T_Bool, F_Bool, ALWAYS, NEVER
16
- from pythonic_fp.booleans.subtypes.flavored import FBool, truthy, falsy
15
+ from pythonic_fp.booleans.truthy_falsy import TF_Bool, TF_Boolean, T_Bool, F_Bool, ALWAYS, NEVER
16
+ from pythonic_fp.booleans.flavored import FBool, truthy, falsy
17
17
  from pythonic_fp.booleans.subtypable import SBool, TRUTH, LIE
18
18
 
19
19
  class TestBoolWithInt():
@@ -1,46 +0,0 @@
1
- CHANGELOG
2
- =========
3
-
4
- PyPI pythonic-fp-booleans project.
5
-
6
- Semantic Versioning
7
- -------------------
8
-
9
- Strict 3 digit semantic versioning adopted 2025-05-19.
10
-
11
- - **MAJOR** version incremented for incompatible API changes
12
- - **MINOR** version incremented for backward compatible added functionality
13
- - **PATCH** version incremented for backward compatible bug fixes
14
-
15
- See `Semantic Versioning 2.0.0 <https://semver.org>`_.
16
-
17
- Releases and Important Milestones
18
- ---------------------------------
19
-
20
- PyPI v1.1.1 - 2025-09-03
21
- ~~~~~~~~~~~~~~~~~~~~~~~~
22
-
23
- Only change was to give README.rst a final edit. Missed this on
24
- the v1.1.0 release.
25
-
26
-
27
- PyPI v1.1.0 - 2025-09-02
28
- ~~~~~~~~~~~~~~~~~~~~~~~~
29
-
30
- First PyPI release as pythonic-fp-booleans.
31
-
32
- - module booleans.subtypable
33
- - package booleans.subtypes
34
-
35
- - module booleans.subtypes.flavored
36
- - module booleans.subtypes.truthy_falsy
37
-
38
- Needs
39
-
40
- - "pythonic-fp>=3.0.0" for gadgets
41
- - "pythonic-fp-sentinels>=2.1.0",
42
-
43
- Created pythonic-fp-boolean GitHub repo - 2025-08-06
44
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
45
-
46
- Moved pythonic_fp.singletons.sbool -> pythonic_fp.booleans.sbool
@@ -1,99 +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
- """
16
- Subtypable Boolean like classes
17
- ===============================
18
-
19
- While still compatible with Python shortcut logic, these classes can
20
- be non-shortcut logically composed with Python's bitwise operators.
21
- These classes are implemented with the Singleton Pattern.
22
-
23
- Covariant class hierarchy
24
- -------------------------
25
-
26
- .. graphviz::
27
-
28
- digraph Booleans {
29
- bgcolor="deepskyblue";
30
- int -> bool;
31
- int -> SBool;
32
- SBool -> "FBool(h1)";
33
- SBool -> "FBool(h2)";
34
- SBool -> "FBool(h3)";
35
- SBool -> TF_Bool;
36
- TF_Bool -> T_Bool;
37
- TF_Bool -> F_Bool;
38
- }
39
-
40
- Contravariant non-shortcut "bitwise" operators
41
- ----------------------------------------------
42
-
43
- +-------------------+--------+------------+
44
- | Boolean operation | symbol | dunder |
45
- +===================+========+============+
46
- | not | ``~`` | __invert__ |
47
- +-------------------+--------+------------+
48
- | and | ``&`` | __and__ |
49
- +-------------------+--------+------------+
50
- | or | ``|`` | __or__ |
51
- +-------------------+--------+------------+
52
- | xor | ``^`` | __xor__ |
53
- +-------------------+--------+------------+
54
-
55
- These operators are contravariant, that is they will return
56
- the instance of the latest common ancestor of their arguments.
57
- More specifically, the instance returned will have the type
58
- of the least upper bound in the inheritance graph of the classes
59
- of the two arguments.
60
-
61
- .. warning::
62
-
63
- These "bitwise" operators could raise ``TypeError`` exceptions
64
- when applied against an ``SBool`` and objects not descended
65
- from ``int``.
66
-
67
- Classes
68
- -------
69
-
70
- Class SBool
71
- ~~~~~~~~~~~
72
-
73
- Base of the hierarchy.
74
-
75
- Like Python's built-in ``bool``, ``SBool`` is a subclass of ``int``,
76
- unlike ``bool``, class ``SBool`` can be further subclassed.
77
-
78
- Class FBool
79
- ~~~~~~~~~~~
80
-
81
- For when you need to deal with different "flavors" of the truth.
82
-
83
- Each "flavor" corresponds to a hashable value. Instances of ``FBool``
84
- are invariant in their flavor. Best to think of the "flavor" as an
85
- index.
86
-
87
- Class TF_Bool
88
- ~~~~~~~~~~~~~
89
-
90
- Class ``TF_Bool`` consists of just two disjoint subclasses, each one
91
- a singleton.
92
-
93
- - class ``T_Bool`` is the always truthy ``TF_Bool`` subtype
94
- - class ``F_Bool`` is the always falsy ``TF_Bool`` subtype
95
- """
96
-
97
- __author__ = 'Geoffrey R. Scheller'
98
- __copyright__ = 'Copyright (c) 2025 Geoffrey R. Scheller'
99
- __license__ = 'Apache License 2.0'