enforce-rules 3.1.6__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,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Loepker-James
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,395 @@
1
+ Metadata-Version: 2.4
2
+ Name: enforce-rules
3
+ Version: 3.1.6
4
+ Summary: Runtime rule validation for Python using dictionary-based constraints.
5
+ Author: Jake Loepker, Microsoft Copilot (AI Assistant)
6
+ License: BSD 3-Clause License
7
+
8
+ Copyright (c) 2026, Loepker-James
9
+
10
+ Redistribution and use in source and binary forms, with or without
11
+ modification, are permitted provided that the following conditions are met:
12
+
13
+ 1. Redistributions of source code must retain the above copyright notice, this
14
+ list of conditions and the following disclaimer.
15
+
16
+ 2. Redistributions in binary form must reproduce the above copyright notice,
17
+ this list of conditions and the following disclaimer in the documentation
18
+ and/or other materials provided with the distribution.
19
+
20
+ 3. Neither the name of the copyright holder nor the names of its
21
+ contributors may be used to endorse or promote products derived from
22
+ this software without specific prior written permission.
23
+
24
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
28
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+
35
+ Project-URL: Homepage, https://github.com/Loepker-James/enforce-rules
36
+ Project-URL: Issues, https://github.com/Loepker-James/enforce-rules/issues
37
+ Project-URL: Copilot, https://copilot.microsoft.com
38
+ Classifier: Development Status :: 5 - Production/Stable
39
+ Classifier: Intended Audience :: Developers
40
+ Classifier: License :: OSI Approved :: BSD License
41
+ Classifier: Programming Language :: Python
42
+ Classifier: Programming Language :: Python :: 3
43
+ Classifier: Programming Language :: Python :: 3.10
44
+ Classifier: Programming Language :: Python :: 3.11
45
+ Classifier: Programming Language :: Python :: 3.12
46
+ Classifier: Programming Language :: Python :: 3.13
47
+ Classifier: Topic :: Software Development
48
+ Classifier: Topic :: Software Development :: Libraries
49
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
50
+ Classifier: Topic :: Utilities
51
+ Classifier: Typing :: Typed
52
+ Requires-Python: >=3.10
53
+ Description-Content-Type: text/markdown
54
+ License-File: LICENSE
55
+ Dynamic: license-file
56
+
57
+ Project Enforce Rules
58
+ Project Enforce Rules (or PER for short) expands the Python type system to allow more constraints.
59
+
60
+ Version #
61
+ ----------------
62
+ MAJOR: 3
63
+
64
+ MINOR: 1
65
+
66
+ PATCH: 6
67
+
68
+ If you need to catch up, you can see the full version history in the [CHANGELOG](https://github.com/Loepker-James/enforce-rules/blob/main/CHANGELOG.md).
69
+
70
+ Documentation can be found [here](https://github.com/Loepker-James/enforce-rules/tree/main/docs).
71
+
72
+ It exists to let you use features Python doesn't already provide in the typing system — things you probably want, like min, max, length, all_same, and many more.
73
+
74
+ PER now supports runtime validation anywhere using:
75
+
76
+ ```python
77
+ validate(value: object, rules: Dict[str, Any])
78
+ ```
79
+
80
+ This enforces rule dictionaries and returns the original value if valid.
81
+ If invalid, it raises a descriptive error.
82
+
83
+ Why I Made It
84
+ I wanted type‑hint features Python didn’t give me. I thought dictionaries would work until I learned... well...
85
+ Python didn’t enforce them.
86
+
87
+ So with the help of Microsoft Copilot and my dad, we designed a module that enforces this type of stuff.
88
+ Then I realized everyone in the Python community could use this, so it became a project. Hence, creating PER.
89
+
90
+ PER uses rule dictionaries and validate() to enforce constraints at runtime.
91
+
92
+ Install it with pip:
93
+
94
+ ```bash
95
+ pip install enforce-rules
96
+ ```
97
+ Then use it like:
98
+
99
+ ```python
100
+ from enforce_rules import validate
101
+ ```
102
+ Features
103
+ 1. Runtime enforcement of rule dictionaries
104
+ 2. Dictionary‑based rule definitions
105
+ 3. No extra objects required
106
+ 4. Works anywhere in your code
107
+ 5. Extensible via must_be_true
108
+
109
+ How It Works
110
+ PER validates values using:
111
+
112
+ ```python
113
+ validate(value, rules)
114
+ ```
115
+ If the value violates a rule, PER raises an error.
116
+ If the value passes, PER returns the original value unchanged.
117
+
118
+ This means validated values behave exactly like normal Python values.
119
+
120
+ Keywords and Usage
121
+ Below are all supported keywords.
122
+
123
+ length
124
+
125
+ The length of the object must be exactly this.
126
+
127
+ ```python
128
+ lst = validate([1, 2, 3, 4, 5], {"length": 5})
129
+ ```
130
+
131
+ min_length
132
+
133
+ Minimum length (inclusive).
134
+
135
+ ```python
136
+ lst = validate(['a', 'b', 'c', 'd', 'e'], {"min_length": 3})
137
+ ```
138
+ max_length
139
+
140
+ Maximum length (inclusive).
141
+
142
+ ```python
143
+ lst = validate([1, 2, 3, 4, 5, 6], {"max_length": 7})
144
+ ```
145
+ min
146
+
147
+ Minimum numeric value (inclusive).
148
+
149
+ ```python
150
+ number = validate(10, {"min": 0})
151
+ ```
152
+ max
153
+
154
+ Maximum numeric value (inclusive).
155
+
156
+ ```python
157
+ number = validate(10, {"max": 20})
158
+ ```
159
+ allowed_values
160
+
161
+ Similar to Literal; value must be one of the allowed values.
162
+
163
+ ```python
164
+ val = validate("a", {"allowed_values": ("a", "b", "c", "d")})
165
+ ```
166
+ invariant
167
+
168
+ Value must be truthy.
169
+
170
+ ```python
171
+ val = validate((0 == 0), {"invariant": True})
172
+ ```
173
+ all_same
174
+
175
+ All values in the collection must be the same.
176
+
177
+ ```python
178
+ numbers = validate([1, 1, 1], {"all_same": True})
179
+ ```
180
+ all_unique
181
+
182
+ All values in the collection must be unique.
183
+
184
+ ```python
185
+ numbers = validate([1, 2, 3], {"all_unique": True})
186
+ ```
187
+ non_empty
188
+
189
+ Collection must not be empty.
190
+
191
+ ```python
192
+ my_strings = validate(['a', 'b', 'c'], {"non_empty": True})
193
+ ```
194
+ no_nulls
195
+
196
+ Collection must not contain None.
197
+
198
+ ```python
199
+ my_things = validate([1, 2, 3, "a", "b", "c"], {"no_nulls": True})
200
+ ```
201
+
202
+ sorted
203
+
204
+ List must be sorted (increasing or decreasing).
205
+
206
+ ```python
207
+ numbers = validate([1, 5, 9], {"sorted": True})
208
+ ```
209
+
210
+ increasing
211
+
212
+ List must be strictly increasing.
213
+
214
+ ```python
215
+ numbers = validate([1, 5, 9], {"increasing": True})
216
+ ```
217
+ decreasing
218
+
219
+ List must be strictly decreasing.
220
+
221
+ ```python
222
+ numbers = validate([9, 5, 1], {"decreasing": True})
223
+ ```
224
+ sum_min
225
+
226
+ Minimum sum of the collection (inclusive).
227
+
228
+ ```python
229
+ numbers = validate([10, 20, 30], {"sum_min": 50})
230
+ ```
231
+ sum_max
232
+
233
+ Maximum sum of the collection (inclusive).
234
+
235
+ ```python
236
+ numbers = validate([10, 20, 30], {"sum_max": 70})
237
+ ```
238
+ element_min
239
+
240
+ Minimum value for any element (inclusive).
241
+
242
+ ```python
243
+ numbers = validate([10, 20, 30], {"element_min": 5})
244
+ ```
245
+ element_max
246
+
247
+ Maximum value for any element (inclusive).
248
+
249
+ ```python
250
+ numbers = validate([10, 20, 30], {"element_max": 40})
251
+ ```
252
+ regex
253
+
254
+ String must match the regex.
255
+
256
+ ```python
257
+ cat_or_dog = validate("cat", {"regex": "cat|dog"})
258
+ ```
259
+ regex_flags
260
+
261
+ Turns out I didn't notice this in my code, until 1.1.0. This is the regex flags
262
+
263
+ ```python
264
+ from re import RegexFlag
265
+ cat_or_dog = validate("cat", {"regex": "cat|dog", {"regex_flags": RegexFlag.I | RegexFlag.M | RegexFlag.X
266
+ ```
267
+ before_date
268
+
269
+ Value must be strictly before the given datetime.
270
+
271
+ ```python
272
+ validate(datetime(1999, 8, 29), {"before_date": datetime(2000, 1, 1)})
273
+ ```
274
+
275
+ after_date
276
+
277
+ Value must be strictly after the given datetime.
278
+ ```python
279
+ validate(datetime(2026, 8, 29), {"after_date": datetime(2000, 1, 1)})
280
+ ```
281
+
282
+ piece_color
283
+
284
+ The piece must have this exact color.
285
+
286
+ ```python
287
+ import chess
288
+ piece = validate(chess.Piece(chess.ROOK, chess.WHITE), {"piece_color": chess.WHITE})
289
+ ```
290
+ piece_type
291
+
292
+ The piece must be exactly this type (e.g., chess.KNIGHT, chess.ROOK).
293
+
294
+ ```python
295
+ import chess
296
+ piece = validate(chess.Piece(chess.KNIGHT, chess.WHITE), {"piece_type": chess.KNIGHT})
297
+ ```
298
+ chess_symbol
299
+
300
+ The piece’s symbol must match this string ("P", "n", "r", etc.).
301
+
302
+ ```python
303
+ import chess
304
+ piece = validate(chess.Piece(chess.ROOK, chess.BLACK), {"chess_symbol": "r"})
305
+ ```
306
+
307
+ is_password
308
+
309
+ The value must satisfy all password requirements when this is set to True.
310
+
311
+ Requirements (sorted):
312
+
313
+ 1. At least 8 characters
314
+ 2. At least one uppercase letter
315
+ 3. At least one lowercase letter
316
+ 4. At least one digit
317
+ 5. At least one symbol
318
+
319
+ ```python
320
+ password = validate("Abcdef!1", {"is_password": True})
321
+ ```
322
+
323
+ must_be_true
324
+
325
+ Custom rule: a function that returns True for allowed values.
326
+
327
+ ```python
328
+ def is_even(x: int) -> bool:
329
+ return x % 2 == 0
330
+ even_number = validate(8, {"must_be_true": is_even})
331
+ ```
332
+ This calls:
333
+
334
+ ```python
335
+ is_even(8)
336
+ ```
337
+ If enough people use a must_be_true lambda, it may become an added keyword in a later version
338
+
339
+ Contributing
340
+ Contributions are welcome. Please open an issue or pull request.
341
+ When contributing, ensure backwards compatibility (you cannot remove keywords and/or features).
342
+
343
+ Please note, when using my module, that you will manually have to validate each time should you choose to mutate a variable.
344
+
345
+
346
+ Versioning Policy
347
+ -----------------
348
+
349
+ This project guarantees full backwards compatibility. Existing rule files, keyword meanings, validator behaviors, and metadata formats will continue to work exactly as before. No update will ever break existing configurations.
350
+
351
+ Version Numbering
352
+ -----------------
353
+ This project uses a non-breaking semantic versioning model:
354
+
355
+ MAJOR.MINOR.PATCH
356
+
357
+ MAJOR = large new feature families
358
+ MINOR = small additive keywords or enhancements
359
+ PATCH = bug fixes or internal improvements
360
+
361
+ Major bumps do not imply breaking changes. They only indicate that a significant new capability has been added.
362
+
363
+ Major bumps always reset MINOR and PATCH to 0. For example:
364
+ 1.7.3 -> 2.0.0
365
+
366
+ 1.12.0 -> 2.0.0
367
+
368
+ 1.0.0 -> 2.0.0
369
+
370
+ Minor bumps always reset PATCH to 0. For example:
371
+ 1.7.3 -> 1.8.0
372
+
373
+ 1.12.9 -> 1.13.0
374
+
375
+ 1.0.4 -> 1.1.0
376
+
377
+ Minor Version Bumps
378
+ -------------------
379
+ Minor bumps occur when adding small keywords. Examples include:
380
+
381
+ min_inclusive
382
+
383
+ max_inclusive
384
+
385
+ trim_whitespace
386
+
387
+ pattern_flags
388
+
389
+ These additions do not change the meaning of existing keywords, do not require users to modify rule files, and do not alter validator behavior. They are classified as minor updates.
390
+
391
+ Minor bumps always reset PATCH to 0
392
+
393
+ Credits:
394
+ * Copilot
395
+ * Dad