Skip to content

FHIR Model Factory

Main class and utilities for dynamically constructing FHIR resource models.

ModelAssembler

Path: fhircraft.fhir.resources.factory.assembler.ModelAssembler

ModelAssembler(index: DefinitionIndex, ctx: BuildContext, resource_name: str = 'Unknown')

Methods:

Name Description
assemble

Dynamically assembles and returns a Pydantic model class based on the provided name, base classes,

build_model_fixed_value_constraint

Constructs a ValidatorInformation object for enforcing a fixed value constraint on a FHIR model element.

build_model_pattern_constraint

Constructs a ValidatorInformation object for enforcing a pattern constraint on a FHIR model element.

Attributes:

Name Type Description
index DefinitionIndex

The definition index to assemble from.

ctx BuildContext

The build context.

resource_name str

The name of the resource being assembled (for error messages).

builder_chain Sequence[Builder]

The chain of builders to use for assembling fields. Initialized from :attr:BUILDER_CHAIN.

Source code in fhircraft/fhir/resources/factory/assembler.py
def __init__(
    self,
    index: DefinitionIndex,
    ctx: BuildContext,
    resource_name: str = "Unknown",
) -> None:
    self.index = index
    self.ctx = ctx
    self.resource_name = resource_name
    self.builder_chain = [builder(ctx) for builder in BUILDER_CHAIN]

index instance-attribute

index: DefinitionIndex = index

The definition index to assemble from.

ctx instance-attribute

ctx: BuildContext = ctx

The build context.

resource_name instance-attribute

resource_name: str = resource_name

The name of the resource being assembled (for error messages).

builder_chain instance-attribute

builder_chain: Sequence[Builder] = [(builder(ctx)) for builder in BUILDER_CHAIN]

The chain of builders to use for assembling fields. Initialized from :attr:BUILDER_CHAIN.

assemble

assemble(name: str, base: type | tuple[type, ...] | None = None) -> type

Dynamically assembles and returns a Pydantic model class based on the provided name, base classes, and the structure defined in the internal index. Args: name (str): The name of the model to be created. base (type or tuple[type, ...], optional): The base class(es) for the model. If None, uses the default base from context or FHIRBaseModel. Returns: type: The dynamically created Pydantic model class. Raises: ValueError: If a child element in the index lacks a definition. FactoryAssemblerError: If a builder fails to construct a field or validator. Notes: - Resolves base classes and iterates over child elements to build fields, validators, and properties. - Handles model-level constraints, including fixed values and patterns. - Attaches properties and sets constraint defaults as needed. - Issues a warning if no fields are built and no fields are inherited from base classes.

Source code in fhircraft/fhir/resources/factory/assembler.py
def assemble(
    self,
    name: str,
    base: type | tuple[type, ...] | None = None,
) -> type:
    """
    Dynamically assembles and returns a Pydantic model class based on the provided name, base classes,
    and the structure defined in the internal index.
    Args:
        name (str): The name of the model to be created.
        base (type or tuple[type, ...], optional): The base class(es) for the model. If None, uses the default base from context or FHIRBaseModel.
    Returns:
        type: The dynamically created Pydantic model class.
    Raises:
        ValueError: If a child element in the index lacks a definition.
        FactoryAssemblerError: If a builder fails to construct a field or validator.
    Notes:
        - Resolves base classes and iterates over child elements to build fields, validators, and properties.
        - Handles model-level constraints, including fixed values and patterns.
        - Attaches properties and sets constraint defaults as needed.
        - Issues a warning if no fields are built and no fields are inherited from base classes.
    """

    # Resolve base classes
    if base is None:
        base_classes: tuple[type, ...] = (self.ctx.base or FHIRBaseModel,)
    elif isinstance(base, tuple):
        base_classes = base
    else:
        base_classes = (base,)

    # ------------------------------------------------------------------
    # Iterate children (direct non-slice elements of the root)
    # ------------------------------------------------------------------
    root = self.index.root()
    fields = {}
    field_validators = {}
    properties = {}

    for child_node in self.index.get_children(root.id):
        if not child_node.definition:
            raise ValueError(
                f"Element '{child_node.id}' has no definition in the index."
            )

        # ----------------------------------------------------------
        # Dispatch through builder chain
        # ----------------------------------------------------------
        builder = self._find_builder(child_node)

        try:
            build = builder.build(
                child_node,
                self.index,
            )
        except Exception as exc:
            raise FactoryAssemblerError(
                f"Builder failed for element '{child_node.id}': {exc}"
            ) from exc

        # ----------------------------------------------------------
        # Accumulate results
        # ----------------------------------------------------------
        fields.update(
            {info.name: info.as_pydantic_definition() for info in build.fields}
        )
        field_validators.update(
            {info.name: info.as_pydantic_definition() for info in build.validators}
        )
        properties.update(build.properties)

    model_validators = {
        info.name: info.as_pydantic_definition()
        for constraint in (root.definition.constraint or [])
        if (
            info := Builder.build_invariant_constraint(
                root.path, constraint, kind="model"
            )
        )
    }
    # Handle fixed value and pattern constraints on the model itself (e.g. for slices)
    if root.fixed is not None:
        fixed_validator = self.build_model_fixed_value_constraint(root)
        model_validators[fixed_validator.name] = (
            fixed_validator.as_pydantic_definition()
        )

    if root.pattern is not None:
        pattern_validator = self.build_model_pattern_constraint(root)
        model_validators[pattern_validator.name] = (
            pattern_validator.as_pydantic_definition()
        )

    has_inherited_fields = any(
        len(getattr(base, "model_fields", [])) for base in base_classes
    )
    if not fields and not has_inherited_fields:
        warnings.warn(
            f"No fields built for model '{name}' and no fields defined on base class(es) {base_classes}.",
            FactoryWarning,
        )

    # ------------------------------------------------------------------
    # Build the Pydantic model
    # ------------------------------------------------------------------

    model = create_model(
        name,
        **fields,  # type: ignore[arg-type]
        __base__=base_classes,
        __validators__={**field_validators, **model_validators},  # type: ignore[arg-type]
        __doc__=root.documentation,
        __module__=self.ctx.factory.__module__,
    )

    if root.fixed or root.pattern:
        # If the slice entry has a fixed value or pattern, set it as default on the field and register a validator
        self._set_constraint_default_values(model, root)

    # Attach properties
    for attr_name, property_getter in properties.items():
        setattr(model, attr_name, property(property_getter))

    return model

build_model_fixed_value_constraint staticmethod

build_model_fixed_value_constraint(node: ElementNode) -> ValidatorInformation

Constructs a ValidatorInformation object for enforcing a fixed value constraint on a FHIR model element. Args: node (ElementNode): The element node containing the fixed value to be validated. Returns: ValidatorInformation: An object encapsulating the validator's name, kind, function, and arguments for the fixed value constraint.

Source code in fhircraft/fhir/resources/factory/assembler.py
@staticmethod
def build_model_fixed_value_constraint(node: ElementNode) -> ValidatorInformation:
    """
    Constructs a ValidatorInformation object for enforcing a fixed value constraint on a FHIR model element.
    Args:
        node (ElementNode): The element node containing the fixed value to be validated.
    Returns:
        ValidatorInformation: An object encapsulating the validator's name, kind, function, and arguments for the fixed value constraint.
    """

    return ValidatorInformation(
        name=f"FHIR_{node.name}_fixed_value_constraint",
        kind="model",
        function=partial(validate_FHIR_model_fixed_value, constant=node.fixed),
        arguments={"constant": node.fixed},
    )

build_model_pattern_constraint staticmethod

build_model_pattern_constraint(node: ElementNode) -> ValidatorInformation

Constructs a ValidatorInformation object for enforcing a pattern constraint on a FHIR model element. Args: node (ElementNode): The element node containing the pattern to be validated. Returns: ValidatorInformation: An object containing the validator's name, kind, and a partial function for pattern validation specific to the provided node.

Source code in fhircraft/fhir/resources/factory/assembler.py
@staticmethod
def build_model_pattern_constraint(node: ElementNode) -> ValidatorInformation:
    """
    Constructs a ValidatorInformation object for enforcing a pattern constraint on a FHIR model element.
    Args:
        node (ElementNode): The element node containing the pattern to be validated.
    Returns:
        ValidatorInformation: An object containing the validator's name, kind, and a partial function
        for pattern validation specific to the provided node.
    """

    return ValidatorInformation(
        name=f"FHIR_{node.name}_pattern_constraint",
        kind="model",
        function=partial(validate_FHIR_model_pattern, pattern=node.pattern),
    )

BackboneFieldBuilder

Path: fhircraft.fhir.resources.factory.builders.backbone.BackboneFieldBuilder

BackboneFieldBuilder(context: BuildContext)

Bases: Builder

Source code in fhircraft/fhir/resources/factory/builders/base.py
def __init__(self, context: BuildContext):
    self.context = context

Build

Path: fhircraft.fhir.resources.factory.builders.base.Build

dataclass

Build(fields: List[FieldInformation] = list(), validators: List[ValidatorInformation] = list(), properties: dict[str, Callable] = dict())

The result of a Builder's build() method, containing the field and validator information needed to construct a Pydantic model.

Parameters:

Name Type Description Default
fields List[FieldInformation]

Built-in mutable sequence.

If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.

<dynamic>
validators List[ValidatorInformation]

Built-in mutable sequence.

If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.

<dynamic>
properties dict[str, Callable]

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

<class 'dict'>

Attributes:

Name Type Description
fields List[FieldInformation]

The list of fields to include in the model.

validators List[ValidatorInformation]

The list of validators to include in the model.

properties dict[str, Callable]

The dictionary of properties to include in the model.

fields class-attribute instance-attribute

fields: List[FieldInformation] = field(default_factory=list)

The list of fields to include in the model.

validators class-attribute instance-attribute

validators: List[ValidatorInformation] = field(default_factory=list)

The list of validators to include in the model.

properties class-attribute instance-attribute

properties: dict[str, Callable] = field(default_factory=dict)

The dictionary of properties to include in the model.

Builder

Path: fhircraft.fhir.resources.factory.builders.base.Builder

Builder(context: BuildContext)

Bases: ABC

Methods:

Name Description
handle_python_keyword

Handle Python keywords and reserved class keywords by creating safe aliases.

build_field_information

Build field information for a FHIR resource field.

build_invariant_constraint

Build validator information for a FHIR invariant constraint.

resolve_type

Resolve a FHIR type definition to its corresponding type information.

build_field_validators

Constructs a list of field validators for a given FHIR element node.

Source code in fhircraft/fhir/resources/factory/builders/base.py
def __init__(self, context: BuildContext):
    self.context = context

handle_python_keyword staticmethod

handle_python_keyword(field_name: str) -> tuple[str, AliasChoices | None]

Handle Python keywords and reserved class keywords by creating safe aliases.

This function checks if a field name is a Python keyword or a reserved class keyword. If it is, the function creates a safe alternative by appending an underscore and returns both the safe name and an AliasChoices object mapping the original name to the safe name.

Parameters:

Name Type Description Default
field_name str

The name of the field to check.

required

Returns:

Type Description
tuple[str, AliasChoices | None]

