Initial upload: EnvSchema v0.1.0 with CI/CD workflow
Some checks failed
CI / test (push) Has been cancelled
Some checks failed
CI / test (push) Has been cancelled
This commit is contained in:
153
envschema/validators.py
Normal file
153
envschema/validators.py
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
"""Type validators for environment variable values."""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from envschema.schema import EnvVarType
|
||||||
|
|
||||||
|
|
||||||
|
class ValidationError:
|
||||||
|
"""Represents a validation error."""
|
||||||
|
|
||||||
|
def __init__(self, message: str, value: Optional[str] = None):
|
||||||
|
self.message = message
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
if self.value is not None:
|
||||||
|
return f"{self.message} (got: {self.value!r})"
|
||||||
|
return self.message
|
||||||
|
|
||||||
|
|
||||||
|
class StringValidator:
|
||||||
|
"""Validator for string type - always passes."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def validate(value: Optional[str]) -> tuple[bool, Optional[ValidationError]]:
|
||||||
|
if value is None:
|
||||||
|
return True, None
|
||||||
|
return True, None
|
||||||
|
|
||||||
|
|
||||||
|
class IntegerValidator:
|
||||||
|
"""Validator for integer type."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def validate(value: Optional[str]) -> tuple[bool, Optional[ValidationError]]:
|
||||||
|
if value is None:
|
||||||
|
return True, None
|
||||||
|
try:
|
||||||
|
int(value)
|
||||||
|
return True, None
|
||||||
|
except ValueError:
|
||||||
|
return False, ValidationError(
|
||||||
|
f"Invalid integer value",
|
||||||
|
value=value
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BooleanValidator:
|
||||||
|
"""Validator for boolean type."""
|
||||||
|
|
||||||
|
TRUE_VALUES = {"true", "1", "yes", "on"}
|
||||||
|
FALSE_VALUES = {"false", "0", "no", "off"}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def validate(value: Optional[str]) -> tuple[bool, Optional[ValidationError]]:
|
||||||
|
if value is None:
|
||||||
|
return True, None
|
||||||
|
value_lower = value.lower().strip()
|
||||||
|
if value_lower in BooleanValidator.TRUE_VALUES:
|
||||||
|
return True, None
|
||||||
|
if value_lower in BooleanValidator.FALSE_VALUES:
|
||||||
|
return True, None
|
||||||
|
return False, ValidationError(
|
||||||
|
f"Invalid boolean value (expected: true, false, 1, 0, yes, no, on, off)",
|
||||||
|
value=value
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ListValidator:
|
||||||
|
"""Validator for list type (comma-separated values)."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def validate(value: Optional[str]) -> tuple[bool, Optional[ValidationError]]:
|
||||||
|
if value is None:
|
||||||
|
return True, None
|
||||||
|
if "," in value:
|
||||||
|
return True, None
|
||||||
|
return False, ValidationError(
|
||||||
|
f"Invalid list value (expected comma-separated values)",
|
||||||
|
value=value
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse(value: str) -> list[str]:
|
||||||
|
"""Parse a comma-separated string into a list.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: Comma-separated string.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of values.
|
||||||
|
"""
|
||||||
|
return [item.strip() for item in value.split(",") if item.strip()]
|
||||||
|
|
||||||
|
|
||||||
|
class PatternValidator:
|
||||||
|
"""Validator for pattern/regex validation."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def validate(value: Optional[str], pattern: str) -> tuple[bool, Optional[ValidationError]]:
|
||||||
|
if value is None:
|
||||||
|
return True, None
|
||||||
|
try:
|
||||||
|
if re.match(pattern, value):
|
||||||
|
return True, None
|
||||||
|
return False, ValidationError(
|
||||||
|
f"Value does not match pattern: {pattern}",
|
||||||
|
value=value
|
||||||
|
)
|
||||||
|
except re.error:
|
||||||
|
return False, ValidationError(
|
||||||
|
f"Invalid regex pattern: {pattern}",
|
||||||
|
value=value
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_validator(var_type: EnvVarType):
|
||||||
|
"""Get the validator class for a given type.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
var_type: The environment variable type.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Validator class.
|
||||||
|
"""
|
||||||
|
validators = {
|
||||||
|
EnvVarType.STRING: StringValidator,
|
||||||
|
EnvVarType.INTEGER: IntegerValidator,
|
||||||
|
EnvVarType.BOOLEAN: BooleanValidator,
|
||||||
|
EnvVarType.LIST: ListValidator,
|
||||||
|
}
|
||||||
|
return validators.get(var_type, StringValidator)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_value(value: Optional[str], var_type: EnvVarType, pattern: Optional[str] = None) -> tuple[bool, Optional[ValidationError]]:
|
||||||
|
"""Validate a value against a type and optional pattern.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: The value to validate.
|
||||||
|
var_type: The expected type.
|
||||||
|
pattern: Optional regex pattern.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of (is_valid, error).
|
||||||
|
"""
|
||||||
|
validator = get_validator(var_type)
|
||||||
|
is_valid, error = validator.validate(value)
|
||||||
|
|
||||||
|
if is_valid and pattern and value is not None:
|
||||||
|
is_valid, error = PatternValidator.validate(value, pattern)
|
||||||
|
|
||||||
|
return is_valid, error
|
||||||
Reference in New Issue
Block a user