Source code for flexicon.code.System.phonological_context

#
#   phonological_context.py
#
#   Class: PhonologicalContext
#          Wrapper for phonological context objects providing unified interface
#          access across multiple concrete types.
#
#   Platform: Python.NET
#             FieldWorks Version 9+
#
#   Copyright 2025
#

"""
Wrapper class for phonological context objects with unified interface.

This module provides PhonologicalContext, a wrapper class that transparently
handles the multiple concrete types of phonological contexts:
- PhSimpleContextSeg: Simple context with single segment
- PhSimpleContextNC: Simple context with natural class
- PhComplexContextSeg: Complex context with segments
- PhComplexContextNC: Complex context with natural class
- PhBoundaryContext: Boundary context

All share a base interface IPhPhonContext or similar.

Problem:
    Phonological contexts have different properties depending on their concrete type:
    - PhSimpleContextSeg: Represents a single segment
    - PhSimpleContextNC: Represents a natural class
    - PhComplexContextSeg: Complex segment specifications
    - PhComplexContextNC: Complex natural class specifications
    - PhBoundaryContext: Word/morpheme boundaries

    All have Name, Description, and other common properties.

    Users working with mixed collections need to check ClassName and cast to
    access type-specific properties, which is error-prone and verbose.

Solution:
    PhonologicalContext wrapper provides:
    - Simple properties for common features (context_name, description)
    - Capability check properties (is_simple_context_seg, is_boundary, etc.)
    - Property access that works across all types
    - Optional: Methods for advanced users who know C# types

Example::

    from flexlibs2.code.System.phonological_context import PhonologicalContext

    # Wrap a context from a rule's input contexts
    context = rule.input_contexts[0]  # Typed as IPhPhonContext
    wrapped = PhonologicalContext(context)

    # Access common properties
    print(wrapped.context_name)  # Works for all context types

    # Check capabilities
    if wrapped.is_simple_context_seg:
        print("This is a simple segment context")

    if wrapped.is_boundary_context:
        print("This is a boundary context")

    # Optional: Advanced users can access concrete types
    if wrapped.as_simple_context_seg():
        concrete = wrapped.as_simple_context_seg()
        # Use concrete interface for advanced operations
"""

from ..Shared.wrapper_base import LCMObjectWrapper
from ..lcm_casting import cast_to_concrete