tuple[str, AliasChoices | None]: A tuple containing: - str: The safe field name (with underscore appended if it's a keyword, otherwise unchanged) - AliasChoices | None: An AliasChoices object mapping original to safe name if a conflict exists, None otherwise.

Example

handle_python_keyword("class") ("class_", AliasChoices("class", "class_")) handle_python_keyword("my_field") ("my_field", None)

Source code in fhircraft/fhir/resources/factory/builders/base.py
@staticmethod
def handle_python_keyword(field_name: str) -> tuple[str, AliasChoices | None]:
    """
    Handle Python keywords and reserved class keywords by creating safe aliases.

    This function checks if a field name is a Python keyword or a reserved class keyword.
    If it is, the function creates a safe alternative by appending an underscore and returns
    both the safe name and an AliasChoices object mapping the original name to the safe name.

    Args:
        field_name (str): The name of the field to check.

    Returns:
        tuple[str, AliasChoices | None]: A tuple containing:
            - str: The safe field name (with underscore appended if it's a keyword, otherwise unchanged)
            - AliasChoices | None: An AliasChoices object mapping original to safe name if a conflict exists,
                                    None otherwise.

    Example:
        >>> handle_python_keyword("class")
        ("class_", AliasChoices("class", "class_"))
        >>> handle_python_keyword("my_field")
        ("my_field", None)
    """
    if keyword.iskeyword(field_name) or field_name in CLASS_RESERVED_KEYWORDS:
        safe = f"{field_name}_"
        alias = AliasChoices(field_name, safe)
        return safe, alias
    return field_name, None

build_field_information staticmethod

build_field_information(name: str, node: ElementNode, type: Any, alias: str | None = None, validation_alias: AliasChoices | None = None, description: str | None = None, default: Any = _Unset) -> FieldInformation

Build field information for a FHIR resource field.

Parameters:

Name Type Description Default
name str

The name of the field.

required
node ElementNode

The ElementNode containing field metadata and constraints.

required
type Any

The base type annotation for the field (type or Annotated).

required
alias str | None

Optional alias for the field during serialization.

None
validation_alias AliasChoices | None

Optional validation alias choices for the field.

None
description str | None

Optional description of the field. If not provided, uses node.documentation.

None
default Any

Optional default value for the field. If not provided, it will be determined based on node's default_value, fixed, or pattern.

_Unset

Returns:

Name Type Description
FieldInformation FieldInformation

A FieldInformation object containing the field's name, annotation, default value, alias, validation alias, description, and constraints (min/max length and min/max value).

Notes
  • Default value priority: node.default_value > node.fixed > node.pattern > None
  • If the field is an array and a default is set, it is converted to a list.
  • Arrays are annotated as List[type].
  • All fields are made Optional to enforce optionality.
  • Min/max cardinality constraints only apply to array fields.
Source code in fhircraft/fhir/resources/factory/builders/base.py
@staticmethod
def build_field_information(
    name: str,
    node: ElementNode,
    type: Any,
    alias: str | None = None,
    validation_alias: AliasChoices | None = None,
    description: str | None = None,
    default: Any = _Unset,
) -> FieldInformation:
    """
    Build field information for a FHIR resource field.

    Args:
        name: The name of the field.
        node: The ElementNode containing field metadata and constraints.
        type: The base type annotation for the field (type or Annotated).
        alias: Optional alias for the field during serialization.
        validation_alias: Optional validation alias choices for the field.
        description: Optional description of the field. If not provided, uses node.documentation.
        default: Optional default value for the field. If not provided, it will be determined based on node's default_value, fixed, or pattern.

    Returns:
        FieldInformation: A FieldInformation object containing the field's name, annotation,
            default value, alias, validation alias, description, and constraints (min/max
            length and min/max value).

    Notes:
        - Default value priority: node.default_value > node.fixed > node.pattern > None
        - If the field is an array and a default is set, it is converted to a list.
        - Arrays are annotated as List[type].
        - All fields are made Optional to enforce optionality.
        - Min/max cardinality constraints only apply to array fields.
    """

    if default is _Unset:
        if node.default_value is not None:
            default = node.default_value
        elif node.fixed is not None:
            default = node.fixed
        elif node.pattern is not None:
            default = node.pattern
        else:
            default = None

    effective_is_array = (
        node.base_is_array if node.base_is_array is not None else node.is_array
    )
    if effective_is_array and default is not None:
        default = ensure_list(default)

    annotation = type
    if effective_is_array:
        annotation = List[annotation]

    if node.is_prohibited:
        annotation = None
    else:
        annotation = Optional[annotation]

    return FieldInformation(
        name=name,
        annotation=annotation,
        default=default,
        alias=alias,
        validation_alias=validation_alias,
        description=description or node.documentation,
        min_length=node.min_cardinality if effective_is_array else None,
        max_length=node.max_length
        or (node.max_cardinality if effective_is_array else None),
        min_value=node.min_value,
        max_value=node.max_value,
    )

build_invariant_constraint staticmethod

build_invariant_constraint(field_name: str, constraint: ElementDefinitionConstraint | ElementDefinitionConstraint | ElementDefinitionConstraint, kind: Literal['field', 'model'] = 'field') -> ValidatorInformation

Build validator information for a FHIR invariant constraint. This function creates a ValidatorInformation object that encapsulates the details of a FHIR invariant constraint, which can be used to validate elements against user-defined constraints expressed in FHIRPath expressions.

Parameters:

Name Type Description Default
field_name str

The name of the field/element to apply the constraint to.

required
constraint ElementDefinitionConstraint | ElementDefinitionConstraint | ElementDefinitionConstraint

An ElementDefinitionConstraint object (from FHIR R4, R4B, or R5) containing the constraint definition with required attributes: key, expression, human, and severity.

required
kind Literal['field', 'model']

The kind of validator to create, either "field" for field-level validation or "model" for model-level validation. Defaults to "field".

'field'

Returns:

Name Type Description
ValidatorInformation ValidatorInformation

An object containing the name, kind, function, and arguments for the constraint validator.

Raises:

Type Description
FactoryBuilderError

If the constraint is missing any required attributes

Source code in fhircraft/fhir/resources/factory/builders/base.py
@staticmethod
def build_invariant_constraint(
    field_name: str,
    constraint: "R4_ElementDefinitionConstraint | R4B_ElementDefinitionConstraint | R5_ElementDefinitionConstraint",
    kind: Literal["field", "model"] = "field",
) -> ValidatorInformation:
    """
    Build validator information for a FHIR invariant constraint.
    This function creates a ValidatorInformation object that encapsulates the details
    of a FHIR invariant constraint, which can be used to validate elements against
    user-defined constraints expressed in FHIRPath expressions.

    Args:
        field_name: The name of the field/element to apply the constraint to.
        constraint: An ElementDefinitionConstraint object (from FHIR R4, R4B, or R5)
                   containing the constraint definition with required attributes:
                   key, expression, human, and severity.
        kind: The kind of validator to create, either "field" for field-level validation or "model" for model-level validation. Defaults to "field".

    Returns:
        ValidatorInformation: An object containing the name, kind, function, and arguments for the constraint validator.

    Raises:
        FactoryBuilderError: If the constraint is missing any required attributes
    """

    if not (
        constraint.key
        and constraint.expression
        and constraint.human
        and constraint.severity
    ):
        raise FactoryBuilderError(
            "Invalid constraint definition: missing required attributes."
        )

    constraint_name = constraint.key.replace("-", "_")
    validator_name = f"FHIR_{constraint_name}_constraint_validator"

    if kind == "field":
        return ValidatorInformation(
            name=validator_name,
            kind="model",
            function=validate_element_constraint,
            arguments={
                "elements": field_name,
                "expression": constraint.expression,
                "human": constraint.human,
                "key": constraint.key,
                "severity": constraint.severity,
            },
        )
    elif kind == "model":
        return ValidatorInformation(
            name=validator_name,
            kind="model",
            function=validate_model_constraint,
            arguments={
                "expression": constraint.expression,
                "human": constraint.human,
                "key": constraint.key,
                "severity": constraint.severity,
            },
        )

resolve_type

Resolve a FHIR type definition to its corresponding type information. This method processes FHIR ElementDefinitionType objects and returns TypeInformation containing the resolved type, its kind, and whether it requires primitive extensions.

Parameters:

Name Type Description Default
type ElementDefinitionType | ElementDefinitionType | ElementDefinitionType

An ElementDefinitionType from FHIR R4, R4B, or R5 release that specifies a type code, optional profile URL, and other type metadata.

required

Returns:

Name Type Description
TypeInformation TypeInformation

An object containing the resolved Python type, its kind (primitive, complex, resource), and whether it requires a primitive extension field.

Raises:

Type Description
FactoryTypeResolutionError

If the type definition has no code attribute, making it impossible to resolve the type for the given FHIR release.

Source code in fhircraft/fhir/resources/factory/builders/base.py
def resolve_type(
    self,
    type: "R4_ElementDefinitionType | R4B_ElementDefinitionType | R5_ElementDefinitionType",
) -> TypeInformation:
    """
    Resolve a FHIR type definition to its corresponding type information.
    This method processes FHIR ElementDefinitionType objects and returns TypeInformation
    containing the resolved type, its kind, and whether it requires primitive extensions.

    Args:
        type: An ElementDefinitionType from FHIR R4, R4B, or R5 release that specifies
            a type code, optional profile URL, and other type metadata.

    Returns:
        TypeInformation: An object containing the resolved Python type, its kind (primitive, complex, resource), and whether it requires a primitive extension field.

    Raises:
        FactoryTypeResolutionError: If the type definition has no code attribute, making it
            impossible to resolve the type for the given FHIR release.
    """

    fhir_release = type._fhir_release
    # Normalise the identifier: strip well-known URL prefixes
    if not type.code:
        raise FactoryTypeResolutionError(
            f"Cannot resolve FHIR type with no code for release '{fhir_release}'."
        )

    type_code = str(type.code)
    is_fhirpath_system_type = type_code.startswith(FHIRPATH_TYPE_PREFIX)

    # This is a rare case, only logical models and FHIRPath system types should use this
    if type_code.startswith(FHIR_SD_PREFIX):
        type_code = type_code.removeprefix(FHIR_SD_PREFIX)
        type_code = capitalize(str(type_code))

    # If a profile is specified and it's not a FHIRPath system type, resolve and build the profile to get the actual type to use
    if type.profile and not is_fhirpath_system_type:
        fhir_type = self.context.factory.build(canonical_url=str(type.profile[0]))
    elif is_fhirpath_system_type:
        fhir_type = FHIRPATH_TYPE_MAPPING[
            type_code.removeprefix(FHIRPATH_TYPE_PREFIX)
        ]
    else:
        # Get the Fhircraft type
        fhir_type = get_fhir_type(type_code, fhir_release)

    kind = (
        fhir_type._kind
        if inspect.isclass(fhir_type) and issubclass(fhir_type, FHIRBaseModel)
        else "primitive-type"
    )
    return TypeInformation(
        type=fhir_type,
        kind=kind,
    )

build_field_validators

build_field_validators(node: ElementNode, safe_name: str) -> List[ValidatorInformation]

Constructs a list of field validators for a given FHIR element node. This method inspects the provided ElementNode and generates appropriate validators based on fixed values, patterns, and custom constraints defined in the node's definition. Each validator is represented as a ValidatorInformation object. Args: node (ElementNode): The FHIR element node for which validators are built. safe_name (str): The sanitized field name used in validator identification. Returns: List[ValidatorInformation]: A list of field-level validators for the element.

Source code in fhircraft/fhir/resources/factory/builders/base.py
def build_field_validators(
    self, node: ElementNode, safe_name: str
) -> List[ValidatorInformation]:
    """
    Constructs a list of field validators for a given FHIR element node.
    This method inspects the provided `ElementNode` and generates appropriate
    validators based on fixed values, patterns, and custom constraints defined
    in the node's definition. Each validator is represented as a
    `ValidatorInformation` object.
    Args:
        node (ElementNode): The FHIR element node for which validators are built.
        safe_name (str): The sanitized field name used in validator identification.
    Returns:
        List[ValidatorInformation]: A list of field-level validators for the element.
    """

    validators = []
    # Build the field validators for this element
    if node.fixed is not None:
        validators.append(
            ValidatorInformation(
                name=f"FHIR_{safe_name}_fixed_value_constraint",
                kind="field",
                field=safe_name,
                function=partial(
                    validate_FHIR_element_fixed_value,
                ),
                arguments={"constant": node.fixed},
            )
        )

    # Build the field validators for this element
    if node.pattern is not None:
        validators.append(
            ValidatorInformation(
                name=f"FHIR_{safe_name}_pattern_constraint",
                kind="field",
                field=safe_name,
                function=partial(
                    validate_FHIR_element_pattern, pattern=node.pattern
                ),
            )
        )

    for constraint in node.definition.constraint or []:
        validators.append(
            self.build_invariant_constraint(safe_name, constraint, kind="field")
        )

    return validators

FieldInformation

Path: fhircraft.fhir.resources.factory.builders.base.FieldInformation

dataclass

FieldInformation(name: str, annotation: Any, default: Any = _Unset, alias: str | None = None, validation_alias: AliasChoices | None = None, description: str | None = None, min_length: int | None = None, max_length: int | None = None, min_value: int | None = None, max_value: int | None = None)

FieldInformation(name: str, annotation: Any, default: Any = PydanticUndefined, alias: str | None = None, validation_alias: pydantic.aliases.AliasChoices | None = None, description: str | None = None, min_length: int | None = None, max_length: int | None = None, min_value: int | None = None, max_value: int | None = None)

Parameters:

Name Type Description Default
name str

The name of the field.

required
annotation Any

The type annotation for the field.

required
default Any

The default value for the field.

PydanticUndefined
alias str | None

The alias for the field.

None
validation_alias AliasChoices | None

The validation alias for the field.

None
description str | None

The description of the field.

None
min_length int | None

The minimum length of the field.

None
max_length int | None

The maximum length of the field.

None
min_value int | None

The minimum value of the field.

None
max_value int | None

The maximum value of the field.

None

Attributes:

Name Type Description
name str

The name of the field.

annotation Any

The type annotation for the field.

default Any

The default value for the field.

alias str | None

The alias for the field.

validation_alias AliasChoices | None

The validation alias for the field.

description str | None

The description of the field.

min_length int | None

The minimum length of the field.

max_length int | None

The maximum length of the field.

min_value int | None

The minimum value of the field.

max_value int | None

The maximum value of the field.

name instance-attribute

name: str

The name of the field.

annotation instance-attribute

annotation: Any

The type annotation for the field.

default class-attribute instance-attribute

default: Any = _Unset

The default value for the field.

alias class-attribute instance-attribute

alias: str | None = None

The alias for the field.

validation_alias class-attribute instance-attribute

validation_alias: AliasChoices | None = None

The validation alias for the field.

description class-attribute instance-attribute

description: str | None = None

The description of the field.

min_length class-attribute instance-attribute

min_length: int | None = None

The minimum length of the field.

max_length class-attribute instance-attribute

max_length: int | None = None

The maximum length of the field.

min_value class-attribute instance-attribute

min_value: int | None = None

The minimum value of the field.

max_value class-attribute instance-attribute

max_value: int | None = None

The maximum value of the field.

TypeInformation

Path: fhircraft.fhir.resources.factory.builders.base.TypeInformation

dataclass

TypeInformation(type: type, kind: str)

Resolved type information for an ElementDefinitionType.

Parameters:

Name Type Description Default
type type

The resolved Python type.

required
kind str

The kind of FHIR type (e.g., "primitive", "complex", "resource").

required

Attributes:

Name Type Description
type type

The resolved Python type.

kind str

The kind of FHIR type (e.g., "primitive", "complex", "resource").

type instance-attribute

type: type

The resolved Python type.

kind instance-attribute

kind: str

The kind of FHIR type (e.g., "primitive", "complex", "resource").

ValidatorInformation

Path: fhircraft.fhir.resources.factory.builders.base.ValidatorInformation

dataclass

ValidatorInformation(name: str, kind: Literal['field', 'model'], function: Callable, arguments: dict[str, Any] = dict(), field: str | None = None)

Resolved validator information for a Pydantic validator.

Parameters:

Name Type Description Default
name str

The name of the validator.

required
kind Literal['field', 'model']

The kind of validator (field-level or model-level).

required
function Callable

The actual validator function.

required
arguments dict[str, Any]

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

<class 'dict'>
field str | None

The fields this validator applies to (for field validators).

None

Attributes:

Name Type Description
name str

The name of the validator.

kind Literal['field', 'model']

The kind of validator (field-level or model-level).

function Callable

The actual validator function.

arguments dict[str, Any]

The arguments for the validator function.

field str | None

The fields this validator applies to (for field validators).

name instance-attribute

name: str

The name of the validator.

kind instance-attribute

kind: Literal['field', 'model']

The kind of validator (field-level or model-level).

function instance-attribute

function: Callable

The actual validator function.

arguments class-attribute instance-attribute

arguments: dict[str, Any] = field(default_factory=dict)

The arguments for the validator function.

field class-attribute instance-attribute

field: str | None = None

The fields this validator applies to (for field validators).

SimpleFieldBuilder

Path: fhircraft.fhir.resources.factory.builders.simple.SimpleFieldBuilder

SimpleFieldBuilder(context: BuildContext)

Bases: Builder

Fallback builder for all other elements.

Produces a plain typed field, and — for FHIR primitive types — also appends a _{name} extension placeholder field.

Source code in fhircraft/fhir/resources/factory/builders/base.py
def __init__(self, context: BuildContext):
    self.context = context

SlicedFieldBuilder

Path: fhircraft.fhir.resources.factory.builders.slices.SlicedFieldBuilder

SlicedFieldBuilder(context: BuildContext)

Bases: Builder

Source code in fhircraft/fhir/resources/factory/builders/base.py
def __init__(self, context: BuildContext):
    self.context = context

TypeChoiceFieldBuilder

Path: fhircraft.fhir.resources.factory.builders.type_choice.TypeChoiceFieldBuilder

TypeChoiceFieldBuilder(context: BuildContext)

Bases: Builder

Source code in fhircraft/fhir/resources/factory/builders/base.py
def __init__(self, context: BuildContext):
    self.context = context

BuildContext

Path: fhircraft.fhir.resources.factory.context.BuildContext

dataclass

BuildContext(fhir_release: str, fhir_version: str, base: type, resource_name: str, factory: FHIRModelFactory, registry: 'StructureDefinitionRegistry')

Immutable context container passed through the entire factory pipeline.

Parameters:

Name Type Description Default
fhir_release str

FHIR release version (e.g. 'R4', 'STU3', 'R5').

required
fhir_version str

Full FHIR version string (e.g. '4.3.0').

required
base type

The base model class for this build session.

required
resource_name str

The resource name for this build session.

required
factory FHIRModelFactory

The FHIR structure factory for this build session.

required
registry 'StructureDefinitionRegistry'

The structure definition registry for resolving references and looking up base definitions.

required

Attributes:

Name Type Description
fhir_release str

FHIR release version (e.g. 'R4', 'STU3', 'R5').

fhir_version str

Full FHIR version string (e.g. '4.3.0').

base type

The base model class for this build session.

resource_name str

The resource name for this build session.

factory FHIRModelFactory

The FHIR structure factory for this build session.

registry 'StructureDefinitionRegistry'

The structure definition registry for resolving references and looking up base definitions.

fhir_release instance-attribute

fhir_release: str

FHIR release version (e.g. 'R4', 'STU3', 'R5').

fhir_version instance-attribute

fhir_version: str

Full FHIR version string (e.g. '4.3.0').

base instance-attribute

base: type

The base model class for this build session.

resource_name instance-attribute

resource_name: str

The resource name for this build session.

factory instance-attribute

The FHIR structure factory for this build session.

registry instance-attribute

registry: 'StructureDefinitionRegistry'

The structure definition registry for resolving references and looking up base definitions.

FHIRModelFactory

Path: fhircraft.fhir.resources.factory.core.FHIRModelFactory

FHIRModelFactory(fhir_release: str, registry: StructureDefinitionRegistry | None = None)

FHIRModelFactory constructs Pydantic model classes from FHIR StructureDefinitions.

The factory manages a registry of StructureDefinitions, supports loading FHIR packages, and caches constructed models to optimise performance.

Attributes:

Name Type Description
fhir_release str

The FHIR release version (e.g. "R4", "R5") used by the factory.

definition_registry StructureDefinitionRegistry

Registry for storing and retrieving StructureDefinitions.

construction_cache dict[str, type[BaseModel]]

Cache mapping canonical URLs to constructed Pydantic model classes.

Methods:

Name Description
build

Constructs and returns a Pydantic model class based on a FHIR StructureDefinition.

register_package

Download and register all StructureDefinitions from a FHIR npm package.

register

Register a StructureDefinition with the factory.

unregister

Remove a StructureDefinition from the registry and evict it from the cache.

reset_cache

Discard the entire construction cache.

has_registered_definition

Return True if url is present in the definition registry.

get_registered_definition

Retrieve a registered StructureDefinition by canonical URL.

list_registered_definitions

Return all canonical URLs registered in the definition registry.

is_built

Return True if a model for url is present in the construction cache.

list_built

Return all canonical URLs whose models are currently cached.

evict

Remove a single entry from the construction cache.

rebuild

Evict url from the cache and build a fresh model.

enable_internet_access

Allow the definition registry to resolve unknown URLs from the internet.

disable_internet_access

Prevent the definition registry from making any outgoing HTTP requests.

Source code in fhircraft/fhir/resources/factory/core.py
def __init__(
    self, fhir_release: str, registry: StructureDefinitionRegistry | None = None
) -> None:

    self.fhir_release: str = fhir_release
    if registry and registry.fhir_release != fhir_release:
        raise ValueError(
            f"Provided registry FHIR release '{registry.fhir_release}' does not match factory FHIR release '{fhir_release}'."
        )
    self.definition_registry = registry or StructureDefinitionRegistry(fhir_release)
    # Global cache: canonical URL → built Pydantic model
    self.construction_cache: dict[str, type[BaseModel]] = {}

build

build(structure_definition: Any = None, *, canonical_url: str | None = None, mixins: Sequence[type] | None = None, mode: Literal['auto', 'snapshot', 'differential'] = 'auto') -> type[BaseModel]

Constructs and returns a Pydantic model class based on a FHIR StructureDefinition.

Parameters:

Name Type Description Default
structure_definition Any

The FHIR StructureDefinition object to build the model from.

None
canonical_url str

The canonical URL of the StructureDefinition to retrieve from the registry.

None
mixins Sequence[type]

Additional mixin classes to include in the generated model.

None
mode Literal['auto', 'snapshot', 'differential']

The mode for building the model. "auto" selects the best mode automatically, "snapshot" uses the snapshot representation, and "differential" uses the differential representation.

'auto'

Returns:

Type Description
type[BaseModel]

type[BaseModel]: The constructed Pydantic model class.

Raises:

Type Description
KeyError

If neither structure_definition nor canonical_url is provided, or if the canonical_url is not found in the registry.

Notes
  • Uses a cache to avoid rebuilding models for the same StructureDefinition URL.
  • If both structure_definition and canonical_url are provided, structure_definition takes precedence.
Source code in fhircraft/fhir/resources/factory/core.py
def build(
    self,
    structure_definition: Any = None,
    *,
    canonical_url: str | None = None,
    mixins: Sequence[type] | None = None,
    mode: Literal["auto", "snapshot", "differential"] = "auto",
) -> type[BaseModel]:
    """
    Constructs and returns a Pydantic model class based on a FHIR StructureDefinition.

    Args:
        structure_definition (Any, optional): The FHIR StructureDefinition object to build the model from.
        canonical_url (str, optional): The canonical URL of the StructureDefinition to retrieve from the registry.
        mixins (Sequence[type], optional): Additional mixin classes to include in the generated model.
        mode (Literal["auto", "snapshot", "differential"], optional): The mode for building the model.
            "auto" selects the best mode automatically, "snapshot" uses the snapshot representation,
            and "differential" uses the differential representation.

    Returns:
        type[BaseModel]: The constructed Pydantic model class.

    Raises:
        KeyError: If neither structure_definition nor canonical_url is provided, or if the canonical_url is not found in the registry.

    Notes:
        - Uses a cache to avoid rebuilding models for the same StructureDefinition URL.
        - If both structure_definition and canonical_url are provided, structure_definition takes precedence.
    """

    if structure_definition:
        structure_definition = self._normalise_structure_definition(
            self.definition_registry, structure_definition
        )
    elif canonical_url:
        structure_definition = self.definition_registry.get(canonical_url)
    # Cache check
    if structure_definition.url in self.construction_cache:
        return self.construction_cache[structure_definition.url]

    return self._build(structure_definition, mixins=mixins, mode=mode)

register_package

register_package(package_name: str, version: str, skip_invalid: bool = False, include_dependencies: bool = True) -> None

Download and register all StructureDefinitions from a FHIR npm package.

Parameters:

Name Type Description Default
package_name str

Package identifier (e.g. "hl7.fhir.us.mcode").

required
version str

Package version string (e.g. "1.0.0").

required
skip_invalid bool

Whether to skip invalid StructureDefinitions.

False
include_dependencies bool

Whether to include dependencies when downloading the package.

True
Source code in fhircraft/fhir/resources/factory/core.py
def register_package(
    self,
    package_name: str,
    version: str,
    skip_invalid: bool = False,
    include_dependencies: bool = True,
) -> None:
    """
    Download and register all StructureDefinitions from a FHIR npm package.

    Args:
        package_name: Package identifier (e.g. ``"hl7.fhir.us.mcode"``).
        version: Package version string (e.g. ``"1.0.0"``).
        skip_invalid: Whether to skip invalid StructureDefinitions.
        include_dependencies: Whether to include dependencies when downloading the package.
    """
    self.definition_registry.download_package(
        package_name, version, skip_invalid, include_dependencies
    )

register

Register a StructureDefinition with the factory.

Accepts a typed StructureDefinition model instance or a plain dict. The normalised, validated SD instance is returned so callers can inspect it.

Parameters:

Name Type Description Default
sd StructureDefinition | StructureDefinition | StructureDefinition | dict

A StructureDefinition model instance or a dict representation.

required

Returns:

Type Description
StructureDefinition | StructureDefinition | StructureDefinition

The normalised StructureDefinition instance that was stored.

Raises:

Type Description
ValueError

If sd is neither a dict nor a StructureDefinition instance.

Source code in fhircraft/fhir/resources/factory/core.py
def register(
    self,
    sd: "R4_StructureDefinition | R4B_StructureDefinition | R5_StructureDefinition | dict",
) -> "R4_StructureDefinition | R4B_StructureDefinition | R5_StructureDefinition":
    """
    Register a StructureDefinition with the factory.

    Accepts a typed StructureDefinition model instance or a plain dict.  The
    normalised, validated SD instance is returned so callers can inspect it.

    Args:
        sd: A StructureDefinition model instance or a ``dict`` representation.

    Returns:
        The normalised StructureDefinition instance that was stored.

    Raises:
        ValueError: If *sd* is neither a dict nor a StructureDefinition instance.
    """
    if isinstance(sd, dict):
        return self.definition_registry.from_dict(sd)
    elif getattr(sd, "_resource_type", None) == "StructureDefinition":
        self.definition_registry.add(sd)
        return sd
    else:
        raise ValueError(
            "Input must be a dict or a StructureDefinition model instance."
        )

unregister

unregister(url: str) -> None

Remove a StructureDefinition from the registry and evict it from the cache.

If url is not registered, the call is a no-op.

Parameters:

Name Type Description Default
url str

The canonical URL of the StructureDefinition to remove.

required
Source code in fhircraft/fhir/resources/factory/core.py
def unregister(self, url: str) -> None:
    """
    Remove a StructureDefinition from the registry and evict it from the cache.

    If *url* is not registered, the call is a no-op.

    Args:
        url: The canonical URL of the StructureDefinition to remove.
    """
    self.definition_registry.structure_definitions_by_url.pop(url, None)
    self.evict(url)

reset_cache

reset_cache() -> None

Discard the entire construction cache.

Source code in fhircraft/fhir/resources/factory/core.py
def reset_cache(self) -> None:
    """Discard the entire construction cache."""
    self.construction_cache.clear()

has_registered_definition

has_registered_definition(url: str) -> bool

Return True if url is present in the definition registry.

Source code in fhircraft/fhir/resources/factory/core.py
def has_registered_definition(self, url: str) -> bool:
    """Return ``True`` if *url* is present in the definition registry."""
    return url in self.definition_registry

get_registered_definition

get_registered_definition(url: str) -> StructureDefinition | StructureDefinition | StructureDefinition

Retrieve a registered StructureDefinition by canonical URL.

Parameters:

Name Type Description Default
url str

The canonical URL of the StructureDefinition.

required

Returns:

Type Description
StructureDefinition | StructureDefinition | StructureDefinition

The StructureDefinition instance stored in the registry.

Raises:

Type Description
KeyError

If url is not registered.

Source code in fhircraft/fhir/resources/factory/core.py
def get_registered_definition(
    self, url: str
) -> "R4_StructureDefinition | R4B_StructureDefinition | R5_StructureDefinition":
    """
    Retrieve a registered StructureDefinition by canonical URL.

    Args:
        url: The canonical URL of the StructureDefinition.

    Returns:
        The StructureDefinition instance stored in the registry.

    Raises:
        KeyError: If *url* is not registered.
    """
    return self.definition_registry.get(url)

list_registered_definitions

list_registered_definitions(kind: str | None = None) -> list[str]

Return all canonical URLs registered in the definition registry.

Parameters:

Name Type Description Default
kind str | None

When provided, only URLs whose StructureDefinition has a matching kind field (e.g. "resource", "complex-type") are returned.

None

Returns:

Type Description
list[str]

Sorted list of canonical URL strings.

Source code in fhircraft/fhir/resources/factory/core.py
def list_registered_definitions(self, kind: str | None = None) -> list[str]:
    """
    Return all canonical URLs registered in the definition registry.

    Args:
        kind: When provided, only URLs whose StructureDefinition has a matching
            ``kind`` field (e.g. ``"resource"``, ``"complex-type"``) are returned.

    Returns:
        Sorted list of canonical URL strings.
    """
    sds = self.definition_registry.structure_definitions_by_url
    if kind is None:
        return sorted(sds)
    return sorted(
        url for url, sd in sds.items() if getattr(sd, "kind", None) == kind
    )

is_built

is_built(url: str) -> bool

Return True if a model for url is present in the construction cache.

Source code in fhircraft/fhir/resources/factory/core.py
def is_built(self, url: str) -> bool:
    """Return ``True`` if a model for *url* is present in the construction cache."""
    return url in self.construction_cache

list_built

list_built() -> list[str]

Return all canonical URLs whose models are currently cached.

Source code in fhircraft/fhir/resources/factory/core.py
def list_built(self) -> list[str]:
    """Return all canonical URLs whose models are currently cached."""
    return list(self.construction_cache)

evict

evict(url: str) -> None

Remove a single entry from the construction cache.

If url is not cached, the call is a no-op.

Parameters:

Name Type Description Default
url str

Canonical URL of the model to evict.

required
Source code in fhircraft/fhir/resources/factory/core.py
def evict(self, url: str) -> None:
    """
    Remove a single entry from the construction cache.

    If *url* is not cached, the call is a no-op.

    Args:
        url: Canonical URL of the model to evict.
    """
    self.construction_cache.pop(url, None)

rebuild

rebuild(url: str, *, mixins: Sequence[type] | None = None, mode: Literal['auto', 'snapshot', 'differential'] = 'auto') -> type[BaseModel]

Evict url from the cache and build a fresh model.

Useful when the underlying StructureDefinition has changed after the initial build (e.g. after calling :meth:register again with an updated SD).

Parameters:

Name Type Description Default
url str

Canonical URL of the StructureDefinition to rebuild.

required
mixins Sequence[type] | None

Optional mixin classes forwarded to :meth:build.

None
mode Literal['auto', 'snapshot', 'differential']

Build mode forwarded to :meth:build.

'auto'

Returns:

Type Description
type[BaseModel]

The newly constructed Pydantic model class.

Source code in fhircraft/fhir/resources/factory/core.py
def rebuild(
    self,
    url: str,
    *,
    mixins: Sequence[type] | None = None,
    mode: Literal["auto", "snapshot", "differential"] = "auto",
) -> type[BaseModel]:
    """
    Evict *url* from the cache and build a fresh model.

    Useful when the underlying StructureDefinition has changed after the
    initial build (e.g. after calling :meth:`register` again with an updated SD).

    Args:
        url: Canonical URL of the StructureDefinition to rebuild.
        mixins: Optional mixin classes forwarded to :meth:`build`.
        mode: Build mode forwarded to :meth:`build`.

    Returns:
        The newly constructed Pydantic model class.
    """
    self.evict(url)
    return self.build(canonical_url=url, mixins=mixins, mode=mode)

enable_internet_access

enable_internet_access() -> None

Allow the definition registry to resolve unknown URLs from the internet.

Source code in fhircraft/fhir/resources/factory/core.py
def enable_internet_access(self) -> None:
    """Allow the definition registry to resolve unknown URLs from the internet."""
    self.definition_registry.enable_internet_access()

disable_internet_access

disable_internet_access() -> None

Prevent the definition registry from making any outgoing HTTP requests.

Source code in fhircraft/fhir/resources/factory/core.py
def disable_internet_access(self) -> None:
    """Prevent the definition registry from making any outgoing HTTP requests."""
    self.definition_registry.disable_internet_access()

ElementNode

Path: fhircraft.fhir.resources.factory.element_node.ElementNode

dataclass

Immutable wrapper around a FHIR ElementDefinition that exposes a rich set of computed properties used throughout the factory pipeline.

Attributes:

Name Type Description

Parameters:

Name Type Description Default
definition ElementDefinition | ElementDefinition | ElementDefinition
required

Attributes:

Name Type Description
id str

Full element id (dot-separated, slices encoded with colon).

id_segments list[str]

List of segments of id.

id_ancestry list[str]

List of ancestor ids of id.

path str

Element path (dot-separated, no slice names).

depth int

Number of '.' separators in the id (root = 0).

path_segments list[str]

List of segments of path.

path_ancestry list[str]

List of ancestor segments of path.

name str

Official element name

local_id str

Leaf segment of id (may include :sliceName).

parent_id str | None

id with the last .segment removed. None for root-level elements.

is_root bool

Whether this element is a root element (has no parent).

is_type_choice_slice bool

True when this element is a type-choice type-slice.

is_slice bool

True when this element is a named slice definition.

is_slice_entry bool

The first element that declares slicing is considered to be the slicing entry

is_slice_child bool

True when any ancestor segment of the id contains a named-slice

is_content_reference bool

True when definition.contentReference is set.

slice_name str | None

The slice name (part after : in :attr:local_id), or None if this

is_slicing_ordered bool

If elements must be in same order as slices

slicing_rules Literal['openAtEnd', 'open', 'closed']

The slicing rules for this slice entry element, one of "openAtEnd", "open", or "closed". Defaults to "open" when not specified.

slice_ancestry list[str]

Names of all named ancestor slices in outermost-first order.

base_min_cardinality int | None

Minimum cardinality of the base element when this node was produced by merging a differential element with its base. None means this node was not produced by a merge (snapshot or root element).

base_max_cardinality int | None

Maximum cardinality of the base element when this node was produced by merging a differential element with its base. None means unbounded (*).).

