Source code for kif_lib.model.value.property

# Copyright (C) 2024-2025 IBM Corp.
# SPDX-License-Identifier: Apache-2.0

from __future__ import annotations

from typing import TYPE_CHECKING

from typing_extensions import overload

from ...typing import (
    Any,
    cast,
    ClassVar,
    Iterable,
    Mapping,
    override,
    Self,
    Set,
    TypeAlias,
    TypedDict,
    Union,
)
from ..term import OpenTerm, Variable
from .datatype import (
    Datatype,
    DatatypeVariable,
    TDatatype,
    VDatatype,
    VTDatatype,
)
from .entity import Entity, EntityTemplate, EntityVariable, VTEntity
from .iri import IRI_Template, T_IRI, VT_IRI
from .string import TString
from .text import Text, TText, TTextLanguage, TTextSet
from .value import VTValue

if TYPE_CHECKING:               # pragma: no cover
    from ...store import Store
    from ..rank import VTRank
    from ..set import VTQualifierRecord, VTReferenceRecordSet
    from ..snak import (
        NoValueSnak,
        NoValueSnakTemplate,
        SomeValueSnak,
        SomeValueSnakTemplate,
        ValueSnak,
        ValueSnakTemplate,
    )
    from ..statement import Statement, StatementTemplate

TProperty: TypeAlias = Union['Property', T_IRI]
VProperty: TypeAlias =\
    Union['PropertyTemplate', 'PropertyVariable', 'Property']
VTProperty: TypeAlias = Union[Variable, VProperty, TProperty]
VTPropertyContent: TypeAlias = Union[Variable, IRI_Template, TProperty]