[docs] class PhonologicalContext(LCMObjectWrapper): """ Wrapper for phonological context objects providing unified interface access. Handles the multiple concrete types of phonological contexts transparently, providing common properties and capability checks without exposing ClassName or casting. Attributes: _obj: The base interface object (IPhPhonContext) _concrete: The concrete type object (IPhSimpleContextSeg, IPhSimpleContextNC, IPhComplexContextSeg, IPhComplexContextNC, IPhBoundaryContext, etc.) Example:: context = rule.input_contexts[0] wrapped = PhonologicalContext(context) print(wrapped.context_name) if wrapped.is_simple_context_seg: print("Simple segment context") """ def __init__(self, lcm_context): """ Initialize PhonologicalContext wrapper with a context object. Args: lcm_context: An IPhPhonContext object (or derived type). Typically from a phonological rule's input_contexts. Example:: context = rule.input_contexts[0] wrapped = PhonologicalContext(context) """ super().__init__(lcm_context) # ========== Common Properties (work across all context types) ========== @property def context_name(self) -> str: """ Get the context's name or identifier. Returns: str: The context name/identifier, or empty string if not set. Example:: print(f"Context: {wrapped.context_name}") Notes: - For simple contexts, this may be the segment or natural class name - For boundary contexts, this identifies the boundary type """ try: # Many contexts have a Name property if hasattr(self._concrete, "Name"): return str(self._concrete.Name) if self._concrete.Name else "" return "" except Exception: return "" @property def description(self) -> str: """ Get the context's description if available. Returns: str: The description, or empty string if not set. Example:: print(f"Context: {wrapped.description}") """ try: if hasattr(self._concrete, "Description"): return str(self._concrete.Description) if self._concrete.Description else "" return "" except Exception: return "" # ========== Capability Checks (for type-specific properties) ========== @property def is_simple_context_seg(self): """ Check if this is a simple segment context. Returns: bool: True if this is a PhSimpleContextSeg. Example:: if wrapped.is_simple_context_seg: print("Simple segment context") Notes: - Simple segment contexts represent a single segment - Use segment property to access the actual segment """ try: return self.class_type == "PhSimpleContextSeg" except Exception: return False @property def is_simple_context_nc(self): """ Check if this is a simple natural class context. Returns: bool: True if this is a PhSimpleContextNC. Example:: if wrapped.is_simple_context_nc: print("Simple natural class context") Notes: - Simple natural class contexts represent a natural class - Use natural_class property to access the actual natural class """ try: return self.class_type == "PhSimpleContextNC" except Exception: return False @property def is_simple_context(self): """ Check if this is any simple context (segment or natural class). Returns: bool: True if this is either PhSimpleContextSeg or PhSimpleContextNC. Example:: if wrapped.is_simple_context: print("This is a simple context") Notes: - Convenience property for checking if context is "simple" - Use is_simple_context_seg or is_simple_context_nc for type specificity """ return self.is_simple_context_seg or self.is_simple_context_nc @property def is_complex_context_seg(self): """ Check if this is a complex segment context. Returns: bool: True if this is a PhComplexContextSeg. Example:: if wrapped.is_complex_context_seg: print("Complex segment context") Notes: - Complex segment contexts have multiple segment specifications """ try: return self.class_type == "PhComplexContextSeg" except Exception: return False @property def is_complex_context_nc(self): """ Check if this is a complex natural class context. Returns: bool: True if this is a PhComplexContextNC. Example:: if wrapped.is_complex_context_nc: print("Complex natural class context") Notes: - Complex natural class contexts have multiple natural class specifications """ try: return self.class_type == "PhComplexContextNC" except Exception: return False @property def is_complex_context(self): """ Check if this is any complex context (segment or natural class). Returns: bool: True if this is either PhComplexContextSeg or PhComplexContextNC. Example:: if wrapped.is_complex_context: print("This is a complex context") Notes: - Convenience property for checking if context is "complex" - Use is_complex_context_seg or is_complex_context_nc for type specificity """ return self.is_complex_context_seg or self.is_complex_context_nc @property def is_boundary_context(self): """ Check if this is a boundary context. Returns: bool: True if this is a PhBoundaryContext. Example:: if wrapped.is_boundary_context: print("Word/morpheme boundary context") Notes: - Boundary contexts represent word or morpheme boundaries - Different from other context types in purpose and properties """ try: return self.class_type == "PhBoundaryContext" except Exception: return False # ========== Type-Specific Property Access (via capability checks) ========== @property def segment(self) -> "Optional[object]": """ Get the segment from a simple segment context. Returns: The segment object if this is a PhSimpleContextSeg, None otherwise. Example:: if wrapped.is_simple_context_seg: seg = wrapped.segment if seg: print(f"Segment: {seg.Name}") Notes: - Only meaningful for PhSimpleContextSeg - Returns None for other context types """ if not self.is_simple_context_seg: return None try: if hasattr(self._concrete, "SegmentRA"): return self._concrete.SegmentRA return None except Exception: return None @property def natural_class(self) -> "Optional[object]": """ Get the natural class from a simple natural class context. Returns: The natural class object if this is a PhSimpleContextNC, None otherwise. Example:: if wrapped.is_simple_context_nc: nc = wrapped.natural_class if nc: print(f"Natural Class: {nc.Name}") Notes: - Only meaningful for PhSimpleContextNC - Returns None for other context types """ if not self.is_simple_context_nc: return None try: if hasattr(self._concrete, "NaturalClassRA"): return self._concrete.NaturalClassRA return None except Exception: return None @property def boundary_type(self): """ Get the boundary type from a boundary context. Returns: int: The boundary type (0=word, 1=morpheme, etc.), or -1 if not applicable. Example:: if wrapped.is_boundary_context: btype = wrapped.boundary_type print(f"Boundary type: {btype}") Notes: - Only meaningful for PhBoundaryContext - Different boundary types have different values """ if not self.is_boundary_context: return -1 try: if hasattr(self._concrete, "Type"): return self._concrete.Type return -1 except Exception: return -1 # ========== Advanced: Direct C# class access (optional for power users) ==========
[docs] def as_simple_context_seg(self): """ Cast to IPhSimpleContextSeg if this is a simple segment context. For advanced users who need direct access to the C# concrete interface. Returns None if this is not a PhSimpleContextSeg. Returns: IPhSimpleContextSeg or None: The concrete interface if this is a PhSimpleContextSeg, None otherwise. Example:: if context_obj.as_simple_context_seg(): concrete = context_obj.as_simple_context_seg() # Can now access IPhSimpleContextSeg-specific methods/properties segment = concrete.SegmentRA # Advanced operations... Notes: - For users who know C# interfaces and want advanced control - Most users should use properties like is_simple_context_seg and segment instead """ if self.class_type == "PhSimpleContextSeg": return self._concrete return None
[docs] def as_simple_context_nc(self): """ Cast to IPhSimpleContextNC if this is a simple natural class context. For advanced users who need direct access to the C# concrete interface. Returns None if this is not a PhSimpleContextNC. Returns: IPhSimpleContextNC or None: The concrete interface if this is a PhSimpleContextNC, None otherwise. Example:: if context_obj.as_simple_context_nc(): concrete = context_obj.as_simple_context_nc() # Can now access IPhSimpleContextNC-specific methods/properties Notes: - For users who know C# interfaces and want advanced control - Most users should use properties like is_simple_context_nc and natural_class instead """ if self.class_type == "PhSimpleContextNC": return self._concrete return None
[docs] def as_complex_context_seg(self): """ Cast to IPhComplexContextSeg if this is a complex segment context. For advanced users who need direct access to the C# concrete interface. Returns None if this is not a PhComplexContextSeg. Returns: IPhComplexContextSeg or None: The concrete interface if applicable. Example:: if context_obj.as_complex_context_seg(): concrete = context_obj.as_complex_context_seg() # Advanced operations... Notes: - For users who know C# interfaces and want advanced control - Most users should use properties like is_complex_context_seg instead """ if self.class_type == "PhComplexContextSeg": return self._concrete return None
[docs] def as_complex_context_nc(self): """ Cast to IPhComplexContextNC if this is a complex natural class context. For advanced users who need direct access to the C# concrete interface. Returns None if this is not a PhComplexContextNC. Returns: IPhComplexContextNC or None: The concrete interface if applicable. Example:: if context_obj.as_complex_context_nc(): concrete = context_obj.as_complex_context_nc() # Advanced operations... Notes: - For users who know C# interfaces and want advanced control - Most users should use properties like is_complex_context_nc instead """ if self.class_type == "PhComplexContextNC": return self._concrete return None
[docs] def as_boundary_context(self): """ Cast to IPhBoundaryContext if this is a boundary context. For advanced users who need direct access to the C# concrete interface. Returns None if this is not a PhBoundaryContext. Returns: IPhBoundaryContext or None: The concrete interface if this is a PhBoundaryContext, None otherwise. Example:: if context_obj.as_boundary_context(): concrete = context_obj.as_boundary_context() # Can now access IPhBoundaryContext-specific methods/properties Notes: - For users who know C# interfaces and want advanced control - Most users should use properties like is_boundary_context and boundary_type instead """ if self.class_type == "PhBoundaryContext": return self._concrete return None
@property def concrete(self): """ Get the raw concrete interface object. For advanced users who need to access the underlying C# interface directly without going through wrapper properties. Returns: The concrete interface object (IPhSimpleContextSeg, IPhSimpleContextNC, IPhComplexContextSeg, IPhComplexContextNC, IPhBoundaryContext, etc., depending on the context's actual type). Example:: # Direct access to concrete interface concrete = context_obj.concrete segment = concrete.SegmentRA # PhSimpleContextSeg property Notes: - For power users only - Bypasses the wrapper's abstraction - Normal users should prefer wrapper properties like is_simple_context_seg, segment, etc. """ return self._concrete def __repr__(self): """String representation showing context type.""" return f"PhonologicalContext({self.class_type})" def __str__(self): """Human-readable description.""" name = self.context_name or "Unnamed" return f"Context '{name}' ({self.class_type})"