Python library that leverages the __eq__ method to make unit tests more declarative and readable through flexible comparison classes.
—
Specialized comparison types for common data formats including JSON, UUIDs, URLs, hashes, IP addresses, dataclasses, enums, and custom function-based validation. These types enable validation of complex data formats and structures beyond basic Python types.
UUID (Universally Unique Identifier) validation with optional version constraints.
class IsUUID(DirtyEquals):
"""
UUID validation with optional version checking.
Validates that a value is a properly formatted UUID,
optionally checking for a specific UUID version.
"""
def __init__(self, version: Optional[int] = None):
"""
Initialize UUID validator.
Args:
version: Specific UUID version to validate (1-5), None for any version
"""
def equals(self, other: Any) -> bool:
"""
Check if value is a valid UUID.
Args:
other: Value to validate (string or uuid.UUID object)
Returns:
bool: True if valid UUID (and correct version if specified)
"""from dirty_equals import IsUUID
import uuid
# Basic UUID validation - any version
valid_uuid = str(uuid.uuid4())
assert valid_uuid == IsUUID
# UUID object validation
uuid_obj = uuid.uuid4()
assert uuid_obj == IsUUID
# Version-specific validation
uuid_v4 = str(uuid.uuid4()) # Version 4 UUID
assert uuid_v4 == IsUUID(version=4)
uuid_v1 = str(uuid.uuid1()) # Version 1 UUID
assert uuid_v1 == IsUUID(version=1)
# API response with UUIDs
api_response = {
'user_id': str(uuid.uuid4()),
'session_id': str(uuid.uuid4()),
'request_id': str(uuid.uuid4())
}
assert api_response == {
'user_id': IsUUID,
'session_id': IsUUID,
'request_id': IsUUID
}
# Database record validation
user_record = {
'id': '123e4567-e89b-12d3-a456-426614174000',
'external_id': uuid.uuid4(),
'tenant_id': str(uuid.uuid1())
}
assert user_record == {
'id': IsUUID,
'external_id': IsUUID,
'tenant_id': IsUUID(version=1) # Specific version requirement
}JSON parsing and validation with support for expected value matching and generic type syntax.
class IsJson(DirtyEquals):
"""
JSON parsing and validation.
Parses JSON strings and validates the resulting data structure
against expected values or patterns.
"""
def __init__(
self,
expected_value: Any = AnyJson,
**expected_kwargs: Any
):
"""
Initialize JSON validator (overloaded constructor).
Args:
expected_value: Expected value after JSON parsing
**expected_kwargs: Expected key-value pairs for JSON objects
"""
@classmethod
def __class_getitem__(cls, expected_value: Any) -> 'IsJson':
"""
Support generic syntax: IsJson[expected] equivalent to IsJson(expected).
Args:
expected_value: Expected value after parsing
Returns:
IsJson: New instance configured for the expected value
"""
def equals(self, other: Any) -> bool:
"""
Parse JSON and validate against expected value.
Args:
other: JSON string to parse and validate
Returns:
bool: True if JSON parses and matches expected structure
"""from dirty_equals import IsJson, IsPositive, IsStr, AnyThing
# Basic JSON validation - any valid JSON
assert '{"key": "value"}' == IsJson
assert '[1, 2, 3]' == IsJson
assert '"hello"' == IsJson
assert 'true' == IsJson
# Specific value matching
assert '{"name": "John", "age": 25}' == IsJson({"name": "John", "age": 25})
# Generic syntax
assert '{"user_id": 123}' == IsJson[{"user_id": 123}]
# Keyword arguments for JSON objects
assert '{"name": "John", "age": 25}' == IsJson(name="John", age=25)
# With validators for JSON content
json_string = '{"user_id": 123, "username": "john_doe", "active": true}'
assert json_string == IsJson({
"user_id": IsPositive,
"username": IsStr,
"active": True
})
# API response validation
api_response = '{"status": "success", "data": [1, 2, 3], "count": 3}'
assert api_response == IsJson({
"status": "success",
"data": [1, 2, 3],
"count": IsPositive
})
# Configuration validation
config_json = '{"debug": true, "port": 8080, "features": ["auth", "logging"]}'
assert config_json == IsJson(
debug=True,
port=IsPositive,
features=["auth", "logging"]
)
# Nested JSON validation
nested_json = '''
{
"user": {
"id": 123,
"profile": {
"name": "John Doe",
"settings": {"theme": "dark"}
}
},
"metadata": {"version": 1}
}
'''
from dirty_equals import IsDict, IsPartialDict
assert nested_json == IsJson({
"user": IsDict({
"id": IsPositive,
"profile": IsPartialDict({"name": IsStr})
}),
"metadata": AnyThing # Accept any metadata
})
# Database field validation (JSON column)
user_preferences = '{"notifications": true, "theme": "light", "language": "en"}'
assert user_preferences == IsJson[IsDict({
"notifications": bool,
"theme": IsStr,
"language": IsStr
})]Custom function-based validation that allows arbitrary validation logic through user-provided functions.
class FunctionCheck(DirtyEquals):
"""
Custom function-based validation.
Validates values using a user-provided function that returns
True for valid values and False for invalid ones.
"""
def __init__(self, func: Callable[[Any], bool]):
"""
Initialize function-based validator.
Args:
func: Function that takes a value and returns bool
"""
def equals(self, other: Any) -> bool:
"""
Validate using the provided function.
Args:
other: Value to validate
Returns:
bool: Result of calling func(other)
"""from dirty_equals import FunctionCheck
# Simple validation functions
def is_even(x):
return isinstance(x, int) and x % 2 == 0
def is_valid_email(email):
return isinstance(email, str) and '@' in email and '.' in email
# Basic function validation
assert 42 == FunctionCheck(is_even)
assert 21 != FunctionCheck(is_even) # Odd number fails
assert "user@example.com" == FunctionCheck(is_valid_email)
assert "invalid-email" != FunctionCheck(is_valid_email)
# Lambda functions for inline validation
assert "HELLO" == FunctionCheck(lambda x: x.isupper())
assert "hello" != FunctionCheck(lambda x: x.isupper())
# Complex business logic validation
def is_valid_user_id(user_id):
"""Custom business rule: user IDs must be 6-digit integers starting with 1 or 2"""
if not isinstance(user_id, (int, str)):
return False
str_id = str(user_id)
return (len(str_id) == 6 and
str_id.isdigit() and
str_id[0] in ['1', '2'])
assert 123456 == FunctionCheck(is_valid_user_id)
assert 234567 == FunctionCheck(is_valid_user_id)
assert 345678 != FunctionCheck(is_valid_user_id) # Doesn't start with 1 or 2
# Data structure validation
user_data = {
'user_id': 123456,
'email': 'user@company.com',
'score': 88
}
assert user_data == {
'user_id': FunctionCheck(is_valid_user_id),
'email': FunctionCheck(is_valid_email),
'score': FunctionCheck(lambda x: 0 <= x <= 100) # Score range validation
}
# Advanced validation with external dependencies
def is_valid_credit_card(card_num):
"""Luhn algorithm check (simplified example)"""
if not isinstance(card_num, str) or not card_num.isdigit():
return False
# Simplified Luhn check
digits = [int(d) for d in card_num]
for i in range(len(digits) - 2, -1, -2):
digits[i] *= 2
if digits[i] > 9:
digits[i] -= 9
return sum(digits) % 10 == 0
# Validate credit card number
card_number = "4532015112830366" # Valid test card number
assert card_number == FunctionCheck(is_valid_credit_card)
# API response validation with custom rules
api_response = {
'transaction_id': 'TXN123456789',
'amount': 299.99,
'currency': 'USD'
}
def is_valid_transaction_id(txn_id):
return (isinstance(txn_id, str) and
txn_id.startswith('TXN') and
len(txn_id) == 12)
def is_valid_currency(currency):
valid_currencies = ['USD', 'EUR', 'GBP', 'JPY', 'CAD']
return currency in valid_currencies
assert api_response == {
'transaction_id': FunctionCheck(is_valid_transaction_id),
'amount': FunctionCheck(lambda x: isinstance(x, (int, float)) and x > 0),
'currency': FunctionCheck(is_valid_currency)
}URL validation with support for different URL types (requires Pydantic for full functionality).
class IsUrl(DirtyEquals):
"""
URL validation using Pydantic (requires Pydantic dependency).
Validates URLs with support for different URL schemes and
additional attribute validation.
"""
def __init__(
self,
any_url: bool = False,
any_http_url: bool = False,
http_url: bool = False,
file_url: bool = False,
postgres_dsn: bool = False,
ampqp_dsn: bool = False,
redis_dsn: bool = False,
**expected_attributes: Any
):
"""
Initialize URL validator.
Args:
any_url: Accept any valid URL scheme
any_http_url: Accept HTTP/HTTPS URLs
http_url: Accept only HTTP URLs
file_url: Accept only file:// URLs
postgres_dsn: Accept PostgreSQL connection strings
ampqp_dsn: Accept AMQP connection strings
redis_dsn: Accept Redis connection strings
**expected_attributes: Expected URL component values
"""
def equals(self, other: Any) -> bool:
"""
Validate URL format and attributes.
Args:
other: URL string to validate
Returns:
bool: True if valid URL matching constraints
"""from dirty_equals import IsUrl
# Basic URL validation (requires Pydantic)
assert "https://example.com" == IsUrl(any_url=True)
assert "http://localhost:8080" == IsUrl(any_http_url=True)
assert "file:///path/to/file" == IsUrl(file_url=True)
# Database connection strings
assert "postgresql://user:pass@localhost/db" == IsUrl(postgres_dsn=True)
assert "redis://localhost:6379/0" == IsUrl(redis_dsn=True)
# With attribute validation
assert "https://api.example.com/v1" == IsUrl(
any_url=True,
host="api.example.com"
)Hash string validation for common hash algorithms.
class IsHash(DirtyEquals):
"""
Hash string validation for common hash types.
Validates that a string matches the expected format
for common cryptographic hash algorithms.
"""
def __init__(self, hash_type: str):
"""
Initialize hash validator.
Args:
hash_type: Type of hash to validate ('md5', 'sha-1', 'sha-256')
"""
def equals(self, other: Any) -> bool:
"""
Validate hash string format.
Args:
other: String to validate as hash
Returns:
bool: True if matches expected hash format
"""from dirty_equals import IsHash
import hashlib
# Generate test hashes
test_data = b"hello world"
md5_hash = hashlib.md5(test_data).hexdigest()
sha1_hash = hashlib.sha1(test_data).hexdigest()
sha256_hash = hashlib.sha256(test_data).hexdigest()
# Validate different hash types
assert md5_hash == IsHash('md5')
assert sha1_hash == IsHash('sha-1')
assert sha256_hash == IsHash('sha-256')
# API response with file hashes
file_info = {
'filename': 'document.pdf',
'size': 1024,
'md5': md5_hash,
'sha256': sha256_hash
}
assert file_info == {
'filename': 'document.pdf',
'size': 1024,
'md5': IsHash('md5'),
'sha256': IsHash('sha-256')
}IP address validation with version and netmask support.
class IsIP(DirtyEquals):
"""
IP address validation with version and netmask support.
Validates IPv4 and IPv6 addresses with optional
version constraints and netmask validation.
"""
def __init__(
self,
*,
version: Optional[int] = None,
netmask: Optional[str] = None
):
"""
Initialize IP address validator.
Args:
version: IP version constraint (4 or 6), None for any
netmask: Expected netmask/CIDR notation
"""
def equals(self, other: Any) -> bool:
"""
Validate IP address format and constraints.
Args:
other: IP address string to validate
Returns:
bool: True if valid IP address matching constraints
"""from dirty_equals import IsIP
# Basic IP validation
assert "192.168.1.1" == IsIP
assert "::1" == IsIP # IPv6 localhost
# Version-specific validation
assert "192.168.1.1" == IsIP(version=4)
assert "2001:db8::1" == IsIP(version=6)
# With CIDR/netmask
assert "192.168.1.0/24" == IsIP(version=4, netmask="255.255.255.0")
# Network configuration validation
network_config = {
'gateway': '192.168.1.1',
'dns_primary': '8.8.8.8',
'dns_secondary': '2001:4860:4860::8888',
'subnet': '192.168.1.0/24'
}
assert network_config == {
'gateway': IsIP(version=4),
'dns_primary': IsIP(version=4),
'dns_secondary': IsIP(version=6),
'subnet': IsIP(version=4)
}Validation types for Python dataclasses with field validation and matching strategies.
class IsDataclassType(DirtyEquals):
"""Checks if value is a dataclass type (class, not instance)."""
class IsDataclass(DirtyEquals):
"""
Dataclass instance validation with field checking.
Validates dataclass instances and optionally checks
specific field values.
"""
def __init__(self, **fields: Any):
"""
Initialize dataclass validator.
Args:
**fields: Expected field names and values
"""
def settings(
self,
*,
strict: bool = False,
partial: bool = False
) -> 'IsDataclass':
"""
Configure dataclass matching behavior.
Args:
strict: Enforce field order and exact matching
partial: Allow subset matching (only check specified fields)
Returns:
IsDataclass: New instance with updated settings
"""
class IsPartialDataclass(IsDataclass):
"""Partial dataclass field matching."""
class IsStrictDataclass(IsDataclass):
"""Strict dataclass field matching with order enforcement."""from dirty_equals import IsDataclass, IsDataclassType, IsPartialDataclass, IsStr, IsPositive
from dataclasses import dataclass
@dataclass
class User:
id: int
name: str
email: str
active: bool = True
# Check if something is a dataclass type
assert User == IsDataclassType
# Dataclass instance validation
user = User(123, "John Doe", "john@example.com")
assert user == IsDataclass() # Any dataclass instance
# Field validation
assert user == IsDataclass(
id=123,
name="John Doe",
email="john@example.com",
active=True
)
# With validators
assert user == IsDataclass(
id=IsPositive,
name=IsStr,
email=IsStr,
active=bool
)
# Partial matching - only check some fields
assert user == IsPartialDataclass(
id=IsPositive,
name=IsStr
)Enum instance and value validation.
class IsEnum(DirtyEquals):
"""
Enum instance/value checking.
Validates enum instances or checks if a value
matches any enum member value.
"""
def __init__(self, enum_cls: type = Enum):
"""
Initialize enum validator.
Args:
enum_cls: Enum class to validate against (default: any Enum)
"""
def equals(self, other: Any) -> bool:
"""
Check if value is enum instance or matches enum value.
Args:
other: Value to validate
Returns:
bool: True if valid enum instance or matching value
"""from dirty_equals import IsEnum
from enum import Enum
class Status(Enum):
ACTIVE = "active"
INACTIVE = "inactive"
PENDING = "pending"
class Priority(Enum):
LOW = 1
MEDIUM = 2
HIGH = 3
# Enum instance validation
assert Status.ACTIVE == IsEnum(Status)
assert Priority.HIGH == IsEnum(Priority)
# Any enum validation
assert Status.ACTIVE == IsEnum # Any enum type
# API response with enum values
task_data = {
'id': 123,
'status': Status.ACTIVE,
'priority': Priority.HIGH
}
assert task_data == {
'id': 123,
'status': IsEnum(Status),
'priority': IsEnum(Priority)
}from typing import Any, Callable, Optional, Union
from enum import Enum
import uuid
# Special constant for JSON validation
AnyJson = object() # Placeholder for any valid JSON value
# Hash type options
HashTypes = Union['md5', 'sha-1', 'sha-256']
# All specialized types inherit from DirtyEquals
# External dependencies: Pydantic (optional, for IsUrl)Install with Tessl CLI
npx tessl i tessl/pypi-dirty-equals