base_is_array bool | None

Whether the base element is multi-valued and represented as a list (max > 1 or *) when this node was produced by merging a differential element with its base. None means this node was not produced by a merge (snapshot or root element).

min_cardinality int

Minimum cardinality (defaults to 0 when not specified).

max_cardinality int | None

Maximum cardinality. None means unbounded (*).

is_required bool

True when min_cardinality >= 1.

is_prohibited bool

True when max == "0".

is_array bool | None

Whether this element is multi-valued and represented as a list (max > 1 or *). Returns None if cardinality is not specified.

types Sequence[ElementDefinitionType] | Sequence[ElementDefinitionType] | Sequence[ElementDefinitionType]

List of FHIR type codes from definition.type.

type_codes list[str]

List of FHIR type codes from definition.type.

is_polymorphic_type bool

Whether the element is polymorphic (a.k.a type-choice).

profile_urls list[str]

All profile canonical URLs collected from definition.type[*].profile.

documentation str

Full combination of the elemments's short, definition, and comment fields, in

pattern

The pattern[x] value for this element, if any. Returns None if no pattern is specified.

fixed

The fixed[x] value for this element, if any. Returns None if no fixed is specified.

default_value

The defaultValue[x] value for this element, if any. Returns None if no default is specified.

min_value

The minValue[x] value for this element, if any. Returns None if no minValue is specified.

