| 641 | |
| 642 | @final |
| 643 | class NodeKeywords(MutableMapping[str, Any]): |
| 644 | __slots__ = ("_markers", "node", "parent") |
| 645 | |
| 646 | def __init__(self, node: Node) -> None: |
| 647 | self.node = node |
| 648 | self.parent = node.parent |
| 649 | self._markers = {node.name: True} |
| 650 | |
| 651 | def __getitem__(self, key: str) -> Any: |
| 652 | try: |
| 653 | return self._markers[key] |
| 654 | except KeyError: |
| 655 | if self.parent is None: |
| 656 | raise |
| 657 | return self.parent.keywords[key] |
| 658 | |
| 659 | def __setitem__(self, key: str, value: Any) -> None: |
| 660 | self._markers[key] = value |
| 661 | |
| 662 | # Note: we could've avoided explicitly implementing some of the methods |
| 663 | # below and use the collections.abc fallback, but that would be slow. |
| 664 | |
| 665 | def __contains__(self, key: object) -> bool: |
| 666 | return key in self._markers or ( |
| 667 | self.parent is not None and key in self.parent.keywords |
| 668 | ) |
| 669 | |
| 670 | def update( # type: ignore[override] |
| 671 | self, |
| 672 | other: Mapping[str, Any] | Iterable[tuple[str, Any]] = (), |
| 673 | **kwds: Any, |
| 674 | ) -> None: |
| 675 | self._markers.update(other) |
| 676 | self._markers.update(kwds) |
| 677 | |
| 678 | def __delitem__(self, key: str) -> None: |
| 679 | raise ValueError("cannot delete key in keywords dict") |
| 680 | |
| 681 | def __iter__(self) -> Iterator[str]: |
| 682 | # Doesn't need to be fast. |
| 683 | yield from self._markers |
| 684 | if self.parent is not None: |
| 685 | for keyword in self.parent.keywords: |
| 686 | # self._marks and self.parent.keywords can have duplicates. |
| 687 | if keyword not in self._markers: |
| 688 | yield keyword |
| 689 | |
| 690 | def __len__(self) -> int: |
| 691 | # Doesn't need to be fast. |
| 692 | return sum(1 for keyword in self) |
| 693 | |
| 694 | def __repr__(self) -> str: |
| 695 | return f"<NodeKeywords for node {self.node}>" |