0
# String Types
1
2
String and byte sequence validation with support for length constraints, case sensitivity, and regular expression matching. These types enable comprehensive validation of text and binary data with flexible pattern matching and formatting constraints.
3
4
## Capabilities
5
6
### IsAnyStr
7
8
Base class for string and bytes comparison providing common functionality for length constraints, case handling, and regular expression matching. Supports both string and bytes objects.
9
10
```python { .api }
11
class IsAnyStr(DirtyEquals):
12
"""
13
Base string/bytes comparison with configurable constraints.
14
15
Provides length validation, case handling, and regex matching
16
for both string and bytes objects.
17
"""
18
19
def __init__(
20
self,
21
*,
22
min_length: Optional[int] = None,
23
max_length: Optional[int] = None,
24
case: Optional[str] = None,
25
regex: Optional[Union[str, Pattern]] = None,
26
regex_flags: Optional[int] = None
27
):
28
"""
29
Initialize string/bytes validator.
30
31
Args:
32
min_length: Minimum string/bytes length
33
max_length: Maximum string/bytes length
34
case: Case constraint ('upper', 'lower', 'title', 'capitalize')
35
regex: Regular expression pattern to match
36
regex_flags: Regex flags (e.g., re.IGNORECASE, re.MULTILINE)
37
"""
38
39
expected_types: ClassVar[Tuple[type, ...]] = (str, bytes)
40
41
def equals(self, other: Any) -> bool:
42
"""
43
Check if value matches string/bytes constraints.
44
45
Args:
46
other: String or bytes value to validate
47
48
Returns:
49
bool: True if value satisfies all constraints
50
"""
51
```
52
53
#### Usage Examples
54
55
```python
56
from dirty_equals import IsAnyStr
57
import re
58
59
# Basic string or bytes validation
60
assert "hello" == IsAnyStr
61
assert b"hello" == IsAnyStr
62
63
# Length constraints
64
assert "hello world" == IsAnyStr(min_length=5, max_length=20)
65
assert b"data" == IsAnyStr(min_length=1, max_length=10)
66
67
# Case constraints
68
assert "HELLO" == IsAnyStr(case='upper')
69
assert "hello" == IsAnyStr(case='lower')
70
assert "Hello World" == IsAnyStr(case='title')
71
assert "Hello" == IsAnyStr(case='capitalize')
72
73
# Regex matching
74
assert "test@example.com" == IsAnyStr(regex=r'.+@.+\..+')
75
assert "123-456-7890" == IsAnyStr(regex=r'\d{3}-\d{3}-\d{4}')
76
77
# Regex with flags
78
assert "HELLO world" == IsAnyStr(
79
regex=r'hello world',
80
regex_flags=re.IGNORECASE
81
)
82
83
# Combined constraints
84
assert "Hello World!" == IsAnyStr(
85
min_length=5,
86
max_length=20,
87
regex=r'^[A-Za-z\s!]+$' # Letters, spaces, exclamation only
88
)
89
90
# API response validation
91
api_data = {
92
'username': 'john_doe',
93
'email': 'john@example.com',
94
'status': 'ACTIVE',
95
'description': b'binary data here'
96
}
97
98
assert api_data == {
99
'username': IsAnyStr(min_length=3, max_length=20),
100
'email': IsAnyStr(regex=r'.+@.+\..+'),
101
'status': IsAnyStr(case='upper'),
102
'description': IsAnyStr(min_length=1) # Any non-empty bytes
103
}
104
```
105
106
### IsStr
107
108
String-specific validation that inherits all functionality from IsAnyStr but restricts validation to string objects only, excluding bytes.
109
110
```python { .api }
111
class IsStr(IsAnyStr):
112
"""
113
String comparison with constraints (excludes bytes).
114
115
Inherits all functionality from IsAnyStr but only
116
accepts str objects, not bytes.
117
"""
118
119
expected_types: ClassVar[Tuple[type, ...]] = (str,)
120
```
121
122
#### Usage Examples
123
124
```python
125
from dirty_equals import IsStr
126
import re
127
128
# Basic string validation
129
assert "hello world" == IsStr
130
assert "12345" == IsStr
131
132
# Bytes won't match
133
# assert b"hello" == IsStr # Would fail
134
135
# Length validation
136
assert "username" == IsStr(min_length=3, max_length=20)
137
assert "short" == IsStr(min_length=1, max_length=10)
138
139
# Case validation
140
assert "HELLO" == IsStr(case='upper')
141
assert "hello" == IsStr(case='lower')
142
assert "Hello World" == IsStr(case='title')
143
assert "Hello" == IsStr(case='capitalize')
144
145
# Pattern matching
146
assert "john@example.com" == IsStr(regex=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
147
assert "+1-555-123-4567" == IsStr(regex=r'^\+\d{1,3}-\d{3}-\d{3}-\d{4}$')
148
149
# URL validation
150
assert "https://www.example.com/path" == IsStr(regex=r'^https?://.+')
151
152
# Username validation
153
assert "user_name123" == IsStr(
154
min_length=3,
155
max_length=20,
156
regex=r'^[a-zA-Z0-9_]+$' # Alphanumeric and underscore only
157
)
158
159
# Password strength validation
160
assert "MyPassword123!" == IsStr(
161
min_length=8,
162
regex=r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*]).*$'
163
)
164
165
# Form data validation
166
form_input = {
167
'first_name': 'John',
168
'last_name': 'Doe',
169
'email': 'john.doe@company.com',
170
'phone': '555-0123',
171
'zip_code': '12345',
172
'country': 'US'
173
}
174
175
assert form_input == {
176
'first_name': IsStr(min_length=1, max_length=50, case='capitalize'),
177
'last_name': IsStr(min_length=1, max_length=50, case='capitalize'),
178
'email': IsStr(regex=r'^[^@]+@[^@]+\.[^@]+$'),
179
'phone': IsStr(regex=r'^\d{3}-\d{4}$'),
180
'zip_code': IsStr(regex=r'^\d{5}$'),
181
'country': IsStr(case='upper', min_length=2, max_length=2)
182
}
183
184
# Configuration validation
185
config = {
186
'app_name': 'MyApplication',
187
'environment': 'production',
188
'log_level': 'INFO',
189
'secret_key': 'abc123def456ghi789'
190
}
191
192
assert config == {
193
'app_name': IsStr(min_length=1),
194
'environment': IsStr(regex=r'^(development|staging|production)$'),
195
'log_level': IsStr(regex=r'^(DEBUG|INFO|WARNING|ERROR|CRITICAL)$'),
196
'secret_key': IsStr(min_length=16)
197
}
198
199
# File path validation
200
file_paths = {
201
'config_file': '/etc/myapp/config.json',
202
'log_file': '/var/log/myapp.log',
203
'temp_dir': '/tmp/myapp/'
204
}
205
206
assert file_paths == {
207
'config_file': IsStr(regex=r'^/.+\.json$'),
208
'log_file': IsStr(regex=r'^/.+\.log$'),
209
'temp_dir': IsStr(regex=r'^/.+/$') # Ends with slash
210
}
211
212
# Social media validation
213
social_handles = {
214
'twitter': '@johndoe',
215
'instagram': 'john.doe.photography',
216
'linkedin': 'john-doe-engineer'
217
}
218
219
assert social_handles == {
220
'twitter': IsStr(regex=r'^@[a-zA-Z0-9_]{1,15}$'),
221
'instagram': IsStr(regex=r'^[a-zA-Z0-9_.]{1,30}$'),
222
'linkedin': IsStr(regex=r'^[a-zA-Z0-9-]{3,100}$')
223
}
224
225
# API token validation
226
tokens = {
227
'api_key': 'sk-1234567890abcdef1234567890abcdef',
228
'session_token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
229
'refresh_token': 'rt_1234567890abcdef'
230
}
231
232
assert tokens == {
233
'api_key': IsStr(regex=r'^sk-[a-f0-9]{32}$'),
234
'session_token': IsStr(min_length=100), # JWT tokens are long
235
'refresh_token': IsStr(regex=r'^rt_[a-f0-9]{16}$')
236
}
237
```
238
239
### IsBytes
240
241
Bytes-specific validation that inherits all functionality from IsAnyStr but restricts validation to bytes objects only, excluding strings.
242
243
```python { .api }
244
class IsBytes(IsAnyStr):
245
"""
246
Bytes comparison with constraints (excludes strings).
247
248
Inherits all functionality from IsAnyStr but only
249
accepts bytes objects, not strings.
250
"""
251
252
expected_types: ClassVar[Tuple[type, ...]] = (bytes,)
253
```
254
255
#### Usage Examples
256
257
```python
258
from dirty_equals import IsBytes
259
import re
260
261
# Basic bytes validation
262
assert b"hello world" == IsBytes
263
assert b"\x00\x01\x02\x03" == IsBytes
264
265
# Strings won't match
266
# assert "hello" == IsBytes # Would fail
267
268
# Length validation
269
assert b"binary_data" == IsBytes(min_length=5, max_length=50)
270
assert b"short" == IsBytes(min_length=1, max_length=10)
271
272
# Pattern matching on bytes
273
assert b"Content-Type: application/json" == IsBytes(regex=rb'Content-Type: \w+/\w+')
274
assert b"HTTP/1.1 200 OK" == IsBytes(regex=rb'HTTP/\d\.\d \d{3} \w+')
275
276
# File signature validation (magic numbers)
277
png_header = b'\x89PNG\r\n\x1a\n'
278
jpeg_header = b'\xff\xd8\xff'
279
pdf_header = b'%PDF-'
280
281
assert png_header == IsBytes(regex=rb'^\x89PNG\r\n\x1a\n')
282
assert jpeg_header == IsBytes(regex=rb'^\xff\xd8\xff')
283
assert pdf_header == IsBytes(regex=rb'^%PDF-')
284
285
# Network packet validation
286
http_request = b'GET /api/users HTTP/1.1\r\nHost: example.com\r\n\r\n'
287
assert http_request == IsBytes(
288
regex=rb'^GET .+ HTTP/\d\.\d\r\n.*Host: .+\r\n\r\n$'
289
)
290
291
# Binary protocol validation
292
protocol_message = b'\x02\x00\x10Hello World!\x03' # STX + length + data + ETX
293
assert protocol_message == IsBytes(
294
min_length=4, # At least header + footer
295
regex=rb'^\x02.+\x03$' # Starts with STX, ends with ETX
296
)
297
298
# Cryptographic data validation
299
encrypted_data = {
300
'iv': b'\x12\x34\x56\x78' * 4, # 16 bytes IV
301
'ciphertext': b'encrypted_payload_here_with_padding',
302
'tag': b'\xaa\xbb\xcc\xdd' * 4 # 16 bytes auth tag
303
}
304
305
assert encrypted_data == {
306
'iv': IsBytes(min_length=16, max_length=16), # Exactly 16 bytes
307
'ciphertext': IsBytes(min_length=1), # Non-empty
308
'tag': IsBytes(min_length=16, max_length=16) # Exactly 16 bytes
309
}
310
311
# File content validation
312
file_data = {
313
'image': b'\x89PNG\r\n\x1a\n' + b'image_data_here',
314
'document': b'%PDF-1.4\n' + b'pdf_content_here',
315
'archive': b'PK\x03\x04' + b'zip_content_here'
316
}
317
318
assert file_data == {
319
'image': IsBytes(regex=rb'^\x89PNG'), # PNG file
320
'document': IsBytes(regex=rb'^%PDF-'), # PDF file
321
'archive': IsBytes(regex=rb'^PK\x03\x04') # ZIP file
322
}
323
324
# Database blob validation
325
blob_data = {
326
'profile_picture': b'binary_image_data_here',
327
'document_content': b'binary_document_data',
328
'metadata': b'{"created": "2023-01-01"}'
329
}
330
331
assert blob_data == {
332
'profile_picture': IsBytes(min_length=1, max_length=5*1024*1024), # Max 5MB
333
'document_content': IsBytes(min_length=1),
334
'metadata': IsBytes(regex=rb'^\{.*\}$') # JSON-like structure
335
}
336
337
# Message queue payloads
338
queue_messages = [
339
b'{"type": "user_created", "data": {...}}',
340
b'{"type": "order_processed", "data": {...}}',
341
b'{"type": "email_sent", "data": {...}}'
342
]
343
344
message_pattern = IsBytes(regex=rb'^\{"type": "\w+", "data": .*\}$')
345
for message in queue_messages:
346
assert message == message_pattern
347
348
# Network response validation
349
api_response_bytes = b'{"status": "success", "data": [...]}'
350
assert api_response_bytes == IsBytes(
351
min_length=10, # Minimum JSON structure
352
regex=rb'^\{.*"status".*\}$' # Contains status field
353
)
354
355
# Base64 encoded data (still bytes before decoding)
356
base64_data = b'SGVsbG8gV29ybGQh' # "Hello World!" encoded
357
assert base64_data == IsBytes(regex=rb'^[A-Za-z0-9+/]+=*$')
358
359
# Binary log entries
360
log_entry = b'\x00\x00\x00\x20' + b'2023-01-01 12:00:00 INFO: App started'
361
assert log_entry == IsBytes(
362
min_length=8, # Header + some content
363
regex=rb'^\x00\x00\x00.+INFO:' # Starts with header, contains INFO
364
)
365
```
366
367
## Type Definitions
368
369
```python { .api }
370
from typing import ClassVar, Optional, Pattern, Tuple, Union
371
import re
372
373
# All string types inherit from IsAnyStr which inherits from DirtyEquals
374
# They work with Python's standard str and bytes types
375
376
# Common regex flags that can be used with regex_flags parameter:
377
# re.IGNORECASE, re.MULTILINE, re.DOTALL, re.VERBOSE, etc.
378
```