requireit 0.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.
@@ -0,0 +1,5 @@
1
+ # Credits
2
+
3
+ ## Development Leads
4
+
5
+ - [Eric Hutton](https://github.com/mcflugen)
@@ -0,0 +1,11 @@
1
+ # Release Notes
2
+
3
+ ## 0.1.0 (not yet released)
4
+
5
+ - Added documentation to the README [#1](https://github.com/mcflugen/requireit/issues/1)
6
+ - Added project metadata files [#2](https://github.com/mcflugen/requireit/issues/2)
7
+ - Added the `requireit` module [#3](https://github.com/mcflugen/requireit/issues/3)
8
+ - Added `pyproject.toml` file [#4](https://github.com/mcflugen/requireit/issues/4)
9
+ - Added `noxfile.py` file and linters [#5](https://github.com/mcflugen/requireit/issues/5)
10
+ - Added unit tests for `requireit` [#6](https://github.com/mcflugen/requireit/issues/6)
11
+ - Added GitHub Actions for CI [#7](https://github.com/mcflugen/requireit/issues/7)
@@ -0,0 +1,24 @@
1
+ # The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Eric Hutton
4
+
5
+ Permission is hereby granted, free of charge, to any person
6
+ obtaining a copy of this software and associated documentation
7
+ files (the "Software"), to deal in the Software without
8
+ restriction, including without limitation the rights to use,
9
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the
11
+ Software is furnished to do so, subject to the following
12
+ conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,205 @@
1
+ Metadata-Version: 2.4
2
+ Name: requireit
3
+ Version: 0.1.0
4
+ Summary: Tiny runtime validators for explicit precondition checks
5
+ Author-email: Eric Hutton <mcflugen@gmail.com>
6
+ Maintainer-email: Eric Hutton <mcflugen@gmail.com>
7
+ License-Expression: MIT
8
+ Project-URL: homepage, https://github.com/mcflugen/requireit
9
+ Project-URL: documentation, https://github.com/mcflugen/requireit/blob/main/README.md
10
+ Project-URL: repository, https://github.com/mcflugen/requireit
11
+ Project-URL: changelog, https://github.com/mcflugen/requireit/blob/main/CHANGES.md
12
+ Keywords: preconditions,runtime-checks,utilities,validation
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Programming Language :: Python :: 3 :: Only
23
+ Classifier: Programming Language :: Python :: Implementation :: CPython
24
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
25
+ Requires-Python: >=3.10
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE.md
28
+ Requires-Dist: numpy
29
+ Provides-Extra: dev
30
+ Requires-Dist: nox; extra == "dev"
31
+ Dynamic: license-file
32
+
33
+ # requireit
34
+
35
+ **Tiny, numpy-aware runtime validators for explicit precondition checks.**
36
+
37
+ `requireit` provides a small collection of lightweight helper functions such as
38
+ `require_positive`, `require_between`, and `validate_array` for validating values
39
+ and arrays at runtime.
40
+
41
+ It is intentionally minimal and dependency-light (*numpy* only).
42
+
43
+ ## Why `requireit`?
44
+
45
+ * **Explicit** – reads clearly
46
+ * **numpy-aware** – works correctly with scalars *and* arrays
47
+ * **Fail-fast** – raises immediately with clear error messages
48
+ * **Lightweight** – just a bunch of small functions
49
+ * **Reusable** – avoids copy-pasted validation code across projects
50
+
51
+ ```python
52
+ from requireit import require_one_of
53
+ from requireit import require_positive
54
+
55
+ require_positive(dt)
56
+ require_one_of(method, allowed={"foo", "bar"})
57
+ ```
58
+
59
+ ## Design principles
60
+
61
+ * Prefer small, single-purpose functions
62
+ * Raise standard exceptions (`ValidationError`)
63
+ * Never coerce or "fix" invalid inputs
64
+ * Validate *all* elements for array-like inputs
65
+ * Keep the public API small
66
+
67
+ ## Non-goals
68
+
69
+ `requireit` is **not**:
70
+
71
+ * a schema or data-modeling system
72
+ * a replacement for static typing
73
+ * a validation framework
74
+ * a substitute for unit tests
75
+ * a coercion or parsing library
76
+
77
+ If you need structured validation, transformations, or user-facing error
78
+ aggregation, you probably want something heavier.
79
+
80
+ ## Installation
81
+
82
+ ```bash
83
+ pip install requireit
84
+ ```
85
+
86
+ ## API overview
87
+
88
+ All validators:
89
+
90
+ * return the original value/array on success
91
+ * raise `ValidationError` on failure
92
+
93
+ ### Membership validation
94
+
95
+ ```python
96
+ require_one_of(value, *, allowed)
97
+ ```
98
+
99
+ Validate that a value is one of a set of allowed values.
100
+
101
+ ```python
102
+ require_one_of("foo", allowed=("foo", "bar")) # ok, returns "foo"
103
+ require_one_of("baz", allowed=("foo", "bar")) # raises ValidationError
104
+ ```
105
+
106
+ ### Range validation
107
+
108
+ ```python
109
+ require_between(
110
+ value,
111
+ a_min=None,
112
+ a_max=None,
113
+ *,
114
+ inclusive_min=True,
115
+ inclusive_max=True,
116
+ )
117
+ ```
118
+
119
+ Validate that a scalar or array lies within specified bounds.
120
+
121
+ * Bounds may be inclusive or strict
122
+ * Validation fails if **any element** violates the constraint
123
+
124
+ ```python
125
+ require_between([0, 1], a_min=0.0) # ok, returns [0, 1]
126
+ require_between([0, 1], a_min=0.0, inclusive_min=False) # raises
127
+ ```
128
+
129
+ ### Sign-based helpers
130
+
131
+ Convenience wrappers around `require_between`:
132
+
133
+ ```python
134
+ require_positive(value) # > 0
135
+ require_nonnegative(value) # >= 0
136
+ require_negative(value) # < 0
137
+ require_nonpositive(value) # <= 0
138
+ ```
139
+
140
+ All accept scalars or array-like inputs.
141
+
142
+
143
+ ### Array validation
144
+
145
+ ```python
146
+ validate_array(
147
+ array,
148
+ *,
149
+ dtype=None,
150
+ shape=None,
151
+ writable=None,
152
+ contiguous=None,
153
+ )
154
+ ```
155
+
156
+ Validate NumPy array properties without copying or modifying the array.
157
+
158
+ ```python
159
+ validate_array(x, dtype=np.float64, shape=(100,))
160
+ validate_array(x, writable=True, contiguous=True)
161
+ ```
162
+
163
+ Checks are applied only if the corresponding keyword is provided.
164
+
165
+
166
+ ## Errors
167
+
168
+ All validation failures raise:
169
+
170
+ ```python
171
+ requireit.ValidationError
172
+ ```
173
+
174
+ This allows callers to catch validation failures distinctly from other errors.
175
+
176
+
177
+ ## Contributing
178
+
179
+ This project is intentionally small.
180
+
181
+ Contributions should preserve:
182
+
183
+ * minimal surface area
184
+ * explicit semantics
185
+ * no additional dependencies
186
+
187
+ If a proposed change needs much explanation, it probably doesn’t belong here.
188
+
189
+ # Credits
190
+
191
+ ## Development Leads
192
+
193
+ - [Eric Hutton](https://github.com/mcflugen)
194
+
195
+ # Release Notes
196
+
197
+ ## 0.1.0 (not yet released)
198
+
199
+ - Added documentation to the README [#1](https://github.com/mcflugen/requireit/issues/1)
200
+ - Added project metadata files [#2](https://github.com/mcflugen/requireit/issues/2)
201
+ - Added the `requireit` module [#3](https://github.com/mcflugen/requireit/issues/3)
202
+ - Added `pyproject.toml` file [#4](https://github.com/mcflugen/requireit/issues/4)
203
+ - Added `noxfile.py` file and linters [#5](https://github.com/mcflugen/requireit/issues/5)
204
+ - Added unit tests for `requireit` [#6](https://github.com/mcflugen/requireit/issues/6)
205
+ - Added GitHub Actions for CI [#7](https://github.com/mcflugen/requireit/issues/7)
@@ -0,0 +1,155 @@
1
+ # requireit
2
+
3
+ **Tiny, numpy-aware runtime validators for explicit precondition checks.**
4
+
5
+ `requireit` provides a small collection of lightweight helper functions such as
6
+ `require_positive`, `require_between`, and `validate_array` for validating values
7
+ and arrays at runtime.
8
+
9
+ It is intentionally minimal and dependency-light (*numpy* only).
10
+
11
+ ## Why `requireit`?
12
+
13
+ * **Explicit** – reads clearly
14
+ * **numpy-aware** – works correctly with scalars *and* arrays
15
+ * **Fail-fast** – raises immediately with clear error messages
16
+ * **Lightweight** – just a bunch of small functions
17
+ * **Reusable** – avoids copy-pasted validation code across projects
18
+
19
+ ```python
20
+ from requireit import require_one_of
21
+ from requireit import require_positive
22
+
23
+ require_positive(dt)
24
+ require_one_of(method, allowed={"foo", "bar"})
25
+ ```
26
+
27
+ ## Design principles
28
+
29
+ * Prefer small, single-purpose functions
30
+ * Raise standard exceptions (`ValidationError`)
31
+ * Never coerce or "fix" invalid inputs
32
+ * Validate *all* elements for array-like inputs
33
+ * Keep the public API small
34
+
35
+ ## Non-goals
36
+
37
+ `requireit` is **not**:
38
+
39
+ * a schema or data-modeling system
40
+ * a replacement for static typing
41
+ * a validation framework
42
+ * a substitute for unit tests
43
+ * a coercion or parsing library
44
+
45
+ If you need structured validation, transformations, or user-facing error
46
+ aggregation, you probably want something heavier.
47
+
48
+ ## Installation
49
+
50
+ ```bash
51
+ pip install requireit
52
+ ```
53
+
54
+ ## API overview
55
+
56
+ All validators:
57
+
58
+ * return the original value/array on success
59
+ * raise `ValidationError` on failure
60
+
61
+ ### Membership validation
62
+
63
+ ```python
64
+ require_one_of(value, *, allowed)
65
+ ```
66
+
67
+ Validate that a value is one of a set of allowed values.
68
+
69
+ ```python
70
+ require_one_of("foo", allowed=("foo", "bar")) # ok, returns "foo"
71
+ require_one_of("baz", allowed=("foo", "bar")) # raises ValidationError
72
+ ```
73
+
74
+ ### Range validation
75
+
76
+ ```python
77
+ require_between(
78
+ value,
79
+ a_min=None,
80
+ a_max=None,
81
+ *,
82
+ inclusive_min=True,
83
+ inclusive_max=True,
84
+ )
85
+ ```
86
+
87
+ Validate that a scalar or array lies within specified bounds.
88
+
89
+ * Bounds may be inclusive or strict
90
+ * Validation fails if **any element** violates the constraint
91
+
92
+ ```python
93
+ require_between([0, 1], a_min=0.0) # ok, returns [0, 1]
94
+ require_between([0, 1], a_min=0.0, inclusive_min=False) # raises
95
+ ```
96
+
97
+ ### Sign-based helpers
98
+
99
+ Convenience wrappers around `require_between`:
100
+
101
+ ```python
102
+ require_positive(value) # > 0
103
+ require_nonnegative(value) # >= 0
104
+ require_negative(value) # < 0
105
+ require_nonpositive(value) # <= 0
106
+ ```
107
+
108
+ All accept scalars or array-like inputs.
109
+
110
+
111
+ ### Array validation
112
+
113
+ ```python
114
+ validate_array(
115
+ array,
116
+ *,
117
+ dtype=None,
118
+ shape=None,
119
+ writable=None,
120
+ contiguous=None,
121
+ )
122
+ ```
123
+
124
+ Validate NumPy array properties without copying or modifying the array.
125
+
126
+ ```python
127
+ validate_array(x, dtype=np.float64, shape=(100,))
128
+ validate_array(x, writable=True, contiguous=True)
129
+ ```
130
+
131
+ Checks are applied only if the corresponding keyword is provided.
132
+
133
+
134
+ ## Errors
135
+
136
+ All validation failures raise:
137
+
138
+ ```python
139
+ requireit.ValidationError
140
+ ```
141
+
142
+ This allows callers to catch validation failures distinctly from other errors.
143
+
144
+
145
+ ## Contributing
146
+
147
+ This project is intentionally small.
148
+
149
+ Contributions should preserve:
150
+
151
+ * minimal surface area
152
+ * explicit semantics
153
+ * no additional dependencies
154
+
155
+ If a proposed change needs much explanation, it probably doesn’t belong here.
@@ -0,0 +1,80 @@
1
+ [build-system]
2
+ requires = ["setuptools", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "requireit"
7
+ description = "Tiny runtime validators for explicit precondition checks"
8
+ authors = [
9
+ {name = "Eric Hutton", email = "mcflugen@gmail.com"},
10
+ ]
11
+ maintainers = [
12
+ {name = "Eric Hutton", email = "mcflugen@gmail.com"},
13
+ ]
14
+ keywords = [
15
+ "preconditions",
16
+ "runtime-checks",
17
+ "utilities",
18
+ "validation",
19
+ ]
20
+ license = "MIT"
21
+ license-files = ["LICENSE.md"]
22
+ classifiers = [
23
+ "Development Status :: 4 - Beta",
24
+ "Intended Audience :: Developers",
25
+ "Operating System :: OS Independent",
26
+ "Programming Language :: Python :: 3",
27
+ "Programming Language :: Python :: 3.10",
28
+ "Programming Language :: Python :: 3.11",
29
+ "Programming Language :: Python :: 3.12",
30
+ "Programming Language :: Python :: 3.13",
31
+ "Programming Language :: Python :: 3.14",
32
+ "Programming Language :: Python :: 3 :: Only",
33
+ "Programming Language :: Python :: Implementation :: CPython",
34
+ "Programming Language :: Python :: Implementation :: PyPy",
35
+ ]
36
+ requires-python = ">=3.10"
37
+ dependencies = [
38
+ "numpy",
39
+ ]
40
+ dynamic = ["readme", "version"]
41
+
42
+ [project.urls]
43
+ homepage = "https://github.com/mcflugen/requireit"
44
+ documentation = "https://github.com/mcflugen/requireit/blob/main/README.md"
45
+ repository = "https://github.com/mcflugen/requireit"
46
+ changelog = "https://github.com/mcflugen/requireit/blob/main/CHANGES.md"
47
+
48
+ [project.optional-dependencies]
49
+ dev = ["nox"]
50
+
51
+ [tool.setuptools]
52
+ package-dir = {"" = "src"}
53
+ py-modules = ["requireit"]
54
+
55
+ [tool.setuptools.dynamic]
56
+ readme = {file = ["README.md", "AUTHORS.md", "CHANGES.md"], content-type="text/markdown"}
57
+ version = {attr = "requireit.__version__"}
58
+
59
+ [tool.pytest.ini_options]
60
+ minversion = "6.0"
61
+ testpaths = ["tests", "src/requireit.py"]
62
+ norecursedirs = [".*", "*.egg*", "build", "dist", "utilities"]
63
+ addopts = [
64
+ "--ignore=setup.py",
65
+ "--tb=native",
66
+ "--strict-config",
67
+ "--strict-markers",
68
+ "--durations=16",
69
+ "--doctest-modules",
70
+ "-vvv",
71
+ ]
72
+ doctest_optionflags = [
73
+ "NORMALIZE_WHITESPACE",
74
+ "IGNORE_EXCEPTION_DETAIL",
75
+ "ALLOW_UNICODE"
76
+ ]
77
+
78
+ [tool.isort]
79
+ combine_as_imports = true
80
+ profile = "black"
@@ -0,0 +1,10 @@
1
+ [flake8]
2
+ exclude = build, .nox
3
+ extend-ignore = B024, C901, E203
4
+ max-line-length = 88
5
+ max-complexity = 4
6
+
7
+ [egg_info]
8
+ tag_build =
9
+ tag_date = 0
10
+
@@ -0,0 +1,205 @@
1
+ Metadata-Version: 2.4
2
+ Name: requireit
3
+ Version: 0.1.0
4
+ Summary: Tiny runtime validators for explicit precondition checks
5
+ Author-email: Eric Hutton <mcflugen@gmail.com>
6
+ Maintainer-email: Eric Hutton <mcflugen@gmail.com>
7
+ License-Expression: MIT
8
+ Project-URL: homepage, https://github.com/mcflugen/requireit
9
+ Project-URL: documentation, https://github.com/mcflugen/requireit/blob/main/README.md
10
+ Project-URL: repository, https://github.com/mcflugen/requireit
11
+ Project-URL: changelog, https://github.com/mcflugen/requireit/blob/main/CHANGES.md
12
+ Keywords: preconditions,runtime-checks,utilities,validation
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Programming Language :: Python :: 3 :: Only
23
+ Classifier: Programming Language :: Python :: Implementation :: CPython
24
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
25
+ Requires-Python: >=3.10
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE.md
28
+ Requires-Dist: numpy
29
+ Provides-Extra: dev
30
+ Requires-Dist: nox; extra == "dev"
31
+ Dynamic: license-file
32
+
33
+ # requireit
34
+
35
+ **Tiny, numpy-aware runtime validators for explicit precondition checks.**
36
+
37
+ `requireit` provides a small collection of lightweight helper functions such as
38
+ `require_positive`, `require_between`, and `validate_array` for validating values
39
+ and arrays at runtime.
40
+
41
+ It is intentionally minimal and dependency-light (*numpy* only).
42
+
43
+ ## Why `requireit`?
44
+
45
+ * **Explicit** – reads clearly
46
+ * **numpy-aware** – works correctly with scalars *and* arrays
47
+ * **Fail-fast** – raises immediately with clear error messages
48
+ * **Lightweight** – just a bunch of small functions
49
+ * **Reusable** – avoids copy-pasted validation code across projects
50
+
51
+ ```python
52
+ from requireit import require_one_of
53
+ from requireit import require_positive
54
+
55
+ require_positive(dt)
56
+ require_one_of(method, allowed={"foo", "bar"})
57
+ ```
58
+
59
+ ## Design principles
60
+
61
+ * Prefer small, single-purpose functions
62
+ * Raise standard exceptions (`ValidationError`)
63
+ * Never coerce or "fix" invalid inputs
64
+ * Validate *all* elements for array-like inputs
65
+ * Keep the public API small
66
+
67
+ ## Non-goals
68
+
69
+ `requireit` is **not**:
70
+
71
+ * a schema or data-modeling system
72
+ * a replacement for static typing
73
+ * a validation framework
74
+ * a substitute for unit tests
75
+ * a coercion or parsing library
76
+
77
+ If you need structured validation, transformations, or user-facing error
78
+ aggregation, you probably want something heavier.
79
+
80
+ ## Installation
81
+
82
+ ```bash
83
+ pip install requireit
84
+ ```
85
+
86
+ ## API overview
87
+
88
+ All validators:
89
+
90
+ * return the original value/array on success
91
+ * raise `ValidationError` on failure
92
+
93
+ ### Membership validation
94
+
95
+ ```python
96
+ require_one_of(value, *, allowed)
97
+ ```
98
+
99
+ Validate that a value is one of a set of allowed values.
100
+
101
+ ```python
102
+ require_one_of("foo", allowed=("foo", "bar")) # ok, returns "foo"
103
+ require_one_of("baz", allowed=("foo", "bar")) # raises ValidationError
104
+ ```
105
+
106
+ ### Range validation
107
+
108
+ ```python
109
+ require_between(
110
+ value,
111
+ a_min=None,
112
+ a_max=None,
113
+ *,
114
+ inclusive_min=True,
115
+ inclusive_max=True,
116
+ )
117
+ ```
118
+
119
+ Validate that a scalar or array lies within specified bounds.
120
+
121
+ * Bounds may be inclusive or strict
122
+ * Validation fails if **any element** violates the constraint
123
+
124
+ ```python
125
+ require_between([0, 1], a_min=0.0) # ok, returns [0, 1]
126
+ require_between([0, 1], a_min=0.0, inclusive_min=False) # raises
127
+ ```
128
+
129
+ ### Sign-based helpers
130
+
131
+ Convenience wrappers around `require_between`:
132
+
133
+ ```python
134
+ require_positive(value) # > 0
135
+ require_nonnegative(value) # >= 0
136
+ require_negative(value) # < 0
137
+ require_nonpositive(value) # <= 0
138
+ ```
139
+
140
+ All accept scalars or array-like inputs.
141
+
142
+
143
+ ### Array validation
144
+
145
+ ```python
146
+ validate_array(
147
+ array,
148
+ *,
149
+ dtype=None,
150
+ shape=None,
151
+ writable=None,
152
+ contiguous=None,
153
+ )
154
+ ```
155
+
156
+ Validate NumPy array properties without copying or modifying the array.
157
+
158
+ ```python
159
+ validate_array(x, dtype=np.float64, shape=(100,))
160
+ validate_array(x, writable=True, contiguous=True)
161
+ ```
162
+
163
+ Checks are applied only if the corresponding keyword is provided.
164
+
165
+
166
+ ## Errors
167
+
168
+ All validation failures raise:
169
+
170
+ ```python
171
+ requireit.ValidationError
172
+ ```
173
+
174
+ This allows callers to catch validation failures distinctly from other errors.
175
+
176
+
177
+ ## Contributing
178
+
179
+ This project is intentionally small.
180
+
181
+ Contributions should preserve:
182
+
183
+ * minimal surface area
184
+ * explicit semantics
185
+ * no additional dependencies
186
+
187
+ If a proposed change needs much explanation, it probably doesn’t belong here.
188
+
189
+ # Credits
190
+
191
+ ## Development Leads
192
+
193
+ - [Eric Hutton](https://github.com/mcflugen)
194
+
195
+ # Release Notes
196
+
197
+ ## 0.1.0 (not yet released)
198
+
199
+ - Added documentation to the README [#1](https://github.com/mcflugen/requireit/issues/1)
200
+ - Added project metadata files [#2](https://github.com/mcflugen/requireit/issues/2)
201
+ - Added the `requireit` module [#3](https://github.com/mcflugen/requireit/issues/3)
202
+ - Added `pyproject.toml` file [#4](https://github.com/mcflugen/requireit/issues/4)
203
+ - Added `noxfile.py` file and linters [#5](https://github.com/mcflugen/requireit/issues/5)
204
+ - Added unit tests for `requireit` [#6](https://github.com/mcflugen/requireit/issues/6)
205
+ - Added GitHub Actions for CI [#7](https://github.com/mcflugen/requireit/issues/7)
@@ -0,0 +1,12 @@
1
+ AUTHORS.md
2
+ CHANGES.md
3
+ LICENSE.md
4
+ README.md
5
+ pyproject.toml
6
+ setup.cfg
7
+ src/requireit.py
8
+ src/requireit.egg-info/PKG-INFO
9
+ src/requireit.egg-info/SOURCES.txt
10
+ src/requireit.egg-info/dependency_links.txt
11
+ src/requireit.egg-info/requires.txt
12
+ src/requireit.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+ numpy
2
+
3
+ [dev]
4
+ nox
@@ -0,0 +1 @@
1
+ requireit
@@ -0,0 +1,306 @@
1
+ from collections.abc import Callable
2
+ from collections.abc import Collection
3
+ from collections.abc import Iterable
4
+ from typing import Any
5
+
6
+ import numpy as np
7
+ from numpy.typing import ArrayLike
8
+ from numpy.typing import DTypeLike
9
+ from numpy.typing import NDArray
10
+
11
+ __version__ = "0.1.0"
12
+
13
+
14
+ class RequireItError(Exception):
15
+ """Base class for exceptions raised from this module."""
16
+
17
+
18
+ class ValidationError(RequireItError):
19
+ """Error to indicate that a validation has failed."""
20
+
21
+
22
+ def require_one_of(value: Any, *, allowed: Iterable[Any]) -> Any:
23
+ """Validate that ``value`` is one of ``allowed``.
24
+
25
+ Parameters
26
+ ----------
27
+ value : any
28
+ A scalar value to validate.
29
+ allowed : iterable of any
30
+ Allowed values.
31
+
32
+ Returns
33
+ -------
34
+ any
35
+ The original value.
36
+
37
+ Raises
38
+ ------
39
+ ValidationError
40
+ If the value is not in ``allowed``.
41
+
42
+ Examples
43
+ --------
44
+ >>> require_one_of("foo", allowed=("foo", "bar"))
45
+ 'foo'
46
+ >>> require_one_of("baz", allowed=("foo", "bar"))
47
+ Traceback (most recent call last):
48
+ ...
49
+ requireit.ValidationError: invalid value: 'baz' is not one of 'bar', 'foo'
50
+ >>> require_one_of("Foo", allowed=("foo", "bar"))
51
+ Traceback (most recent call last):
52
+ ...
53
+ requireit.ValidationError: invalid value: 'Foo' is not one of 'bar', 'foo'
54
+ """
55
+ try:
56
+ collection_of_allowed: Collection = set(allowed)
57
+ in_collection = value in collection_of_allowed
58
+ except TypeError:
59
+ collection_of_allowed = list(allowed)
60
+ in_collection = value in collection_of_allowed
61
+
62
+ if not in_collection:
63
+ allowed_str = ", ".join(sorted(repr(x) for x in collection_of_allowed))
64
+ raise ValidationError(f"invalid value: {value!r} is not one of {allowed_str}")
65
+ return value
66
+
67
+
68
+ def require_between(
69
+ value: ArrayLike,
70
+ a_min: float | None = None,
71
+ a_max: float | None = None,
72
+ *,
73
+ inclusive_min: bool = True,
74
+ inclusive_max: bool = True,
75
+ ) -> ArrayLike:
76
+ """Validate that a value lies within a specified interval.
77
+
78
+ Parameters
79
+ ----------
80
+ value : scalar or array-like
81
+ The input value(s) to be validated.
82
+ a_min : float or None, optional
83
+ Minimum allowable value. If ``None``, no lower bound is applied.
84
+ a_max : float or None, optional
85
+ Maximum allowable value. If ``None``, no upper bound is applied.
86
+ inclusive_min : bool, optional
87
+ If ``True`` (default), the lower bound is inclusive (``>=``).
88
+ If ``False``, the lower bound is strict (``>``).
89
+ inclusive_max : bool, optional
90
+ If ``True`` (default), the upper bound is inclusive (``<=``).
91
+ If ``False``, the upper bound is strict (``<``).
92
+
93
+ Returns
94
+ -------
95
+ value : scalar or array-like
96
+ The validated value.
97
+
98
+ Raises
99
+ ------
100
+ ValidationError
101
+ If any element of ``value`` violates the specified bounds.
102
+
103
+ Examples
104
+ --------
105
+ >>> require_between([0, 1], a_min=0.0, inclusive_min=True)
106
+ [0, 1]
107
+ >>> require_between([0, 1], a_min=0.0, inclusive_min=False)
108
+ Traceback (most recent call last):
109
+ ...
110
+ requireit.ValidationError: value must be > 0.0
111
+ """
112
+ arr = np.asarray(value)
113
+
114
+ if a_min is not None:
115
+ cmp: Callable = np.less if inclusive_min else np.less_equal
116
+ op = ">=" if inclusive_min else ">"
117
+ if np.any(cmp(arr, a_min)):
118
+ raise ValidationError(f"value must be {op} {a_min}")
119
+
120
+ if a_max is not None:
121
+ cmp = np.greater if inclusive_max else np.greater_equal
122
+ op = "<=" if inclusive_max else "<"
123
+ if np.any(cmp(arr, a_max)):
124
+ raise ValidationError(f"value must be {op} {a_max}")
125
+
126
+ return value
127
+
128
+
129
+ def require_positive(value: ArrayLike) -> ArrayLike:
130
+ """Validate that a value is strictly greater than zero.
131
+
132
+ Parameters
133
+ ----------
134
+ value : scalar or array-like
135
+ The input value(s) to be validated.
136
+
137
+ Returns
138
+ -------
139
+ value : scalar or array-like
140
+ The validated value.
141
+
142
+ Raises
143
+ ------
144
+ ValidationError
145
+ If any element of ``value`` is less than or equal to zero.
146
+
147
+ Examples
148
+ --------
149
+ >>> require_positive(1.0)
150
+ 1.0
151
+ >>> require_positive(0.0)
152
+ Traceback (most recent call last):
153
+ ...
154
+ requireit.ValidationError: value must be > 0.0
155
+ """
156
+ return require_between(value, a_min=0.0, a_max=None, inclusive_min=False)
157
+
158
+
159
+ def require_nonnegative(value: ArrayLike) -> ArrayLike:
160
+ """Validate that a value is greater than or equal to zero.
161
+
162
+ Parameters
163
+ ----------
164
+ value : scalar or array-like
165
+ The input value(s) to be validated.
166
+
167
+ Returns
168
+ -------
169
+ value : scalar or array-like
170
+ The validated value.
171
+
172
+ Raises
173
+ ------
174
+ ValidationError
175
+ If any element of ``value`` is less than zero.
176
+
177
+ Examples
178
+ --------
179
+ >>> require_nonnegative(-1.0)
180
+ Traceback (most recent call last):
181
+ ...
182
+ requireit.ValidationError: value must be >= 0.0
183
+ >>> require_nonnegative(0.0)
184
+ 0.0
185
+ >>> require_nonnegative(1.0)
186
+ 1.0
187
+ """
188
+ return require_between(value, a_min=0.0, a_max=None, inclusive_min=True)
189
+
190
+
191
+ def require_negative(value: ArrayLike) -> ArrayLike:
192
+ """Validate that a value is strictly less than zero.
193
+
194
+ Parameters
195
+ ----------
196
+ value : scalar or array-like
197
+ The input value(s) to be validated.
198
+
199
+ Returns
200
+ -------
201
+ value : scalar or array-like
202
+ The validated value.
203
+
204
+ Raises
205
+ ------
206
+ ValidationError
207
+ If any element of ``value`` is greater than or equal to zero.
208
+
209
+ Examples
210
+ --------
211
+ >>> require_negative(-1.0)
212
+ -1.0
213
+ >>> require_negative(0.0)
214
+ Traceback (most recent call last):
215
+ ...
216
+ requireit.ValidationError: value must be < 0.0
217
+ """
218
+ return require_between(value, a_min=None, a_max=0.0, inclusive_max=False)
219
+
220
+
221
+ def require_nonpositive(value: ArrayLike) -> ArrayLike:
222
+ """Validate that a value is less than or equal to zero.
223
+
224
+ Parameters
225
+ ----------
226
+ value : scalar or array-like
227
+ The input value(s) to be validated.
228
+
229
+ Returns
230
+ -------
231
+ value : scalar or array-like
232
+ The validated value.
233
+
234
+ Raises
235
+ ------
236
+ ValidationError
237
+ If any element of ``value`` is greater than zero.
238
+
239
+ Examples
240
+ --------
241
+ >>> require_nonpositive(-1.0)
242
+ -1.0
243
+ >>> require_nonpositive(0.0)
244
+ 0.0
245
+ >>> require_nonpositive(1.0)
246
+ Traceback (most recent call last):
247
+ ...
248
+ requireit.ValidationError: value must be <= 0.0
249
+ """
250
+ return require_between(value, a_min=None, a_max=0.0, inclusive_max=True)
251
+
252
+
253
+ def validate_array(
254
+ array: NDArray,
255
+ *,
256
+ dtype: DTypeLike | None = None,
257
+ shape: tuple[int, ...] | None = None,
258
+ writable: bool | None = None,
259
+ contiguous: bool | None = None,
260
+ ):
261
+ """Validate an array to satisfy requirements.
262
+
263
+ Parameters
264
+ ----------
265
+ array : ndarray
266
+ The array to be validated.
267
+ dtype : data-type, optional
268
+ The required data type.
269
+ shape : tuple of int, optional
270
+ The required shape.
271
+ writable : bool, optional
272
+ Require the array to be writable.
273
+ contiguous : bool, optional
274
+ Require the array to be c-contiguous.
275
+
276
+ Returns
277
+ -------
278
+ array : ndarray
279
+ The validated array.
280
+
281
+ Raises
282
+ ------
283
+ ValidationError
284
+ If the array is invalid.
285
+
286
+ Examples
287
+ --------
288
+ >>> import numpy as np
289
+ >>> validate_array(np.array([1, 2, 3, 4]), shape=(2, 2))
290
+ Traceback (most recent call last):
291
+ ...
292
+ requireit.ValidationError: incorrect shape: expected (2, 2), got (4,)
293
+ """
294
+ if shape is not None and array.shape != shape:
295
+ raise ValidationError(f"incorrect shape: expected {shape}, got {array.shape}")
296
+
297
+ if dtype is not None and array.dtype != np.dtype(dtype):
298
+ raise ValidationError(f"incorrect type: expected {dtype}, got {array.dtype}")
299
+
300
+ if writable and not array.flags.writeable:
301
+ raise ValidationError("array is not writable")
302
+
303
+ if contiguous and not array.flags.c_contiguous:
304
+ raise ValidationError("array is not contiguous")
305
+
306
+ return array