valid8r 0.6.3__py3-none-any.whl → 0.7.1__py3-none-any.whl
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.
Potentially problematic release.
This version of valid8r might be problematic. Click here for more details.
- valid8r/core/maybe.py +1 -1
- valid8r/core/validators.py +283 -0
- {valid8r-0.6.3.dist-info → valid8r-0.7.1.dist-info}/METADATA +36 -13
- {valid8r-0.6.3.dist-info → valid8r-0.7.1.dist-info}/RECORD +8 -8
- {valid8r-0.6.3.dist-info → valid8r-0.7.1.dist-info}/WHEEL +1 -1
- valid8r-0.7.1.dist-info/entry_points.txt +3 -0
- valid8r-0.6.3.dist-info/entry_points.txt +0 -4
- {valid8r-0.6.3.dist-info → valid8r-0.7.1.dist-info}/licenses/LICENSE +0 -0
valid8r/core/maybe.py
CHANGED
valid8r/core/validators.py
CHANGED
|
@@ -7,6 +7,7 @@ that either contains the validated value or an error message.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import re
|
|
10
11
|
from typing import (
|
|
11
12
|
TYPE_CHECKING,
|
|
12
13
|
Generic,
|
|
@@ -280,3 +281,285 @@ def length(min_length: int, max_length: int, error_message: str | None = None) -
|
|
|
280
281
|
return Maybe.failure(error_message or f'String length must be between {min_length} and {max_length}')
|
|
281
282
|
|
|
282
283
|
return Validator(validator)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def matches_regex(pattern: str | re.Pattern[str], error_message: str | None = None) -> Validator[str]:
|
|
287
|
+
r"""Create a validator that ensures a string matches a regular expression pattern.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
pattern: Regular expression pattern (string or compiled Pattern object)
|
|
291
|
+
error_message: Optional custom error message
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
Validator[str]: A validator function that checks pattern matching
|
|
295
|
+
|
|
296
|
+
Examples:
|
|
297
|
+
>>> from valid8r.core.validators import matches_regex
|
|
298
|
+
>>> import re
|
|
299
|
+
>>> # String pattern
|
|
300
|
+
>>> validator = matches_regex(r'^\\d{3}-\\d{2}-\\d{4}$')
|
|
301
|
+
>>> validator('123-45-6789')
|
|
302
|
+
Success('123-45-6789')
|
|
303
|
+
>>> validator('invalid').is_failure()
|
|
304
|
+
True
|
|
305
|
+
>>> # Compiled regex pattern
|
|
306
|
+
>>> pattern = re.compile(r'^[A-Z][a-z]+$')
|
|
307
|
+
>>> validator = matches_regex(pattern)
|
|
308
|
+
>>> validator('Hello')
|
|
309
|
+
Success('Hello')
|
|
310
|
+
>>> validator('hello').is_failure()
|
|
311
|
+
True
|
|
312
|
+
>>> # With custom error message
|
|
313
|
+
>>> validator = matches_regex(r'^\\d{5}$', error_message='Must be a 5-digit ZIP code')
|
|
314
|
+
>>> validator('1234').error_or('')
|
|
315
|
+
'Must be a 5-digit ZIP code'
|
|
316
|
+
|
|
317
|
+
"""
|
|
318
|
+
compiled_pattern = re.compile(pattern) if isinstance(pattern, str) else pattern
|
|
319
|
+
|
|
320
|
+
def validator(value: str) -> Maybe[str]:
|
|
321
|
+
if compiled_pattern.match(value):
|
|
322
|
+
return Maybe.success(value)
|
|
323
|
+
return Maybe.failure(error_message or f'Value must match pattern {compiled_pattern.pattern}')
|
|
324
|
+
|
|
325
|
+
return Validator(validator)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def in_set(allowed_values: set[T], error_message: str | None = None) -> Validator[T]:
|
|
329
|
+
"""Create a validator that ensures a value is in a set of allowed values.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
allowed_values: Set of allowed values
|
|
333
|
+
error_message: Optional custom error message
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
Validator[T]: A validator function that checks membership
|
|
337
|
+
|
|
338
|
+
Examples:
|
|
339
|
+
>>> from valid8r.core.validators import in_set
|
|
340
|
+
>>> # String values
|
|
341
|
+
>>> validator = in_set({'red', 'green', 'blue'})
|
|
342
|
+
>>> validator('red')
|
|
343
|
+
Success('red')
|
|
344
|
+
>>> validator('yellow').is_failure()
|
|
345
|
+
True
|
|
346
|
+
>>> # Numeric values
|
|
347
|
+
>>> validator = in_set({1, 2, 3, 4, 5})
|
|
348
|
+
>>> validator(3)
|
|
349
|
+
Success(3)
|
|
350
|
+
>>> validator(10).is_failure()
|
|
351
|
+
True
|
|
352
|
+
>>> # With custom error message
|
|
353
|
+
>>> validator = in_set({'small', 'medium', 'large'}, error_message='Size must be S, M, or L')
|
|
354
|
+
>>> validator('extra-large').error_or('')
|
|
355
|
+
'Size must be S, M, or L'
|
|
356
|
+
|
|
357
|
+
"""
|
|
358
|
+
|
|
359
|
+
def validator(value: T) -> Maybe[T]:
|
|
360
|
+
if value in allowed_values:
|
|
361
|
+
return Maybe.success(value)
|
|
362
|
+
return Maybe.failure(error_message or f'Value must be one of {allowed_values}')
|
|
363
|
+
|
|
364
|
+
return Validator(validator)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def non_empty_string(error_message: str | None = None) -> Validator[str]:
|
|
368
|
+
"""Create a validator that ensures a string is not empty.
|
|
369
|
+
|
|
370
|
+
Validates that a string contains at least one non-whitespace character.
|
|
371
|
+
Both empty strings and whitespace-only strings are rejected.
|
|
372
|
+
|
|
373
|
+
Args:
|
|
374
|
+
error_message: Optional custom error message
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
Validator[str]: A validator function that checks for non-empty strings
|
|
378
|
+
|
|
379
|
+
Examples:
|
|
380
|
+
>>> from valid8r.core.validators import non_empty_string
|
|
381
|
+
>>> validator = non_empty_string()
|
|
382
|
+
>>> validator('hello')
|
|
383
|
+
Success('hello')
|
|
384
|
+
>>> validator(' hello ')
|
|
385
|
+
Success(' hello ')
|
|
386
|
+
>>> validator('').is_failure()
|
|
387
|
+
True
|
|
388
|
+
>>> validator(' ').is_failure()
|
|
389
|
+
True
|
|
390
|
+
>>> # With custom error message
|
|
391
|
+
>>> validator = non_empty_string(error_message='Name is required')
|
|
392
|
+
>>> validator('').error_or('')
|
|
393
|
+
'Name is required'
|
|
394
|
+
|
|
395
|
+
"""
|
|
396
|
+
|
|
397
|
+
def validator(value: str) -> Maybe[str]:
|
|
398
|
+
if value.strip():
|
|
399
|
+
return Maybe.success(value)
|
|
400
|
+
return Maybe.failure(error_message or 'String must not be empty')
|
|
401
|
+
|
|
402
|
+
return Validator(validator)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def unique_items(error_message: str | None = None) -> Validator[list[T]]:
|
|
406
|
+
"""Create a validator that ensures all items in a list are unique.
|
|
407
|
+
|
|
408
|
+
Validates that a list contains no duplicate elements by comparing
|
|
409
|
+
the list length to the set length.
|
|
410
|
+
|
|
411
|
+
Args:
|
|
412
|
+
error_message: Optional custom error message
|
|
413
|
+
|
|
414
|
+
Returns:
|
|
415
|
+
Validator[list[T]]: A validator function that checks for unique items
|
|
416
|
+
|
|
417
|
+
Examples:
|
|
418
|
+
>>> from valid8r.core.validators import unique_items
|
|
419
|
+
>>> validator = unique_items()
|
|
420
|
+
>>> validator([1, 2, 3, 4, 5])
|
|
421
|
+
Success([1, 2, 3, 4, 5])
|
|
422
|
+
>>> validator([1, 2, 2, 3]).is_failure()
|
|
423
|
+
True
|
|
424
|
+
>>> # Works with strings
|
|
425
|
+
>>> validator(['a', 'b', 'c'])
|
|
426
|
+
Success(['a', 'b', 'c'])
|
|
427
|
+
>>> validator(['a', 'b', 'a']).is_failure()
|
|
428
|
+
True
|
|
429
|
+
>>> # With custom error message
|
|
430
|
+
>>> validator = unique_items(error_message='Duplicate items found')
|
|
431
|
+
>>> validator([1, 1, 2]).error_or('')
|
|
432
|
+
'Duplicate items found'
|
|
433
|
+
|
|
434
|
+
"""
|
|
435
|
+
|
|
436
|
+
def validator(value: list[T]) -> Maybe[list[T]]:
|
|
437
|
+
if len(value) == len(set(value)):
|
|
438
|
+
return Maybe.success(value)
|
|
439
|
+
return Maybe.failure(error_message or 'All items must be unique')
|
|
440
|
+
|
|
441
|
+
return Validator(validator)
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def subset_of(allowed_set: set[T], error_message: str | None = None) -> Validator[set[T]]:
|
|
445
|
+
"""Create a validator that ensures a set is a subset of allowed values.
|
|
446
|
+
|
|
447
|
+
Validates that all elements in the input set are contained within
|
|
448
|
+
the allowed set. An empty set is always a valid subset.
|
|
449
|
+
|
|
450
|
+
Args:
|
|
451
|
+
allowed_set: The set of allowed values
|
|
452
|
+
error_message: Optional custom error message
|
|
453
|
+
|
|
454
|
+
Returns:
|
|
455
|
+
Validator[set[T]]: A validator function that checks subset relationship
|
|
456
|
+
|
|
457
|
+
Examples:
|
|
458
|
+
>>> from valid8r.core.validators import subset_of
|
|
459
|
+
>>> validator = subset_of({1, 2, 3, 4, 5})
|
|
460
|
+
>>> validator({1, 2, 3})
|
|
461
|
+
Success({1, 2, 3})
|
|
462
|
+
>>> validator({1, 2, 3, 4, 5, 6}).is_failure()
|
|
463
|
+
True
|
|
464
|
+
>>> # Empty set is valid subset
|
|
465
|
+
>>> validator(set())
|
|
466
|
+
Success(set())
|
|
467
|
+
>>> # With custom error message
|
|
468
|
+
>>> validator = subset_of({'a', 'b', 'c'}, error_message='Invalid characters')
|
|
469
|
+
>>> validator({'a', 'd'}).error_or('')
|
|
470
|
+
'Invalid characters'
|
|
471
|
+
|
|
472
|
+
"""
|
|
473
|
+
|
|
474
|
+
def validator(value: set[T]) -> Maybe[set[T]]:
|
|
475
|
+
if value.issubset(allowed_set):
|
|
476
|
+
return Maybe.success(value)
|
|
477
|
+
return Maybe.failure(error_message or f'Value must be a subset of {allowed_set}')
|
|
478
|
+
|
|
479
|
+
return Validator(validator)
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def superset_of(required_set: set[T], error_message: str | None = None) -> Validator[set[T]]:
|
|
483
|
+
"""Create a validator that ensures a set is a superset of required values.
|
|
484
|
+
|
|
485
|
+
Validates that the input set contains all elements from the required set.
|
|
486
|
+
The input set may contain additional elements beyond those required.
|
|
487
|
+
|
|
488
|
+
Args:
|
|
489
|
+
required_set: The set of required values
|
|
490
|
+
error_message: Optional custom error message
|
|
491
|
+
|
|
492
|
+
Returns:
|
|
493
|
+
Validator[set[T]]: A validator function that checks superset relationship
|
|
494
|
+
|
|
495
|
+
Examples:
|
|
496
|
+
>>> from valid8r.core.validators import superset_of
|
|
497
|
+
>>> validator = superset_of({1, 2, 3})
|
|
498
|
+
>>> validator({1, 2, 3, 4, 5})
|
|
499
|
+
Success({1, 2, 3, 4, 5})
|
|
500
|
+
>>> validator({1, 2}).is_failure()
|
|
501
|
+
True
|
|
502
|
+
>>> # Exact match is valid
|
|
503
|
+
>>> validator({1, 2, 3})
|
|
504
|
+
Success({1, 2, 3})
|
|
505
|
+
>>> # With custom error message
|
|
506
|
+
>>> validator = superset_of({'read', 'write'}, error_message='Missing required permissions')
|
|
507
|
+
>>> validator({'read'}).error_or('')
|
|
508
|
+
'Missing required permissions'
|
|
509
|
+
|
|
510
|
+
"""
|
|
511
|
+
|
|
512
|
+
def validator(value: set[T]) -> Maybe[set[T]]:
|
|
513
|
+
if value.issuperset(required_set):
|
|
514
|
+
return Maybe.success(value)
|
|
515
|
+
return Maybe.failure(error_message or f'Value must be a superset of {required_set}')
|
|
516
|
+
|
|
517
|
+
return Validator(validator)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def is_sorted(*, reverse: bool = False, error_message: str | None = None) -> Validator[list[N]]:
|
|
521
|
+
"""Create a validator that ensures a list is sorted.
|
|
522
|
+
|
|
523
|
+
Validates that a list is sorted in either ascending or descending order.
|
|
524
|
+
Uses keyword-only parameters to avoid boolean trap anti-pattern.
|
|
525
|
+
|
|
526
|
+
Args:
|
|
527
|
+
reverse: If True, checks for descending order; otherwise ascending (default)
|
|
528
|
+
error_message: Optional custom error message
|
|
529
|
+
|
|
530
|
+
Returns:
|
|
531
|
+
Validator[list[N]]: A validator function that checks if list is sorted
|
|
532
|
+
|
|
533
|
+
Examples:
|
|
534
|
+
>>> from valid8r.core.validators import is_sorted
|
|
535
|
+
>>> # Ascending order (default)
|
|
536
|
+
>>> validator = is_sorted()
|
|
537
|
+
>>> validator([1, 2, 3, 4, 5])
|
|
538
|
+
Success([1, 2, 3, 4, 5])
|
|
539
|
+
>>> validator([3, 1, 4, 2]).is_failure()
|
|
540
|
+
True
|
|
541
|
+
>>> # Descending order
|
|
542
|
+
>>> validator = is_sorted(reverse=True)
|
|
543
|
+
>>> validator([5, 4, 3, 2, 1])
|
|
544
|
+
Success([5, 4, 3, 2, 1])
|
|
545
|
+
>>> validator([1, 2, 3]).is_failure()
|
|
546
|
+
True
|
|
547
|
+
>>> # Works with strings
|
|
548
|
+
>>> validator = is_sorted()
|
|
549
|
+
>>> validator(['a', 'b', 'c'])
|
|
550
|
+
Success(['a', 'b', 'c'])
|
|
551
|
+
>>> # With custom error message
|
|
552
|
+
>>> validator = is_sorted(error_message='List must be in order')
|
|
553
|
+
>>> validator([3, 1, 2]).error_or('')
|
|
554
|
+
'List must be in order'
|
|
555
|
+
|
|
556
|
+
"""
|
|
557
|
+
|
|
558
|
+
def validator(value: list[N]) -> Maybe[list[N]]:
|
|
559
|
+
sorted_value = sorted(value, reverse=reverse)
|
|
560
|
+
if value == sorted_value:
|
|
561
|
+
return Maybe.success(value)
|
|
562
|
+
direction = 'descending' if reverse else 'ascending'
|
|
563
|
+
return Maybe.failure(error_message or f'List must be sorted in {direction} order')
|
|
564
|
+
|
|
565
|
+
return Validator(validator)
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: valid8r
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: Clean, flexible input validation for Python applications
|
|
5
|
+
Project-URL: Homepage, https://valid8r.readthedocs.io/
|
|
6
|
+
Project-URL: Repository, https://github.com/mikelane/valid8r
|
|
7
|
+
Project-URL: Documentation, https://valid8r.readthedocs.io/
|
|
8
|
+
Project-URL: Issues, https://github.com/mikelane/valid8r/issues
|
|
9
|
+
Author-email: Mike Lane <mikelane@gmail.com>
|
|
5
10
|
License: MIT
|
|
6
11
|
License-File: LICENSE
|
|
7
|
-
Keywords:
|
|
8
|
-
Author: Mike Lane
|
|
9
|
-
Author-email: mikelane@gmail.com
|
|
10
|
-
Requires-Python: >=3.11,<4.0
|
|
12
|
+
Keywords: cli,functional-programming,input,maybe-monad,parsing,validation
|
|
11
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
14
|
Classifier: Intended Audience :: Developers
|
|
13
15
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -20,13 +22,11 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
20
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
23
|
Classifier: Topic :: Software Development :: Quality Assurance
|
|
22
24
|
Classifier: Typing :: Typed
|
|
23
|
-
Requires-
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist: pydantic-core
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
|
|
28
|
-
Project-URL: Homepage, https://valid8r.readthedocs.io/
|
|
29
|
-
Project-URL: Repository, https://github.com/mikelane/valid8r
|
|
25
|
+
Requires-Python: >=3.11
|
|
26
|
+
Requires-Dist: email-validator>=2.3.0
|
|
27
|
+
Requires-Dist: pydantic-core>=2.27.0
|
|
28
|
+
Requires-Dist: pydantic>=2.0
|
|
29
|
+
Requires-Dist: uuid-utils>=0.11.0
|
|
30
30
|
Description-Content-Type: text/markdown
|
|
31
31
|
|
|
32
32
|
# Valid8r
|
|
@@ -78,6 +78,30 @@ A clean, flexible input validation library for Python applications.
|
|
|
78
78
|
- **Enums**: `parse_enum` (type-safe enum parsing)
|
|
79
79
|
- **Custom**: `create_parser`, `make_parser`, `validated_parser` (parser factories)
|
|
80
80
|
|
|
81
|
+
## Available Validators
|
|
82
|
+
|
|
83
|
+
### Numeric Validators
|
|
84
|
+
- **`minimum(min_value)`** - Ensures value is at least the minimum (inclusive)
|
|
85
|
+
- **`maximum(max_value)`** - Ensures value is at most the maximum (inclusive)
|
|
86
|
+
- **`between(min_value, max_value)`** - Ensures value is within range (inclusive)
|
|
87
|
+
|
|
88
|
+
### String Validators
|
|
89
|
+
- **`non_empty_string()`** - Rejects empty strings and whitespace-only strings
|
|
90
|
+
- **`matches_regex(pattern)`** - Validates string matches regex pattern (string or compiled)
|
|
91
|
+
- **`length(min_length, max_length)`** - Validates string length within bounds
|
|
92
|
+
|
|
93
|
+
### Collection Validators
|
|
94
|
+
- **`in_set(allowed_values)`** - Ensures value is in set of allowed values
|
|
95
|
+
- **`unique_items()`** - Ensures all items in a list are unique
|
|
96
|
+
- **`subset_of(allowed_set)`** - Validates set is subset of allowed values
|
|
97
|
+
- **`superset_of(required_set)`** - Validates set is superset of required values
|
|
98
|
+
- **`is_sorted(reverse=False)`** - Ensures list is sorted (ascending or descending)
|
|
99
|
+
|
|
100
|
+
### Custom Validators
|
|
101
|
+
- **`predicate(func, error_message)`** - Create custom validator from any predicate function
|
|
102
|
+
|
|
103
|
+
**Note**: All validators support custom error messages and can be combined using `&` (and), `|` (or), and `~` (not) operators.
|
|
104
|
+
|
|
81
105
|
## Installation
|
|
82
106
|
|
|
83
107
|
**Requirements**: Python 3.11 or higher
|
|
@@ -272,4 +296,3 @@ poetry run tox -e bdd
|
|
|
272
296
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
273
297
|
|
|
274
298
|
Copyright (c) 2025 Mike Lane
|
|
275
|
-
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
valid8r/__init__.py,sha256=2fzSl6XtKX44sdqzf0GBTn4oEaCvhmyGkdsPDMJjZz8,447
|
|
2
2
|
valid8r/core/__init__.py,sha256=ASOdzqCtpZHbHjjYMZkb78Z-nKxtD26ruTY0bd43ImA,520
|
|
3
3
|
valid8r/core/combinators.py,sha256=KvRiDEqoZgH58cBYPO6SW9pdtkyijk0lS8aGSB5DbO4,2349
|
|
4
|
-
valid8r/core/maybe.py,sha256=
|
|
4
|
+
valid8r/core/maybe.py,sha256=kuD5SsWc6148FkcLBcwmRTPi6D7H4w1dRT8lPrUvYMw,4643
|
|
5
5
|
valid8r/core/parsers.py,sha256=WYAgwCIh8HyrXr0AoZeqa0c43-_iVIK_S3wb2lha73c,67456
|
|
6
|
-
valid8r/core/validators.py,sha256=
|
|
6
|
+
valid8r/core/validators.py,sha256=e3B_fi7ch5m0Zczg87r8AhrgEdBTcbz-aygrKCvi0dg,18537
|
|
7
7
|
valid8r/prompt/__init__.py,sha256=XYB3NEp-tmqT6fGmETVEeXd7Urj0M4ijlwdRAjj-rG8,175
|
|
8
8
|
valid8r/prompt/basic.py,sha256=fFARuy5nGTE7xM3dB1jpRC3OPNmp4WwaymFMz7BSgdo,7635
|
|
9
|
-
valid8r/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
9
|
valid8r/testing/__init__.py,sha256=8mk54zt0Ai2dK0a3GMOTfDPsVQWXaS6uvQJDrkRV9hs,779
|
|
11
10
|
valid8r/testing/assertions.py,sha256=9KGz1JooCoyikyxMX7VuXB9VYAtj-4H_LPYFGdvS-ps,1820
|
|
12
11
|
valid8r/testing/generators.py,sha256=kAV6NRO9x1gPy0BfGs07ETVxjpTIxOZyV9wH2BA1nHA,8791
|
|
13
12
|
valid8r/testing/mock_input.py,sha256=9GRT7h0PCh9Dea-OcQ5Uls7YqhsTdqMWuX6I6ZlW1aw,2334
|
|
14
|
-
valid8r
|
|
15
|
-
valid8r-0.
|
|
16
|
-
valid8r-0.
|
|
17
|
-
valid8r-0.
|
|
18
|
-
valid8r-0.
|
|
13
|
+
valid8r/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
valid8r-0.7.1.dist-info/METADATA,sha256=O1-f1iS7afFkdqpudNeQvk_lbQ8oAalmh0uzJPhnZgE,10476
|
|
15
|
+
valid8r-0.7.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
16
|
+
valid8r-0.7.1.dist-info/entry_points.txt,sha256=x2MRzcIGcUR5e4GmhLadGdT7YbbohJRMUVubgaR8v_s,82
|
|
17
|
+
valid8r-0.7.1.dist-info/licenses/LICENSE,sha256=JpEmJvRYOTIUt0UjgvpDrd3U94Wnbt_Grr5z-xU2jtk,1066
|
|
18
|
+
valid8r-0.7.1.dist-info/RECORD,,
|
|
File without changes
|