From 7d48bf9c4963ce753ee2ae873ed9d21b17df66eb Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner <inform@tiker.net> Date: Tue, 22 Dec 2020 13:26:19 -0600 Subject: [PATCH] Propose tag uniqueness check algorithm --- pytools/tag.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/pytools/tag.py b/pytools/tag.py index cc6922c..0eff8f4 100644 --- a/pytools/tag.py +++ b/pytools/tag.py @@ -28,6 +28,20 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ +from pytools import memoize + + +@memoize +def _immediate_unique_tag_descendants(cls): + if UniqueTag in cls.__bases__: + return frozenset([cls]) + else: + result = frozenset() + for base in cls.__bases__: + result = result | _immediate_unique_tag_descendants(base) + return result + + # {{{ docs __doc__ = """ @@ -90,6 +104,7 @@ class DottedName: # }}} + # {{{ tag tag_dataclass = dataclass(init=True, eq=True, frozen=True, repr=True) @@ -118,6 +133,10 @@ class Tag: def tag_name(self) -> DottedName: return DottedName.from_class(type(self)) +# }}} + + +# {{{ unique tag class UniqueTag(Tag): """ @@ -126,12 +145,16 @@ class UniqueTag(Tag): """ pass +# }}} + TagsType = FrozenSet[Tag] TagOrIterableType = Union[Iterable[Tag], Tag, None] T_co = TypeVar("T_co", bound="Taggable") +# {{{ taggable + class Taggable: """ Parent class for objects with a `tags` attribute. @@ -184,8 +207,15 @@ class Taggable: for c in in_class.__bases__: _check_recursively(c, class_set) + unique_tag_descendants = set() for tag in self.tags: - _check_recursively(type(tag), class_set) + tag_unique_tag_descendants = _immediate_unique_tag_descendants( + type(tag)) + intersection = unique_tag_descendants & tag_unique_tag_descendants + if intersection: + raise ValueError("Multiple tags are direct subclasses of " + "the following UniqueTag(s): " + f"{', '.join(d.__name__ for d in intersection)}") def copy(self: T_co, **kwargs: Any) -> T_co: """ -- GitLab