[docs] class PropertyTemplate(EntityTemplate): """Property template. Parameters: iri: IRI, IRI template, or IRI variable. range: Datatype or datatype variable. """ object_class: ClassVar[type[Property]] # pyright: ignore
[docs] def __init__(self, iri: VT_IRI, range: VTDatatype | None = None) -> None: super().__init__(iri, range)
@override def _preprocess_arg(self, arg: Any, i: int) -> Any: if i == 1: # iri if isinstance(arg, OpenTerm): return super()._preprocess_arg(arg, i) else: return Property._static_preprocess_arg(self, arg, i) elif i == 2: # range if isinstance(arg, Variable): return DatatypeVariable.check(arg, type(self), None, i) else: return Property._static_preprocess_arg(self, arg, i) else: raise self._should_not_get_here() @overload def __call__( self, arg1: VTEntity, arg2: VTValue, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> StatementTemplate: """Constructs statement template from property template. Parameters: arg1: Subject. arg2: Value. qualifiers: Qualifier record. references: Reference record set. rank: Rank. Returns: Statement template. """ ... # pragma: no cover @overload def __call__(self, arg1: VTValue) -> ValueSnakTemplate: """Constructs value snak template from property template. Parameters: arg1: Value. Returns: Value snak template. """ ... # pragma: no cover def __call__( self, arg1: VTEntity | VTValue, arg2: VTValue | None = None, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> StatementTemplate | ValueSnakTemplate: if arg2 is not None: from ..snak import ValueSnak from ..statement import StatementTemplate stmt = StatementTemplate( arg1, ValueSnak(self, arg2)) # type: ignore if qualifiers is None and references is None and rank is None: return stmt else: return stmt.annotate(qualifiers, references, rank) else: from ..snak import ValueSnakTemplate return ValueSnakTemplate(self, arg1) @property def range(self) -> VDatatype | None: """The range of property template.""" return self.get_range()
[docs] def get_range(self, default: VDatatype | None = None) -> VDatatype | None: """Gets the range of property template. If range is ``None``, returns `default`. Parameters: default: Default range. Returns: Datatype or datatype variable. """ return self.get(1, default)
@overload def no_value( self, subject: VTEntity, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> StatementTemplate: """Constructs no-value statement template from property template. Parameters: subject: Entity. qualifiers: Qualifier record. references: Reference record set. rank: Rank. Returns: Statement template. """ ... # pragma: no cover @overload def no_value(self) -> NoValueSnakTemplate: """Constructs no-value snak template from property template. Returns: No-value snak template. """ ... # pragma: no cover def no_value( self, subject: VTEntity | None = None, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> StatementTemplate | NoValueSnakTemplate: from ..snak import NoValueSnakTemplate snak = NoValueSnakTemplate(self) if subject is None: return snak else: from ..statement import StatementTemplate stmt = StatementTemplate(subject, snak) if qualifiers is None and references is None and rank is None: return stmt else: return stmt.annotate(qualifiers, references, rank) @overload def some_value( self, subject: VTEntity, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> StatementTemplate: """Constructs some-value statement template from property template. Parameters: subject: Entity. qualifiers: Qualifier record. references: Reference record set. rank: Rank. Returns: Statement template. """ ... # pragma: no cover @overload def some_value(self) -> SomeValueSnakTemplate: """Constructs some-value snak template from property template. Returns: Some-value snak template. """ ... # pragma: no cover def some_value( self, subject: VTEntity | None = None, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> StatementTemplate | SomeValueSnakTemplate: from ..snak import SomeValueSnakTemplate snak = SomeValueSnakTemplate(self) if subject is None: return snak else: from ..statement import StatementTemplate stmt = StatementTemplate(subject, snak) if qualifiers is None and references is None and rank is None: return stmt else: return stmt.annotate(qualifiers, references, rank)
[docs] class PropertyVariable(EntityVariable): """Property variable. Parameters: name: Name. """ object_class: ClassVar[type[Property]] # pyright: ignore @overload def __call__( self, arg1: VTEntity, arg2: VTValue, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> StatementTemplate: """Constructs statement template from property variable. Parameters: arg1: Subject. arg2: Value. qualifiers: Qualifier record. references: Reference record set. rank: Rank. Returns: Statement template. """ ... # pragma: no cover @overload def __call__(self, arg1: VTValue) -> ValueSnakTemplate: """Constructs value snak template from property variable. Parameters: arg1: Value. Returns: Value snak template. """ ... # pragma: no cover def __call__( self, arg1: VTEntity | VTValue, arg2: VTValue | None = None, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> StatementTemplate | ValueSnakTemplate: if arg2 is not None: from ..snak import ValueSnak from ..statement import StatementTemplate stmt = StatementTemplate( arg1, ValueSnak(self, arg2)) # type: ignore if qualifiers is None and references is None and rank is None: return stmt else: return stmt.annotate(qualifiers, references, rank) else: from ..snak import ValueSnakTemplate return ValueSnakTemplate(self, arg1) @overload def no_value( self, subject: VTEntity, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> StatementTemplate: """Constructs no-value statement template from property variable. Parameters: subject: Entity. qualifiers: Qualifier record. references: Reference record set. rank: Rank. Returns: Statement template. """ ... # pragma: no cover @overload def no_value(self) -> NoValueSnakTemplate: """Constructs no-value snak template from property variable. Returns: No-value snak template. """ ... # pragma: no cover def no_value( self, subject: VTEntity | None = None, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> StatementTemplate | NoValueSnakTemplate: from ..snak import NoValueSnakTemplate snak = NoValueSnakTemplate(self) if subject is None: return snak else: from ..statement import StatementTemplate stmt = StatementTemplate(subject, snak) if qualifiers is None and references is None and rank is None: return stmt else: return stmt.annotate(qualifiers, references, rank) @overload def some_value( self, subject: VTEntity, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> StatementTemplate: """Constructs some-value statement template from property variable. Parameters: subject: Entity. qualifiers: Qualifier record. references: Reference record set. rank: Rank. Returns: Statement template. """ ... # pragma: no cover @overload def some_value(self) -> SomeValueSnakTemplate: """Constructs some-value snak template from property variable. Returns: Some-value snak template. """ ... # pragma: no cover def some_value( self, subject: VTEntity | None = None, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> StatementTemplate | SomeValueSnakTemplate: from ..snak import SomeValueSnakTemplate snak = SomeValueSnakTemplate(self) if subject is None: return snak else: from ..statement import StatementTemplate stmt = StatementTemplate(subject, snak) if qualifiers is None and references is None and rank is None: return stmt else: return stmt.annotate(qualifiers, references, rank)
[docs] class PropertyDatatype(Datatype): """Property datatype.""" instance: ClassVar[PropertyDatatype] # pyright: ignore value_class: ClassVar[type[Property]] # pyright: ignore
[docs] class Property( Entity, datatype_class=PropertyDatatype, template_class=PropertyTemplate, variable_class=PropertyVariable ): """Binary relationship. Parameters: iri: IRI. range: Datatype. """ datatype_class: ClassVar[type[PropertyDatatype]] # pyright: ignore datatype: ClassVar[PropertyDatatype] # pyright: ignore template_class: ClassVar[type[PropertyTemplate]] # pyright: ignore variable_class: ClassVar[type[PropertyVariable]] # pyright: ignore
[docs] class Descriptor(TypedDict, total=False): """Property descriptor.""" #: Label indexed by language. labels: Mapping[str, Text] #: Aliases indexed by language. aliases: Mapping[str, Set[Text]] #: Description indexed by language. descriptions: Mapping[str, Text] #: Range datatype. range: Datatype #: Inverse property. inverse: Property
[docs] def __init__( self, iri: VTPropertyContent, range: VTDatatype | None = None ) -> None: super().__init__(iri, range)
@staticmethod @override def _static_preprocess_arg(self_, arg: Any, i: int) -> Any: if i == 1: # iri return Entity._static_preprocess_arg(self_, arg, i) elif i == 2: # range return Datatype.check_optional(arg, None, type(self_), None, i) else: raise self_._should_not_get_here() @overload def __call__( self, arg1: VTEntity, arg2: VTValue, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> Statement: """Constructs statement from property. Parameters: arg1: Subject. arg2: Value. qualifiers: Qualifier record. references: Reference record set. rank: Rank. Returns: Statement. """ ... # pragma: no cover @overload def __call__(self, arg1: VTValue) -> ValueSnak: """Constructs value snak from property. Parameters: arg1: Value. Returns: Value snak. """ ... # pragma: no cover def __call__( self, arg1: VTEntity | VTValue, arg2: VTValue | None = None, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> Statement | ValueSnak: if arg2 is not None: from ..snak import ValueSnak from ..statement import Statement stmt = Statement(arg1, ValueSnak(self, arg2)) # type: ignore if qualifiers is None and references is None and rank is None: return stmt else: return stmt.annotate(qualifiers, references, rank) else: from ..snak import ValueSnak return ValueSnak(self, arg1) @property def range(self) -> Datatype | None: """The range of property.""" return self.get_range()
[docs] def get_range(self, default: Datatype | None = None) -> Datatype | None: """Gets the range of property. If range is ``None``, returns `default`. Parameters: default: Default range. Returns: Datatype. """ return self.get(1, default)
@overload def no_value( self, subject: VTEntity, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> Statement: """Constructs no-value statement from property. Parameters: subject: Entity. qualifiers: Qualifier record. references: Reference record set. rank: Rank. Returns: Statement. """ ... # pragma: no cover @overload def no_value(self) -> NoValueSnak: """Constructs no-value snak from property. Returns: No-value snak. """ ... # pragma: no cover def no_value( self, subject: VTEntity | None = None, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> Statement | NoValueSnak: from ..snak import NoValueSnak snak = NoValueSnak(self) if subject is None: return snak else: from ..statement import Statement stmt = Statement(subject, snak) if qualifiers is None and references is None and rank is None: return stmt else: return stmt.annotate(qualifiers, references, rank) @overload def some_value( self, subject: VTEntity, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> Statement: """Constructs some-value statement from property. Parameters: subject: Entity. qualifiers: Qualifier record. references: Reference record set. rank: Rank. Returns: Statement. """ ... # pragma: no cover @overload def some_value(self) -> SomeValueSnak: """Constructs some-value snak from property. Returns: Some-value snak. """ ... # pragma: no cover def some_value( self, subject: VTEntity | None = None, qualifiers: VTQualifierRecord | None = None, references: VTReferenceRecordSet | None = None, rank: VTRank | None = None ) -> Statement | SomeValueSnak: from ..snak import SomeValueSnak snak = SomeValueSnak(self) if subject is None: return snak else: from ..statement import Statement stmt = Statement(subject, snak) if qualifiers is None and references is None and rank is None: return stmt else: return stmt.annotate(qualifiers, references, rank)
[docs] @override def display(self, language: TString | None = None) -> str: label = self.get_label(language) if label: return label.content else: return super().display(language) # fallback
[docs] def describe( self, language: TTextLanguage | None = None, resolve: bool | None = None, resolver: Store | None = None, force: bool | None = None ) -> Property.Descriptor | None: """Gets the descriptor of property in KIF context. If `language` is given, resolves only text in `language`. Otherwise, resolves text in all languages. If `resolve` is ``True``, resolves property data. If `resolver` is given, uses it to resolve property data. Otherwise, uses the resolver registered in context (if any). If `force` is given, forces resolution. Parameters: language: Language. resolve: Whether to resolve descriptor. resolver: Resolver store. force: Whether to force resolution. Returns: Property descriptor or ``None``. """ return self.context.describe( self, language=language, resolve=resolve, resolver=resolver, force=force, function=self.describe)
@property def label(self) -> Text | None: """The label of property in KIF context.""" return self.get_label()
[docs] def get_label( self, language: TTextLanguage | None = None, resolve: bool | None = None, resolver: Store | None = None, force: bool | None = None ) -> Text | None: """Gets the label of property in KIF context. Parameters: language: Language. resolve: Whether to resolve label. resolver: Resolver store. force: Whether to force resolution. Returns: Label or ``None``. """ return self.context.get_label( self, language=language, resolve=resolve, resolver=resolver, force=force, function=self.get_label)
@property def aliases(self) -> Set[Text] | None: """The aliases of property in KIF context.""" return self.get_aliases()
[docs] def get_aliases( self, language: TTextLanguage | None = None, resolve: bool | None = None, resolver: Store | None = None, force: bool | None = None ) -> Set[Text] | None: """Gets the aliases of property in KIF context. Parameters: language: Language. resolve: Whether to resolve aliases. resolver: Resolver store. force: Whether to force resolution. Returns: Aliases or ``None``. """ return self.context.get_aliases( self, language=language, resolve=resolve, resolver=resolver, force=force, function=self.get_aliases)
@property def description(self) -> Text | None: """The description of property in KIF context.""" return self.get_description()
[docs] def get_description( self, language: TTextLanguage | None = None, resolve: bool | None = None, resolver: Store | None = None, force: bool | None = None ) -> Text | None: """Gets the description of property in KIF context. Parameters: language: Language. resolve: Whether to resolve description. resolver: Resolver store. force: Whether to force resolution. Returns: Description or ``None``. """ return self.context.get_description( self, language=language, resolve=resolve, resolver=resolver, force=force, function=self.get_description)
@property def registered_range(self) -> Datatype | None: """The range of property in KIF context.""" return self.get_registered_range()
[docs] def get_registered_range( self, resolve: bool | None = None, resolver: Store | None = None, force: bool | None = None ) -> Datatype | None: """Gets the range of property in KIF context. Parameters: resolve: Whether to resolve range. resolver: Resolver store. force: Whether to force resolution. Returns: Range or ``None``. """ return self.context.get_range( self, resolve=resolve, resolver=resolver, force=force, function=self.get_range)
@property def inverse(self) -> Property | None: """The inverse of property in KIF context.""" return self.get_inverse()
[docs] def get_inverse( self, resolve: bool | None = None, resolver: Store | None = None, force: bool | None = None ) -> Property | None: """Gets the inverse of property in KIF context. Parameters: resolve: Whether to resolve inverse. resolver: Resolver store. force: Whether to force resolution. Returns: Property or ``None``. """ return self.context.get_inverse( self, resolve=resolve, resolver=resolver, force=force, function=self.get_inverse)
[docs] def register( self, label: TText | None = None, labels: TTextSet | None = None, alias: TText | None = None, aliases: TTextSet | None = None, description: TText | None = None, descriptions: TTextSet | None = None, range: TDatatype | None = None, inverse: TProperty | None = None ) -> Self: """Adds or updates property data in KIF context. Parameters: label: Label. labels: Labels. alias: Alias. aliases: Aliases. description: Description. descriptions: Descriptions. range: Range. inverse: Inverse property. Returns: Property. """ return cast(Self, self.context.entities.register( self, label=label, labels=labels, alias=alias, aliases=aliases, description=description, descriptions=descriptions, range=range, inverse=inverse, function=self.register))
[docs] def unregister( self, label: TText | None = None, labels: TTextSet | None = None, alias: TText | None = None, aliases: TTextSet | None = None, description: TText | None = None, descriptions: TTextSet | None = None, label_language: TTextLanguage | None = None, alias_language: TTextLanguage | None = None, description_language: TTextLanguage | None = None, all_labels: bool = False, all_aliases: bool = False, all_descriptions: bool = False, range: bool = False, inverse: bool = False ) -> bool: """Removes property data from KIF context. If called with no arguments, removes all property data. Parameters: label: Label. labels: Labels. alias: Alias. aliases: Aliases. description: Description. descriptions: Descriptions. label_language: Language. alias_language: Language. description_language: Language. all_labels: Whether to remove all labels. all_aliases: Whether to remove all aliases. all_descriptions: Whether to remove all descriptions. range: Whether to remove range. inverse: Whether to remove inverse. Returns: ``True`` if successful; ``False`` otherwise. """ if (label is None and labels is None and alias is None and aliases is None and description is None and descriptions is None and label_language is None and alias_language is None and description_language is None and all_labels is False and all_aliases is False and all_descriptions is False and range is False and inverse is False): return self.context.entities.unregister( self, all=True, function=self.unregister) else: return self.context.entities.unregister( self, label=label, labels=labels, alias=alias, aliases=aliases, description=description, descriptions=descriptions, label_language=label_language, alias_language=alias_language, description_language=description_language, all_labels=all_labels, all_aliases=all_aliases, all_descriptions=all_descriptions, range=range, inverse=inverse, function=self.unregister)
def Properties( iri: VTPropertyContent, *iris: VTPropertyContent ) -> Iterable[Property]: """Constructs one or more properties. Parameters: iri: IRI. iris: IRIs. Returns: The resulting properties. """ from ... import itertools return map(Property, itertools.chain((iri,), iris))