max_value

The maxValue[x] value for this element, if any. Returns None if no maxValue is specified.

max_length int | None

The maxLength[x] value for this element, if any. Returns None if no maxLength is specified.

id property

id: str

Full element id (dot-separated, slices encoded with colon).

id_segments property

id_segments: list[str]

List of segments of id.

id_ancestry property

id_ancestry: list[str]

List of ancestor ids of id.

path property

path: str

Element path (dot-separated, no slice names).

depth property

depth: int

Number of '.' separators in the id (root = 0).

path_segments property

path_segments: list[str]

List of segments of path.

path_ancestry property

path_ancestry: list[str]

List of ancestor segments of path.

name property

name: str

Official element name

local_id property

local_id: str

Leaf segment of id (may include :sliceName).

parent_id property

parent_id: str | None

id with the last .segment removed. None for root-level elements.

Note: for a top-level slice like Observation.component:systolic the parent_id is "Observation" (not "Observation.component"). This is intentional — slices are accessed via :meth:DefinitionIndex.slices, never via :meth:DefinitionIndex.children.

is_root property

is_root: bool

Whether this element is a root element (has no parent).

is_type_choice_slice property

is_type_choice_slice: bool

True when this element is a type-choice type-slice.

Per the FHIR spec, when a polymorphic element (path ends in [x]) is constrained to a specific type, the id reflects that type with a colon suffix directly after the [x], e.g. Patient.deceased[x]:deceasedBoolean.

