Source code for rs_metadata.vocabulary.classes

"""schema.org's class hierarchy.

The hierarchy is what makes subtyping possible. CodeMeta types
``citation`` as ``CreativeWork``; schema.org says a
``ScholarlyArticle`` is an ``Article`` is a ``CreativeWork``, so one
belongs there. Without this, accepting only the exact class name is a
false alarm about correct metadata.
"""

from __future__ import annotations

from functools import cache
from typing import Any, cast

from .loader import DATA_PACKAGE, load


[docs] @cache def schema_org_classes() -> dict[str, Any]: """schema.org's class hierarchy, and the properties of the classes in use. Returns ------- dict Class name mapped to ``ancestors``, and for classes any range names, a ``properties`` map from property name to its range. Examples -------- >>> schema_org_classes()["ScholarlyArticle"]["ancestors"] ['Article', 'CreativeWork', 'Thing'] """ return cast( dict[str, Any], load(DATA_PACKAGE, "schema-org-classes.json")["classes"] )
[docs] @cache def subclasses(name: str) -> tuple[str, ...]: """Every class schema.org derives from this one, directly or not. Parameters ---------- name : str A class name, for example ``CreativeWork``. Returns ------- tuple of str Descendant class names, sorted. Notes ----- Used to express subtyping in the generated schema: a property typed ``CreativeWork`` accepts an ``@type`` from this list, so one ``jsonschema`` pass enforces what would otherwise need the validator. Examples -------- >>> "ScholarlyArticle" in subclasses("CreativeWork") True >>> "Person" in subclasses("CreativeWork") False >>> subclasses("ComputerLanguage") () """ return tuple( sorted( child for child, entry in schema_org_classes().items() if name in entry.get("ancestors", ()) ) )
[docs] def satisfies(declared: str, expected: str) -> bool: """Whether a node of class ``declared`` can stand where ``expected`` is asked for. Parameters ---------- declared : str The node's own ``@type``. expected : str A class named in the property's range. Returns ------- bool ``True`` when the classes are the same, or when ``declared`` is a subclass of ``expected``. Notes ----- CodeMeta types ``citation`` as ``CreativeWork``. schema.org says a ``ScholarlyArticle`` is an ``Article`` is a ``CreativeWork``, so one belongs there. Comparing the names alone would reject it, which is a false alarm about correct metadata. Examples -------- >>> satisfies("CreativeWork", "CreativeWork") True >>> satisfies("ScholarlyArticle", "CreativeWork") True >>> satisfies("SoftwareSourceCode", "CreativeWork") True Subtyping runs one way only, and unrelated classes stay unrelated: >>> satisfies("CreativeWork", "ScholarlyArticle") False >>> satisfies("Person", "Organization") False """ if declared == expected: return True entry = schema_org_classes().get(declared) if entry is None: return False return expected in entry.get("ancestors", ())
#: CodeMeta range entries that are literals rather than nodes. A bare scalar #: is a conformant value for any of these. ``URL`` is deliberately absent: it #: is a literal too, but only an *absolute* IRI satisfies it, so it needs the #: extra check in :mod:`rs_metadata.profile.types`.