Filters by (type, name, predicate). - type_filter: a type or an iterable of types - name_filter: - str: exact match - list/set/tuple: membership - regex-like: has callable `.match(name)` - callable: name -> bool - predicate: SelectionTarget -> bool
| 24 | |
| 25 | |
| 26 | class Selector: |
| 27 | """ |
| 28 | Filters by (type, name, predicate). |
| 29 | |
| 30 | - type_filter: a type or an iterable of types |
| 31 | - name_filter: |
| 32 | - str: exact match |
| 33 | - list/set/tuple: membership |
| 34 | - regex-like: has callable `.match(name)` |
| 35 | - callable: name -> bool |
| 36 | - predicate: SelectionTarget -> bool |
| 37 | |
| 38 | Two explicit entry points exist to keep APIs clear: |
| 39 | - match(obj): runtime instance matching (hooks) |
| 40 | - match_declaration(name, cls): declaration matching (NodeTemplate) |
| 41 | """ |
| 42 | |
| 43 | def __init__( |
| 44 | self, |
| 45 | *, |
| 46 | type_filter=None, |
| 47 | name_filter=None, |
| 48 | predicate: Callable[[SelectionTarget], bool] | None = None, |
| 49 | ): |
| 50 | """Create a selector. |
| 51 | |
| 52 | Args: |
| 53 | type_filter: Type or tuple/list/set of types to match by `issubclass`. |
| 54 | name_filter: Name filter. Supported forms include exact string, set/list/tuple |
| 55 | membership, regex-like objects with `.match(name)`, or a callable `name -> bool`. |
| 56 | predicate: Optional predicate applied to the normalized `SelectionTarget`. |
| 57 | """ |
| 58 | self._type_filter = self._normalize_type_filter(type_filter) |
| 59 | self._name_filter = name_filter |
| 60 | self._predicate = predicate |
| 61 | |
| 62 | def match(self, obj: object) -> bool: |
| 63 | """Match a runtime instance against this selector.""" |
| 64 | return self._match_target(SelectionTarget.from_obj(obj)) |
| 65 | |
| 66 | def match_declaration(self, *, name: str | None, cls: type | None) -> bool: |
| 67 | """Match a (name, type) declaration against this selector. |
| 68 | |
| 69 | This is used by NodeTemplate materialization where only a declared name/type is |
| 70 | available (no live instance). |
| 71 | """ |
| 72 | return self._match_target(SelectionTarget(name=name, cls=cls, obj=None)) |
| 73 | |
| 74 | def _match_target(self, target: SelectionTarget) -> bool: |
| 75 | if self._type_filter: |
| 76 | if target.cls is None: |
| 77 | return False |
| 78 | if not issubclass(target.cls, self._type_filter): |
| 79 | return False |
| 80 | |
| 81 | if self._name_filter is not None: |
| 82 | if target.name is None: |
| 83 | return False |