This is distinct from a named list-slice such as Observation.component:systolic where the colon follows a plain element name.

is_slice property

is_slice: bool

True when this element is a named slice definition.

A named slice has a colon in its :attr:local_id that is not immediately preceded by [x], e.g. Observation.component:systoliclocal_id = "component:systolic".

Type-choice type-slices such as Patient.deceased[x]:deceasedBoolean are not considered named slices; use :attr:is_type_choice_slice for those.

is_slice_entry property

is_slice_entry: bool

The first element that declares slicing is considered to be the slicing entry

is_slice_child property

is_slice_child: bool

True when any ancestor segment of the id contains a named-slice colon — this element lives inside a named-slice sub-tree.

Type-choice colon suffixes ([x]:TypeName) are excluded; they are not a slice ancestry boundary.

is_content_reference property

is_content_reference: bool

True when definition.contentReference is set.

slice_name property

slice_name: str | None

The slice name (part after : in :attr:local_id), or None if this element is not a named slice or type-choice type-slice.

is_slicing_ordered property

is_slicing_ordered: bool

If elements must be in same order as slices

slicing_rules property

slicing_rules: Literal['openAtEnd', 'open', 'closed']

The slicing rules for this slice entry element, one of "openAtEnd", "open", or "closed". Defaults to "open" when not specified.

slice_ancestry property

slice_ancestry: list[str]

Names of all named ancestor slices in outermost-first order.

Type-choice colon suffixes ([x]:TypeName) are excluded because they denote a type specialisation, not a named list-slice boundary.

base_min_cardinality property

base_min_cardinality: int | None

Minimum cardinality of the base element when this node was produced by merging a differential element with its base. None means this node was not produced by a merge (snapshot or root element).

base_max_cardinality property

base_max_cardinality: int | None

Maximum cardinality of the base element when this node was produced by merging a differential element with its base. None means unbounded (*).).

base_is_array property

base_is_array: bool | None

Whether the base element is multi-valued and represented as a list (max > 1 or *) when this node was produced by merging a differential element with its base. None means this node was not produced by a merge (snapshot or root element).

min_cardinality property

min_cardinality: int

Minimum cardinality (defaults to 0 when not specified).

max_cardinality property

max_cardinality: int | None

Maximum cardinality. None means unbounded (*).

is_required property

is_required: bool

True when min_cardinality >= 1.

is_prohibited property

is_prohibited: bool

True when max == "0".

is_array property

is_array: bool | None

Whether this element is multi-valued and represented as a list (max > 1 or *). Returns None if cardinality is not specified.

types property

List of FHIR type codes from definition.type.

type_codes property

type_codes: list[str]

List of FHIR type codes from definition.type.

is_polymorphic_type property

is_polymorphic_type: bool

Whether the element is polymorphic (a.k.a type-choice).

Note: If the element is polymorphic (has more than one datatype), then the end of the path for the element SHALL be "[x]" to designate that the name of the element may vary when serialized.

Type-choice slice nodes (e.g. Patient.deceased[x]:deceasedBoolean) are not considered polymorphic — they represent a single concrete type constraint and must not trigger type-choice synthesis in the index.

profile_urls property

profile_urls: list[str]

All profile canonical URLs collected from definition.type[*].profile.

documentation property

documentation: str

Full combination of the elemments's short, definition, and comment fields, in that order of preference. Returns an empty string if none of those fields are set.

Strings that contain no alphanumeric characters are treated as absent and the next candidate is tried instead.

pattern property

pattern

The pattern[x] value for this element, if any. Returns None if no pattern is specified.

fixed property

fixed

The fixed[x] value for this element, if any. Returns None if no fixed is specified.

default_value property

default_value

The defaultValue[x] value for this element, if any. Returns None if no default is specified.

min_value property

min_value

The minValue[x] value for this element, if any. Returns None if no minValue is specified.

max_value property

max_value

The maxValue[x] value for this element, if any. Returns None if no maxValue is specified.

max_length property

max_length: int | None

The maxLength[x] value for this element, if any. Returns None if no maxLength is specified.

DefinitionIndex

Path: fhircraft.fhir.resources.factory.index.DefinitionIndex

DefinitionIndex(nodes: list[ElementNode])

Indexed collection of :class:ElementNode objects keyed by element.id and element.path.

Methods:

Name Description
from_elements

Wrap each ElementDefinition in an :class:ElementNode and build the index.

add

Add an :class:ElementNode to this index.

update

Add multiple :class:ElementNode objects to this index.

contains

Check if an element node exists by id or path.

get

Get element node by id

get_by_path

Get element node by path, can return multiple matches

get_single_by_path

Get a single element node by path, raises an error if multiple matches are found.

ids

Sorted list of all element ids in this index.

paths

Set of all element paths in this index.

root

Return the single root element of this index.

get_parent

Return the parent of the element with id.

get_children

Return immediate non-slice children of id.

get_slices

Return the named slices defined under id.

get_slice_children

Convenience alias for :meth:children when navigating inside a slice.

get_subtree

Return a new :class:DefinitionIndex scoped to id and all of

Attributes:

Name Type Description
nodes list[ElementNode]

List of all nodes in this index.

Source code in fhircraft/fhir/resources/factory/index.py
def __init__(self, nodes: list[ElementNode]) -> None:
    self._nodes_by_id: dict[str, ElementNode] = {}
    self._nodes_by_path: dict[str, List[ElementNode]] = {}
    self._synthetic_ids: set[str] = set()
    for node in nodes:
        self.add(node, replace=True)

nodes property

nodes: list[ElementNode]

List of all nodes in this index.

from_elements classmethod

from_elements(elements: list[Any]) -> DefinitionIndex

Wrap each ElementDefinition in an :class:ElementNode and build the index.

Parameters:

Name Type Description Default
elements list[Any]

List of raw FHIR ElementDefinition objects (any version).

required

Returns:

Type Description
DefinitionIndex

A fully populated :class:DefinitionIndex.

Source code in fhircraft/fhir/resources/factory/index.py
@classmethod
def from_elements(cls, elements: list[Any]) -> DefinitionIndex:
    """Wrap each *ElementDefinition* in an :class:`ElementNode` and build the index.

    Args:
        elements: List of raw FHIR ``ElementDefinition`` objects (any version).

    Returns:
        A fully populated :class:`DefinitionIndex`.
    """
    return cls([ElementNode(definition=element) for element in elements])

add

add(node: ElementNode, replace: bool = False) -> None

Add an :class:ElementNode to this index.

Source code in fhircraft/fhir/resources/factory/index.py
def add(self, node: ElementNode, replace: bool = False) -> None:
    """Add an :class:`ElementNode` to this index."""
    if node.id in self._nodes_by_id:
        if not replace:
            raise FactoryDefinitionIndexError(
                f"Duplicate element id {node.id!r} cannot be added to index."
            )
        else:
            self._nodes_by_id[node.id] = node
    self._nodes_by_id[node.id] = node
    self._nodes_by_path.setdefault(node.path, []).append(node)
    if node.is_polymorphic_type and node.type_codes:
        # Add synthetic nodes for each type choice (e.g. Observation.valueString)
        for type_code in node.type_codes:
            type_node = ElementNode(
                definition=node.definition.model_copy(
                    update={
                        "id": node.id.replace("[x]", capitalize(type_code)),
                        "path": node.path.replace("[x]", capitalize(type_code)),
                        "type": [node.definition.type[0].model_copy(update={"code": type_code})],  # type: ignore
                        "slicing": None,
                    }
                )
            )
            if type_node.id not in self:
                self._synthetic_ids.add(type_node.id)
                self.add(type_node)

