| 331 | |
| 332 | @attrs(repr=False, slots=True, unsafe_hash=True) |
| 333 | class _DeepIterable: |
| 334 | member_validator = attrib(validator=is_callable()) |
| 335 | iterable_validator = attrib( |
| 336 | default=None, validator=optional(is_callable()) |
| 337 | ) |
| 338 | |
| 339 | def __call__(self, inst, attr, value): |
| 340 | """ |
| 341 | We use a callable class to be able to change the ``__repr__``. |
| 342 | """ |
| 343 | if self.iterable_validator is not None: |
| 344 | self.iterable_validator(inst, attr, value) |
| 345 | |
| 346 | for member in value: |
| 347 | self.member_validator(inst, attr, member) |
| 348 | |
| 349 | def __repr__(self): |
| 350 | iterable_identifier = ( |
| 351 | "" |
| 352 | if self.iterable_validator is None |
| 353 | else f" {self.iterable_validator!r}" |
| 354 | ) |
| 355 | return ( |
| 356 | f"<deep_iterable validator for{iterable_identifier}" |
| 357 | f" iterables of {self.member_validator!r}>" |
| 358 | ) |
| 359 | |
| 360 | |
| 361 | def deep_iterable(member_validator, iterable_validator=None): |
no test coverage detected