valid8r.core.validators
Core validators for validating values against specific criteria.
This module provides a collection of validator functions for common validation scenarios. All validators follow the same pattern - they take a value and return a Maybe object that either contains the validated value or an error message.
Attributes
Classes
Base class for protocol classes. |
|
A wrapper class for validator functions that supports operator overloading. |
Functions
|
Create a validator that ensures a value is at least the minimum. |
|
Create a validator that ensures a value is at most the maximum. |
|
Create a validator that ensures a value is between minimum and maximum (inclusive). |
|
Create a validator using a custom predicate function. |
|
Create a validator that ensures a string's length is within bounds. |
|
Create a validator that ensures a string matches a regular expression pattern. |
|
Create a validator that ensures a value is in a set of allowed values. |
|
Create a validator that ensures a string is not empty. |
|
Create a validator that ensures all items in a list are unique. |
|
Create a validator that ensures a set is a subset of allowed values. |
|
Create a validator that ensures a set is a superset of required values. |
|
Create a validator that ensures a list is sorted. |
|
Create a validator that ensures a path exists on the filesystem. |
|
Create a validator that ensures a path is a regular file. |
|
Create a validator that ensures a path is a directory. |
Create a validator that ensures a path has read permissions. |
|
Create a validator that ensures a path has write permissions. |
|
Create a validator that ensures a path has execute permissions. |
|
|
Create a validator that ensures a file does not exceed a maximum size. |
|
Create a validator that ensures a file meets a minimum size requirement. |
|
Create a validator that ensures a file has one of the allowed extensions. |
Module Contents
- class valid8r.core.validators.SupportsComparison[source]
Bases:
ProtocolBase class for protocol classes.
Protocol classes are defined as:
class Proto(Protocol): def meth(self) -> int: ...
Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).
For example:
class C: def meth(self) -> int: return 0 def func(x: Proto) -> int: return x.meth() func(C()) # Passes static type check
See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as:
class GenProto[T](Protocol): def meth(self) -> T: ...
- class valid8r.core.validators.Validator(func)[source]
Bases:
Generic[T]A wrapper class for validator functions that supports operator overloading.
- Parameters:
func (collections.abc.Callable[[T], valid8r.core.maybe.Maybe[T]])
- __call__(value)[source]
Apply the validator to a value.
- Parameters:
value (T) – The value to validate
- Returns:
A Maybe containing either the validated value or an error
- Return type:
- valid8r.core.validators.minimum(min_value, error_message=None)[source]
Create a validator that ensures a value is at least the minimum.
- Parameters:
min_value (N) – The minimum allowed value (inclusive)
error_message (str | None) – Optional custom error message
- Returns:
A validator function that accepts values >= min_value
- Return type:
Validator[N]
Examples
>>> from valid8r.core.validators import minimum >>> validator = minimum(0) >>> validator(5) Success(5) >>> validator(0) Success(0) >>> validator(-1).is_failure() True >>> # With custom error message >>> validator = minimum(18, error_message="Must be an adult") >>> validator(17).error_or("") 'Must be an adult'
- valid8r.core.validators.maximum(max_value, error_message=None)[source]
Create a validator that ensures a value is at most the maximum.
- Parameters:
max_value (N) – The maximum allowed value (inclusive)
error_message (str | None) – Optional custom error message
- Returns:
A validator function that accepts values <= max_value
- Return type:
Validator[N]
Examples
>>> from valid8r.core.validators import maximum >>> validator = maximum(100) >>> validator(50) Success(50) >>> validator(100) Success(100) >>> validator(101).is_failure() True >>> # With custom error message >>> validator = maximum(120, error_message="Age too high") >>> validator(150).error_or("") 'Age too high'
- valid8r.core.validators.between(min_value, max_value, error_message=None)[source]
Create a validator that ensures a value is between minimum and maximum (inclusive).
- Parameters:
min_value (N) – The minimum allowed value (inclusive)
max_value (N) – The maximum allowed value (inclusive)
error_message (str | None) – Optional custom error message
- Returns:
A validator function that accepts values where min_value <= value <= max_value
- Return type:
Validator[N]
Examples
>>> from valid8r.core.validators import between >>> validator = between(0, 100) >>> validator(50) Success(50) >>> validator(0) Success(0) >>> validator(100) Success(100) >>> validator(-1).is_failure() True >>> validator(101).is_failure() True >>> # With custom error message >>> validator = between(1, 10, error_message="Rating must be 1-10") >>> validator(11).error_or("") 'Rating must be 1-10'
- valid8r.core.validators.predicate(pred, error_message)[source]
Create a validator using a custom predicate function.
Allows creating custom validators for any validation logic by providing a predicate function that returns True for valid values.
- Parameters:
pred (collections.abc.Callable[[T], bool]) – A function that takes a value and returns True if valid, False otherwise
error_message (str) – Error message to return when validation fails
- Returns:
A validator function that applies the predicate
- Return type:
Validator[T]
Examples
>>> from valid8r.core.validators import predicate >>> # Validate even numbers >>> is_even = predicate(lambda x: x % 2 == 0, "Must be even") >>> is_even(4) Success(4) >>> is_even(3).is_failure() True >>> # Validate string patterns >>> starts_with_a = predicate(lambda s: s.startswith('a'), "Must start with 'a'") >>> starts_with_a("apple") Success('apple') >>> starts_with_a("banana").error_or("") "Must start with 'a'"
- valid8r.core.validators.length(min_length, max_length, error_message=None)[source]
Create a validator that ensures a string’s length is within bounds.
- Parameters:
- Returns:
A validator function that checks string length
- Return type:
Examples
>>> from valid8r.core.validators import length >>> validator = length(3, 10) >>> validator("hello") Success('hello') >>> validator("abc") Success('abc') >>> validator("abcdefghij") Success('abcdefghij') >>> validator("ab").is_failure() True >>> validator("abcdefghijk").is_failure() True >>> # With custom error message >>> validator = length(8, 20, error_message="Password must be 8-20 characters") >>> validator("short").error_or("") 'Password must be 8-20 characters'
- valid8r.core.validators.matches_regex(pattern, error_message=None)[source]
Create a validator that ensures a string matches a regular expression pattern.
- Parameters:
pattern (str | re.Pattern[str]) – Regular expression pattern (string or compiled Pattern object)
error_message (str | None) – Optional custom error message
- Returns:
A validator function that checks pattern matching
- Return type:
Examples
>>> from valid8r.core.validators import matches_regex >>> import re >>> # String pattern >>> validator = matches_regex(r'^\d{3}-\d{2}-\d{4}$') >>> validator('123-45-6789') Success('123-45-6789') >>> validator('invalid').is_failure() True >>> # Compiled regex pattern >>> pattern = re.compile(r'^[A-Z][a-z]+$') >>> validator = matches_regex(pattern) >>> validator('Hello') Success('Hello') >>> validator('hello').is_failure() True >>> # With custom error message >>> validator = matches_regex(r'^\d{5}$', error_message='Must be a 5-digit ZIP code') >>> validator('1234').error_or('') 'Must be a 5-digit ZIP code'
- valid8r.core.validators.in_set(allowed_values, error_message=None)[source]
Create a validator that ensures a value is in a set of allowed values.
- Parameters:
- Returns:
A validator function that checks membership
- Return type:
Validator[T]
Examples
>>> from valid8r.core.validators import in_set >>> # String values >>> validator = in_set({'red', 'green', 'blue'}) >>> validator('red') Success('red') >>> validator('yellow').is_failure() True >>> # Numeric values >>> validator = in_set({1, 2, 3, 4, 5}) >>> validator(3) Success(3) >>> validator(10).is_failure() True >>> # With custom error message >>> validator = in_set({'small', 'medium', 'large'}, error_message='Size must be S, M, or L') >>> validator('extra-large').error_or('') 'Size must be S, M, or L'
- valid8r.core.validators.non_empty_string(error_message=None)[source]
Create a validator that ensures a string is not empty.
Validates that a string contains at least one non-whitespace character. Both empty strings and whitespace-only strings are rejected.
- Parameters:
error_message (str | None) – Optional custom error message
- Returns:
A validator function that checks for non-empty strings
- Return type:
Examples
>>> from valid8r.core.validators import non_empty_string >>> validator = non_empty_string() >>> validator('hello') Success('hello') >>> validator(' hello ') Success(' hello ') >>> validator('').is_failure() True >>> validator(' ').is_failure() True >>> # With custom error message >>> validator = non_empty_string(error_message='Name is required') >>> validator('').error_or('') 'Name is required'
- valid8r.core.validators.unique_items(error_message=None)[source]
Create a validator that ensures all items in a list are unique.
Validates that a list contains no duplicate elements by comparing the list length to the set length.
- Parameters:
error_message (str | None) – Optional custom error message
- Returns:
A validator function that checks for unique items
- Return type:
Examples
>>> from valid8r.core.validators import unique_items >>> validator = unique_items() >>> validator([1, 2, 3, 4, 5]) Success([1, 2, 3, 4, 5]) >>> validator([1, 2, 2, 3]).is_failure() True >>> # Works with strings >>> validator(['a', 'b', 'c']) Success(['a', 'b', 'c']) >>> validator(['a', 'b', 'a']).is_failure() True >>> # With custom error message >>> validator = unique_items(error_message='Duplicate items found') >>> validator([1, 1, 2]).error_or('') 'Duplicate items found'
- valid8r.core.validators.subset_of(allowed_set, error_message=None)[source]
Create a validator that ensures a set is a subset of allowed values.
Validates that all elements in the input set are contained within the allowed set. An empty set is always a valid subset.
- Parameters:
- Returns:
A validator function that checks subset relationship
- Return type:
Examples
>>> from valid8r.core.validators import subset_of >>> validator = subset_of({1, 2, 3, 4, 5}) >>> validator({1, 2, 3}) Success({1, 2, 3}) >>> validator({1, 2, 3, 4, 5, 6}).is_failure() True >>> # Empty set is valid subset >>> validator(set()) Success(set()) >>> # With custom error message >>> validator = subset_of({'a', 'b', 'c'}, error_message='Invalid characters') >>> validator({'a', 'd'}).error_or('') 'Invalid characters'
- valid8r.core.validators.superset_of(required_set, error_message=None)[source]
Create a validator that ensures a set is a superset of required values.
Validates that the input set contains all elements from the required set. The input set may contain additional elements beyond those required.
- Parameters:
- Returns:
A validator function that checks superset relationship
- Return type:
Examples
>>> from valid8r.core.validators import superset_of >>> validator = superset_of({1, 2, 3}) >>> validator({1, 2, 3, 4, 5}) Success({1, 2, 3, 4, 5}) >>> validator({1, 2}).is_failure() True >>> # Exact match is valid >>> validator({1, 2, 3}) Success({1, 2, 3}) >>> # With custom error message >>> validator = superset_of({'read', 'write'}, error_message='Missing required permissions') >>> validator({'read'}).error_or('') 'Missing required permissions'
- valid8r.core.validators.is_sorted(*, reverse=False, error_message=None)[source]
Create a validator that ensures a list is sorted.
Validates that a list is sorted in either ascending or descending order. Uses keyword-only parameters to avoid boolean trap anti-pattern.
- Parameters:
- Returns:
A validator function that checks if list is sorted
- Return type:
Examples
>>> from valid8r.core.validators import is_sorted >>> # Ascending order (default) >>> validator = is_sorted() >>> validator([1, 2, 3, 4, 5]) Success([1, 2, 3, 4, 5]) >>> validator([3, 1, 4, 2]).is_failure() True >>> # Descending order >>> validator = is_sorted(reverse=True) >>> validator([5, 4, 3, 2, 1]) Success([5, 4, 3, 2, 1]) >>> validator([1, 2, 3]).is_failure() True >>> # Works with strings >>> validator = is_sorted() >>> validator(['a', 'b', 'c']) Success(['a', 'b', 'c']) >>> # With custom error message >>> validator = is_sorted(error_message='List must be in order') >>> validator([3, 1, 2]).error_or('') 'List must be in order'
- valid8r.core.validators.exists()[source]
Create a validator that ensures a path exists on the filesystem.
Validates that a Path object points to an existing file or directory. Follows symbolic links by default (uses Path.exists()).
- Returns:
A validator function that checks path existence
- Return type:
Validator[Path]
Examples
>>> from pathlib import Path >>> from valid8r.core.validators import exists >>> # Existing path (doctest creates temp file) >>> import tempfile >>> with tempfile.NamedTemporaryFile() as tmp: ... path = Path(tmp.name) ... exists()(path).is_success() True >>> # Non-existent path >>> path = Path('/nonexistent/file.txt') >>> exists()(path).is_failure() True >>> exists()(path).error_or('') 'Path does not exist: /nonexistent/file.txt'
- valid8r.core.validators.is_file()[source]
Create a validator that ensures a path is a regular file.
Validates that a Path object points to an existing regular file (not a directory, symlink, socket, etc.). Note that this also checks that the path exists. For better error messages, chain with exists() first.
- Returns:
A validator function that checks path is a file
- Return type:
Validator[Path]
Examples
>>> from pathlib import Path >>> from valid8r.core.validators import is_file >>> # Regular file >>> import tempfile >>> with tempfile.NamedTemporaryFile() as tmp: ... path = Path(tmp.name) ... is_file()(path).is_success() True >>> # Directory >>> import os >>> path = Path(os.getcwd()) >>> result = is_file()(path) >>> result.is_failure() True >>> 'not a file' in result.error_or('').lower() True
- valid8r.core.validators.is_dir()[source]
Create a validator that ensures a path is a directory.
Validates that a Path object points to an existing directory. Note that this also checks that the path exists. For better error messages, chain with exists() first.
- Returns:
A validator function that checks path is a directory
- Return type:
Validator[Path]
Examples
>>> from pathlib import Path >>> from valid8r.core.validators import is_dir >>> # Directory >>> import os >>> path = Path(os.getcwd()) >>> is_dir()(path).is_success() True >>> # Regular file >>> import tempfile >>> with tempfile.NamedTemporaryFile() as tmp: ... path = Path(tmp.name) ... result = is_dir()(path) ... result.is_failure() True >>> # Non-existent path >>> path = Path('/nonexistent/dir') >>> result = is_dir()(path) >>> result.is_failure() True >>> 'not a directory' in result.error_or('').lower() True
- valid8r.core.validators.is_readable()[source]
Create a validator that ensures a path has read permissions.
Validates that a Path object has read permissions using os.access(). Works with files, directories, and symbolic links. For symlinks, checks the target’s permissions (follows the link).
- Returns:
A validator function that checks read permissions
- Return type:
Validator[Path]
Examples
>>> from pathlib import Path >>> from valid8r.core.validators import is_readable >>> # Readable file >>> import tempfile >>> import os >>> with tempfile.NamedTemporaryFile() as tmp: ... path = Path(tmp.name) ... os.chmod(tmp.name, 0o444) # r--r--r-- ... is_readable()(path).is_success() True >>> # Non-existent path >>> path = Path('/nonexistent/file.txt') >>> is_readable()(path).is_failure() True >>> 'not readable' in is_readable()(path).error_or('').lower() True
- valid8r.core.validators.is_writable()[source]
Create a validator that ensures a path has write permissions.
Validates that a Path object has write permissions using os.access(). Works with files, directories, and symbolic links. For symlinks, checks the target’s permissions (follows the link).
- Returns:
A validator function that checks write permissions
- Return type:
Validator[Path]
Examples
>>> from pathlib import Path >>> from valid8r.core.validators import is_writable >>> # Writable file >>> import tempfile >>> import os >>> with tempfile.NamedTemporaryFile() as tmp: ... path = Path(tmp.name) ... os.chmod(tmp.name, 0o644) # rw-r--r-- ... is_writable()(path).is_success() True >>> # Non-existent path >>> path = Path('/nonexistent/file.txt') >>> is_writable()(path).is_failure() True >>> 'not writable' in is_writable()(path).error_or('').lower() True
- valid8r.core.validators.is_executable()[source]
Create a validator that ensures a path has execute permissions.
Validates that a Path object has execute permissions using os.access(). Works with files, directories, and symbolic links. For symlinks, checks the target’s permissions (follows the link).
- Returns:
A validator function that checks execute permissions
- Return type:
Validator[Path]
Examples
>>> from pathlib import Path >>> from valid8r.core.validators import is_executable >>> # Executable file >>> import tempfile >>> import os >>> with tempfile.NamedTemporaryFile() as tmp: ... path = Path(tmp.name) ... os.chmod(tmp.name, 0o755) # rwxr-xr-x ... is_executable()(path).is_success() True >>> # Non-existent path >>> path = Path('/nonexistent/file.sh') >>> is_executable()(path).is_failure() True >>> 'not executable' in is_executable()(path).error_or('').lower() True
- valid8r.core.validators.max_size(max_bytes)[source]
Create a validator that ensures a file does not exceed a maximum size.
Validates that a file’s size in bytes is at most the specified maximum. This validator checks that the path is a regular file before checking size.
- Parameters:
max_bytes (int) – Maximum allowed file size in bytes (inclusive)
- Returns:
A validator function that checks file size
- Return type:
Validator[Path]
Examples
>>> from pathlib import Path >>> from valid8r.core.validators import max_size >>> # File under size limit >>> import tempfile >>> with tempfile.NamedTemporaryFile() as tmp: ... path = Path(tmp.name) ... path.write_bytes(b'x' * 1024) ... max_size(2048)(path).is_success() 1024 True >>> # File over size limit >>> with tempfile.NamedTemporaryFile() as tmp: ... path = Path(tmp.name) ... path.write_bytes(b'x' * 5120) ... result = max_size(1024)(path) ... result.is_failure() 5120 True >>> # Error includes actual size >>> with tempfile.NamedTemporaryFile() as tmp: ... path = Path(tmp.name) ... path.write_bytes(b'x' * 5120) ... result = max_size(1024)(path) ... '5120' in result.error_or('') 5120 True
- valid8r.core.validators.min_size(min_bytes)[source]
Create a validator that ensures a file meets a minimum size requirement.
Validates that a file’s size in bytes is at least the specified minimum. This validator checks that the path is a regular file before checking size.
- Parameters:
min_bytes (int) – Minimum required file size in bytes (inclusive)
- Returns:
A validator function that checks file size
- Return type:
Validator[Path]
Examples
>>> from pathlib import Path >>> from valid8r.core.validators import min_size >>> # File above size limit >>> import tempfile >>> with tempfile.NamedTemporaryFile() as tmp: ... path = Path(tmp.name) ... path.write_bytes(b'x' * 2048) ... min_size(1024)(path).is_success() 2048 True >>> # File below size limit >>> with tempfile.NamedTemporaryFile() as tmp: ... path = Path(tmp.name) ... path.write_bytes(b'x' * 512) ... result = min_size(1024)(path) ... result.is_failure() 512 True >>> # Error includes minimum size >>> with tempfile.NamedTemporaryFile() as tmp: ... path = Path(tmp.name) ... path.write_bytes(b'x' * 512) ... result = min_size(1024)(path) ... '1024' in result.error_or('') 512 True
- valid8r.core.validators.has_extension(*extensions)[source]
Create a validator that ensures a file has one of the allowed extensions.
Validates that a file’s extension matches one of the specified extensions. Extension matching is case-insensitive. Extensions should include the dot (e.g., ‘.txt’).
- Parameters:
*extensions (str | list[str] | tuple[str, Ellipsis]) – Variable number of allowed file extensions (e.g., ‘.pdf’, ‘.txt’) or a single list/tuple of extensions (e.g., [‘.pdf’, ‘.txt’])
- Returns:
A validator function that checks file extension
- Return type:
Validator[Path]
Examples
>>> from pathlib import Path >>> from valid8r.core.validators import has_extension >>> # Single extension >>> import tempfile >>> import os >>> with tempfile.TemporaryDirectory() as tmpdir: ... path = Path(tmpdir) / 'document.pdf' ... path.write_text('content') ... has_extension('.pdf')(path).is_success() 7 True >>> # Multiple extensions (variadic) >>> with tempfile.TemporaryDirectory() as tmpdir: ... path = Path(tmpdir) / 'document.docx' ... path.write_text('content') ... has_extension('.pdf', '.doc', '.docx')(path).is_success() 7 True >>> # Multiple extensions (list) >>> with tempfile.TemporaryDirectory() as tmpdir: ... path = Path(tmpdir) / 'document.docx' ... path.write_text('content') ... has_extension(['.pdf', '.doc', '.docx'])(path).is_success() 7 True >>> # Case-insensitive >>> with tempfile.TemporaryDirectory() as tmpdir: ... path = Path(tmpdir) / 'DOCUMENT.PDF' ... path.write_text('content') ... has_extension('.pdf')(path).is_success() 7 True >>> # Wrong extension >>> with tempfile.TemporaryDirectory() as tmpdir: ... path = Path(tmpdir) / 'image.png' ... path.write_text('content') ... result = has_extension('.pdf', '.docx')(path) ... result.is_failure() 7 True