update

update(nodes: list[ElementNode], replace: bool = False) -> None

Add multiple :class:ElementNode objects to this index.

Source code in fhircraft/fhir/resources/factory/index.py
def update(self, nodes: list[ElementNode], replace: bool = False) -> None:
    """Add multiple :class:`ElementNode` objects to this index."""
    for node in nodes:
        self.add(node, replace=replace)

contains

contains(*, id: str | None = None, path: str | None = None, ignore_root: bool = False) -> bool

Check if an element node exists by id or path.

Source code in fhircraft/fhir/resources/factory/index.py
def contains(
    self,
    *,
    id: str | None = None,
    path: str | None = None,
    ignore_root: bool = False,
) -> bool:
    """
    Check if an element node exists by id or path.
    """
    if ignore_root:
        if id:
            node = self._get_without_root(id=id)
        elif path:
            node = self._get_without_root(path=path)
        else:
            return False
    else:
        if id:
            node = self._nodes_by_id.get(id)
        elif path:
            node = self._nodes_by_path.get(path, [])
        else:
            return False
    return bool(node)

get

get(id: str, ignore_root: bool = False) -> ElementNode

Get element node by id

Source code in fhircraft/fhir/resources/factory/index.py
def get(self, id: str, ignore_root: bool = False) -> ElementNode:
    """
    Get element node by id
    """
    if ignore_root:
        node = self._get_without_root(id=id)
    else:
        node = self._nodes_by_id.get(id)
    if not node:
        raise FactoryDefinitionIndexError(f"Element id {id!r} not found in index.")
    return node

get_by_path

get_by_path(path: str, ignore_root: bool = False, ignore_slices: bool = False) -> List[ElementNode]

Get element node by path, can return multiple matches

Source code in fhircraft/fhir/resources/factory/index.py
def get_by_path(
    self, path: str, ignore_root: bool = False, ignore_slices: bool = False
) -> List[ElementNode]:
    """
    Get element node by path, can return multiple matches
    """
    if ignore_root:
        nodes = self._get_without_root(path=path)
    else:
        nodes = self._nodes_by_path.get(path, [])
    if nodes and ignore_slices:
        nodes = [n for n in nodes if not n.is_slice and not n.is_type_choice_slice]
        if len(nodes) > 1:
            non_slice_child_nodes = [n for n in nodes if not n.is_slice_child]
            if non_slice_child_nodes:
                nodes = non_slice_child_nodes
    if not nodes:
        raise FactoryDefinitionIndexError(f"Element path {path!r} not found in index.")
    return nodes

get_single_by_path

get_single_by_path(path: str, ignore_root: bool = False, ignore_slices: bool = False) -> ElementNode

Get a single element node by path, raises an error if multiple matches are found.

Source code in fhircraft/fhir/resources/factory/index.py
def get_single_by_path(
    self, path: str, ignore_root: bool = False, ignore_slices: bool = False
) -> ElementNode:
    """
    Get a single element node by path, raises an error if multiple matches are found.
    """
    nodes = self.get_by_path(
        path=path, ignore_root=ignore_root, ignore_slices=ignore_slices
    )
    if len(nodes) > 1:
        raise FactoryDefinitionIndexError(
            f"expected a single element node but found {len(nodes)} for path {path!r} in index. The following nodes were found: {[n.id for n in nodes]}"
        )
    return nodes[0]

ids

ids() -> list[str]

Sorted list of all element ids in this index.

Source code in fhircraft/fhir/resources/factory/index.py
def ids(self) -> list[str]:
    """Sorted list of all element ids in this index."""
    return sorted(self._nodes_by_id.keys())

paths

paths() -> set[str]

Set of all element paths in this index.

Source code in fhircraft/fhir/resources/factory/index.py
def paths(self) -> set[str]:
    """Set of all element paths in this index."""
    return set(self._nodes_by_path.keys())

root

root() -> ElementNode

Return the single root element of this index.

Source code in fhircraft/fhir/resources/factory/index.py
def root(self) -> ElementNode:
    """
    Return the single root element of this index.
    """
    root_node = [n for n in self.nodes if n.is_root]
    if not root_node:
        raise FactoryDefinitionIndexError("DefinitionIndex has no root element.")
    if len(root_node) > 1:
        raise FactoryDefinitionIndexError(
            f"DefinitionIndex has multiple root candidates: {[n.id for n in root_node]}"
        )
    return root_node[0]

get_parent

get_parent(id: str) -> ElementNode

Return the parent of the element with id.

Source code in fhircraft/fhir/resources/factory/index.py
def get_parent(self, id: str) -> ElementNode:
    """
    Return the parent of the element with *id*.
    """
    if parent_id := self.get(id).parent_id:
        return self.get(parent_id)
    else:
        raise FactoryDefinitionIndexError(
            f"Element {id!r} has no parent (it is a root element)."
        )

get_children

get_children(id: str) -> list[ElementNode]

Return immediate non-slice children of id.

Synthetic type-expansion nodes (e.g. Observation.valueQuantity generated from Observation.value[x]) are excluded — the :class:TypeChoiceFieldBuilder already handles the canonical value[x] element and emits one typed field per type.

Source code in fhircraft/fhir/resources/factory/index.py
def get_children(self, id: str) -> list[ElementNode]:
    """
    Return immediate non-slice children of *id*.

    Synthetic type-expansion nodes (e.g. ``Observation.valueQuantity``
    generated from ``Observation.value[x]``) are excluded — the
    :class:`TypeChoiceFieldBuilder` already handles the canonical
    ``value[x]`` element and emits one typed field per type.
    """
    return [
        n
        for n in self.nodes
        if n.parent_id == id
        and not n.is_slice
        and not n.is_type_choice_slice
        and n.id not in self._synthetic_ids
    ]

get_slices

get_slices(id: str) -> list[ElementNode]

Return the named slices defined under id.

Source code in fhircraft/fhir/resources/factory/index.py
def get_slices(self, id: str) -> list[ElementNode]:
    """
    Return the named slices defined under *id*.
    """
    node = self.get(id)
    if not node.is_slice_entry:
        raise FactoryDefinitionIndexError(
            f"Element {id!r} is not a slicing entry and cannot have slices."
        )
    return [n for n in self.nodes if n.is_slice and n.id.startswith(node.id + ":")]

get_slice_children

get_slice_children(slice_id: str) -> list[ElementNode]

Convenience alias for :meth:children when navigating inside a slice.

Returns immediate non-slice elements whose parent_id == slice_id.

Source code in fhircraft/fhir/resources/factory/index.py
def get_slice_children(self, slice_id: str) -> list[ElementNode]:
    """
    Convenience alias for :meth:`children` when navigating inside a slice.

    Returns immediate non-slice elements whose ``parent_id == slice_id``.
    """
    return self.get_children(slice_id)

get_subtree

get_subtree(id: str) -> DefinitionIndex

Return a new :class:DefinitionIndex scoped to id and all of its descendants (children, slices, and their sub-elements).

The returned index is re-rooted: the matched element becomes the root with an id equal to its capitalised :attr:~ElementNode.name (e.g. Observation.componentComponent), and all descendant ids and paths are rewritten accordingly.

Source code in fhircraft/fhir/resources/factory/index.py
def get_subtree(self, id: str) -> DefinitionIndex:
    """
    Return a new :class:`DefinitionIndex` scoped to *id* and all of
    its descendants (children, slices, and their sub-elements).

    The returned index is re-rooted: the matched element becomes the root
    with an id equal to its capitalised :attr:`~ElementNode.name`
    (e.g. ``Observation.component`` → ``Component``), and all descendant
    ids and paths are rewritten accordingly.
    """
    root_node = self.get(id)
    raw_name = root_node.name
    new_root_name = raw_name[0].upper() + raw_name[1:]

    # Original path prefix used for rewriting path fields (no slice names).
    original_path = root_node.path
    original_path_dot = original_path + "."

    prefix_dot = id + "."
    prefix_colon = id + ":"

    def _rewrite_id(original_id: str) -> str:
        if original_id == id:
            return new_root_name
        if original_id.startswith(prefix_dot):
            return new_root_name + "." + original_id[len(prefix_dot) :]
        if original_id.startswith(prefix_colon):
            return new_root_name + ":" + original_id[len(prefix_colon) :]
        return original_id

    def _rewrite_path(original_path_value: str) -> str:
        if original_path_value == original_path:
            return new_root_name
        if original_path_value.startswith(original_path_dot):
            return (
                new_root_name + "." + original_path_value[len(original_path_dot) :]
            )
        return original_path_value

    subtree_nodes = []
    for node in self.nodes:
        node_id = node.id
        if node_id in self._synthetic_ids:
            continue
        if (
            node_id != id
            and not node_id.startswith(prefix_dot)
            and not node_id.startswith(prefix_colon)
        ):
            continue
        new_definition = node.definition.model_copy(
            update={
                "id": _rewrite_id(node_id),
                "path": _rewrite_path(node.path),
            }
        )
        subtree_nodes.append(ElementNode(definition=new_definition))

    return DefinitionIndex(subtree_nodes)

SnapshotResolver

Path: fhircraft.fhir.resources.factory.resolver.SnapshotResolver

SnapshotResolver(registry: StructureDefinitionRegistry)

Resolves a FHIR StructureDefinition into a complete :class:DefinitionIndex.

Methods:

Name Description
resolve

Resolve a StructureDefinition into a DefinitionIndex.

Source code in fhircraft/fhir/resources/factory/resolver.py
def __init__(self, registry: StructureDefinitionRegistry) -> None:
    self._registry = registry

resolve

resolve(sd: StructureDefinition | StructureDefinition | StructureDefinition, mode: Literal['auto', 'snapshot', 'differential'] = 'auto') -> DefinitionIndex

Resolve a StructureDefinition into a DefinitionIndex. This method resolves either the snapshot or differential representation of a StructureDefinition into a normalized DefinitionIndex. When mode is set to "auto", the method automatically selects the differential mode if available, otherwise uses snapshot mode.

Parameters:

Name Type Description Default
sd StructureDefinition | StructureDefinition | StructureDefinition

A FHIR StructureDefinition resource (R4, R4B, or R5 version).

required
mode Literal['auto', 'snapshot', 'differential']

Resolution mode to use. Defaults to "auto". Options are: - "auto": Automatically selects "differential" if available, else "snapshot" - "snapshot": Uses the snapshot representation directly - "differential": Merges the differential over the base_index

'auto'

Returns:

Name Type Description
DefinitionIndex DefinitionIndex

A resolved index of FHIR elements.

Raises:

Type Description
AssertionError

If the selected mode's required elements are missing or contain None values.

FactoryDefinitionResolutionError

If neither snapshot nor differential elements are available.

