Source code for hs_py.ontology.taxonomy
"""Taxonomy tree operations over the def hierarchy.
Provides utilities for working with the ``is`` tag inheritance tree,
conjunct detection, and tag inheritance through the taxonomy.
See: https://project-haystack.org/doc/docHaystack/Subtyping
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from hs_py.kinds import Symbol, sym_name
if TYPE_CHECKING:
from hs_py.ontology.namespace import Namespace
__all__ = [
"effective_tags",
"is_conjunct",
"marker_tags",
"resolve_conjunct_parts",
"tag_on_defs",
]
[docs]
def is_conjunct(symbol: str | Symbol) -> bool:
"""Check if a symbol is a conjunct (compound term with ``-``).
Conjuncts like ``hot-water`` are composed from their dash-separated parts.
"""
return "-" in sym_name(symbol)
[docs]
def resolve_conjunct_parts(symbol: str | Symbol) -> list[str]:
"""Split a conjunct symbol into its component part names.
:param symbol: e.g. ``hot-water-plant``
:returns: e.g. ``["hot", "water", "plant"]``
"""
return sym_name(symbol).split("-")
[docs]
def effective_tags(ns: Namespace, symbol: str | Symbol) -> dict[str, Any]:
"""Compute the effective tag set for a def, inheriting from supertypes.
Walks up the taxonomy tree and merges tags from all supertypes.
Tags defined on the def itself take precedence over inherited tags.
:param ns: Namespace to resolve symbols in.
:param symbol: Def symbol to compute tags for.
:returns: Merged tag dict.
"""
d = ns.get(symbol)
if d is None:
return {}
# Collect all supertypes (most-specific first via BFS)
all_supers = ns.all_supertypes(symbol)
# Start with inherited tags (least-specific first = reverse order)
merged: dict[str, Any] = {}
for sup in reversed(all_supers):
for key, val in sup.tags.items():
if key not in ("def", "is", "lib"):
merged[key] = val
# Apply own tags last (highest precedence)
for key, val in d.tags.items():
if key not in ("def", "is", "lib"):
merged[key] = val
return merged
[docs]
def marker_tags(ns: Namespace, symbol: str | Symbol) -> set[str]:
"""Return the set of marker tag names for a def and all its supertypes.
This is useful for reflection: determining which marker defs an entity
implements based on its tag set.
:param ns: Namespace to resolve symbols in.
:param symbol: Def symbol.
:returns: Set of marker tag names.
"""
markers: set[str] = set()
d = ns.get(symbol)
if d is None:
return markers
markers.add(d.name)
for sup in ns.all_supertypes(symbol):
markers.add(sup.name)
return markers
[docs]
def tag_on_defs(ns: Namespace, tag: str | Symbol) -> list[str]:
"""Return the entity def names that a tag applies to via ``tagOn``.
:param ns: Namespace to resolve symbols in.
:param tag: Tag name to look up.
:returns: List of entity def names.
"""
tag_name = sym_name(tag)
d = ns.get(tag_name)
if d is None:
return []
tag_on = d.tags.get("tagOn")
if tag_on is None:
return []
if isinstance(tag_on, Symbol):
return [tag_on.val]
if isinstance(tag_on, list):
return [s.val for s in tag_on if isinstance(s, Symbol)]
return []