0
# Inspection Types
1
2
Object introspection and type validation classes for checking instances, attributes, names, and string representations. These types enable validation of object properties and metadata beyond simple value comparison.
3
4
## Capabilities
5
6
### IsInstance
7
8
Type instance checking with support for generic type syntax and optional direct instance validation. Provides flexible type validation including inheritance checking and strict instance validation.
9
10
```python { .api }
11
class IsInstance(DirtyEquals):
12
"""
13
Type instance checking with generic support.
14
15
Validates that a value is an instance of the specified type,
16
with options for inheritance checking or direct instance validation.
17
"""
18
19
def __init__(self, expected_type: type, *, only_direct_instance: bool = False):
20
"""
21
Initialize instance type checker.
22
23
Args:
24
expected_type: The type to check against
25
only_direct_instance: If True, require exact type match (no inheritance)
26
"""
27
28
@classmethod
29
def __class_getitem__(cls, expected_type: type) -> 'IsInstance':
30
"""
31
Support generic syntax: IsInstance[Type] equivalent to IsInstance(Type).
32
33
Args:
34
expected_type: The type to check against
35
36
Returns:
37
IsInstance: New instance configured for the specified type
38
"""
39
40
def equals(self, other: Any) -> bool:
41
"""
42
Check if value is an instance of the expected type.
43
44
Args:
45
other: Value to type-check
46
47
Returns:
48
bool: True if isinstance(other, expected_type) or direct match
49
"""
50
```
51
52
#### Usage Examples
53
54
```python
55
from dirty_equals import IsInstance
56
57
# Basic type checking
58
assert 42 == IsInstance(int)
59
assert "hello" == IsInstance(str)
60
assert [1, 2, 3] == IsInstance(list)
61
assert {"key": "value"} == IsInstance(dict)
62
63
# Generic syntax (equivalent to above)
64
assert 42 == IsInstance[int]
65
assert "hello" == IsInstance[str]
66
assert [1, 2, 3] == IsInstance[list]
67
68
# Inheritance checking (default behavior)
69
class Animal:
70
pass
71
72
class Dog(Animal):
73
pass
74
75
dog = Dog()
76
assert dog == IsInstance(Animal) # True - Dog inherits from Animal
77
assert dog == IsInstance(Dog) # True - Dog is direct instance
78
79
# Direct instance only (no inheritance)
80
assert dog == IsInstance(Dog, only_direct_instance=True) # True
81
# assert dog == IsInstance(Animal, only_direct_instance=True) # False
82
83
# Multiple type options with unions
84
from typing import Union
85
import numbers
86
87
assert 42 == IsInstance((int, float)) # Built-in union
88
assert 3.14 == IsInstance((int, float))
89
assert 42 == IsInstance(numbers.Number) # Abstract base class
90
91
# Complex data validation
92
api_response = {
93
'user_id': 123,
94
'data': [1, 2, 3],
95
'metadata': {'created': 'today'},
96
'success': True
97
}
98
99
assert api_response == {
100
'user_id': IsInstance[int],
101
'data': IsInstance[list],
102
'metadata': IsInstance[dict],
103
'success': IsInstance[bool]
104
}
105
106
# Custom class validation
107
from datetime import datetime
108
109
class User:
110
def __init__(self, name, created_at):
111
self.name = name
112
self.created_at = created_at
113
114
user = User("John", datetime.now())
115
user_data = {
116
'user_object': user,
117
'created_timestamp': datetime.now()
118
}
119
120
assert user_data == {
121
'user_object': IsInstance[User],
122
'created_timestamp': IsInstance[datetime]
123
}
124
125
# With boolean logic
126
from dirty_equals import IsPositive
127
128
# Must be both an integer and positive
129
assert 42 == (IsInstance[int] & IsPositive)
130
131
# Can be either string or integer
132
assert "test" == (IsInstance[str] | IsInstance[int])
133
assert 123 == (IsInstance[str] | IsInstance[int])
134
```
135
136
### HasName
137
138
Checks an object's `__name__` attribute against an expected value. Useful for validating functions, classes, or other named objects, with optional support for instances.
139
140
```python { .api }
141
class HasName(DirtyEquals):
142
"""
143
Checks object's __name__ attribute.
144
145
Validates that an object has a __name__ attribute matching
146
the expected value, with configurable instance support.
147
"""
148
149
def __init__(self, expected_name: str, *, allow_instances: bool = True):
150
"""
151
Initialize name checker.
152
153
Args:
154
expected_name: Expected value of __name__ attribute
155
allow_instances: If True, check instances' class names too
156
"""
157
158
@classmethod
159
def __class_getitem__(cls, expected_name: str) -> 'HasName':
160
"""
161
Support generic syntax: HasName['name'] equivalent to HasName('name').
162
163
Args:
164
expected_name: Expected name value
165
166
Returns:
167
HasName: New instance configured for the specified name
168
"""
169
170
def equals(self, other: Any) -> bool:
171
"""
172
Check if object's __name__ matches expected value.
173
174
Args:
175
other: Object to check (should have __name__ attribute)
176
177
Returns:
178
bool: True if __name__ matches expected value
179
"""
180
```
181
182
#### Usage Examples
183
184
```python
185
from dirty_equals import HasName
186
187
# Function name checking
188
def my_function():
189
pass
190
191
assert my_function == HasName('my_function')
192
assert my_function == HasName['my_function'] # Generic syntax
193
194
# Class name checking
195
class MyClass:
196
pass
197
198
assert MyClass == HasName('MyClass')
199
assert MyClass == HasName['MyClass']
200
201
# Instance name checking (class name)
202
obj = MyClass()
203
assert obj == HasName('MyClass') # Checks obj.__class__.__name__
204
205
# Disable instance checking
206
assert obj == HasName('MyClass', allow_instances=True) # True
207
# assert obj == HasName('MyClass', allow_instances=False) # False (no __name__ on instance)
208
209
# Built-in type/function validation
210
assert int == HasName('int')
211
assert len == HasName('len')
212
assert list == HasName('list')
213
214
# Module validation
215
import json
216
import datetime
217
218
assert json == HasName('json')
219
assert datetime == HasName('datetime')
220
221
# API endpoint validation
222
def get_users():
223
pass
224
225
def create_user():
226
pass
227
228
def update_user():
229
pass
230
231
endpoints = [get_users, create_user, update_user]
232
expected_names = ['get_users', 'create_user', 'update_user']
233
234
for endpoint, expected in zip(endpoints, expected_names):
235
assert endpoint == HasName(expected)
236
237
# Class method validation
238
class APIHandler:
239
def handle_request(self):
240
pass
241
242
def validate_input(self):
243
pass
244
245
handler = APIHandler()
246
method = getattr(handler, 'handle_request')
247
assert method == HasName('handle_request')
248
249
# Dynamic validation
250
function_registry = {
251
'handler1': get_users,
252
'handler2': create_user
253
}
254
255
# Validate function names match registry expectations
256
for key, func in function_registry.items():
257
if key == 'handler1':
258
assert func == HasName('get_users')
259
elif key == 'handler2':
260
assert func == HasName('create_user')
261
```
262
263
### HasRepr
264
265
Checks an object's string representation (via `repr()`) against an expected value. Useful for validating object representations, especially for debugging or testing object state.
266
267
```python { .api }
268
class HasRepr(DirtyEquals):
269
"""
270
Checks object's repr() output.
271
272
Validates that an object's string representation matches
273
the expected value exactly.
274
"""
275
276
def __init__(self, expected_repr: str):
277
"""
278
Initialize repr checker.
279
280
Args:
281
expected_repr: Expected result of repr(object)
282
"""
283
284
@classmethod
285
def __class_getitem__(cls, expected_repr: str) -> 'HasRepr':
286
"""
287
Support generic syntax: HasRepr['repr'] equivalent to HasRepr('repr').
288
289
Args:
290
expected_repr: Expected repr string
291
292
Returns:
293
HasRepr: New instance configured for the specified repr
294
"""
295
296
def equals(self, other: Any) -> bool:
297
"""
298
Check if object's repr() matches expected value.
299
300
Args:
301
other: Object to check repr() of
302
303
Returns:
304
bool: True if repr(other) equals expected_repr
305
"""
306
```
307
308
#### Usage Examples
309
310
```python
311
from dirty_equals import HasRepr
312
313
# Basic repr checking
314
numbers = [1, 2, 3]
315
assert numbers == HasRepr('[1, 2, 3]')
316
assert numbers == HasRepr['[1, 2, 3]'] # Generic syntax
317
318
# String repr checking
319
text = "hello"
320
assert text == HasRepr("'hello'") # Note the quotes in repr
321
322
# Dict repr checking
323
data = {'a': 1, 'b': 2}
324
assert data == HasRepr("{'a': 1, 'b': 2}")
325
326
# Custom class with __repr__
327
class Point:
328
def __init__(self, x, y):
329
self.x = x
330
self.y = y
331
332
def __repr__(self):
333
return f"Point({self.x}, {self.y})"
334
335
point = Point(3, 4)
336
assert point == HasRepr('Point(3, 4)')
337
338
# Complex object validation
339
from datetime import datetime
340
from collections import namedtuple
341
342
# Named tuple representation
343
Person = namedtuple('Person', ['name', 'age'])
344
person = Person('Alice', 25)
345
assert person == HasRepr("Person(name='Alice', age=25)")
346
347
# Set representation (order may vary, so use with caution)
348
small_set = {1, 2}
349
# Note: set repr order is not guaranteed, so this might be fragile
350
# assert small_set == HasRepr('{1, 2}') or assert small_set == HasRepr('{2, 1}')
351
352
# Regex pattern repr
353
import re
354
pattern = re.compile(r'\d+')
355
expected_repr = "re.compile('\\\\d+')" # Escaped backslashes in repr
356
assert pattern == HasRepr[expected_repr]
357
358
# Exception representation
359
try:
360
raise ValueError("test error")
361
except ValueError as e:
362
assert e == HasRepr("ValueError('test error')")
363
364
# Debugging object state
365
class StatefulObject:
366
def __init__(self, value):
367
self.value = value
368
self.modified = False
369
370
def __repr__(self):
371
return f"StatefulObject(value={self.value}, modified={self.modified})"
372
373
obj = StatefulObject(42)
374
assert obj == HasRepr('StatefulObject(value=42, modified=False)')
375
376
obj.modified = True
377
assert obj == HasRepr('StatefulObject(value=42, modified=True)')
378
379
# Testing object snapshots in different states
380
class Counter:
381
def __init__(self):
382
self.count = 0
383
384
def increment(self):
385
self.count += 1
386
387
def __repr__(self):
388
return f"Counter(count={self.count})"
389
390
counter = Counter()
391
assert counter == HasRepr('Counter(count=0)')
392
393
counter.increment()
394
assert counter == HasRepr('Counter(count=1)')
395
396
counter.increment()
397
assert counter == HasRepr('Counter(count=2)')
398
```
399
400
### HasAttributes
401
402
Checks that an object has specified attributes with optional value validation. Supports both attribute presence checking and attribute value validation.
403
404
```python { .api }
405
class HasAttributes(DirtyEquals):
406
"""
407
Checks object has specified attributes.
408
409
Validates that an object contains the specified attributes,
410
optionally checking their values as well.
411
"""
412
413
def __init__(self, *expected_args, **expected_kwargs):
414
"""
415
Initialize attribute checker (overloaded constructor).
416
417
Can be called in multiple ways:
418
- HasAttributes('attr1', 'attr2') - check presence only
419
- HasAttributes(attr1=value1, attr2=value2) - check presence and values
420
- HasAttributes({'attr1': value1}, attr2='value2') - mixed approach
421
422
Args:
423
*expected_args: Attribute names or dict of attribute-value pairs
424
**expected_kwargs: Attribute names and expected values
425
"""
426
427
def equals(self, other: Any) -> bool:
428
"""
429
Check if object has the specified attributes.
430
431
Args:
432
other: Object to check for attributes
433
434
Returns:
435
bool: True if all specified attributes exist (and match values if given)
436
"""
437
```
438
439
#### Usage Examples
440
441
```python
442
from dirty_equals import HasAttributes, IsPositive, IsStr
443
444
# Basic attribute presence checking
445
class User:
446
def __init__(self, name, email, age):
447
self.name = name
448
self.email = email
449
self.age = age
450
451
user = User("John", "john@example.com", 25)
452
453
# Check that attributes exist
454
assert user == HasAttributes('name', 'email', 'age')
455
456
# Check attributes with specific values
457
assert user == HasAttributes(name="John", email="john@example.com", age=25)
458
459
# Check attributes with validation
460
assert user == HasAttributes(
461
name=IsStr,
462
email=IsStr,
463
age=IsPositive
464
)
465
466
# Mixed approach - some presence, some values
467
assert user == HasAttributes('name', email="john@example.com", age=IsPositive)
468
469
# Complex object validation
470
import datetime
471
472
class Article:
473
def __init__(self, title, author, created_at):
474
self.title = title
475
self.author = author
476
self.created_at = created_at
477
self.view_count = 0
478
self.published = False
479
480
article = Article("Python Guide", "Jane Doe", datetime.datetime.now())
481
482
# Validate essential attributes exist
483
assert article == HasAttributes('title', 'author', 'created_at', 'view_count')
484
485
# Validate attributes with conditions
486
from dirty_equals import IsNow, IsFalseLike
487
488
assert article == HasAttributes(
489
title=IsStr,
490
author=IsStr,
491
created_at=IsNow(delta=60), # Created within last minute
492
view_count=0,
493
published=IsFalseLike
494
)
495
496
# Dictionary-style attribute checking
497
expected_attrs = {
498
'title': 'Python Guide',
499
'author': IsStr,
500
'view_count': IsPositive | 0 # Can be positive or zero
501
}
502
assert article == HasAttributes(expected_attrs)
503
504
# API response object validation
505
class APIResponse:
506
def __init__(self, status_code, data, headers):
507
self.status_code = status_code
508
self.data = data
509
self.headers = headers
510
self.response_time = 0.123
511
512
response = APIResponse(200, {"users": [1, 2, 3]}, {"Content-Type": "application/json"})
513
514
# Validate response structure
515
assert response == HasAttributes(
516
status_code=200,
517
data=dict, # Just check it's a dict
518
headers=dict,
519
response_time=IsPositive
520
)
521
522
# Database model validation
523
class DatabaseRecord:
524
def __init__(self, id, created_at, updated_at):
525
self.id = id
526
self.created_at = created_at
527
self.updated_at = updated_at
528
self.version = 1
529
530
record = DatabaseRecord(123, datetime.datetime.now(), datetime.datetime.now())
531
532
# Validate standard database fields
533
assert record == HasAttributes(
534
'id', 'created_at', 'updated_at', 'version'
535
)
536
537
# Validate with type and value constraints
538
assert record == HasAttributes(
539
id=IsPositive,
540
created_at=IsNow(delta=300), # Within 5 minutes
541
updated_at=IsNow(delta=300),
542
version=1
543
)
544
545
# Configuration object validation
546
class Config:
547
def __init__(self):
548
self.database_url = "postgresql://localhost/mydb"
549
self.debug = True
550
self.secret_key = "super-secret"
551
self.port = 8000
552
553
config = Config()
554
555
# Validate configuration completeness
556
assert config == HasAttributes(
557
'database_url', 'debug', 'secret_key', 'port'
558
)
559
560
# Validate configuration values
561
assert config == HasAttributes(
562
database_url=IsStr,
563
debug=bool,
564
secret_key=IsStr,
565
port=IsPositive
566
)
567
```
568
569
## Type Definitions
570
571
```python { .api }
572
from typing import Any, Union
573
574
# All inspection types inherit from DirtyEquals
575
# They work with any Python objects that have the inspected properties
576
```