Source code in fhircraft/fhir/resources/factory/resolver.py
def resolve(
    self,
    sd: "R4_StructureDefinition | R4B_StructureDefinition | R5_StructureDefinition",
    mode: Literal["auto", "snapshot", "differential"] = "auto",
) -> DefinitionIndex:
    """
    Resolve a StructureDefinition into a DefinitionIndex.
    This method resolves either the snapshot or differential representation of a
    StructureDefinition into a normalized DefinitionIndex. When mode is set to "auto",
    the method automatically selects the differential mode if available, otherwise uses
    snapshot mode.

    Args:
        sd: A FHIR StructureDefinition resource (R4, R4B, or R5 version).
        mode: Resolution mode to use. Defaults to "auto". Options are:
              - "auto": Automatically selects "differential" if available, else "snapshot"
              - "snapshot": Uses the snapshot representation directly
              - "differential": Merges the differential over the base_index

    Returns:
        DefinitionIndex: A resolved index of FHIR elements.

    Raises:
        AssertionError: If the selected mode's required elements are missing or contain None values.
        FactoryDefinitionResolutionError: If neither snapshot nor differential elements are available.
    """

    if mode == "auto":
        mode = (
            "differential"
            if sd.baseDefinition
            and sd.differential
            and len(sd.differential.element or []) > 0
            else "snapshot"
        )
    if mode == "snapshot":
        # Type check assertions
        if not sd.snapshot:
            raise FactoryDefinitionResolutionError(
                f"StructureDefinition {sd.name or sd.url} snapshot is None"
            )
        if not sd.snapshot.element:
            raise FactoryDefinitionResolutionError(
                f"StructureDefinition {sd.name or sd.url} snapshot.element is None"
            )
        if not all([e is not None for e in sd.snapshot.element]):
            raise FactoryDefinitionResolutionError(
                f"StructureDefinition {sd.name or sd.url} snapshot.element contains None"
            )
        # Fast path: wrap snapshot elements directly without merging
        elements = sd.snapshot.element
        resolved_index = DefinitionIndex.from_elements(elements)

    elif mode == "differential":
        # Type check assertions
        if not sd.differential:
            raise FactoryDefinitionResolutionError(
                f"StructureDefinition {sd.name or sd.url} differential is None"
            )
        if not sd.differential.element:
            raise FactoryDefinitionResolutionError(
                f"StructureDefinition {sd.name or sd.url} differential.element is None"
            )
        if not all([e is not None for e in sd.differential.element]):
            raise FactoryDefinitionResolutionError(
                f"StructureDefinition {sd.name or sd.url} differential.element contains None"
            )

        if not (base_canonical := sd.baseDefinition):
            raise FactoryDefinitionResolutionError(
                f"StructureDefinition '{getattr(sd, 'name', '?')}' has no baseDefinition, which is required for differential resolution."
            )
        # Build base index by recursively applying ancestor differentials.
        base_definition = self._registry.get(str(base_canonical))
        base_index = self._resolve_base_chain(base_definition)
        # Merge differential over base snapshot to produce a synthetic snapshot
        resolved_index = self._resolve_differential(
            sd.differential.element, base_index
        )
    else:
        raise FactoryDefinitionResolutionError(
            f"StructureDefinition '{getattr(sd, 'name', '?')}' has neither a "
            "snapshot nor a differential element list."
        )

    # Finally, resolve any contentReferences in the resulting index
    resolved_index = self._resolve_content_references(resolved_index)

    return resolved_index

T module-attribute

T = TypeVar('T', bound=BaseModel)

get_type_choice_value_by_base

get_type_choice_value_by_base(instance: BaseModel, base: str) -> Any

Retrieve the value of a type-choice field in an instance based on the field name starting with a specific base string.

Parameters:

Name Type Description Default
instance object

The instance object to retrieve the value from.

required
base str

The base string that the field name should start with.

required

Returns:

Name Type Description
value Any

The value of the first field found in the instance that starts with the specified base string, or None if no such field exists or the value is None.

Source code in fhircraft/fhir/resources/validators.py
def get_type_choice_value_by_base(instance: BaseModel, base: str) -> Any:
    """
    Retrieve the value of a type-choice field in an instance based on the field
    name starting with a specific base string.

    Args:
        instance (object): The instance object to retrieve the value from.
        base (str): The base string that the field name should start with.

    Returns:
        value (Any): The value of the first field found in the instance that starts with the specified base string,
                    or `None` if no such field exists or the value is `None`.
    """
    for field in instance.__class__.model_fields:
        if field.startswith(base):
            value = getattr(instance, field)
            if value is not None:
                return value

validate_FHIR_element_fixed_value

validate_FHIR_element_fixed_value(cls: Any, element: Union[FHIRBaseModel, List[FHIRBaseModel], Any], constant: Union[FHIRBaseModel, List[FHIRBaseModel], Any]) -> Any

Validate the FHIR element against a specified constant value and return the element if it fulfills the constant.

Parameters:

Name Type Description Default
cls Any

Placeholder for an argument that is not used in the function.

required
element Union[FHIRBaseModel, List[FHIRBaseModel]]

The FHIR element to validate against the constant.

required
constant Union[FHIRBaseModel, List[FHIRBaseModel]]

The constant value to validate the element against.

required

Returns:

Type Description
Any

Union[FHIRBaseModel, List[FHIRBaseModel]]: The validated FHIR element.

Raises:

Type Description
PydanticCustomError

If the element does not fulfill the specified constant.

Source code in fhircraft/fhir/resources/validators.py
def validate_FHIR_element_fixed_value(
    cls: Any,
    element: Union["FHIRBaseModel", List["FHIRBaseModel"], Any],
    constant: Union["FHIRBaseModel", List["FHIRBaseModel"], Any],
) -> Any:
    """
    Validate the FHIR element against a specified constant value and return the element if it fulfills the constant.

    Args:
        cls (Any): Placeholder for an argument that is not used in the function.
        element (Union[FHIRBaseModel, List[FHIRBaseModel]]): The FHIR element to validate against the constant.
        constant (Union[FHIRBaseModel, List[FHIRBaseModel]]): The constant value to validate the element against.

    Returns:
        Union[FHIRBaseModel, List[FHIRBaseModel]]: The validated FHIR element.

    Raises:
        PydanticCustomError: If the element does not fulfill the specified constant.
    """
    from fhircraft.fhir.resources.base import FHIRBaseModel

    config = get_config()
    if config.mode == "skip":
        return element

    if isinstance(constant, list):
        constant = constant[0]
    _element = element[0] if isinstance(element, list) else element

    if isinstance(constant, FHIRBaseModel):
        constant = constant.model_dump()
    if isinstance(_element, FHIRBaseModel):
        _element = _element.model_dump()
    if constant != _element:
        error = f"Value does not fulfill constant:\n{constant.model_dump_json(indent=2) if isinstance(constant, FHIRBaseModel) else constant}"
        if config.mode == "lenient":
            warnings.warn(error, FhirValidationWarning, stacklevel=2)
        else:
            raise PydanticCustomError("fhir_pattern_violation", str(error))  # type: ignore
    return element

validate_FHIR_element_pattern

validate_FHIR_element_pattern(cls: Any, element: Union[FHIRBaseModel, List[FHIRBaseModel], Any], pattern: Union[FHIRBaseModel, List[FHIRBaseModel], Any]) -> Any

Validate the FHIR element against a specified pattern and return the element if it fulfills the pattern.

Parameters:

Name Type Description Default
cls Any

Placeholder for an argument that is not used in the function.

required
element Union[FHIRBaseModel, List[FHIRBaseModel]]

The FHIR element to validate against the pattern.

required
pattern Union[FHIRBaseModel, List[FHIRBaseModel]]

The pattern to validate the element against.

required

Returns:

Type Description
Any

Union[FHIRBaseModel, List[FHIRBaseModel]]: The validated FHIR element.

Raises:

Type Description
PydanticCustomError

If the element does not fulfill the specified pattern.

Source code in fhircraft/fhir/resources/validators.py
def validate_FHIR_element_pattern(
    cls: Any,
    element: Union["FHIRBaseModel", List["FHIRBaseModel"], Any],
    pattern: Union["FHIRBaseModel", List["FHIRBaseModel"], Any],
) -> Any:
    """
    Validate the FHIR element against a specified pattern and return the element if it fulfills the pattern.

    Args:
        cls (Any): Placeholder for an argument that is not used in the function.
        element (Union[FHIRBaseModel, List[FHIRBaseModel]]): The FHIR element to validate against the pattern.
        pattern (Union[FHIRBaseModel, List[FHIRBaseModel]]): The pattern to validate the element against.

    Returns:
        Union[FHIRBaseModel, List[FHIRBaseModel]]: The validated FHIR element.

    Raises:
        PydanticCustomError: If the element does not fulfill the specified pattern.
    """
    from fhircraft.fhir.resources.base import FHIRBaseModel

    config = get_config()
    if config.mode == "skip":
        return element

    if isinstance(pattern, list):
        pattern = pattern[0]
    _element = element[0] if isinstance(element, list) else element
    _element = (
        _element.model_dump() if isinstance(_element, FHIRBaseModel) else _element
    )
    _pattern = pattern.model_dump() if isinstance(pattern, FHIRBaseModel) else pattern
    if (isinstance(pattern, dict) and not is_dict_subset(_pattern, _element)) or (
        not isinstance(pattern, dict) and _element != _pattern
    ):
        error = f"Value does not fulfill pattern:\n{pattern.model_dump_json(indent=2) if isinstance(pattern, FHIRBaseModel) else pattern}"
        if config.mode == "lenient":
            warnings.warn(str(error), FhirValidationWarning, stacklevel=2)
        else:
            raise PydanticCustomError("fhir_pattern_violation", str(error))  # type: ignore
    return element

validate_FHIR_model_fixed_value

validate_FHIR_model_fixed_value(model: Union[FHIRBaseModel, List[FHIRBaseModel], Any], constant: Union[FHIRBaseModel, List[FHIRBaseModel], Any]) -> Any

Validate the FHIR model against a specified constant value and return the model if it fulfills the constant.

Parameters:

Name Type Description Default
model Union[FHIRBaseModel, List[FHIRBaseModel]]

The FHIR model to validate against the constant.

required
constant Union[FHIRBaseModel, List[FHIRBaseModel]]

The constant value to validate the model against.

required

Returns:

Type Description
Any

Union[FHIRBaseModel, List[FHIRBaseModel]]: The validated FHIR element.

Raises:

Type Description
PydanticCustomError

If the element does not fulfill the specified constant.

Source code in fhircraft/fhir/resources/validators.py
def validate_FHIR_model_fixed_value(
    model: Union["FHIRBaseModel", List["FHIRBaseModel"], Any],
    constant: Union["FHIRBaseModel", List["FHIRBaseModel"], Any],
) -> Any:
    """
    Validate the FHIR model against a specified constant value and return the model if it fulfills the constant.

    Args:
        model (Union[FHIRBaseModel, List[FHIRBaseModel]]): The FHIR model to validate against the constant.
        constant (Union[FHIRBaseModel, List[FHIRBaseModel]]): The constant value to validate the model against.

    Returns:
        Union[FHIRBaseModel, List[FHIRBaseModel]]: The validated FHIR element.

    Raises:
        PydanticCustomError: If the element does not fulfill the specified constant.
    """
    return validate_FHIR_element_fixed_value(cls=None, element=model, constant=constant)

validate_FHIR_model_pattern

validate_FHIR_model_pattern(model: Union[FHIRBaseModel, List[FHIRBaseModel], Any], pattern: Union[FHIRBaseModel, List[FHIRBaseModel], Any]) -> Any

Validate the FHIR model against a specified pattern and return the model if it fulfills the pattern.

Parameters:

Name Type Description Default
model Union[FHIRBaseModel, List[FHIRBaseModel]]

The FHIR model to validate against the pattern.

required
pattern Union[FHIRBaseModel, List[FHIRBaseModel]]

The pattern to validate the model against.

required

Returns:

Type Description
Any

Union[FHIRBaseModel, List[FHIRBaseModel]]: The validated FHIR model.

Raises:

Type Description
PydanticCustomError

If the model does not fulfill the specified pattern.

Source code in fhircraft/fhir/resources/validators.py
def validate_FHIR_model_pattern(
    model: Union["FHIRBaseModel", List["FHIRBaseModel"], Any],
    pattern: Union["FHIRBaseModel", List["FHIRBaseModel"], Any],
) -> Any:
    """
    Validate the FHIR model against a specified pattern and return the model if it fulfills the pattern.

    Args:
        model (Union[FHIRBaseModel, List[FHIRBaseModel]]): The FHIR model to validate against the pattern.
        pattern (Union[FHIRBaseModel, List[FHIRBaseModel]]): The pattern to validate the model against.

    Returns:
        Union[FHIRBaseModel, List[FHIRBaseModel]]: The validated FHIR model.

    Raises:
        PydanticCustomError: If the model does not fulfill the specified pattern.
    """
    return validate_FHIR_element_pattern(cls=None, element=model, pattern=pattern)

validate_element_constraint

validate_element_constraint(instance: T, elements: Sequence[str], expression: str, human: str, key: str, severity: str) -> T

Validates a FHIR element constraint based on a FHIRPath expression.

Parameters:

Name Type Description Default
instance T

The instance to be validated.

required
elements Sequence[str]

The elements to be validated.

required
expression str

The FHIRPath expression to evaluate.

required
human str

A human-readable description of the constraint.

required
key str

The key associated with the constraint.

required
severity str

The severity level of the constraint ('warning' or 'error').

required

Returns:

Name Type Description
Any T

The validated value.

Raises:

Type Description
PydanticCustomError

If the validation fails and severity is not warning.

Warning

If the validation fails and severity is warning.

Source code in fhircraft/fhir/resources/validators.py
def validate_element_constraint(
    instance: T,
    elements: Sequence[str],
    expression: str,
    human: str,
    key: str,
    severity: str,
) -> T:
    """
    Validates a FHIR element constraint based on a FHIRPath expression.

    Args:
        instance (T): The instance to be validated.
        elements (Sequence[str]): The elements to be validated.
        expression (str): The FHIRPath expression to evaluate.
        human (str): A human-readable description of the constraint.
        key (str): The key associated with the constraint.
        severity (str): The severity level of the constraint ('warning' or 'error').

    Returns:
        Any: The validated value.

    Raises:
        PydanticCustomError: If the validation fails and severity is not `warning`.
        Warning: If the validation fails and severity is `warning`.
    """

    config = get_config()
    if config.mode == "skip":
        return instance

    values = {}

    def _get_path_value(obj: Any, element_path: str, current_path: str = "") -> None:
        """
        Recursively extract values from nested object paths, handling lists correctly.

        Args:
            obj: Current object to traverse
            element_path: Remaining path to traverse (dot-separated)
            current_path: Path traversed so far (for error reporting)
        """
        if not element_path:
            # We've reached the end of the path
            values[current_path] = obj
            return

        parts = element_path.split(".", 1)
        current_attr = parts[0]
        remaining_path = parts[1] if len(parts) > 1 else ""

        # Build the new current path
        new_current_path = (
            f"{current_path}.{current_attr}" if current_path else current_attr
        )

        # Get the attribute value
        current_value = getattr(obj, current_attr, None) if obj is not None else None

        if current_value is None:
            # Attribute doesn't exist or is None
            values[
                (
                    new_current_path
                    if not remaining_path
                    else f"{new_current_path}.{remaining_path}"
                )
            ] = None
            return

        if isinstance(current_value, list):
            if not remaining_path:
                # We want the list itself
                values[new_current_path] = current_value
            else:
                # We need to traverse into each item in the list
                for idx, item in enumerate(current_value):
                    item_path = f"{new_current_path}[{idx}]"
                    _get_path_value(item, remaining_path, item_path)
        else:
            if not remaining_path:
                # We want this value
                values[new_current_path] = current_value
            else:
                # Continue traversing
                _get_path_value(current_value, remaining_path, new_current_path)

    for element_path in elements:
        _get_path_value(instance, element_path)

    for path, value in values.items():
        _validate_FHIR_element_constraint(
            value, instance, expression, human, key, severity, element=path
        )
    return instance

validate_model_constraint

validate_model_constraint(instance: T, expression: str, human: str, key: str, severity: str) -> T

Validates a FHIR model constraint based on a FHIRPath expression.

Parameters:

Name Type Description Default
instance T

Instance of the model to be validated.

required
expression str

The FHIRPath expression to evaluate.

required
human str

A human-readable description of the constraint.

required
key str

The key associated with the constraint.

required
severity str

The severity level of the constraint ('warning' or 'error').

required

Returns:

Name Type Description
instance type[T]

The validated model instance.

Raises:

Type Description
PydanticCustomError

If the validation fails and severity is not warning.

Warning

If the validation fails and severity is warning.

Source code in fhircraft/fhir/resources/validators.py
def validate_model_constraint(
    instance: T, expression: str, human: str, key: str, severity: str
) -> T:
    """
    Validates a FHIR model constraint based on a FHIRPath expression.

    Args:
        instance (T): Instance of the model to be validated.
        expression (str): The FHIRPath expression to evaluate.
        human (str): A human-readable description of the constraint.
        key (str): The key associated with the constraint.
        severity (str): The severity level of the constraint ('warning' or 'error').

    Returns:
        instance (type[T]): The validated model instance.

    Raises:
        PydanticCustomError: If the validation fails and severity is not `warning`.
        Warning: If the validation fails and severity is `warning`.
    """
    config = get_config()
    if config.mode == "skip":
        return instance
    return _validate_FHIR_element_constraint(
        instance, instance, expression, human, key, severity
    )

validate_slicing_cardinalities

validate_slicing_cardinalities(cls: Any, values: List[Any] | None, field_name: str) -> List[FHIRSliceModel] | None

Validates the cardinalities of FHIR slices for a specific field within a FHIR resource.

Parameters:

Name Type Description Default
cls Any

The Pydantic FHIR model class.

required
values List[Any]

List of values for the field.

required
field_name str

The name of the field to validate.

required

Returns:

Type Description
List[FHIRSliceModel] | None

List[FHIRSliceModel]: The validated list of values.

Raises:

Type Description
AssertionError

If cardinality constraints are violated for any slice.

Source code in fhircraft/fhir/resources/validators.py
def validate_slicing_cardinalities(
    cls: Any, values: List[Any] | None, field_name: str
) -> List["FHIRSliceModel"] | None:
    """
    Validates the cardinalities of FHIR slices for a specific field within a FHIR resource.

    Args:
        cls (Any): The Pydantic FHIR model class.
        values (List[Any]): List of values for the field.
        field_name (str): The name of the field to validate.

    Returns:
        List[FHIRSliceModel]: The validated list of values.

    Raises:
        AssertionError: If cardinality constraints are violated for any slice.
    """
    from fhircraft.config import get_config
    from fhircraft.fhir.resources.base import FHIRSliceModel

    config = get_config()
    if config.mode == "skip":
        return values

    if values is None:
        return values
    slices = get_all_models_from_field(
        cls.model_fields[field_name], issubclass_of=FHIRSliceModel
    )
    for slice in slices:
        slice_instances_count = sum([isinstance(value, slice) for value in values])
        # Only validate cardinalities if there are instances of the slice present
        if slice_instances_count > 0:
            if slice_instances_count < slice.min_cardinality:
                message = f"Slice '{slice.__name__}' for field '{field_name}' violates its min. cardinality. \
                        Requires min. cardinality of {slice.min_cardinality}, but got {slice_instances_count}"
                if config.mode == "lenient":
                    warnings.warn(message, FhirValidationWarning, stacklevel=2)
                else:
                    raise PydanticCustomError("fhir_cardinality_violation", message)  # type: ignore
            if slice.max_cardinality is not None:
                if slice_instances_count > slice.max_cardinality:
                    message = f"Slice '{slice.__name__}' for field '{field_name}' violates its max. cardinality. \
                            Requires max. cardinality of {slice.max_cardinality}, but got {slice_instances_count}"
                    if config.mode == "lenient":
                        warnings.warn(message, FhirValidationWarning, stacklevel=2)
                    else:
                        raise PydanticCustomError("fhir_cardinality_violation", message)  # type: ignore
    return values

validate_type_choice_element

validate_type_choice_element(instance: T, field_types: List[Any], field_name_base: str, required: bool = False, non_allowed_types=[]) -> T

Validate the type choice element for a given instance.

Parameters:

Name Type Description Default
instance T

The instance to validate.

required
field_types List[Any]

List of field types to check.

required
field_name_base str

Base name of the field.

required
required bool

Whether the type choice element is required.

False
non_allowed_types List[Any] | None

List of types that are not allowed for this element (for negative checks).

[]

Returns:

Name Type Description
T T

The validated instance.

Raises:

Type Description
PydanticCustomError

If more than one value is set for the type choice element or if a non-allowed type is set.

Source code in fhircraft/fhir/resources/validators.py
def validate_type_choice_element(
    instance: T,
    field_types: List[Any],
    field_name_base: str,
    required: bool = False,
    non_allowed_types=[],
) -> T:
    """
    Validate the type choice element for a given instance.

    Args:
        instance (T): The instance to validate.
        field_types (List[Any]): List of field types to check.
        field_name_base (str): Base name of the field.
        required (bool): Whether the type choice element is required.
        non_allowed_types (List[Any] | None): List of types that are not allowed for this element (for negative checks).

    Returns:
        T: The validated instance.

    Raises:
        PydanticCustomError: If more than one value is set for the type choice element or if a non-allowed type is set.
    """

    config = get_config()
    if config.mode == "skip":
        return instance

    _field_types: List[str] = [
        (
            capitalize(field_type)
            if isinstance(field_type, str)
            else capitalize(str(field_type.__name__))
        )
        for field_type in field_types
    ]
    types_set_count = sum(
        (
            element is not None
            if not isinstance(
                element := getattr(
                    instance,
                    (field_name_base + field_type),
                    None,
                ),
                FHIRPrimitiveModel,
            )
            else element.value is not None
        )
        for field_type in _field_types
    )

    def _assert(condition: bool, message: str) -> None:
        if not condition:
            if config.mode == "lenient":
                warnings.warn(message, FhirValidationWarning, stacklevel=2)
            else:
                raise PydanticCustomError("fhir_type_choice_violation", message)  # type: ignore

    _assert(
        types_set_count <= 1,
        f"Type choice element {field_name_base}[x] can only have one value set.",
    )
    _assert(
        not required or types_set_count > 0,
        f"Type choice element {field_name_base}[x] must have one value set. Got {types_set_count}.",
    )
    all_types = [
        field.replace(field_name_base, "")
        for field in instance.__class__.model_fields
        if field.startswith(field_name_base)
    ]
    # Check that non-allowed types are not set
    non_allowed_types = non_allowed_types or [
        field_type for field_type in all_types if field_type not in _field_types
    ]
    if non_allowed_types:
        for non_allowed_type in non_allowed_types:
            field_name = field_name_base + (
                non_allowed_type
                if isinstance(non_allowed_type, str)
                else non_allowed_type.__name__
            )
            value = getattr(instance, field_name, None)
            _assert(
                value is None,
                f"Type choice element {field_name_base}[x] cannot use non-allowed type '{non_allowed_type}'. Only the following types are allowed: {', '.join(_field_types)}. Got non-allowed type '{non_allowed_type}' with value '{value}'.",
            )

    return instance