oscal.oscal_support#

oscal_support — OSCAL support-file database and singleton accessor.

Manages the local SQLite database of NIST-published OSCAL support files
(metaschemas and XML/JSON schemas) for every OSCAL version and model, and
provides the ``OSCALSupport`` class along with helpers for validation and
format conversion.

Always obtain the shared instance via ``get_support()`` (optionally configured
first with ``configure_support()`` / ``setup_support()``). This ensures a single
support instance is shared across the application. The OSCAL content classes rely
on this instance to perform validation and conversion.

More information: https://github.com/brian-ruf/oscal-class/blob/main/docs/SUPPORT_MODULE.md

Module constants:
    SUPPORT_DATABASE_DEFAULT_FILE (str): Default path to the support DB
        (``"./support/oscal_support.db"``), resolved relative to the runtime CWD.
    SUPPORT_DATABASE_DEFAULT_TYPE (str): Default database backend (``"sqlite3"``).
    COMPRESS_SUPPORT_FILES_IN_DATABASE (bool): Whether support files are stored
        compressed in the database.
    OSCAL_DEFAULT_XML_NAMESPACE (str): The NIST OSCAL XML namespace URI.
    NIST_OSCAL_EXTENSION_NAMESPACE (str): The NIST OSCAL property/extension namespace URI.
    NIST_RMF_EXTENSION_NAMESPACE (str): The NIST RMF extension namespace URI.
    OSCAL_FORMATS (list): Supported serialization formats
        (``["xml", "json", "yaml", "yml"]``).
    DEFAULT_EXCLUDE_VERSIONS (list): OSCAL release tags excluded by default
        (release candidates and milestones).
    METASCHEMA_MIN_VERSION (str): Earliest version with NIST-published resolved
        metaschema files (``"v1.1.1"``).
    INDEX_REFRESH (int): Seconds before a cached metaschema index entry is stale
        (86400 = 24 hours).
    METASCHEMA_FILE_PATTERNS (dict): Filename-suffix → support-type map for
        metaschema files.
    SCHEMA_FILE_PATTERNS (dict): Filename-suffix → support-type map for XML/JSON
        schema files.
    OSCAL_SUPPORT_TABLES (dict): Schema definitions for the support database tables
        (``oscal_versions``, ``oscal_support``, ``filecache``).
    OSCAL_DATA_TYPES (dict): Data-type registry populated at runtime from parsed
        metaschemas.

class OSCALSupport#

Access layer for the local OSCAL support-file database.

    Manages a SQLite database of NIST-published support files (metaschemas and
    XML/JSON schemas) for every OSCAL version and model, and exposes methods for
    querying supported versions/models, retrieving assets, building metaschema
    indexes, and updating content from NIST's GitHub releases.

    Prefer the module-level ``get_support()`` accessor over instantiating this
    class directly, so a single instance is shared across the application.

    Note:
        ``OSCAL_support`` is a backward-compatible alias for this class.
methoddef __init__(self, db_conn='./support/oscal_support.db', db_type='sqlite3', db_init_mode='auto', db_compress_files=True)#
Initialize OSCAL support and run startup (table checks / population).

        Args:
            db_conn (str, optional): Database connection string or file path.
                Defaults to ``SUPPORT_DATABASE_DEFAULT_FILE``.
            db_type (str, optional): Database backend type (e.g. "sqlite3").
                Defaults to ``SUPPORT_DATABASE_DEFAULT_TYPE``.
            db_init_mode (str, optional): Database initialization mode. Defaults to "auto".
                - "auto": Extract from packaged resources if the file is missing/empty,
                  otherwise use the existing file.
                - "extract": Always try to extract from resources; create empty if that fails.
                - "create": Always create an empty database from scratch.
            db_compress_files (bool, optional): Whether to store support files
                compressed in the database. Defaults to
                ``COMPRESS_SUPPORT_FILES_IN_DATABASE``.
methoddef add_asset(self, oscal_version, model_name, asset_type, content, filename=None)#
Add an asset to the support database. If the asset already exists, it will be replaced.
        This method supports both string and bytes content types.
        If the content is a string, it will be converted to bytes.
        If the content is already in bytes, it will be used as is.
        Args:
            oscal_version (str, required): The OSCAL version (e.g. "v1.0.0").
            model_name (str, required): The OSCAL model name (e.g. "system-security-plan").
            asset_type (str, required): The asset type (e.g. "xml-schema", "json-schema").
            content (str | bytes, required): The asset content; strings are encoded
                to UTF-8 bytes.
            filename (str, optional): Filename to record for the cached asset.
                Defaults to ``"{model_name}_{asset_type}"``.
        Returns:
            bool: True if the asset was added successfully, False otherwise.
methoddef asset(self, oscal_version, model_name, asset_type)#
Backward-compatible wrapper for :meth:`get_asset`.

        Args:
            oscal_version (str, required): The OSCAL version (e.g. "v1.0.0").
            model_name (str, required): The OSCAL model name (e.g. "system-security-plan").
            asset_type (str, required): The asset type (e.g. "xml-schema", "json-schema").

        Returns:
            Any: The asset content if found, otherwise None.
methoddef download_schemas(self, support_dir: 'str', fetch: 'str' = 'all') -> 'bool'#
Download XML and JSON schema files to the filesystem.

        Files are written to ``{support_dir}/{version}_schemas/`` directories and
        are not stored in the support database.

        Args:
            support_dir: Root directory under which per-version schema folders are created.
            fetch: ``"all"`` to download every known version, or a specific version
                   tag (e.g. ``"v1.2.2"``) to download only that version.
        Returns:
            True if all files were saved without error, False otherwise.
methoddef enumerate_models(self, version: 'str' = 'all') -> 'list[str]'#
Backward-compatible wrapper for :meth:`list_models`.

        Args:
            version (str, optional): The OSCAL version to enumerate models for, or
                "all". Defaults to "all".

        Returns:
            list[str]: Supported model-name strings (may be empty).
methoddef export_support_files(self, export_path='./support_files')#
Export all cached support files to a directory tree, grouped by version.

        Args:
            export_path (str, optional): The directory to export support files to.
                Defaults to "./support_files".

        Returns:
            bool: True if the export was successful, False otherwise.
methoddef get_asset(self, version, model, asset_type)#
Returns the asset for the specified OSCAL version and model name.
        Args:
            version (str): The OSCAL version (e.g., "v1.0.0").
            model (str): The OSCAL model name (e.g., "system-security-plan").
            asset_type (str): The type of asset to retrieve (e.g., "xml-schema", "json-schema").
        Returns:
            The asset content if found, None otherwise.
methoddef get_latest_version(self)#
Backward-compatible wrapper for :meth:`latest_version`.

        Returns:
            Optional[str]: The latest OSCAL version tag, or None if none are loaded.
methoddef get_metaschema_index(self, version: 'str', model: 'str') -> 'dict | None'#
Return the parsed metaschema index dict for the given OSCAL version and model.

        Results are held in the module-level ``_metaschema_index_cache`` so that
        only one copy of each index lives in memory and survives across calls.
        A cached entry is reused until it is older than :data:`INDEX_REFRESH`
        seconds (24 hours), at which point it is refreshed from the database.

        Args:
            version: OSCAL version string, e.g. ``"v1.1.3"``.
            model:   OSCAL model name, e.g. ``"catalog"``.

        Returns:
            The model-specific index dict on success, or ``None`` when the index
            is unavailable.
methoddef is_model_valid(self, model_name, version='all') -> 'bool'#
Backward-compatible wrapper for :meth:`is_valid_model`.

        Args:
            model_name (str, required): The OSCAL model name to check.
            version (str, optional): The OSCAL version to check against, or "all".
                Defaults to "all".

        Returns:
            bool: True if the model is valid for the version, False otherwise.
methoddef is_valid_model(self, model, version='all') -> 'bool'#
Check if the specified OSCAL model is valid for the given version.
        Args:
            model (str): The OSCAL model name to check (e.g., "system-security-plan").
            version (str): The OSCAL version to check against (e.g., "v1.0.0").
        Returns:
            bool: True if the model is valid for the specified version, False otherwise.
methoddef is_valid_version(self, version) -> 'bool'#
Check if the specified OSCAL version is valid and supported.
        Args:
            version (str): The OSCAL version to check (e.g., "v1.0.0").
        Returns:
            bool: True if the version is valid and supported, False otherwise.
methoddef latest_version(self)#
Return the latest supported OSCAL version.

        Returns:
            Optional[str]: The highest OSCAL version tag available in the support
                database, or None if none are loaded.
methoddef list_models(self, version: 'str' = 'all') -> 'list[str]'#
Enumerate the supported models for a given OSCAL version.
        Args:
            version (str): The OSCAL version to enumerate models for (e.g., "v1.0.0").
        Returns:
            list[str]: A list of model-name strings supported for the specified OSCAL version
                       (may be empty).
methoddef load_file(self, name, binary=False, *, as_bytes=None)#
Load a file bundled in the ``oscal.data`` package resources, with caching.

        Args:
            name (str, required): Filename of the resource within ``oscal.data``.
            binary (bool, optional): If True, return raw bytes; otherwise return
                UTF-8 decoded text. Defaults to False.
            as_bytes (bool, optional): Keyword-only alias for ``binary``; overrides
                it when provided. Defaults to None.

        Returns:
            str | bytes | None: The file contents (text or bytes), or None on failure.
methoddef remove_version(self, version: 'str') -> 'bool'#
Delete all support content for a single OSCAL version from the database.

        This is a standalone deletion for withdrawing a version that no longer
        belongs in the local support set (e.g. one removed upstream). Unlike
        :meth:`update`, it performs no GitHub fetch — it only deletes local
        content and keeps in-memory state consistent with the database.

        Args:
            version (str): The OSCAL version tag to remove (e.g. "v1.2.3"). The
                leading "v" is required and the tag is matched case-insensitively.

        Returns:
            bool: True if the version was found and deleted, False if the tag was
                invalid, not present, or the deletion failed.
methoddef startup(self, check_for_updates=False, refresh_all=False)#
Perform startup tasks required to provide OSCAL support.

        Ensures the support database has the required tables and data, populating
        it from NIST's GitHub releases when empty, and sets ``self.ready``.

        Args:
            check_for_updates (bool, optional): Reserved flag to check for newer
                OSCAL versions during startup. Defaults to False.
            refresh_all (bool, optional): Reserved flag to force a full refresh of
                all support content. Defaults to False.

        Returns:
            bool: True if the support capability is ready, False otherwise.

        Process:
        1 Check for tables
          - If tables do not exist:
            - create tables
            - set state to "empty"
          - If tables exist, check for data
            - If no data, set state to "empty"
            - If data exists, set state to "populated"
        2 If state is "empty", check for connection to GitHub
         - If cannot connect to GitHub, EXIT (cannot proceed)
         - If connected to GitHub, update database
           - If update fails, EXIT (cannot proceed)
           - If update succeeds, set state to "populated"
        3 If state is "populated" set self.ready to True
methoddef supported(self, oscal_version, assets)#
Check whether the specified OSCAL version and assets are supported.

        Note:
            Currently not implemented; always returns False.

        Args:
            oscal_version (str, required): The OSCAL version to check (e.g. "v1.0.0").
            assets (list, required): The asset types to check for.

        Returns:
            bool: True if the version and assets are supported, False otherwise.
methoddef update(self, mode='new', fetch=None, save_to_fs=False)#
Update OSCAL support content based on a fetch directive.

        Args:
            mode (str, optional): The fetch directive. Defaults to "new".
                - "all": Clear and re-fetch all OSCAL versions and support files.
                - "latest"/"new": Check for new OSCAL versions and fetch any found.
                - "vX.Y.Z": Clear and re-fetch a specific OSCAL version.
            fetch (str, optional): Legacy alias for ``mode``; when provided it
                overrides ``mode``. Defaults to None.
            save_to_fs (bool, optional): When True, also emit the parsed
                metaschema index files to the local file system in addition to
                updating the database. When False (default), only the database
                is updated. Defaults to False.

        Returns:
            bool: True if the update was successful, False otherwise.
methoddef view_detail(self, version: 'str', model: 'str', format: 'str', reference_uuid: 'str') -> 'str'#
Return an HTML ``<div>`` detail view of a single metaschema node.

        Given a node's reference id (as produced by :meth:`view_outline`), returns its
        formal name and description, a format-appropriate representation, data type and
        regex (where available), constraints, and its immediate parent and children —
        each parent/child clickable by its own reference id.

        Args:
            version (str, required): OSCAL version, e.g. ``"v1.1.3"``.
            model (str, required): OSCAL model name, e.g. ``"catalog"``.
            format (str, required): ``"xml"``, ``"json"``, or ``"yaml"``.
            reference_uuid (str, required): The node reference id to describe.

        Returns:
            str: A detail ``<div>`` fragment, or a ``<div class="ms-error">`` when the
                model/version/format or reference is unknown.
methoddef view_outline(self, version: 'str', model: 'str', format: 'str') -> 'str'#
Return an HTML ``<div>`` outline of a model's metaschema structure.

        The outline is a clickable tree rendered in the requested format's syntax
        (``"xml"``, ``"json"``, or ``"yaml"``), annotated with data types and
        cardinality. Each element links to its node by a stable reference id, for use
        with :meth:`view_detail`. Intended for a front-end: the HTML is a fragment
        (wrapped in a ``<div>``), never a full page.

        Args:
            version (str, required): OSCAL version, e.g. ``"v1.1.3"``.
            model (str, required): OSCAL model name, e.g. ``"catalog"``.
            format (str, required): ``"xml"``, ``"json"``, or ``"yaml"``.

        Returns:
            str: An outline ``<div>`` fragment, or a ``<div class="ms-error">`` when
                the model/version/format is unknown or the index is unavailable.

class OSCAL_support#

Access layer for the local OSCAL support-file database.

    Manages a SQLite database of NIST-published support files (metaschemas and
    XML/JSON schemas) for every OSCAL version and model, and exposes methods for
    querying supported versions/models, retrieving assets, building metaschema
    indexes, and updating content from NIST's GitHub releases.

    Prefer the module-level ``get_support()`` accessor over instantiating this
    class directly, so a single instance is shared across the application.

    Note:
        ``OSCAL_support`` is a backward-compatible alias for this class.
methoddef __init__(self, db_conn='./support/oscal_support.db', db_type='sqlite3', db_init_mode='auto', db_compress_files=True)#
Initialize OSCAL support and run startup (table checks / population).

        Args:
            db_conn (str, optional): Database connection string or file path.
                Defaults to ``SUPPORT_DATABASE_DEFAULT_FILE``.
            db_type (str, optional): Database backend type (e.g. "sqlite3").
                Defaults to ``SUPPORT_DATABASE_DEFAULT_TYPE``.
            db_init_mode (str, optional): Database initialization mode. Defaults to "auto".
                - "auto": Extract from packaged resources if the file is missing/empty,
                  otherwise use the existing file.
                - "extract": Always try to extract from resources; create empty if that fails.
                - "create": Always create an empty database from scratch.
            db_compress_files (bool, optional): Whether to store support files
                compressed in the database. Defaults to
                ``COMPRESS_SUPPORT_FILES_IN_DATABASE``.
methoddef add_asset(self, oscal_version, model_name, asset_type, content, filename=None)#
Add an asset to the support database. If the asset already exists, it will be replaced.
        This method supports both string and bytes content types.
        If the content is a string, it will be converted to bytes.
        If the content is already in bytes, it will be used as is.
        Args:
            oscal_version (str, required): The OSCAL version (e.g. "v1.0.0").
            model_name (str, required): The OSCAL model name (e.g. "system-security-plan").
            asset_type (str, required): The asset type (e.g. "xml-schema", "json-schema").
            content (str | bytes, required): The asset content; strings are encoded
                to UTF-8 bytes.
            filename (str, optional): Filename to record for the cached asset.
                Defaults to ``"{model_name}_{asset_type}"``.
        Returns:
            bool: True if the asset was added successfully, False otherwise.
methoddef asset(self, oscal_version, model_name, asset_type)#
Backward-compatible wrapper for :meth:`get_asset`.

        Args:
            oscal_version (str, required): The OSCAL version (e.g. "v1.0.0").
            model_name (str, required): The OSCAL model name (e.g. "system-security-plan").
            asset_type (str, required): The asset type (e.g. "xml-schema", "json-schema").

        Returns:
            Any: The asset content if found, otherwise None.
methoddef download_schemas(self, support_dir: 'str', fetch: 'str' = 'all') -> 'bool'#
Download XML and JSON schema files to the filesystem.

        Files are written to ``{support_dir}/{version}_schemas/`` directories and
        are not stored in the support database.

        Args:
            support_dir: Root directory under which per-version schema folders are created.
            fetch: ``"all"`` to download every known version, or a specific version
                   tag (e.g. ``"v1.2.2"``) to download only that version.
        Returns:
            True if all files were saved without error, False otherwise.
methoddef enumerate_models(self, version: 'str' = 'all') -> 'list[str]'#
Backward-compatible wrapper for :meth:`list_models`.

        Args:
            version (str, optional): The OSCAL version to enumerate models for, or
                "all". Defaults to "all".

        Returns:
            list[str]: Supported model-name strings (may be empty).
methoddef export_support_files(self, export_path='./support_files')#
Export all cached support files to a directory tree, grouped by version.

        Args:
            export_path (str, optional): The directory to export support files to.
                Defaults to "./support_files".

        Returns:
            bool: True if the export was successful, False otherwise.
methoddef get_asset(self, version, model, asset_type)#
Returns the asset for the specified OSCAL version and model name.
        Args:
            version (str): The OSCAL version (e.g., "v1.0.0").
            model (str): The OSCAL model name (e.g., "system-security-plan").
            asset_type (str): The type of asset to retrieve (e.g., "xml-schema", "json-schema").
        Returns:
            The asset content if found, None otherwise.
methoddef get_latest_version(self)#
Backward-compatible wrapper for :meth:`latest_version`.

        Returns:
            Optional[str]: The latest OSCAL version tag, or None if none are loaded.
methoddef get_metaschema_index(self, version: 'str', model: 'str') -> 'dict | None'#
Return the parsed metaschema index dict for the given OSCAL version and model.

        Results are held in the module-level ``_metaschema_index_cache`` so that
        only one copy of each index lives in memory and survives across calls.
        A cached entry is reused until it is older than :data:`INDEX_REFRESH`
        seconds (24 hours), at which point it is refreshed from the database.

        Args:
            version: OSCAL version string, e.g. ``"v1.1.3"``.
            model:   OSCAL model name, e.g. ``"catalog"``.

        Returns:
            The model-specific index dict on success, or ``None`` when the index
            is unavailable.
methoddef is_model_valid(self, model_name, version='all') -> 'bool'#
Backward-compatible wrapper for :meth:`is_valid_model`.

        Args:
            model_name (str, required): The OSCAL model name to check.
            version (str, optional): The OSCAL version to check against, or "all".
                Defaults to "all".

        Returns:
            bool: True if the model is valid for the version, False otherwise.
methoddef is_valid_model(self, model, version='all') -> 'bool'#
Check if the specified OSCAL model is valid for the given version.
        Args:
            model (str): The OSCAL model name to check (e.g., "system-security-plan").
            version (str): The OSCAL version to check against (e.g., "v1.0.0").
        Returns:
            bool: True if the model is valid for the specified version, False otherwise.
methoddef is_valid_version(self, version) -> 'bool'#
Check if the specified OSCAL version is valid and supported.
        Args:
            version (str): The OSCAL version to check (e.g., "v1.0.0").
        Returns:
            bool: True if the version is valid and supported, False otherwise.
methoddef latest_version(self)#
Return the latest supported OSCAL version.

        Returns:
            Optional[str]: The highest OSCAL version tag available in the support
                database, or None if none are loaded.
methoddef list_models(self, version: 'str' = 'all') -> 'list[str]'#
Enumerate the supported models for a given OSCAL version.
        Args:
            version (str): The OSCAL version to enumerate models for (e.g., "v1.0.0").
        Returns:
            list[str]: A list of model-name strings supported for the specified OSCAL version
                       (may be empty).
methoddef load_file(self, name, binary=False, *, as_bytes=None)#
Load a file bundled in the ``oscal.data`` package resources, with caching.

        Args:
            name (str, required): Filename of the resource within ``oscal.data``.
            binary (bool, optional): If True, return raw bytes; otherwise return
                UTF-8 decoded text. Defaults to False.
            as_bytes (bool, optional): Keyword-only alias for ``binary``; overrides
                it when provided. Defaults to None.

        Returns:
            str | bytes | None: The file contents (text or bytes), or None on failure.
methoddef remove_version(self, version: 'str') -> 'bool'#
Delete all support content for a single OSCAL version from the database.

        This is a standalone deletion for withdrawing a version that no longer
        belongs in the local support set (e.g. one removed upstream). Unlike
        :meth:`update`, it performs no GitHub fetch — it only deletes local
        content and keeps in-memory state consistent with the database.

        Args:
            version (str): The OSCAL version tag to remove (e.g. "v1.2.3"). The
                leading "v" is required and the tag is matched case-insensitively.

        Returns:
            bool: True if the version was found and deleted, False if the tag was
                invalid, not present, or the deletion failed.
methoddef startup(self, check_for_updates=False, refresh_all=False)#
Perform startup tasks required to provide OSCAL support.

        Ensures the support database has the required tables and data, populating
        it from NIST's GitHub releases when empty, and sets ``self.ready``.

        Args:
            check_for_updates (bool, optional): Reserved flag to check for newer
                OSCAL versions during startup. Defaults to False.
            refresh_all (bool, optional): Reserved flag to force a full refresh of
                all support content. Defaults to False.

        Returns:
            bool: True if the support capability is ready, False otherwise.

        Process:
        1 Check for tables
          - If tables do not exist:
            - create tables
            - set state to "empty"
          - If tables exist, check for data
            - If no data, set state to "empty"
            - If data exists, set state to "populated"
        2 If state is "empty", check for connection to GitHub
         - If cannot connect to GitHub, EXIT (cannot proceed)
         - If connected to GitHub, update database
           - If update fails, EXIT (cannot proceed)
           - If update succeeds, set state to "populated"
        3 If state is "populated" set self.ready to True
methoddef supported(self, oscal_version, assets)#
Check whether the specified OSCAL version and assets are supported.

        Note:
            Currently not implemented; always returns False.

        Args:
            oscal_version (str, required): The OSCAL version to check (e.g. "v1.0.0").
            assets (list, required): The asset types to check for.

        Returns:
            bool: True if the version and assets are supported, False otherwise.
methoddef update(self, mode='new', fetch=None, save_to_fs=False)#
Update OSCAL support content based on a fetch directive.

        Args:
            mode (str, optional): The fetch directive. Defaults to "new".
                - "all": Clear and re-fetch all OSCAL versions and support files.
                - "latest"/"new": Check for new OSCAL versions and fetch any found.
                - "vX.Y.Z": Clear and re-fetch a specific OSCAL version.
            fetch (str, optional): Legacy alias for ``mode``; when provided it
                overrides ``mode``. Defaults to None.
            save_to_fs (bool, optional): When True, also emit the parsed
                metaschema index files to the local file system in addition to
                updating the database. When False (default), only the database
                is updated. Defaults to False.

        Returns:
            bool: True if the update was successful, False otherwise.
methoddef view_detail(self, version: 'str', model: 'str', format: 'str', reference_uuid: 'str') -> 'str'#
Return an HTML ``<div>`` detail view of a single metaschema node.

        Given a node's reference id (as produced by :meth:`view_outline`), returns its
        formal name and description, a format-appropriate representation, data type and
        regex (where available), constraints, and its immediate parent and children —
        each parent/child clickable by its own reference id.

        Args:
            version (str, required): OSCAL version, e.g. ``"v1.1.3"``.
            model (str, required): OSCAL model name, e.g. ``"catalog"``.
            format (str, required): ``"xml"``, ``"json"``, or ``"yaml"``.
            reference_uuid (str, required): The node reference id to describe.

        Returns:
            str: A detail ``<div>`` fragment, or a ``<div class="ms-error">`` when the
                model/version/format or reference is unknown.
methoddef view_outline(self, version: 'str', model: 'str', format: 'str') -> 'str'#
Return an HTML ``<div>`` outline of a model's metaschema structure.

        The outline is a clickable tree rendered in the requested format's syntax
        (``"xml"``, ``"json"``, or ``"yaml"``), annotated with data types and
        cardinality. Each element links to its node by a stable reference id, for use
        with :meth:`view_detail`. Intended for a front-end: the HTML is a fragment
        (wrapped in a ``<div>``), never a full page.

        Args:
            version (str, required): OSCAL version, e.g. ``"v1.1.3"``.
            model (str, required): OSCAL model name, e.g. ``"catalog"``.
            format (str, required): ``"xml"``, ``"json"``, or ``"yaml"``.

        Returns:
            str: An outline ``<div>`` fragment, or a ``<div class="ms-error">`` when
                the model/version/format is unknown or the index is unavailable.
functiondef configure_support(support_file='./support/oscal_support.db', db_init_mode='auto', *, db_path: 'Optional[str]' = None, init_mode: 'Optional[str]' = None)#
Configure and create the shared OSCAL support instance.

    Call this before ``get_support()`` and before any OSCAL content is loaded when
    non-default settings are needed. If the shared instance already exists, it is
    returned unchanged.

    Args:
        support_file (str, optional): Path to the support database file.
            Defaults to ``SUPPORT_DATABASE_DEFAULT_FILE``.
        db_init_mode (str, optional): Database initialization mode — ``"auto"``,
            ``"extract"``, or ``"create"``. Defaults to ``"auto"``.
        db_path (str, optional): Keyword-only alias for ``support_file``; overrides
            it when provided.
        init_mode (str, optional): Keyword-only alias for ``db_init_mode``;
            overrides it when provided.

    Returns:
        OSCALSupport: The shared support instance.
functiondef get_support()#
Return the shared OSCAL support instance, creating it if necessary.

    Creates the instance with default settings (via ``configure_support()``) if it
    does not already exist.

    Returns:
        OSCALSupport: The shared support instance.
functiondef setup_support(support_file='./support/oscal_support.db', db_init_mode='auto')#
Compatibility wrapper around ``configure_support()`` for update utility scripts.

    Args:
        support_file (str, optional): Path to the support database file.
            Defaults to ``SUPPORT_DATABASE_DEFAULT_FILE``.
        db_init_mode (str, optional): Database initialization mode
            (``"auto"``, ``"extract"``, or ``"create"``). Defaults to ``"auto"``.

    Returns:
        OSCALSupport: The shared support instance.

oscal.oscal_content#

oscal_content — OSCAL base class and shared content operations.

Defines the ``OSCAL`` base class used by all eight model classes for creating,
loading, manipulating, validating, and format-converting OSCAL content. All
published OSCAL versions, formats, and models can be validated and converted;
newly published versions can be "learned" by updating the OSCAL Support
database. This module also provides import-resolution machinery, dict-building
helpers (props/links/resources), and Metapath/JSON query support.

See https://github.com/brian-ruf/oscal-class for more details.

Module constants:
    INDENT (int): Number of spaces used for indentation in pretty-printed output.
    OSCAL_DEFAULT_XML_NAMESPACE (str): The NIST OSCAL XML namespace URI
        (re-exported from ``oscal_support``).
    OSCAL_FORMATS (list): Supported serialization formats
        (re-exported from ``oscal_support``).
    OSCAL_DATATYPES (dict): OSCAL Metaschema data type definitions
        (re-exported from ``oscal_datatypes``).

class ContentState#

Progressive content-processing state; each level implies all prior levels passed.

    Members (ordered by increasing progress):
        NONE (int): -1 — no content / uninitialized.
        NOT_AVAILABLE (int): 0 — content could not be acquired.
        ACQUIRED (int): 1 — content was acquired (non-empty string).
        WELL_FORMED (int): 2 — content is well-formed XML, JSON, or YAML.
        VALID (int): 3 — content passes OSCAL schema validation (minimum for view/edit).
        IMPORTS_RESOLVED (int): 4 — all imported OSCAL documents resolved successfully.
No public members.

class ImportFailure#

Structured record of a failed import, carrying enough context for a retry attempt.

    Retry sources the calling module may supply:
        - A URI fragment (#uuid) pointing to a back-matter resource
        - A full URI identifying an alternate location for the content
        - The content itself as an XML, JSON, or YAML string
propertyproperty is_fragment_ref#
True when the original import href is a back-matter fragment reference.

class ImportFailureCode#

Typed reason codes describing why an OSCAL import could not be resolved.

    Grouped by failure category — fragment/back-matter, full-URI/file,
    content, and duplicate/retry. Members:
        FRAGMENT_INVALID_UUID (str): Fragment reference is not a valid UUID.
        RESOURCE_NOT_FOUND (str): No back-matter resource matches the UUID.
        RESOURCE_NO_VIABLE_CONTENT (str): Resource has neither rlinks nor base64 content.
        LOCAL_NOT_FOUND (str): Local file was not found.
        REMOTE_UNREACHABLE (str): Remote host could not be reached.
        REMOTE_AUTH_REQUIRED (str): Remote resource requires authentication.
        REMOTE_UNSUPPORTED (str): URI scheme is not supported.
        CONTENT_EMPTY (str): Source returned no content.
        CONTENT_INVALID (str): Content is not valid OSCAL.
        ALREADY_IMPORTED (str): Retry href resolves to a file already loaded elsewhere.
No public members.

class ImportLoadError#

Exception carrying a typed import failure code from ``load_source()`` to ``resolve_imports()``.

    Attributes:
        code (ImportFailureCode): The typed reason the import failed.
        uri (str): The URI that failed to load.
methoddef __init__(self, code: 'ImportFailureCode', uri: 'str', message: 'str' = '')#
Initialize the error.

        Args:
            code (ImportFailureCode, required): The typed import failure reason.
            uri (str, required): The URI that failed to load.
            message (str, optional): Human-readable detail; a default is derived from
                ``code`` and ``uri`` when omitted.

class ImportState#

Resolution state of a single import entry in an OSCAL document's import_list.

    Members:
        READY (str): "ready" — content is valid and loaded.
        NOT_LOADED (str): "not-loaded" — content has not been loaded.
        INVALID (str): "invalid" — content could not be loaded or failed validation.
        EXPIRED (str): "expired" — content is valid but the cached copy has expired.
        DUPLICATE (str): "duplicate" — the resolved href is already loaded by an earlier import.
        IGNORED (str): "ignored" — the caller explicitly chose to ignore this import.
        CYCLIC (str): "cyclic" — this import resolves to one of its own ancestors; the
            ancestor stays valid and recursion stops here to prevent an infinite loop.
No public members.

class OSCAL#

Base class for all OSCAL model documents.

    Provides loading, saving, validation, format conversion (XML/JSON/YAML),
    import resolution, and query support shared by every OSCAL model. Content is
    held internally as a JSON-primary dict (``self._dict``) with an XML tree
    (``self._tree``) maintained for conversion. Do not instantiate directly; use
    the factory classmethods ``load``, ``loads``, or ``new``, or a model subclass.

        Attributes (Content Location):
            href_original: The original href as provided (e.g., in an import statement)
            is_valid_href: True if the href is accessible and the content was loaded successfully
            href         : Working href (may differ from href_original after redirect/retry)

        Attributes (Class States):
            is_valid    : True if the content passed OSCAL validation, False otherwise
            is_local    : True if the source is a local file, False if it's remote (http/https)
            is_cached   : True if remote content has a local cache copy, False otherwise
            is_canonical: True if this is canonical/published content; forces read-only
            is_read_only: True if the content may not be mutated (property; True whenever
                          is_canonical is set, else reflects the loader/caller flag)
            is_unsaved  : True if there are unsaved modifications, False otherwise

        Attributes (Caching and Expiration):
            loaded: Timestamp of when the content was loaded (datetime object)
            ttl: Time to live for cached content in seconds (0 or less means never expire)

        Attributes (Content and Summary):
            content        : The raw content as a string in its original format
            original_format: The original format of the content (xml, json, yaml)
            model          : The identified OSCAL model (e.g., "catalog", "profile")
            oscal_version  : The OSCAL version from the metadata (if available)
            last_modified  : The last modified date from the metadata (if available)
            title          : The title from the metadata (if available)
            published      : The publication date from the metadata (if available)
            version        : The version from the metadata (if available)
            remarks        : Any remarks from the metadata (if available)

        Attributes (Processing Objects):
            self.import_list = [] # An array of dictionaries representing imported OSCAL content

        Properties:
            is_editable: True if the content can be modified, False otherwise
classmethodclassmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)#
Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.
methoddef append_child(self, path: 'str', child: 'dict') -> 'dict | None'#
Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.
methoddef append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'#
Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.
methoddef dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'#
Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.
methoddef dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'#
Serialize the current content to a string in the specified format.
        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.
propertyproperty duplicate_imports#
Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.
propertyproperty failed_imports#
Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.
methoddef find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'#
Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id) or ``party`` (by uuid), or a ``control``/``group``/``param``/``part`` (by id).
        This walks ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.
classmethodclassmethod from_string(cls, content: 'str', *, href: 'str | None' = None)#
Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).
methoddef get_oscal_object(self, uuid, _seen=None)#
Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.
methoddef get_parameter_by_id(self, param_id: 'str') -> 'Optional[dict]'#
Return a safe copy of a parameter defined anywhere in scope, or None.

        Searches this document and its import tree for a ``param`` with the given id —
        covering parameters defined at control, group, or catalog level (and reached
        through imported catalogs/profiles). Subclasses may override to prefer resolved
        content.
methoddef ignore_import(self, href: 'str') -> 'bool'#
Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.
propertyproperty import_tree#
Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.
propertyproperty imports_resolved#
bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).
methoddef initial_validation(self, content: 'str') -> 'bool'#
Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.
propertyproperty is_acquired#
bool: True once content has been acquired (``content_state >= ACQUIRED``).
propertyproperty is_cache_expired#
True when remote cached content has exceeded its TTL.
propertyproperty is_editable#
Can this content be modified?
propertyproperty is_fresh#
True when content is local or cached and within its TTL.
propertyproperty is_read_only#
bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.
propertyproperty is_remote#
bool: True when the content originates from a remote source (not a local file).
propertyproperty is_stale#
True when remote cached content has exceeded its TTL.
propertyproperty is_valid#
bool: True when content passes OSCAL validation (``content_state >= VALID``).
propertyproperty is_well_formed#
bool: True when content is well-formed (``content_state >= WELL_FORMED``).
propertyproperty json#
Return the content as a JSON string.
methoddef json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
classmethodclassmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)#
Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior. Use ``loads(...)``
        for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        resolution and fallback sources.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.
classmethodclassmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)#
Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.
classmethodclassmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')#
Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.
classmethodclassmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)#
Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.
propertyproperty origin_state#
Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.
methoddef put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'#
Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.
methoddef query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
methoddef reachable_ids(self, _seen=None) -> 'set'#
Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.
methoddef rebuild_import_tree(self) -> 'dict'#
Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.
methoddef remove_import(self, href: 'str') -> 'bool'#
Remove an import entry from both import_list and the document content.

        The import *statement* is deleted from ``self._dict``, placing the
        document in an edited and unsaved state.  Any back-matter resource
        referenced by the import via a URI fragment (``href="#uuid"``) is
        intentionally preserved — only the import element itself is removed.

        The cached import_tree is updated in-place (same object, one node
        shorter).  content_state is recomputed: if the removed entry was the
        last thing blocking resolution, content_state advances to
        IMPORTS_RESOLVED.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and IGNORED are preferred over
        INVALID which is preferred over READY — so the problematic entry is
        always targeted.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and removed, False if not found or the
            content is read-only.
methoddef resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'#
Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-assessment-plan/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.
methoddef retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef set_metadata(self, content: 'dict' = {}) -> 'bool'#
Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.
propertyproperty unresolved_imports#
Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.
methoddef validate(self, format: 'str' = '') -> 'bool'#
Validate OSCAL content against the metaschema index in sequenced phases.

        Phases (each recorded in ``validation_status``):
          structure      – all required fields and hierarchy are present
          data-types     – every leaf value matches its declared OSCAL datatype
          allowed-values – every constrained value is within its enumerated set
          cardinality    – every array satisfies its min-occurs/max-occurs bounds
          choice         – every choice is mutually exclusive (at most one member present) and has a member when one is required

        ``validation_status["well-formed"]`` is set by ``initial_validation()``, not here.
        All phases always run regardless of earlier failures, giving a complete picture
        of issues in a single call.  The format argument is accepted for API
        compatibility but does not alter the validation path — ``_dict`` is always the
        authoritative representation.

        Returns True only when every phase passes (content_state reaches VALID).
methoddef walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')#
Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None
propertyproperty xml#
Return the content as an XML string, converting from dict if necessary.
propertyproperty yaml#
Return the content as a YAML string.

class OriginState#

Origin/freshness state of a document's source (not progressive).

    Freshness is time-based and computed on demand rather than stored.

    Members:
        LOCAL (str): "local" — local file system source; always accessible.
        REMOTE_UNCACHED (str): "remote-uncached" — remote source with no local cache copy.
        REMOTE_FRESH (str): "remote-fresh" — remote source cached and within its TTL.
        REMOTE_STALE (str): "remote-stale" — remote source cached but past its TTL.
No public members.

class OscalRef#

A single OSCAL source reference: an href with optional media type and hashes.

    Attributes:
        href (str): The reference target (URI or path).
        media_type (str | None): Optional media type of the target.
        hashes (list[dict] | None): Optional integrity hashes for the target.
        source_type (str): Classified source type (set by classification; not an init arg).
        source_scheme (str): URI scheme of the source (not an init arg).
        source_supported (bool): Whether the source scheme can be fetched (not an init arg).
No public members.

class _ReadableSource#

Protocol for file-like objects that provide read().
methoddef read(self, size: 'int' = -1) -> 'Any'#
Read up to ``size`` bytes/characters from the source (``-1`` reads all).
functiondef append_prop(parent_obj: 'dict', prop: 'dict') -> 'dict'#
Append a single prop dict to ``parent_obj["props"]``.

    Args:
        parent_obj (dict, required): OSCAL JSON object that will receive the prop.
        prop (dict, required): Property dict. Required keys: "name", "value".
            Optional keys: "uuid", "ns", "class", "group", "remarks".

    Returns:
        dict: The appended prop entry (filtered to recognized keys).
functiondef append_props(parent_obj: 'dict', props: 'list') -> 'None'#
Append multiple prop dicts to ``parent_obj["props"]``.

    Args:
        parent_obj (dict, required): OSCAL JSON object that will receive the props.
        props (list, required): Property dicts, each with at minimum "name" and "value".

    Returns:
        None
functiondef append_resource(oscal_obj: 'OSCAL', uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'#
Appends a resource to the back-matter section of the OSCAL JSON content.

    Args:
        oscal_obj:   The OSCAL document to modify.
        uuid:        Resource UUID; generated if not supplied.
        title:       Optional resource title.
        description: Optional resource description.
        props:       Optional list of prop dicts (see append_prop).
        rlinks:      Optional list of rlink dicts with "href" and optional "media-type"/"hashes".
        base64:      Not yet implemented; a warning is logged if supplied.
        remarks:     Optional remarks (OSCAL markup-multiline / markdown string).

    Returns:
        dict | None: The appended resource dict, or None on error.
functiondef classify_source(ref: 'OscalRef', only_oscal: 'bool' = False) -> 'bool'#
Classify a source reference by path/URI type and Python stdlib accessibility.

    Sets the ``source_type``, ``source_scheme``, and ``source_supported`` fields on
    ``ref`` in place. Classification intentionally does not use file extensions,
    because many valid content endpoints (e.g. APIs) lack predictable suffixes.

    Args:
        ref (OscalRef, required): The reference to classify; mutated in place.
        only_oscal (bool, optional): Reserved for future content-shape validation;
            currently does not affect classification. Defaults to False.

    Returns:
        bool: True if the reference was classified (even if unsupported), False only
            when the href is empty.
functiondef create_new_oscal_content(model_name: 'str', title: 'str', version: 'str' = '', published: 'str' = '', format: 'str' = 'xml') -> 'Optional[OSCAL]'#
Returns a validated base OSCAL instance loaded from a template.
    Currently this is based on loading a template file from package data.
    In the future, this should be generated based on the latest metaschema definition.

    The supplied ``title`` (and ``version``/``published`` when given) overwrite the
    template's placeholder metadata, so ``Catalog.new("X")`` / ``Profile.new("X")`` set
    the document title as expected.

    The returned instance is always a base OSCAL object. Callers that need a
    specific model subclass (e.g. Catalog, Profile) are responsible for
    reassigning __class__ and calling _init_common() afterward.

    Args:
        model_name (str): The OSCAL model name (e.g., "catalog", "system-security-plan").
        title (str): The title for the new OSCAL content.
        version (str): Optional content version.
        published (str): Optional publication date.
        format (str): The desired format for the new content ("xml", "json", "yaml"). Defaults to "xml".

    Returns:
        Optional[OSCAL]: A base OSCAL instance loaded from template, or None on failure.
functiondef current_actor() -> "'str | None'"#
Return the current actor (view/session) id, or None when unset.

    Returns:
        str | None: The actor id activated by :func:`use_actor`, else None.
functiondef get_props(parent_obj: 'dict', name: 'str | None' = None, uuid: 'str | None' = None, ns: 'str' = 'http://csrc.nist.gov/ns/oscal', class_: 'str | None' = None, group: 'str | None' = None) -> 'list'#
Retrieve matching prop dicts from ``parent_obj["props"]``.

    Either ``name`` or ``uuid`` must be supplied. The return value is always a
    list — empty when nothing matches (or when the required parameters are
    missing) — never ``None``.

    An absent ``ns`` on a prop is treated as the OSCAL default namespace
    (``_OSCAL_NS``), matching the way ``ns`` defaults on this function's own
    ``ns`` parameter. Correct default-``ns`` handling is essential: a prop with
    no ``ns`` is considered to be in the OSCAL namespace.

    Matching behaviour:
      * ``uuid`` supplied: every prop whose ``uuid`` equals ``uuid`` is
        returned. If any descriptor parameter (``name``, a non-default ``ns``,
        ``class_`` or ``group``) is also supplied and does not match a returned
        prop, a warning is logged, but the prop is still returned.
      * ``uuid`` not supplied: ``name`` is required. Props are matched on
        ``name`` **and** effective ``ns``. When ``class_`` and/or ``group`` are
        supplied they must also match. When ``class_``/``group`` are *not*
        supplied, all name+ns matches are returned, ordered best match first:
        props carrying fewer of the un-queried qualifiers (``class``/``group``)
        sort ahead of more-specific props. Document order is preserved among
        equally specific matches.

    Args:
        parent_obj (dict, required): OSCAL JSON object holding a ``props`` list.
        name (str, optional): Prop ``name`` to match. Required if ``uuid`` is
            not given.
        uuid (str, optional): Prop ``uuid`` to match. Required if ``name`` is
            not given.
        ns (str, optional): Namespace to match; defaults to ``_OSCAL_NS``.
        class_ (str, optional): Prop ``class`` to match. Maps to the ``"class"``
            key (``class`` is a reserved word in Python).
        group (str, optional): Prop ``group`` to match.

    Returns:
        list: Matching prop dicts (possibly empty), ordered best match first.
functiondef if_update_successful(fn)#
Decorator marking content dirty after a successful mutation.

    Wraps a mutation method; when it returns a non-None result, sets
    ``self.is_unsaved = True`` and updates ``self.last_modified``.

    Args:
        fn (Callable, required): The mutation method to wrap.

    Returns:
        Callable: The wrapped method.
functiondef load_content(source: 'str | dict | OscalRef | list', media_type: 'str' = '', only_oscal: 'bool' = False, cache_directive: "'CacheDirective | None'" = None) -> 'str'#
Load content from one or more sources and return the first successful payload.

    Args:
        source (str | dict | OscalRef | list, required): The source(s) to load, as a
            URI/path string, reference dict, ``OscalRef``, or a fallback list of these.
        media_type (str, optional): Expected media type hint. Defaults to "".
        only_oscal (bool, optional): When True, restrict acceptance to OSCAL content.
            Defaults to False.
        cache_directive (CacheDirective | None, optional): Caching directive applied
            to remote fetches. Defaults to the standard 24h behavior.

    Returns:
        str: The first successfully loaded content payload, or "" if none load and no
            typed error was raised.

    Raises:
        ImportLoadError: When a source fails with a typed reason (the last error is
            re-raised when every source in a list fails).
functiondef load_source(ref: 'OscalRef', cache_directive: "'CacheDirective | None'" = None) -> 'str'#
Fetch or read content from a classified ``OscalRef``.

    Args:
        ref (OscalRef, required): A reference that has already been classified
            (via :func:`classify_source`) to set its source type/scheme.
        cache_directive (CacheDirective | None, optional): Caching directive applied
            to remote (http/https) fetches. Defaults to the standard 24h behavior.

    Returns:
        str: The raw content as a string on success.

    Raises:
        ImportLoadError: With a typed ``ImportFailureCode`` on any load failure.
functiondef new_uuid() -> 'str'#
Generate a new random (version 4) UUID string.

    Returns:
        str: A newly generated UUID in canonical string form.
functiondef oscal_markdown_to_html_tree(markdown_text: 'str', multiline: 'bool' = True) -> 'Optional[ElementTree.Element]'#
Convert OSCAL markdown text to an HTML ElementTree element.

    Calls ``oscal_markdown_to_html`` to format the markdown into HTML consistent
    with the OSCAL XML specification for markup-line / markup-multiline, then parses
    the resulting string into an XML element suitable for appending into a parent
    XML object.

    Args:
        markdown_text (str, required): The markdown text to convert.
        multiline (bool, optional): If True, handle markup-multiline (block elements);
            if False, handle markup-line (inline elements only). Defaults to True.

    Returns:
        Optional[ElementTree.Element]: The parsed XML element, or None if conversion fails.
functiondef prune_tree_copy(node: 'dict | None', depth: 'int | None' = None, child_keys: 'tuple' = ('groups', 'controls')) -> 'dict | None'#
Return a SAFE COPY of *node* with nested structural children limited to *depth*.

    Shared, model-agnostic helper for the node getters (catalog/profile groups and
    controls; assessment ``tasks`` once implemented). The returned value shares no
    references with *node*, so callers may read, mutate, or serialize it without
    affecting the source document — mutation of live content must go through the
    OSCAL-standard-enforcing methods, never through a getter's return value.

    Only the collections named in *child_keys* are treated as structural children
    subject to depth pruning. The node's own intrinsic content (e.g. ``props``,
    ``links``, ``params``, ``parts``, ``title``) is always copied in full.

        depth = None  -> unlimited: a full deep copy of the entire subtree (the
                         default; mirrors the historical getter behavior).
        depth = 0     -> node only: the *child_keys* collections are omitted.
        depth = N     -> N levels of structural children retained, each recursively
                         pruned at ``depth - 1``.

    Args:
        node (dict | None, required): The group/control/task dict to copy, or None.
        depth (int | None, optional): Structural-child depth limit. Defaults to None.
        child_keys (tuple, optional): Keys treated as structural children. Defaults
            to ("groups", "controls"). Use ("tasks",) for assessment tasks.

    Returns:
        dict | None: A detached copy, or None when *node* is None.

    Raises:
        ValueError: If *depth* is a negative integer.
functiondef register_model(model_name: 'str', cls: 'type') -> 'None'#
Register an OSCAL model subclass so factory methods return typed instances.

    Args:
        model_name (str, required): The OSCAL model name (e.g. "catalog").
        cls (type, required): The ``OSCAL`` subclass implementing that model.
functiondef requires(**conditions)#
Decorator factory gating a method on instance attribute/property values.

    The wrapped method runs only when every ``attr == expected`` condition holds
    on ``self``; otherwise it logs an error and returns None.

    Args:
        **conditions: Mapping of instance attribute/property name to its required
            value (e.g. ``writable=True``, ``is_remote=True``).

    Returns:
        Callable: A decorator that wraps the target method with the guard.

    Example:
        >>> @requires(is_read_only=False)
        ... def mutate(self): ...
functiondef requires_state(min_state: 'ContentState')#
Decorator factory gating a method on a minimum ``ContentState`` level.

    The wrapped method runs only when ``self.content_state >= min_state``;
    otherwise it logs an error and returns None.

    Args:
        min_state (ContentState, required): Minimum content state required to run
            the method.

    Returns:
        Callable: A decorator that wraps the target method with the guard.
functiondef use_actor(actor: "'str | None'")#
Set the current actor for the duration of the ``with`` block.

    Mutations performed inside the block are attributed to ``actor``; a document
    write-locked by a *different* actor is read-only within the block.

    Args:
        actor (str | None, required): The actor (view/session) id.

    Yields:
        str | None: The activated actor id.

oscal.oscal_datatypes#

oscal_datatypes — OSCAL Metaschema data type definitions and helpers.

Defines the OSCAL Metaschema primitive data types and their validation
patterns, and provides a helper for producing OSCAL-conformant timezone-aware
date-time strings.

Module constants:
    OSCAL_DATATYPES (dict): Mapping of OSCAL Metaschema data type name (str) to
        a definition dict. Each definition contains the keys ``base-type`` (str),
        ``xml-pattern`` (str regex), ``json-pattern`` (str regex),
        ``recommended-pattern`` (str regex), ``documentation`` (str),
        ``remarks`` (str), and ``links`` (list of {"title", "url"} dicts).
        Covers types such as ``string``, ``token``, ``uuid``, ``uri``,
        ``date-time-with-timezone``, ``integer``, ``boolean``, ``markup-line``,
        and ``markup-multiline``.
functiondef oscal_date_time_with_timezone(date_time=None, format='%Y-%m-%dT%H:%M:%SZ') -> str#
Convert a date/time to UTC and format it as an OSCAL date-time-with-timezone string.

    Args:
        date_time (datetime | str, optional): The date and time to convert. May be a
            ``datetime`` object or an ISO-8601 string parseable into one. Naive values
            are assumed to be UTC. Defaults to the current date and time.
        format (str, optional): The ``strftime`` format string to apply.
            Defaults to ``"%Y-%m-%dT%H:%M:%SZ"`` (the OSCAL standard format).

    Returns:
        str: The formatted date-time string, or an empty string if parsing or
            formatting fails.

oscal.oscal_controls#

oscal_controls — OSCAL control-layer model classes.

Provides the editable model classes for the OSCAL control models: ``Catalog``
(defines controls), ``Profile`` (selects and tailors controls into baselines),
and ``Mapping`` (relates controls across frameworks). Each class subclasses
``OSCAL`` from ``oscal_content`` and adds model-specific navigation and
mutation helpers. ``ImportResult`` is the structured return value of
``Profile.add_import``.

Module constants:
    MEDIA_TYPES (dict): Maps a lower-case file extension (``.xml``, ``.json``,
        ``.yaml``, ``.yml``) to its OSCAL media type (``application/xml``,
        ``application/json``, ``application/yaml``). Used to infer an ``rlink``
        media type from a referenced file's href.

class Catalog#

Editable OSCAL Catalog model.

    Subclasses ``OSCAL`` and adds methods for creating, editing, navigating, and
    removing controls and control groups. Read-only guards apply to mutation methods
    when the instance state is not editable.

    Attributes:
        controls_tree (list[dict]): A lightweight, nested view of the catalog's
            group/control hierarchy for UI tree navigation. Each node is
            ``{"id", "label", "title", "group", "children"}`` where ``group`` is True
            for groups and False for controls, and ``children`` holds nested groups
            and controls (control enhancements included). Built when the catalog is
            found to be valid OSCAL and refreshed on every structural or
            title/label change. See :meth:`_build_controls_tree`.
classmethodclassmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)#
Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.
methoddef add_part(self, parent_id: str, name: str, title: str = '', prose: str = '', ns: str = '', part_class: str = '', part_id: str = '', props: list = [], links: list = [], parts: list = []) -> Optional[dict]#
Add a part to an existing control, group, or part.

        Parts are valid on controls and groups (and nest inside other parts), so
        ``parent_id`` may identify any of those by id. There is no limit on how many
        parts of a given ``name`` a level may hold (e.g. multiple ``guidance`` parts).

        Args:
            parent_id (str, required): ID of the control, group, or part to add to.
            name (str, required): The part ``name`` token (e.g. "overview",
                "guidance", "example", "assessment-objective").
            title (str, optional): Part title (markup-line).
            prose (str, optional): Part prose (markup-multiline / markdown).
            ns (str, optional): Part namespace URI.
            part_class (str, optional): Part ``class`` value.
            part_id (str, optional): ID for the new part (needed to target it later,
                e.g. to nest a child part or set its title).
            props (list, optional): Property dicts to add.
            links (list, optional): Link dicts to add.
            parts (list, optional): Pre-built child part dicts to nest.

        Returns:
            Optional[dict]: The newly created part dict, or None on failure — including
                when the part would violate a "leaf part" rule (e.g. a ``guidance``
                part cannot contain child parts).
methoddef append_child(self, path: 'str', child: 'dict') -> 'dict | None'#
Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.
methoddef append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'#
Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.
methoddef create_control(self, parent_id: str, id: str, title: str = '', params: list = [], props: list = [], links: list = [], label: str = '', sort_id: str = '', alt_identifier: str = '', overview: str = '', statements: list = [], guidance: str = '', example: str = '', objectives: list = [], objects: list = [], methods: list = [], remarks: str = '') -> Optional[dict]#
Create a new control under the specified parent group or control.

        Args:
            parent_id (str, required): ID of the parent to add the control to —
                ``'[root]'`` (or an empty string) for the catalog top level, a group
                id, or a control id. Nesting a control under a control models a
                control enhancement (e.g. ``ac-2.1`` under ``ac-2``). The add fails if
                it would mix controls and groups at the same level (not allowed in OSCAL).
            id (str, required): ID of the new control.
            title (str, optional): Title of the new control. Defaults to the label,
                or the id, when empty.
            params (list, optional): Parameters to add. Items may be parameter id
                strings or full parameter dicts.
            props (list, optional): Additional property dicts to add.
            links (list, optional): Link dicts to add.
            label (str, optional): Value for the inline ``label`` property.
            sort_id (str, optional): Value for the inline ``sort-id`` property.
            alt_identifier (str, optional): Value for the inline ``alt-identifier`` property.
            overview (str, optional): Prose (markdown) for the ``overview`` part.
            statements (list, optional): Statement items — strings or
                ``{'id':..., 'prose':...}`` dicts.
            guidance (str, optional): Prose (markdown) for the ``guidance`` part.
            example (str, optional): Prose (markdown) for the ``example`` part.
            objectives (list, optional): Assessment objective items.
            objects (list, optional): Assessment object items.
            methods (list, optional): Assessment method items.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            Optional[dict]: The newly created control dict, or None on failure.

        Note:
            Refreshes ``controls_tree`` when a control is successfully added.
methoddef create_control_group(self, parent_id: str, id: str, title: str = '', params: list = [], props: list = [], links: list = [], label: str = '', sort_id: str = '', alt_identifier: str = '', overview: str = '', instruction: str = '', remarks: str = '') -> Optional[dict]#
Create a new catalog group.

        Args:
            parent_id (str, required): ID of the parent group, or ``'[root]'`` (or an
                empty string) for the catalog top level. The add fails if it would mix
                controls and groups at the same level (not allowed in OSCAL).
            id (str, required): ID of the new group.
            title (str, optional): Title of the new group.
            params (list, optional): Parameters to add to the group.
            props (list, optional): Additional property dicts to add.
            links (list, optional): Link dicts to add.
            label (str, optional): Value for the inline ``label`` property.
            sort_id (str, optional): Value for the inline ``sort-id`` property.
            alt_identifier (str, optional): Value for the inline ``alt-identifier`` property.
            overview (str, optional): Prose (markdown) for the ``overview`` part.
            instruction (str, optional): Prose (markdown) for the ``instruction`` part.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            Optional[dict]: The newly created group dict, or None on failure.

        Note:
            Refreshes ``controls_tree`` when a group is successfully added.
methoddef dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'#
Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.
methoddef dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'#
Serialize the current content to a string in the specified format.
        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.
propertyproperty duplicate_imports#
Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.
propertyproperty failed_imports#
Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.
methoddef find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'#
Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id) or ``party`` (by uuid), or a ``control``/``group``/``param``/``part`` (by id).
        This walks ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.
classmethodclassmethod from_string(cls, content: 'str', *, href: 'str | None' = None)#
Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).
methoddef get_control_by_id(self, control_id: str, depth: Optional[int] = None) -> Optional[dict]#
Retrieve a control by its ID as a safe copy, searching all groups recursively.

        The returned dict is a detached copy — mutating it does NOT change the catalog;
        use the catalog's mutation methods to make persistent changes. The control's own
        content (``parts``, ``props``, ``links``, ``params`` …) is always returned in full;
        ``depth`` limits only nested child controls (enhancements).

        Args:
            control_id (str, required): The ``id`` of the control to find.
            depth (int | None, optional): Nested-enhancement depth. ``None`` (default)
                returns the full subtree; ``0`` omits enhancements; ``N`` keeps N levels.

        Returns:
            Optional[dict]: A safe copy of the matching control, or None if not found.
methoddef get_control_list(self) -> list#
Return a flat list of every control in the catalog, at all levels, as safe copies.

        The returned list is detached from the document: each control is a copy, so
        mutating any element does NOT change the catalog. A single deep copy of the whole
        list preserves internal identity relationships (an enhancement nested inside its
        parent is the same object as its own standalone entry). Use the catalog's mutation
        methods to make persistent changes.

        Returns:
            list: Safe copies of all controls found across the catalog and its groups.
methoddef get_group_by_id(self, group_id: str, depth: Optional[int] = None) -> Optional[dict]#
Retrieve a group by its ID as a safe copy, searching nested groups recursively.

        The returned dict is a detached copy — mutating it does NOT change the catalog;
        use the catalog's mutation methods to make persistent changes. The group's own
        content (``props``, ``links`` …) is always returned in full; ``depth`` limits only
        nested child groups and controls.

        Args:
            group_id (str, required): The ``id`` of the group to find.
            depth (int | None, optional): Nested group/control depth. ``None`` (default)
                returns the full subtree; ``0`` omits child groups/controls; ``N`` keeps
                N levels.

        Returns:
            Optional[dict]: A safe copy of the matching group, or None if not found.
methoddef get_oscal_object(self, uuid, _seen=None)#
Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.
methoddef get_parameter_by_id(self, param_id: 'str') -> 'Optional[dict]'#
Return a safe copy of a parameter defined anywhere in scope, or None.

        Searches this document and its import tree for a ``param`` with the given id —
        covering parameters defined at control, group, or catalog level (and reached
        through imported catalogs/profiles). Subclasses may override to prefer resolved
        content.
methoddef ignore_import(self, href: 'str') -> 'bool'#
Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.
propertyproperty import_tree#
Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.
propertyproperty imports_resolved#
bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).
methoddef initial_validation(self, content: 'str') -> 'bool'#
Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.
methoddef insert_control(self, parent_id: str, control: dict, validate: bool = True) -> Optional[dict]#
Insert a pre-formed control subtree whole under a parent, as a safe copy.

        Unlike :meth:`create_control` (which authors a control from discrete parts),
        this inserts an already-formed control dict — including its nested enhancements,
        parts, props, params, and links — without reshaping it. It is the faithful-copy
        path used by profile resolution (and, later, alter directives) to move a control
        from a source catalog into a resolved one.

        The incoming dict is deep-copied before insertion, so the caller's object is not
        aliased into the catalog (getters still return detached copies).

        Args:
            parent_id (str, required): ID of the parent to add the control to —
                ``'[root]'`` (or an empty string) for the catalog top level, a group id,
                or a control id (control-under-control models an enhancement). The add
                fails if it would mix controls and groups at the same level.
            control (dict, required): The control subtree to insert. Must be a dict with
                a non-empty ``id``.
            validate (bool, optional): When True (default), the control is validated
                against the ``control`` metaschema node first; on any error the insert is
                rejected and the catalog is left unchanged.

        Returns:
            Optional[dict]: A safe copy of the inserted control, or None on failure —
                bad input, parent not found, an id collision with an existing control, a
                controls/groups mix, or failed validation.
methoddef insert_group(self, parent_id: str, group: dict, shallow: bool = True, validate: bool = True) -> Optional[dict]#
Insert a group node under a parent, as a safe copy.

        Companion to :meth:`insert_control` for faithful-copy workflows. By default the
        insert is *shallow*: the group's intrinsic content (title, params, props, links,
        parts) is inserted but its child ``groups``/``controls`` are dropped, to be filled
        in afterward via :meth:`insert_control`/:meth:`insert_group`. This lets callers
        (e.g. profile resolution) build a group hierarchy incrementally while keeping
        empty-group pruning and duplicate handling under their own control.

        Args:
            parent_id (str, required): ID of the parent group, or ``'[root]'`` (or an
                empty string) for the catalog top level. The add fails if it would mix
                controls and groups at the same level.
            group (dict, required): The group node to insert. Must be a dict with a
                non-empty ``id``.
            shallow (bool, optional): When True (default), drop the group's child
                ``groups`` and ``controls`` before insertion. When False, insert the
                group subtree whole.
            validate (bool, optional): When True (default), validate the (possibly
                shallow) group against the ``group`` metaschema node first; on any error
                the insert is rejected and the catalog is left unchanged.

        Returns:
            Optional[dict]: A safe copy of the inserted group, or None on failure — bad
                input, parent not found, an id collision with an existing group, a
                controls/groups mix, or failed validation.
propertyproperty is_acquired#
bool: True once content has been acquired (``content_state >= ACQUIRED``).
propertyproperty is_cache_expired#
True when remote cached content has exceeded its TTL.
propertyproperty is_editable#
Can this content be modified?
propertyproperty is_fresh#
True when content is local or cached and within its TTL.
propertyproperty is_read_only#
bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.
propertyproperty is_remote#
bool: True when the content originates from a remote source (not a local file).
propertyproperty is_stale#
True when remote cached content has exceeded its TTL.
propertyproperty is_valid#
bool: True when content passes OSCAL validation (``content_state >= VALID``).
propertyproperty is_well_formed#
bool: True when content is well-formed (``content_state >= WELL_FORMED``).
propertyproperty json#
Return the content as a JSON string.
methoddef json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
classmethodclassmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)#
Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior. Use ``loads(...)``
        for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        resolution and fallback sources.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.
classmethodclassmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)#
Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.
classmethodclassmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')#
Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.
classmethodclassmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)#
Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.
propertyproperty origin_state#
Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.
methoddef put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'#
Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.
methoddef query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
methoddef reachable_ids(self, _seen=None) -> 'set'#
Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.
methoddef rebuild_import_tree(self) -> 'dict'#
Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.
methoddef remove(self, id: str, cascade: bool = False, ignore_references: bool = False) -> Optional[dict]#
Remove a control or group (found by id) from the catalog.

        Two independent locks guard the delete; either can block it, and the return
        value makes the reason explicit:

          * **cascade** — when ``cascade`` is False, a node with *immediate* children
            (direct groups, controls, or parts) is not removed. The block report
            lists those immediate child ids under ``"children"``. Set ``cascade=True``
            to remove the node together with everything beneath it.
          * **referential integrity** — when ``ignore_references`` is False, the node
            is not removed if any id in its subtree (the node, nested groups/controls,
            or any part) is referenced by a link elsewhere in the catalog. The block
            report lists those referenced ids under ``"referenced_ids"``. Set
            ``ignore_references=True`` to delete anyway; the now-dangling references
            are then returned under ``"dangling_refs"``.

        Both conditions are evaluated; if both block, ``"blocked_by"`` contains both
        reasons and both detail lists are present. Nothing is modified when blocked.

        References in *other* documents (profiles, SSPs, mappings) cannot be seen or
        fixed from here; on a successful delete a warning notes they may now break.
        Refreshes ``controls_tree`` on success.

        Args:
            id (str, required): The id of the control or group to remove.
            cascade (bool, optional): Permit removing a node that has immediate
                children. Defaults to False.
            ignore_references (bool, optional): Permit removing a node that is
                referenced elsewhere in the catalog. Defaults to False.

        Returns:
            Optional[dict]:
                * ``None`` when no control/group has that id.
                * On block: ``{"removed": False, "blocked_by": [...],
                  "children": [...]?, "referenced_ids": [...]?}``.
                * On success: ``{"removed": True, "removed_ids": [...],
                  "dangling_refs": [...]}``.
methoddef remove_import(self, href: 'str') -> 'bool'#
Remove an import entry from both import_list and the document content.

        The import *statement* is deleted from ``self._dict``, placing the
        document in an edited and unsaved state.  Any back-matter resource
        referenced by the import via a URI fragment (``href="#uuid"``) is
        intentionally preserved — only the import element itself is removed.

        The cached import_tree is updated in-place (same object, one node
        shorter).  content_state is recomputed: if the removed entry was the
        last thing blocking resolution, content_state advances to
        IMPORTS_RESOLVED.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and IGNORED are preferred over
        INVALID which is preferred over READY — so the problematic entry is
        always targeted.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and removed, False if not found or the
            content is read-only.
methoddef resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'#
Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-assessment-plan/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.
methoddef retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef set_label(self, id: str, label: str, class_: str = '', group: str = '') -> Optional[dict]#
Set (or clear) the ``label`` property of a control or group, found by id.

        The targeted property is the ``label`` prop in the default OSCAL namespace
        whose ``class``/``group`` qualifiers match the arguments: by default the one
        with **no** class and **no** group — the same property the navigation tree
        reads. Supplying ``class_`` and/or ``group`` targets (or creates) the label
        carrying exactly those qualifiers instead.

        Behaviour:
          * A matching prop exists  → its value is updated (the first, if several).
          * No matching prop exists → one is created with the given qualifiers.
          * ``label`` is empty       → every matching prop is removed.

        Refreshes ``controls_tree`` on success.

        Args:
            id (str, required): The id of the control or group to modify.
            label (str, required): The new label value; an empty string removes the
                matching label property.
            class_ (str, optional): The prop ``class`` to target/create.
            group (str, optional): The prop ``group`` to target/create.

        Returns:
            Optional[dict]: The modified control/group dict, or None if no such id
                exists.
methoddef set_metadata(self, content: 'dict' = {}) -> 'bool'#
Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.
methoddef set_part_title(self, part_id: str, title: str = '') -> Optional[dict]#
Set or remove the title of an existing part.

        Args:
            part_id (str, required): ID of the part to modify. The part must carry an
                ``id`` to be targetable.
            title (str, optional): The new title. When empty, the part's ``title`` is
                removed.

        Returns:
            Optional[dict]: The modified part dict, or None if no part with that id
                is found.
methoddef set_title(self, id: str, title: str) -> Optional[dict]#
Set the title of a control or group, found by id.

        Refreshes ``controls_tree`` on success, since a node's ``title`` is drawn
        from the object's title.

        Args:
            id (str, required): The id of the control or group to modify.
            title (str, required): The new title. Must be non-empty — a control's
                title is required by OSCAL, so blanking it is rejected.

        Returns:
            Optional[dict]: The modified control/group dict, or None if no such id
                exists or ``title`` is empty.
propertyproperty unresolved_imports#
Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.
methoddef validate(self, format: str = '') -> bool#
Validate the catalog, then (re)build ``controls_tree`` on success.

        Extends :meth:`OSCAL.validate` so the navigation tree is refreshed the
        moment the catalog is converted and found to be valid OSCAL. When the
        content is not valid the tree is emptied — an invalid catalog exposes no
        navigable hierarchy.

        Args:
            format (str, optional): Accepted for API compatibility with the base
                method; does not alter the validation path.

        Returns:
            bool: True when every validation phase passes.
methoddef walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')#
Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None
propertyproperty xml#
Return the content as an XML string, converting from dict if necessary.
propertyproperty yaml#
Return the content as a YAML string.

class ImportResult#

Outcome of a :meth:`Profile.add_import` call.

    Attributes:
        status (str): One of "added", "replaced", "duplicate", or "error". A
            "duplicate" is a blocking condition (``ok`` is False) — the href already
            appears among this document's own imports.
        entry (dict | None): The import entry — the newly added/replaced entry for
            "added"/"replaced", or the conflicting existing import for "duplicate".
        resource (dict | None): The back-matter resource created for the import
            (None for "duplicate"/"error").
        message (str): Human-readable detail, primarily for "duplicate"/"error".
propertyproperty is_duplicate#
bool: True when the href already matched one of this document's imports.
propertyproperty ok#
bool: True when an import was actually added or replaced.

class Mapping#

Class representing an OSCAL Mapping object.
    Inherits common OSCAL functionality and adds mapping-specific methods
    for managing mappings between controls and other objects.
classmethodclassmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)#
Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.
methoddef append_child(self, path: 'str', child: 'dict') -> 'dict | None'#
Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.
methoddef append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'#
Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.
methoddef dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'#
Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.
methoddef dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'#
Serialize the current content to a string in the specified format.
        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.
propertyproperty duplicate_imports#
Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.
propertyproperty failed_imports#
Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.
methoddef find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'#
Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id) or ``party`` (by uuid), or a ``control``/``group``/``param``/``part`` (by id).
        This walks ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.
classmethodclassmethod from_string(cls, content: 'str', *, href: 'str | None' = None)#
Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).
methoddef get_oscal_object(self, uuid, _seen=None)#
Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.
methoddef get_parameter_by_id(self, param_id: 'str') -> 'Optional[dict]'#
Return a safe copy of a parameter defined anywhere in scope, or None.

        Searches this document and its import tree for a ``param`` with the given id —
        covering parameters defined at control, group, or catalog level (and reached
        through imported catalogs/profiles). Subclasses may override to prefer resolved
        content.
methoddef ignore_import(self, href: 'str') -> 'bool'#
Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.
propertyproperty import_tree#
Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.
propertyproperty imports_resolved#
bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).
methoddef initial_validation(self, content: 'str') -> 'bool'#
Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.
propertyproperty is_acquired#
bool: True once content has been acquired (``content_state >= ACQUIRED``).
propertyproperty is_cache_expired#
True when remote cached content has exceeded its TTL.
propertyproperty is_editable#
Can this content be modified?
propertyproperty is_fresh#
True when content is local or cached and within its TTL.
propertyproperty is_read_only#
bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.
propertyproperty is_remote#
bool: True when the content originates from a remote source (not a local file).
propertyproperty is_stale#
True when remote cached content has exceeded its TTL.
propertyproperty is_valid#
bool: True when content passes OSCAL validation (``content_state >= VALID``).
propertyproperty is_well_formed#
bool: True when content is well-formed (``content_state >= WELL_FORMED``).
propertyproperty json#
Return the content as a JSON string.
methoddef json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
classmethodclassmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)#
Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior. Use ``loads(...)``
        for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        resolution and fallback sources.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.
classmethodclassmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)#
Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.
classmethodclassmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')#
Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.
classmethodclassmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)#
Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.
propertyproperty origin_state#
Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.
methoddef put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'#
Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.
methoddef query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
methoddef reachable_ids(self, _seen=None) -> 'set'#
Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.
methoddef rebuild_import_tree(self) -> 'dict'#
Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.
methoddef remove_import(self, href: 'str') -> 'bool'#
Remove an import entry from both import_list and the document content.

        The import *statement* is deleted from ``self._dict``, placing the
        document in an edited and unsaved state.  Any back-matter resource
        referenced by the import via a URI fragment (``href="#uuid"``) is
        intentionally preserved — only the import element itself is removed.

        The cached import_tree is updated in-place (same object, one node
        shorter).  content_state is recomputed: if the removed entry was the
        last thing blocking resolution, content_state advances to
        IMPORTS_RESOLVED.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and IGNORED are preferred over
        INVALID which is preferred over READY — so the problematic entry is
        always targeted.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and removed, False if not found or the
            content is read-only.
methoddef resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'#
Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-assessment-plan/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.
methoddef retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef set_metadata(self, content: 'dict' = {}) -> 'bool'#
Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.
propertyproperty unresolved_imports#
Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.
methoddef validate(self, format: 'str' = '') -> 'bool'#
Validate OSCAL content against the metaschema index in sequenced phases.

        Phases (each recorded in ``validation_status``):
          structure      – all required fields and hierarchy are present
          data-types     – every leaf value matches its declared OSCAL datatype
          allowed-values – every constrained value is within its enumerated set
          cardinality    – every array satisfies its min-occurs/max-occurs bounds
          choice         – every choice is mutually exclusive (at most one member present) and has a member when one is required

        ``validation_status["well-formed"]`` is set by ``initial_validation()``, not here.
        All phases always run regardless of earlier failures, giving a complete picture
        of issues in a single call.  The format argument is accepted for API
        compatibility but does not alter the validation path — ``_dict`` is always the
        authoritative representation.

        Returns True only when every phase passes (content_state reaches VALID).
methoddef walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')#
Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None
propertyproperty xml#
Return the content as an XML string, converting from dict if necessary.
propertyproperty yaml#
Return the content as a YAML string.

class Profile#

Editable OSCAL Profile model with tree-driven, lazy resolution.

    A profile selects and tailors controls from one or more imported catalogs/profiles.
    Resolution is split into a cheap load-time step and an on-demand heavy step:

    * **controls_tree (source of truth).** On load — and after any import/directive
      change — :meth:`_build_controls_tree` reads each imported object's own
      ``controls_tree`` and applies the profile's directives to produce this profile's
      ``controls_tree``: the authoritative scope and organization. It is lightweight
      (ids + hierarchy + an ``origin`` back to each node's immediate source); no control
      content is copied. Directives applied here: ``import``/``include``/``exclude``
      selection, ``merge`` (``as-is`` or ``flat``; ``custom`` is deferred and falls back
      to ``as-is``), and ``combine`` duplicate handling (``keep`` renames the node id,
      ``use-first`` drops later duplicates). When an ``as-is`` merge would place controls
      and groups together at the root, root controls are wrapped in a synthetic
      "ROOT CONTROLS" group.

    * **resolve() (heavy, cacheable).** :meth:`resolve` walks the tree and materializes a
      brand-new ``Catalog`` in :attr:`catalog`, fetching real content per node, applying
      ``modify`` directives (removes → adds → set-parameters) and full internal id
      renaming for duplicates, hoisting externally-defined cited parameters to the root,
      carrying forward referenced back-matter resources, and rewriting out-of-scope
      references to their source URIs. ``catalog`` is ``None`` until ``resolve`` is called.

    * **Read-only Catalog surface.** :meth:`get_control_by_id`, :meth:`get_group_by_id`,
      :meth:`get_control_list`, and :meth:`get_parameter_by_id` return safe copies from
      :attr:`catalog` when resolved, or materialize them on demand from the source
      (through the same code path, so the two agree) when unresolved. Content is changed
      via the profile's own directive methods and re-resolved, not by editing returned
      copies.

    * **Serialization.** :meth:`dumps_catalog`/:meth:`dump_catalog` serialize the resolved
      catalog to a string / file; :meth:`resolve_and_dumps_catalog`/
      :meth:`resolve_and_dump_catalog` run the full resolve-then-serialize in one call.

    Key attributes:
        catalog (Catalog | None): The resolved catalog, or None until :meth:`resolve`.
        controls_tree (list[dict]): Scope/organization nodes
            ``{id, label, title, group, origin, children}``.
        duplicates (dict): Controls/groups renamed or dropped by ``combine``, keyed by
            original id (see :meth:`_record_duplicate`).
        resolution_status (ResolutionStatus): UNRESOLVED / RESOLVING / RESOLVED / BLOCKED.
classmethodclassmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)#
Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.
methoddef add_import(self, href: str, title: str = '', description: str = '', remarks: str = '', include_all: bool = False) -> oscal.oscal_controls.ImportResult#
Add an import to the profile, backed by a new back-matter resource.

        Steps:
            1. If ``href`` already appears among this profile's own imports, block it
               and report a "duplicate" (an error condition). Duplicate imports
               farther down the import tree are acceptable and out of scope.
            2. Create a back-matter ``resource`` (with an ``rlink`` to ``href`` and a
               best-effort ``media-type`` inferred from the href's file extension).
            3. Add an ``imports`` entry that references the resource by UUID fragment
               (``href="#<resource-uuid>"``). An existing empty placeholder import
               (href ``""`` or ``"#"``) is replaced in place; otherwise the entry is
               appended.
            4. Refresh the import tree (:meth:`resolve_imports`). The natural import
               process loads the referenced content and reports success or failure;
               an unreachable or invalid href simply resolves to ``INVALID`` in the
               tree, and the caller decides whether that is acceptable.

        Args:
            href (str, required): Reference to the imported OSCAL file (XML, JSON, or
                YAML). Used as the resource ``rlink`` href.
            title (str, optional): Title for the created back-matter resource.
            description (str, optional): Description for the created resource.
            remarks (str, optional): Remarks (markdown) for the created resource.
            include_all (bool, optional): When True, the import selects all controls
                via ``include-all``. Defaults to False.

        Returns:
            ImportResult: The outcome — ``status`` of "added", "replaced",
                "duplicate", or "error", with the relevant ``entry`` and ``resource``.
methoddef append_child(self, path: 'str', child: 'dict') -> 'dict | None'#
Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.
methoddef append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'#
Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.
methoddef control(self, control_id: str, with_history: bool = False, depth: Optional[int] = None) -> Optional[dict]#
Retrieve a control by its ID, as a safe copy (thin alias of
        :meth:`get_control_by_id`).

        Works whether or not the profile is resolved: resolved fetches from
        ``self.catalog``; unresolved materializes on demand from the source via the
        profile's controls_tree.

        Args:
            control_id (str, required): The ``id`` of the control to retrieve.
            with_history (bool, optional): Reserved for including tailoring history.
                Defaults to False.
            depth (int | None, optional): Nested-enhancement depth. ``None`` (default)
                returns the full subtree; ``0`` omits enhancements; ``N`` keeps N levels.

        Returns:
            Optional[dict]: A safe copy of the control, or None if not found.
methoddef dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'#
Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.
methoddef dump_catalog(self, filename: str = '', format: str = '', pretty_print: bool = False) -> bool#
Write the resolved catalog to a file — wrapper over ``self.catalog.dump``.

        Requires the profile to be resolved (``self.catalog`` is None until
        :meth:`resolve`); returns False with a warning otherwise.

        Args:
            filename (str, optional): Path to write to; defaults to the catalog's original
                location when empty.
            format (str, optional): Output format ("xml", "json", "yaml"); defaults to the
                catalog's original format when empty.
            pretty_print (bool, optional): Whether to pretty-print. Defaults to False.

        Returns:
            bool: True on success, False when unresolved or the write fails.
methoddef dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'#
Serialize the current content to a string in the specified format.
        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.
methoddef dumps_catalog(self, format: str = '', pretty_print: bool = False) -> str#
Serialize the resolved catalog to a string — wrapper over ``self.catalog.dumps``.

        Requires the profile to be resolved (``self.catalog`` is None until
        :meth:`resolve`); returns ``""`` with a warning otherwise.

        Args:
            format (str, optional): Target format ("xml", "json", "yaml"); defaults to the
                catalog's original format.
            pretty_print (bool, optional): Whether to pretty-print. Defaults to False.

        Returns:
            str: The serialized resolved catalog, or ``""`` when unresolved.
propertyproperty duplicate_imports#
Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.
propertyproperty failed_imports#
Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.
methoddef find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'#
Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id) or ``party`` (by uuid), or a ``control``/``group``/``param``/``part`` (by id).
        This walks ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.
classmethodclassmethod from_string(cls, content: 'str', *, href: 'str | None' = None)#
Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).
methoddef get_control_by_id(self, control_id: str, depth: Optional[int] = None) -> Optional[dict]#
Retrieve a control as a safe copy — from the resolved catalog if resolved,
        otherwise materialized on demand from the source via the profile's controls_tree.

        Both paths return the same shape (the control with its in-scope enhancements
        nested per ``depth``, this profile's ``modify`` directives applied). When
        unresolved, the control is materialized from its origin source on each call.

        Args:
            control_id (str, required): The control id (as it appears in this profile's
                scope — a duplicate's suffixed id is valid).
            depth (int | None, optional): Enhancement depth (``None`` full).

        Returns:
            Optional[dict]: A safe copy of the control, or None when absent.
methoddef get_control_list(self) -> list#
Return safe copies of every in-scope control, at all levels.

        Resolved: pass-through to :meth:`Catalog.get_control_list`. Unresolved: each
        control node in the profile's controls_tree is materialized standalone (depth 0),
        mirroring the flat, enhancement-inclusive list a catalog returns.
methoddef get_group_by_id(self, group_id: str, depth: Optional[int] = None) -> Optional[dict]#
Retrieve a group as a safe copy — from the resolved catalog if resolved,
        otherwise materialized on demand from the source via the profile's controls_tree.

        Args:
            group_id (str, required): The group id (as it appears in this profile's scope).
            depth (int | None, optional): Nested group/control depth (``None`` full).

        Returns:
            Optional[dict]: A safe copy of the group, or None when absent.
methoddef get_oscal_object(self, uuid, _seen=None)#
Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.
methoddef get_parameter_by_id(self, param_id: str) -> Optional[dict]#
Return a parameter as a safe copy — from the resolved catalog if resolved,
        otherwise located in the import tree (its source, unmutated).
methoddef ignore_import(self, href: 'str') -> 'bool'#
Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.
propertyproperty import_tree#
Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.
propertyproperty imports_resolved#
bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).
methoddef initial_validation(self, content: 'str') -> 'bool'#
Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.
propertyproperty is_acquired#
bool: True once content has been acquired (``content_state >= ACQUIRED``).
propertyproperty is_cache_expired#
True when remote cached content has exceeded its TTL.
propertyproperty is_editable#
Can this content be modified?
propertyproperty is_fresh#
True when content is local or cached and within its TTL.
propertyproperty is_read_only#
bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.
propertyproperty is_remote#
bool: True when the content originates from a remote source (not a local file).
propertyproperty is_stale#
True when remote cached content has exceeded its TTL.
propertyproperty is_valid#
bool: True when content passes OSCAL validation (``content_state >= VALID``).
propertyproperty is_well_formed#
bool: True when content is well-formed (``content_state >= WELL_FORMED``).
propertyproperty json#
Return the content as a JSON string.
methoddef json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
classmethodclassmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)#
Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior. Use ``loads(...)``
        for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        resolution and fallback sources.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.
classmethodclassmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)#
Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.
classmethodclassmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')#
Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.
classmethodclassmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)#
Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.
propertyproperty origin_state#
Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.
methoddef put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'#
Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.
methoddef query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
methoddef reachable_ids(self, _seen=None) -> 'set'#
Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.
methoddef rebuild_import_tree(self) -> 'dict'#
Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.
methoddef remove_import(self, href: 'str') -> 'bool'#
Remove an import entry from both import_list and the document content.

        The import *statement* is deleted from ``self._dict``, placing the
        document in an edited and unsaved state.  Any back-matter resource
        referenced by the import via a URI fragment (``href="#uuid"``) is
        intentionally preserved — only the import element itself is removed.

        The cached import_tree is updated in-place (same object, one node
        shorter).  content_state is recomputed: if the removed entry was the
        last thing blocking resolution, content_state advances to
        IMPORTS_RESOLVED.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and IGNORED are preferred over
        INVALID which is preferred over READY — so the problematic entry is
        always targeted.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and removed, False if not found or the
            content is read-only.
methoddef resolve(self) -> 'ResolutionStatus'#
Materialize the profile's controls_tree into a fresh ``self.catalog``.

        Resolution is the cacheable heavy step: it walks the profile's
        controls_tree — the authoritative scope/organization built at load — and for each
        node fetches the real control/group content from its origin source, applies this
        profile's ``modify`` directives (removes → adds → set-parameters), applies full
        internal ``__<uuid>`` id renaming for duplicates, then inserts it into a brand-new
        ``Catalog``. Any previously resolved catalog is discarded and replaced. After
        placement it: hoists cited-but-externally-defined parameters to the catalog root
        (:meth:`_insert_shared_params`), assembles metadata, carries forward referenced
        back-matter resources (:meth:`_carry_backmatter`), rewrites references to
        out-of-scope ids to their source URIs (:meth:`_rewrite_out_of_scope_refs`), and
        validates.

        Because content is fetched through each source's own getters, imported *profiles*
        need not be pre-resolved — their load-time controls_tree and lazy getters suffice.

        Returns:
            ResolutionStatus: ``RESOLVED`` on success, or ``BLOCKED`` when content is
                missing or an import could not be resolved.
methoddef resolve_and_dump_catalog(self, filename: str = '', format: str = '', pretty_print: bool = False) -> bool#
Resolve the profile, then write the resolved catalog to a file.

        Convenience for the common one-shot case: it runs the (potentially expensive)
        :meth:`resolve` and then :meth:`dump_catalog`. For finer control — e.g. to resolve
        once and write several outputs, or to manage *when* the heavy resolution runs — call
        :meth:`resolve` and :meth:`dump_catalog` separately instead.

        Note: each call performs a full resolution (rebuilds ``self.catalog`` from scratch).

        Args:
            filename (str, optional): Path to write to; defaults to the catalog's original
                location when empty.
            format (str, optional): Output format ("xml", "json", "yaml").
            pretty_print (bool, optional): Whether to pretty-print. Defaults to False.

        Returns:
            bool: True on success, False if resolution was blocked or the write failed.
methoddef resolve_and_dumps_catalog(self, format: str = '', pretty_print: bool = False) -> str#
Resolve the profile, then serialize the resolved catalog to a string.

        Convenience for the common one-shot case: it runs the (potentially expensive)
        :meth:`resolve` and then :meth:`dumps_catalog`. For finer control — e.g. to resolve
        once and serialize many times, or to manage *when* the heavy resolution runs — call
        :meth:`resolve` and :meth:`dumps_catalog` separately instead.

        Note: each call performs a full resolution (rebuilds ``self.catalog`` from scratch).

        Args:
            format (str, optional): Target format ("xml", "json", "yaml").
            pretty_print (bool, optional): Whether to pretty-print. Defaults to False.

        Returns:
            str: The serialized resolved catalog, or ``""`` if resolution was blocked.
methoddef resolve_duplicate(self, control_id: str, keep: Optional[str] = None, parent_id: Optional[str] = None, replacement: Optional[dict] = None) -> Optional[dict]#
Manually collapse duplicate instances of a control in the resolved catalog.

        Requires a resolved catalog. Merging logic is out of scope (handled by the
        caller, e.g. a GUI); this keeps, relocates, or wholesale-replaces:

          * ``replacement`` given — remove every live variant (the original id and each
            tracked ``new_id``) and insert ``replacement`` (validated) under ``parent_id``
            (or the original variant's current parent).
          * ``keep`` given (or defaulted to ``control_id``) — remove every variant except
            ``keep``; if ``parent_id`` is given, relocate the kept control there.

        Args:
            control_id (str, required): The ORIGINAL (unsuffixed) control id.
            keep (str, optional): Which variant id to retain. Defaults to ``control_id``.
            parent_id (str, optional): Where to place the survivor. Defaults to in place.
            replacement (dict, optional): A full control dict superseding all variants.

        Returns:
            Optional[dict]: A safe copy of the surviving control, or None on failure.
methoddef resolve_duplicate_group(self, group_id: str, keep: Optional[str] = None, parent_id: Optional[str] = None, replacement: Optional[dict] = None) -> Optional[dict]#
Manually collapse duplicate instances of a group in the resolved catalog.

        The group analogue of :meth:`resolve_duplicate`. NOTE: removing a group removes
        its contained controls too (cascade), so prefer resolving duplicate *controls*
        first when both are tracked.

        Args:
            group_id (str, required): The ORIGINAL (unsuffixed) group id.
            keep (str, optional): Which variant group id to retain. Defaults to ``group_id``.
            parent_id (str, optional): Where to place the survivor. Defaults to in place.
            replacement (dict, optional): A full group dict superseding all variants.

        Returns:
            Optional[dict]: A safe copy of the surviving group, or None on failure.
methoddef resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'#
Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-assessment-plan/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.
methoddef retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef set_merge(self, flat: bool = False, as_is: Optional[bool] = None, custom: Optional[dict] = None, combine: Optional[str] = None) -> Optional[dict]#
Set the profile's ``merge`` directives (``combine`` plus flat/as-is/custom).

        The ``merge`` assembly instructs how imported controls are organized after
        profile resolution. Exactly one of ``flat``, ``as_is``, or ``custom`` must be
        chosen — they are mutually exclusive — while ``combine`` is optional and may
        accompany any of those choices.

        The ``custom`` object is accepted whole and validated against the ``custom``
        portion of the profile metaschema index; if it fails validation the profile is
        left unchanged. Fine-grained management of ``custom`` internals (its ``groups``
        and ``insert-controls``) is intentionally deferred to future methods, as custom
        merges are uncommon.

        Args:
            flat (bool, optional): When True, select the ``flat`` directive (resolved
                controls are flattened, without groups). Defaults to False.
            as_is (bool, optional): When set, select the ``as-is`` directive with this
                boolean value (True keeps the source organization). Defaults to None
                (not selected).
            custom (dict, optional): When set, select the ``custom`` directive using
                this object. Validated against the metaschema index. Defaults to None
                (not selected).
            combine (str, optional): The ``combine`` method — one of ``"use-first"``,
                ``"merge"``, or ``"keep"``. When None, no ``combine`` is written.

        Returns:
            Optional[dict]: The ``merge`` dict written to the profile, or None on
                failure — when not exactly one of flat/as-is/custom is given, an
                argument has the wrong type, ``combine`` is not a valid method, or the
                ``custom`` object fails metaschema validation.
methoddef set_metadata(self, content: 'dict' = {}) -> 'bool'#
Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.
propertyproperty unresolved_imports#
Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.
methoddef validate(self, format: str = '') -> bool#
Validate the profile, then (re)build ``controls_tree`` on success.

        Mirrors :meth:`Catalog.validate`: the profile's authoritative scope and
        organization tree is refreshed the moment the content is converted and
        found to be valid OSCAL — no call to :meth:`resolve` is required. Building
        the tree resolves the profile's imports if they are not already resolved.
        When the content is not valid the tree is emptied and left marked stale so a
        later access rebuilds it.

        Args:
            format (str, optional): Accepted for API compatibility with the base
                method; does not alter the validation path.

        Returns:
            bool: True when every validation phase passes.
methoddef walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')#
Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None
propertyproperty xml#
Return the content as an XML string, converting from dict if necessary.
propertyproperty yaml#
Return the content as a YAML string.

class ResolutionStatus#

Lifecycle state of a Profile's control resolution.

    Members:
        UNRESOLVED (str): "unresolved" — imports have not yet been resolved.
        RESOLVING (str): "resolving" — resolution is in progress.
        RESOLVED (str): "resolved" — the resolved catalog is available.
        BLOCKED (str): "blocked" — resolution could not complete (e.g. missing import).
        EXPIRED (str): "expired" — a previously resolved catalog is stale.
No public members.
functiondef format_index_errors(errors: list) -> str#
Render metaschema-walk errors (from ``_walk_instance``) as a compact one-liner.

    Args:
        errors (list, required): The structured error dicts collected by a walk.

    Returns:
        str: A ``"; "``-joined summary, one clause per error.

oscal.oscal_implementation#

oscal_implementation — OSCAL implementation-layer model classes and helpers.

Provides the model classes for the OSCAL implementation models:
``ComponentDefinition`` (reusable control implementations for components) and
``SSP`` (System Security Plan). Both subclass ``OSCAL`` from ``oscal_content``.
Module-level helper functions build the nested SSP assemblies (components,
implemented requirements, by-component statements, responsible roles) and are
also exposed as ``SSP`` methods where appropriate.

Module constants:
    (none exported)

class ComponentDefinition#

OSCAL Component Definition (cDef) model.

    Represents reusable component definitions that describe how components
    satisfy controls. Subclasses ``OSCAL``.
classmethodclassmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)#
Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.
methoddef append_child(self, path: 'str', child: 'dict') -> 'dict | None'#
Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.
methoddef append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'#
Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.
methoddef dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'#
Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.
methoddef dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'#
Serialize the current content to a string in the specified format.
        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.
propertyproperty duplicate_imports#
Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.
propertyproperty failed_imports#
Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.
methoddef find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'#
Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id) or ``party`` (by uuid), or a ``control``/``group``/``param``/``part`` (by id).
        This walks ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.
classmethodclassmethod from_string(cls, content: 'str', *, href: 'str | None' = None)#
Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).
methoddef get_oscal_object(self, uuid, _seen=None)#
Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.
methoddef get_parameter_by_id(self, param_id: 'str') -> 'Optional[dict]'#
Return a safe copy of a parameter defined anywhere in scope, or None.

        Searches this document and its import tree for a ``param`` with the given id —
        covering parameters defined at control, group, or catalog level (and reached
        through imported catalogs/profiles). Subclasses may override to prefer resolved
        content.
methoddef ignore_import(self, href: 'str') -> 'bool'#
Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.
propertyproperty import_tree#
Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.
propertyproperty imports_resolved#
bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).
methoddef initial_validation(self, content: 'str') -> 'bool'#
Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.
propertyproperty is_acquired#
bool: True once content has been acquired (``content_state >= ACQUIRED``).
propertyproperty is_cache_expired#
True when remote cached content has exceeded its TTL.
propertyproperty is_editable#
Can this content be modified?
propertyproperty is_fresh#
True when content is local or cached and within its TTL.
propertyproperty is_read_only#
bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.
propertyproperty is_remote#
bool: True when the content originates from a remote source (not a local file).
propertyproperty is_stale#
True when remote cached content has exceeded its TTL.
propertyproperty is_valid#
bool: True when content passes OSCAL validation (``content_state >= VALID``).
propertyproperty is_well_formed#
bool: True when content is well-formed (``content_state >= WELL_FORMED``).
propertyproperty json#
Return the content as a JSON string.
methoddef json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
classmethodclassmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)#
Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior. Use ``loads(...)``
        for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        resolution and fallback sources.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.
classmethodclassmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)#
Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.
classmethodclassmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')#
Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.
classmethodclassmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)#
Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.
propertyproperty origin_state#
Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.
methoddef put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'#
Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.
methoddef query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
methoddef reachable_ids(self, _seen=None) -> 'set'#
Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.
methoddef rebuild_import_tree(self) -> 'dict'#
Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.
methoddef remove_import(self, href: 'str') -> 'bool'#
Remove an import entry from both import_list and the document content.

        The import *statement* is deleted from ``self._dict``, placing the
        document in an edited and unsaved state.  Any back-matter resource
        referenced by the import via a URI fragment (``href="#uuid"``) is
        intentionally preserved — only the import element itself is removed.

        The cached import_tree is updated in-place (same object, one node
        shorter).  content_state is recomputed: if the removed entry was the
        last thing blocking resolution, content_state advances to
        IMPORTS_RESOLVED.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and IGNORED are preferred over
        INVALID which is preferred over READY — so the problematic entry is
        always targeted.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and removed, False if not found or the
            content is read-only.
methoddef resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'#
Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-assessment-plan/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.
methoddef retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef set_metadata(self, content: 'dict' = {}) -> 'bool'#
Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.
propertyproperty unresolved_imports#
Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.
methoddef validate(self, format: 'str' = '') -> 'bool'#
Validate OSCAL content against the metaschema index in sequenced phases.

        Phases (each recorded in ``validation_status``):
          structure      – all required fields and hierarchy are present
          data-types     – every leaf value matches its declared OSCAL datatype
          allowed-values – every constrained value is within its enumerated set
          cardinality    – every array satisfies its min-occurs/max-occurs bounds
          choice         – every choice is mutually exclusive (at most one member present) and has a member when one is required

        ``validation_status["well-formed"]`` is set by ``initial_validation()``, not here.
        All phases always run regardless of earlier failures, giving a complete picture
        of issues in a single call.  The format argument is accepted for API
        compatibility but does not alter the validation path — ``_dict`` is always the
        authoritative representation.

        Returns True only when every phase passes (content_state reaches VALID).
methoddef walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')#
Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None
propertyproperty xml#
Return the content as an XML string, converting from dict if necessary.
propertyproperty yaml#
Return the content as a YAML string.

class SSP#

OSCAL System Security Plan (SSP) model.

    Subclasses ``OSCAL`` and adds SSP-specific methods for managing system
    components, implemented requirements, and by-component statements.
classmethodclassmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)#
Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.
methoddef append_child(self, path: 'str', child: 'dict') -> 'dict | None'#
Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.
methoddef append_component(self, component_type: 'str', component_title: 'str', component_description: 'str', op_status: 'str' = 'operational', component_uuid: 'str' = '', props: 'list' = [], links: 'list' = [], remarks: 'str' = '') -> 'Optional[dict]'#
Add a component to the SSP's ``system-implementation`` section.

        Args:
            component_type (str, required): The component ``type`` (e.g. "software").
            component_title (str, required): The component title.
            component_description (str, required): The component description.
            op_status (str, optional): Operational ``status.state`` value.
                Defaults to "operational".
            component_uuid (str, optional): UUID for the component. A new UUID is
                generated when empty.
            props (list, optional): Property dicts to add.
            links (list, optional): Link dicts to add.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            Optional[dict]: The newly created component dict, or None on failure.
methoddef append_impl_requirement(self, control_id: 'str', props: 'list' = [], links: 'list' = [], remarks: 'str' = '') -> 'Optional[dict]'#
Add an implemented-requirement to the SSP's ``control-implementation`` section.

        Args:
            control_id (str, required): The ID of the control being implemented.
            props (list, optional): Property dicts to add.
            links (list, optional): Link dicts to add.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            Optional[dict]: The newly created implemented-requirement dict (with a
                generated UUID), or None on failure.
methoddef append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'#
Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.
methoddef dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'#
Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.
methoddef dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'#
Serialize the current content to a string in the specified format.
        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.
propertyproperty duplicate_imports#
Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.
propertyproperty failed_imports#
Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.
methoddef find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'#
Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id) or ``party`` (by uuid), or a ``control``/``group``/``param``/``part`` (by id).
        This walks ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.
classmethodclassmethod from_string(cls, content: 'str', *, href: 'str | None' = None)#
Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).
methoddef get_oscal_object(self, uuid, _seen=None)#
Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.
methoddef get_parameter_by_id(self, param_id: 'str') -> 'Optional[dict]'#
Return a safe copy of a parameter defined anywhere in scope, or None.

        Searches this document and its import tree for a ``param`` with the given id —
        covering parameters defined at control, group, or catalog level (and reached
        through imported catalogs/profiles). Subclasses may override to prefer resolved
        content.
methoddef ignore_import(self, href: 'str') -> 'bool'#
Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.
propertyproperty import_tree#
Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.
propertyproperty imports_resolved#
bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).
methoddef initial_validation(self, content: 'str') -> 'bool'#
Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.
propertyproperty is_acquired#
bool: True once content has been acquired (``content_state >= ACQUIRED``).
propertyproperty is_cache_expired#
True when remote cached content has exceeded its TTL.
propertyproperty is_editable#
Can this content be modified?
propertyproperty is_fresh#
True when content is local or cached and within its TTL.
propertyproperty is_read_only#
bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.
propertyproperty is_remote#
bool: True when the content originates from a remote source (not a local file).
propertyproperty is_stale#
True when remote cached content has exceeded its TTL.
propertyproperty is_valid#
bool: True when content passes OSCAL validation (``content_state >= VALID``).
propertyproperty is_well_formed#
bool: True when content is well-formed (``content_state >= WELL_FORMED``).
propertyproperty json#
Return the content as a JSON string.
methoddef json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
classmethodclassmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)#
Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior. Use ``loads(...)``
        for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        resolution and fallback sources.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.
classmethodclassmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)#
Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.
classmethodclassmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')#
Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.
classmethodclassmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)#
Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.
propertyproperty origin_state#
Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.
methoddef put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'#
Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.
methoddef query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
methoddef reachable_ids(self, _seen=None) -> 'set'#
Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.
methoddef rebuild_import_tree(self) -> 'dict'#
Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.
methoddef remove_import(self, href: 'str') -> 'bool'#
Remove an import entry from both import_list and the document content.

        The import *statement* is deleted from ``self._dict``, placing the
        document in an edited and unsaved state.  Any back-matter resource
        referenced by the import via a URI fragment (``href="#uuid"``) is
        intentionally preserved — only the import element itself is removed.

        The cached import_tree is updated in-place (same object, one node
        shorter).  content_state is recomputed: if the removed entry was the
        last thing blocking resolution, content_state advances to
        IMPORTS_RESOLVED.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and IGNORED are preferred over
        INVALID which is preferred over READY — so the problematic entry is
        always targeted.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and removed, False if not found or the
            content is read-only.
methoddef resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'#
Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-assessment-plan/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.
methoddef retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef set_metadata(self, content: 'dict' = {}) -> 'bool'#
Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.
propertyproperty unresolved_imports#
Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.
methoddef validate(self, format: 'str' = '') -> 'bool'#
Validate OSCAL content against the metaschema index in sequenced phases.

        Phases (each recorded in ``validation_status``):
          structure      – all required fields and hierarchy are present
          data-types     – every leaf value matches its declared OSCAL datatype
          allowed-values – every constrained value is within its enumerated set
          cardinality    – every array satisfies its min-occurs/max-occurs bounds
          choice         – every choice is mutually exclusive (at most one member present) and has a member when one is required

        ``validation_status["well-formed"]`` is set by ``initial_validation()``, not here.
        All phases always run regardless of earlier failures, giving a complete picture
        of issues in a single call.  The format argument is accepted for API
        compatibility but does not alter the validation path — ``_dict`` is always the
        authoritative representation.

        Returns True only when every phase passes (content_state reaches VALID).
methoddef walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')#
Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None
propertyproperty xml#
Return the content as an XML string, converting from dict if necessary.
propertyproperty yaml#
Return the content as a YAML string.
functiondef append_by_component(impl_req_obj: 'dict', component_uuid: 'str', description: 'str', by_component_uuid: 'str' = '', implementation_status: 'str' = 'implemented', remarks: 'str' = '') -> 'Optional[dict]'#
Add a by-component statement to an implemented-requirement dict.

    Args:
        impl_req_obj (dict, required): The implemented-requirement dict to modify.
        component_uuid (str, required): UUID of the referenced system component.
        description (str, required): Description of how the component satisfies
            the requirement.
        by_component_uuid (str, optional): UUID for the by-component entry. A new
            UUID is generated when empty.
        implementation_status (str, optional): ``implementation-status.state`` value.
            Defaults to "implemented".
        remarks (str, optional): Remarks prose (markdown).

    Returns:
        Optional[dict]: The newly created by-component dict, or None on failure.
functiondef append_component(ssp_obj: 'OSCAL', component_type: 'str', component_title: 'str', component_description: 'str', op_status: 'str' = 'operational', component_uuid: 'str' = '', props: 'list' = [], links: 'list' = [], remarks: 'str' = '') -> 'Optional[dict]'#
Add a component to an SSP's ``system-implementation`` section.

    Args:
        ssp_obj (OSCAL, required): The SSP instance to modify.
        component_type (str, required): The component ``type`` (e.g. "software").
        component_title (str, required): The component title.
        component_description (str, required): The component description.
        op_status (str, optional): Operational ``status.state`` value.
            Defaults to "operational".
        component_uuid (str, optional): UUID for the component. A new UUID is
            generated when empty.
        props (list, optional): Property dicts to add.
        links (list, optional): Link dicts to add.
        remarks (str, optional): Remarks prose (markdown).

    Returns:
        Optional[dict]: The newly created component dict, or None on failure.
functiondef append_impl_requirement(ssp_obj: 'OSCAL', control_id: 'str', props: 'list' = [], links: 'list' = [], remarks: 'str' = '') -> 'Optional[dict]'#
Add an implemented-requirement to an SSP's ``control-implementation`` section.

    Args:
        ssp_obj (OSCAL, required): The SSP instance to modify.
        control_id (str, required): The ID of the control being implemented.
        props (list, optional): Property dicts to add.
        links (list, optional): Link dicts to add.
        remarks (str, optional): Remarks prose (markdown).

    Returns:
        Optional[dict]: The newly created implemented-requirement dict (with a
            generated UUID), or None on failure.
functiondef append_responsible_role(oscal_obj: 'dict', role_id: 'str', party_uuids: 'list' = [], remarks: 'str' = '') -> 'dict'#
Add a responsible-role entry to an OSCAL object dict.

    Args:
        oscal_obj (dict, required): The parent OSCAL dict to add the role to.
        role_id (str, required): The ID of the role being assigned.
        party_uuids (list, optional): UUIDs of the parties fulfilling the role.
        remarks (str, optional): Remarks prose (markdown).

    Returns:
        dict: The newly created responsible-role dict.

oscal.oscal_assessment#

oscal_assessment — OSCAL assessment-layer model classes.

Provides the model classes for the OSCAL assessment models: ``AssessmentPlan``
(Security Assessment Plan / SAP), ``AssessmentResults`` (Security Assessment
Results / SAR), and ``POAM`` (Plan of Action and Milestones). Each subclasses
``OSCAL`` from ``oscal_content`` and inherits its common load/save/validate and
query behavior.

Module constants:
    (none exported)

class AssessmentPlan#

OSCAL Assessment Plan (AP / SAP) model.

    Represents an assessment plan that defines the scope, assets, activities,
    and tasks for a security assessment. Subclasses ``OSCAL``.
classmethodclassmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)#
Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.
methoddef append_child(self, path: 'str', child: 'dict') -> 'dict | None'#
Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.
methoddef append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'#
Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.
methoddef dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'#
Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.
methoddef dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'#
Serialize the current content to a string in the specified format.
        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.
propertyproperty duplicate_imports#
Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.
propertyproperty failed_imports#
Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.
methoddef find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'#
Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id) or ``party`` (by uuid), or a ``control``/``group``/``param``/``part`` (by id).
        This walks ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.
classmethodclassmethod from_string(cls, content: 'str', *, href: 'str | None' = None)#
Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).
methoddef get_oscal_object(self, uuid, _seen=None)#
Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.
methoddef get_parameter_by_id(self, param_id: 'str') -> 'Optional[dict]'#
Return a safe copy of a parameter defined anywhere in scope, or None.

        Searches this document and its import tree for a ``param`` with the given id —
        covering parameters defined at control, group, or catalog level (and reached
        through imported catalogs/profiles). Subclasses may override to prefer resolved
        content.
methoddef ignore_import(self, href: 'str') -> 'bool'#
Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.
propertyproperty import_tree#
Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.
propertyproperty imports_resolved#
bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).
methoddef initial_validation(self, content: 'str') -> 'bool'#
Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.
propertyproperty is_acquired#
bool: True once content has been acquired (``content_state >= ACQUIRED``).
propertyproperty is_cache_expired#
True when remote cached content has exceeded its TTL.
propertyproperty is_editable#
Can this content be modified?
propertyproperty is_fresh#
True when content is local or cached and within its TTL.
propertyproperty is_read_only#
bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.
propertyproperty is_remote#
bool: True when the content originates from a remote source (not a local file).
propertyproperty is_stale#
True when remote cached content has exceeded its TTL.
propertyproperty is_valid#
bool: True when content passes OSCAL validation (``content_state >= VALID``).
propertyproperty is_well_formed#
bool: True when content is well-formed (``content_state >= WELL_FORMED``).
propertyproperty json#
Return the content as a JSON string.
methoddef json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
classmethodclassmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)#
Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior. Use ``loads(...)``
        for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        resolution and fallback sources.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.
classmethodclassmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)#
Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.
classmethodclassmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')#
Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.
classmethodclassmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)#
Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.
propertyproperty origin_state#
Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.
methoddef put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'#
Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.
methoddef query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
methoddef reachable_ids(self, _seen=None) -> 'set'#
Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.
methoddef rebuild_import_tree(self) -> 'dict'#
Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.
methoddef remove_import(self, href: 'str') -> 'bool'#
Remove an import entry from both import_list and the document content.

        The import *statement* is deleted from ``self._dict``, placing the
        document in an edited and unsaved state.  Any back-matter resource
        referenced by the import via a URI fragment (``href="#uuid"``) is
        intentionally preserved — only the import element itself is removed.

        The cached import_tree is updated in-place (same object, one node
        shorter).  content_state is recomputed: if the removed entry was the
        last thing blocking resolution, content_state advances to
        IMPORTS_RESOLVED.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and IGNORED are preferred over
        INVALID which is preferred over READY — so the problematic entry is
        always targeted.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and removed, False if not found or the
            content is read-only.
methoddef resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'#
Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-assessment-plan/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.
methoddef retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef set_metadata(self, content: 'dict' = {}) -> 'bool'#
Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.
propertyproperty unresolved_imports#
Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.
methoddef validate(self, format: 'str' = '') -> 'bool'#
Validate OSCAL content against the metaschema index in sequenced phases.

        Phases (each recorded in ``validation_status``):
          structure      – all required fields and hierarchy are present
          data-types     – every leaf value matches its declared OSCAL datatype
          allowed-values – every constrained value is within its enumerated set
          cardinality    – every array satisfies its min-occurs/max-occurs bounds
          choice         – every choice is mutually exclusive (at most one member present) and has a member when one is required

        ``validation_status["well-formed"]`` is set by ``initial_validation()``, not here.
        All phases always run regardless of earlier failures, giving a complete picture
        of issues in a single call.  The format argument is accepted for API
        compatibility but does not alter the validation path — ``_dict`` is always the
        authoritative representation.

        Returns True only when every phase passes (content_state reaches VALID).
methoddef walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')#
Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None
propertyproperty xml#
Return the content as an XML string, converting from dict if necessary.
propertyproperty yaml#
Return the content as a YAML string.

class AssessmentResults#

OSCAL Assessment Results (AR / SAR) model.

    Represents the findings, observations, and risks produced by executing an
    assessment plan. Subclasses ``OSCAL``.
classmethodclassmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)#
Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.
methoddef append_child(self, path: 'str', child: 'dict') -> 'dict | None'#
Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.
methoddef append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'#
Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.
methoddef dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'#
Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.
methoddef dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'#
Serialize the current content to a string in the specified format.
        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.
propertyproperty duplicate_imports#
Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.
propertyproperty failed_imports#
Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.
methoddef find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'#
Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id) or ``party`` (by uuid), or a ``control``/``group``/``param``/``part`` (by id).
        This walks ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.
classmethodclassmethod from_string(cls, content: 'str', *, href: 'str | None' = None)#
Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).
methoddef get_oscal_object(self, uuid, _seen=None)#
Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.
methoddef get_parameter_by_id(self, param_id: 'str') -> 'Optional[dict]'#
Return a safe copy of a parameter defined anywhere in scope, or None.

        Searches this document and its import tree for a ``param`` with the given id —
        covering parameters defined at control, group, or catalog level (and reached
        through imported catalogs/profiles). Subclasses may override to prefer resolved
        content.
methoddef ignore_import(self, href: 'str') -> 'bool'#
Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.
propertyproperty import_tree#
Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.
propertyproperty imports_resolved#
bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).
methoddef initial_validation(self, content: 'str') -> 'bool'#
Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.
propertyproperty is_acquired#
bool: True once content has been acquired (``content_state >= ACQUIRED``).
propertyproperty is_cache_expired#
True when remote cached content has exceeded its TTL.
propertyproperty is_editable#
Can this content be modified?
propertyproperty is_fresh#
True when content is local or cached and within its TTL.
propertyproperty is_read_only#
bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.
propertyproperty is_remote#
bool: True when the content originates from a remote source (not a local file).
propertyproperty is_stale#
True when remote cached content has exceeded its TTL.
propertyproperty is_valid#
bool: True when content passes OSCAL validation (``content_state >= VALID``).
propertyproperty is_well_formed#
bool: True when content is well-formed (``content_state >= WELL_FORMED``).
propertyproperty json#
Return the content as a JSON string.
methoddef json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
classmethodclassmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)#
Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior. Use ``loads(...)``
        for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        resolution and fallback sources.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.
classmethodclassmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)#
Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.
classmethodclassmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')#
Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.
classmethodclassmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)#
Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.
propertyproperty origin_state#
Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.
methoddef put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'#
Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.
methoddef query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
methoddef reachable_ids(self, _seen=None) -> 'set'#
Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.
methoddef rebuild_import_tree(self) -> 'dict'#
Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.
methoddef remove_import(self, href: 'str') -> 'bool'#
Remove an import entry from both import_list and the document content.

        The import *statement* is deleted from ``self._dict``, placing the
        document in an edited and unsaved state.  Any back-matter resource
        referenced by the import via a URI fragment (``href="#uuid"``) is
        intentionally preserved — only the import element itself is removed.

        The cached import_tree is updated in-place (same object, one node
        shorter).  content_state is recomputed: if the removed entry was the
        last thing blocking resolution, content_state advances to
        IMPORTS_RESOLVED.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and IGNORED are preferred over
        INVALID which is preferred over READY — so the problematic entry is
        always targeted.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and removed, False if not found or the
            content is read-only.
methoddef resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'#
Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-assessment-plan/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.
methoddef retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef set_metadata(self, content: 'dict' = {}) -> 'bool'#
Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.
propertyproperty unresolved_imports#
Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.
methoddef validate(self, format: 'str' = '') -> 'bool'#
Validate OSCAL content against the metaschema index in sequenced phases.

        Phases (each recorded in ``validation_status``):
          structure      – all required fields and hierarchy are present
          data-types     – every leaf value matches its declared OSCAL datatype
          allowed-values – every constrained value is within its enumerated set
          cardinality    – every array satisfies its min-occurs/max-occurs bounds
          choice         – every choice is mutually exclusive (at most one member present) and has a member when one is required

        ``validation_status["well-formed"]`` is set by ``initial_validation()``, not here.
        All phases always run regardless of earlier failures, giving a complete picture
        of issues in a single call.  The format argument is accepted for API
        compatibility but does not alter the validation path — ``_dict`` is always the
        authoritative representation.

        Returns True only when every phase passes (content_state reaches VALID).
methoddef walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')#
Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None
propertyproperty xml#
Return the content as an XML string, converting from dict if necessary.
propertyproperty yaml#
Return the content as a YAML string.

class POAM#

OSCAL Plan of Action and Milestones (POA&M) model.

    Represents tracked security findings and their planned remediation
    milestones. Subclasses ``OSCAL``.
classmethodclassmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)#
Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.
methoddef append_child(self, path: 'str', child: 'dict') -> 'dict | None'#
Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.
methoddef append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'#
Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.
methoddef dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'#
Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.
methoddef dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'#
Serialize the current content to a string in the specified format.
        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.
propertyproperty duplicate_imports#
Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.
propertyproperty failed_imports#
Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.
methoddef find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'#
Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id) or ``party`` (by uuid), or a ``control``/``group``/``param``/``part`` (by id).
        This walks ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.
classmethodclassmethod from_string(cls, content: 'str', *, href: 'str | None' = None)#
Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).
methoddef get_oscal_object(self, uuid, _seen=None)#
Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.
methoddef get_parameter_by_id(self, param_id: 'str') -> 'Optional[dict]'#
Return a safe copy of a parameter defined anywhere in scope, or None.

        Searches this document and its import tree for a ``param`` with the given id —
        covering parameters defined at control, group, or catalog level (and reached
        through imported catalogs/profiles). Subclasses may override to prefer resolved
        content.
methoddef ignore_import(self, href: 'str') -> 'bool'#
Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.
propertyproperty import_tree#
Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.
propertyproperty imports_resolved#
bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).
methoddef initial_validation(self, content: 'str') -> 'bool'#
Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.
propertyproperty is_acquired#
bool: True once content has been acquired (``content_state >= ACQUIRED``).
propertyproperty is_cache_expired#
True when remote cached content has exceeded its TTL.
propertyproperty is_editable#
Can this content be modified?
propertyproperty is_fresh#
True when content is local or cached and within its TTL.
propertyproperty is_read_only#
bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.
propertyproperty is_remote#
bool: True when the content originates from a remote source (not a local file).
propertyproperty is_stale#
True when remote cached content has exceeded its TTL.
propertyproperty is_valid#
bool: True when content passes OSCAL validation (``content_state >= VALID``).
propertyproperty is_well_formed#
bool: True when content is well-formed (``content_state >= WELL_FORMED``).
propertyproperty json#
Return the content as a JSON string.
methoddef json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
classmethodclassmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)#
Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior. Use ``loads(...)``
        for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        resolution and fallback sources.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.
classmethodclassmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)#
Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.
classmethodclassmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')#
Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.
classmethodclassmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)#
Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.
propertyproperty origin_state#
Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.
methoddef put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'#
Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.
methoddef query(self, path: 'str', context: 'dict | None' = None) -> 'list'#
Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.
methoddef query_one(self, path: 'str', context: 'dict | None' = None, default=None)#
Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.
methoddef reachable_ids(self, _seen=None) -> 'set'#
Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.
methoddef rebuild_import_tree(self) -> 'dict'#
Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.
methoddef remove_import(self, href: 'str') -> 'bool'#
Remove an import entry from both import_list and the document content.

        The import *statement* is deleted from ``self._dict``, placing the
        document in an edited and unsaved state.  Any back-matter resource
        referenced by the import via a URI fragment (``href="#uuid"``) is
        intentionally preserved — only the import element itself is removed.

        The cached import_tree is updated in-place (same object, one node
        shorter).  content_state is recomputed: if the removed entry was the
        last thing blocking resolution, content_state advances to
        IMPORTS_RESOLVED.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and IGNORED are preferred over
        INVALID which is preferred over READY — so the problematic entry is
        always targeted.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and removed, False if not found or the
            content is read-only.
methoddef resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'#
Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-assessment-plan/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.
methoddef retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'#
Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.
methoddef set_metadata(self, content: 'dict' = {}) -> 'bool'#
Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.
propertyproperty unresolved_imports#
Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.
methoddef validate(self, format: 'str' = '') -> 'bool'#
Validate OSCAL content against the metaschema index in sequenced phases.

        Phases (each recorded in ``validation_status``):
          structure      – all required fields and hierarchy are present
          data-types     – every leaf value matches its declared OSCAL datatype
          allowed-values – every constrained value is within its enumerated set
          cardinality    – every array satisfies its min-occurs/max-occurs bounds
          choice         – every choice is mutually exclusive (at most one member present) and has a member when one is required

        ``validation_status["well-formed"]`` is set by ``initial_validation()``, not here.
        All phases always run regardless of earlier failures, giving a complete picture
        of issues in a single call.  The format argument is accepted for API
        compatibility but does not alter the validation path — ``_dict`` is always the
        authoritative representation.

        Returns True only when every phase passes (content_state reaches VALID).
methoddef walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')#
Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None
propertyproperty xml#
Return the content as an XML string, converting from dict if necessary.
propertyproperty yaml#
Return the content as a YAML string.

oscal.oscal_registry#

oscal_registry — process-shared identity map for loaded OSCAL objects.

Ensures a given OSCAL document is held in memory once and reused across branches
of an import tree (and across separate resolves in the same process), so two
references to the same file share a single object instead of loading it twice.

Objects are keyed by a composite **content identity** — ``(root-uuid,
last-modified, published)`` — which treats the same content as identical
regardless of format or location, with a **canonicalized href** as a pre-fetch
fast path. Values are held via weak references (``WeakValueDictionary``), so an
object stays registered only while some importer still holds it and is dropped
automatically once no longer referenced.

The default registry is a process-global singleton (``get_registry()``). The
``ObjectRegistry`` class is injectable so a future Workspace/session can own an
isolated instance.

Module constants:
    (none exported)

class ObjectRegistry#

An identity map of loaded OSCAL objects, keyed by content identity and href.

    Lookups check the canonical href first (cheap, pre-fetch), then the composite
    content-identity key. Stale entries — objects whose own TTL has expired
    (``is_cache_expired``) — are treated as misses and dropped so the caller
    reloads. Thread-safe via an internal lock.
methoddef __init__(self) -> None#
Initialize an empty registry (weak identity/href maps and a resolution stack).
methoddef alias_href(self, href: str, obj: Any) -> None#
Point an additional canonical href at an already-registered object.

        Args:
            href (str, required): The canonical href to alias.
            obj (Any, required): The object the href should resolve to.
methoddef clear(self) -> None#
Drop all entries (primarily for test isolation).
methoddef enter_resolving(self, href: str) -> None#
Mark a canonical href as currently being resolved (push onto the DFS stack).
methoddef exit_resolving(self, href: str) -> None#
Unmark a canonical href once its resolution completes (pop from the stack).
methoddef get(self, *, key: Optional[tuple] = None, href: str = '') -> Optional[Any]#
Return a live, fresh object matching ``href`` (checked first) or ``key``.

        Args:
            key (tuple | None, optional): Composite content-identity key.
            href (str, optional): Canonicalized href.

        Returns:
            Any | None: The registered object, or None on miss or when the match is
                stale (its ``is_cache_expired`` is True), in which case it is dropped.
methoddef is_resolving(self, href: str) -> bool#
Return True when ``href`` is an ancestor currently being resolved (a cycle).
methoddef register(self, obj: Any, *, key: Optional[tuple] = None, href: str = '') -> Any#
Register ``obj`` under its content-identity key and/or canonical href.

        Args:
            obj (Any, required): The object to register.
            key (tuple | None, optional): Composite content-identity key.
            href (str, optional): Canonicalized href.

        Returns:
            Any: The registered object (``obj``).
functiondef get_registry() -> oscal.oscal_registry.ObjectRegistry#
Return the currently active object registry.

    Returns the registry activated by :func:`use_registry` (e.g. a Workspace's own
    registry) when one is in effect on the current context, otherwise the
    process-global default. Because a document load cascades synchronously, every
    object created during the load picks up whichever registry is active.

    Returns:
        ObjectRegistry: The active registry, or the process-global default.
functiondef use_registry(registry: oscal.oscal_registry.ObjectRegistry)#
Activate ``registry`` for the duration of the ``with`` block.

    Objects created while this context is active (including transitively-loaded
    imports) use ``registry`` instead of the process-global default.

    Args:
        registry (ObjectRegistry, required): The registry to activate.

    Yields:
        ObjectRegistry: The activated registry.

oscal.oscal_cache#

oscal_cache — on-disk cache of remote OSCAL content.

Provides a persistent, cross-session cache of content fetched from remote URLs so
the same remote document is not downloaded repeatedly. It reuses the shared
``filecache`` file-store schema (the same table the support database uses) in a
separate ``local_cache.db`` located alongside the support database. The database
is created lazily on first use, not at startup.

Cached content is keyed by its (canonicalized) remote URL via the ``filecache``
``original_location`` column, with the fetch time stored in ``acquired``; an entry
is served only while it is within ``LOCAL_CACHE_TTL`` seconds of that time,
otherwise it is refetched and the entry refreshed.

This complements the in-memory object registry (``oscal_registry``): the registry
avoids re-loading/parsing a live object, while this cache avoids the network round
trip across process runs.

Caching is controlled per fetch by a :class:`CacheDirective`. The directive is
applied first, then the fetch is evaluated for local reuse vs. refresh. Because
the directive's TTL is compared against the entry's last-fetch time, changing the
TTL re-evaluates freshness against that time (e.g. an entry fetched 6h ago is
still fresh under a new 12h TTL). ``CACHE_NEVER`` purges any copy and always
fetches remotely; ``CACHE_FOREVER`` reuses a copy of any age; ``refresh`` forces a
refetch now.

Module constants:
    LOCAL_CACHE_TTL (int): Default seconds a cached item stays fresh (86400 = 24h).
    CACHE_FOREVER (int): TTL sentinel — never expires (reuse a copy of any age).
    CACHE_NEVER (int): TTL sentinel — do not cache (purge and always fetch remotely).
    LOCAL_CACHE_FILENAME (str): Filename of the cache database ("local_cache.db").

class CacheDirective#

A per-fetch instruction for how the remote-content cache should behave.

    The directive is applied first, then the fetch is evaluated: the (possibly
    overridden) TTL is compared against the cached entry's last-fetch time to decide
    whether the local copy is reused or the content is refetched.

    Attributes:
        ttl (int): Freshness window in seconds, or a sentinel — ``CACHE_FOREVER``
            (reuse a copy of any age) or ``CACHE_NEVER`` (purge and always fetch).
            Defaults to ``LOCAL_CACHE_TTL`` (24h).
        refresh (bool): When True, force a refetch now regardless of freshness
            (the refreshed content replaces the cached copy). Defaults to False.
classmethodclassmethod default(cls) -> 'CacheDirective'#
Default behavior: 24h TTL, no forced refresh.

        Returns:
            CacheDirective: A directive with the default TTL and no refresh.
classmethodclassmethod forever(cls) -> 'CacheDirective'#
Keep the cached copy until manually purged or refreshed.

        Returns:
            CacheDirective: A directive with ``ttl=CACHE_FOREVER``.
classmethodclassmethod never(cls) -> 'CacheDirective'#
Never cache: purge any existing copy and always fetch remotely.

        Returns:
            CacheDirective: A directive with ``ttl=CACHE_NEVER``.
classmethodclassmethod of(cls, seconds: int) -> 'CacheDirective'#
Cache with a specific TTL.

        Args:
            seconds (int, required): Freshness window in seconds.

        Returns:
            CacheDirective: A directive with ``ttl=seconds``.
classmethodclassmethod refresh_now(cls, ttl: int = 86400) -> 'CacheDirective'#
Force a refetch now, then cache the result.

        Args:
            ttl (int, optional): TTL to apply to the refreshed copy. Defaults to
                ``LOCAL_CACHE_TTL`` (24h).

        Returns:
            CacheDirective: A directive with ``refresh=True`` and the given ``ttl``.

class LocalCache#

Persistent cache of remote content, backed by a ``filecache`` table.

    The backing ``local_cache.db`` is opened/created lazily on first access. Entries
    are keyed by remote URL and expire ``LOCAL_CACHE_TTL`` seconds after they were
    fetched.
methoddef __init__(self, db_path: str = '') -> None#
Initialize the cache.

        Args:
            db_path (str, optional): Explicit path to the cache database. When empty,
                the path is resolved lazily to ``local_cache.db`` beside the support
                database.
methoddef clear(self) -> None#
Remove all cached entries (primarily for maintenance/tests).
methoddef get(self, url: str, directive: Optional[oscal.oscal_cache.CacheDirective] = None) -> Optional[str]#
Apply ``directive``, then return cached content for ``url`` if reusable.

        The directive is applied first: ``CACHE_NEVER`` purges any copy; ``refresh``
        forces a miss. Freshness is then evaluated by comparing the directive's TTL
        against the entry's last-fetch time (``CACHE_FOREVER`` reuses any age).

        Args:
            url (str, required): The (canonicalized) remote URL key.
            directive (CacheDirective | None, optional): Caching directive; defaults
                to :meth:`CacheDirective.default` (24h, no refresh).

        Returns:
            Optional[str]: The cached content to reuse, or None to fetch remotely.
methoddef purge(self, url: str) -> None#
Remove the cached entry for a single ``url`` (manual deletion).

        Args:
            url (str, required): The (canonicalized) remote URL key.
methoddef put(self, url: str, content, directive: Optional[oscal.oscal_cache.CacheDirective] = None) -> bool#
Store or refresh cached content for ``url``, resetting its last-fetch time.

        A ``CACHE_NEVER`` directive stores nothing (the content is used but not cached).

        Args:
            url (str, required): The (canonicalized) remote URL key.
            content (str | bytes, required): The fetched content to cache.
            directive (CacheDirective | None, optional): Caching directive; defaults
                to :meth:`CacheDirective.default`.

        Returns:
            bool: True when stored, False when skipped or on error.
functiondef get_local_cache() -> oscal.oscal_cache.LocalCache#
Return the process-global default remote-content cache.

    Returns:
        LocalCache: The shared cache instance (its database is created on first use).

oscal.oscal_workspace#

oscal_workspace — a Workspace that owns a set of related OSCAL documents.

A ``Workspace`` is the entry point for opening/creating OSCAL content as a project.
It owns an isolated in-memory object registry (so two workspaces are independent
object graphs) and injects that registry into every document it loads — including
transitively-loaded imports — via :func:`oscal.oscal_registry.use_registry`.

Within one workspace, opening the same file twice returns the **same** object
(root documents are shared, keyed by their source path/href), which is the basis
for multi-view editing. The remote-content disk cache remains process-global
(shared across workspaces).

A workspace can be **saved to a single SQLite project file** (content + state,
reusing the shared ``filecache`` schema) and reloaded self-contained, without
refetching. The project file also carries project-level metadata (title, path,
last-modified, remarks, and an extensible attributes bag) and is the intended
substrate for future multi-view / multi-user (locking, sync) support.

Module constants:
    WORKSPACE_META_TABLE (dict): Schema for the ``workspace_meta`` key/value table.
    WORKSPACE_DOCS_TABLE (dict): Schema for the ``workspace_documents`` table.

class Workspace#

A named set of related OSCAL documents with an isolated object registry.

    Documents opened through the workspace share one registry (imports dedup within
    the workspace) and one document identity map (opening the same source twice
    returns the same object). Carries project metadata and can be persisted to a
    single SQLite project file.
methoddef __init__(self, title: str = '', path: str = '', registry: Optional[oscal.oscal_registry.ObjectRegistry] = None) -> None#
Create a workspace.

        Args:
            title (str, optional): Project title.
            path (str, optional): Default path for the workspace's project file.
            registry (ObjectRegistry | None, optional): Registry to use; a fresh
                isolated one is created when omitted.
methoddef as_actor(self, actor: str)#
Context manager that attributes mutations in the block to ``actor``.

        Args:
            actor (str, required): The actor (view/session) id.

        Returns:
            A context manager activating ``actor`` as the current actor.
methoddef close(self, doc: oscal.oscal_content.OSCAL) -> None#
Stop tracking a document (releasing the workspace's strong reference and lock).
methoddef close_all(self) -> None#
Release all tracked documents and their locks.
propertyproperty documents#
list: The workspace's open root documents.
methoddef is_locked(self, doc: oscal.oscal_content.OSCAL) -> bool#
Return True when ``doc`` is write-locked by any actor.
classmethodclassmethod load(cls, path: str) -> 'Workspace'#
Load a workspace from its SQLite project file (self-contained; no refetch).

        Args:
            path (str, required): The workspace project file.

        Returns:
            Workspace: The reconstructed workspace, with documents rehydrated and
                their import trees rewired from the persisted content and state.
methoddef loads(self, content: str, *, href: Optional[str] = None) -> oscal.oscal_content.OSCAL#
Open in-memory content into the workspace.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): Source URI to key/track the document by.

        Returns:
            OSCAL: The opened document.
methoddef lock(self, doc: oscal.oscal_content.OSCAL, actor: Optional[str] = None) -> bool#
Acquire the write lock on ``doc`` for ``actor`` (exclusive editing).

        While held, the document is read-only to every other actor. Re-locking by
        the same actor succeeds (idempotent).

        Args:
            doc (OSCAL, required): The document to lock.
            actor (str | None, optional): The actor; defaults to the current actor.

        Returns:
            bool: True if the lock is held by ``actor`` afterward, False if another
                actor already holds it.

        Raises:
            ValueError: When no actor is given and none is active.
methoddef lock_holder(self, doc: oscal.oscal_content.OSCAL) -> Optional[str]#
Return the actor holding the write lock on ``doc``, or None.

        Args:
            doc (OSCAL, required): The document.

        Returns:
            Optional[str]: The lock-holding actor, or None when unlocked.
methoddef new(self, model_cls, title: str, **kwargs) -> oscal.oscal_content.OSCAL#
Create a new document in the workspace.

        Args:
            model_cls (type, required): A model class (e.g. ``Catalog``).
            title (str, required): Document title.
            **kwargs: Passed through to ``model_cls.new``.

        Returns:
            OSCAL: The new document, tracked by the workspace.
methoddef open(self, source) -> oscal.oscal_content.OSCAL#
Open a document into the workspace (loading it under the workspace registry).

        Re-opening the same source returns the already-open document (shared root).

        Args:
            source (str, required): A path or URI to load.

        Returns:
            OSCAL: The (possibly already-open) document.
propertyproperty registry#
ObjectRegistry: This workspace's isolated object registry.
methoddef save(self, path: str = '') -> bool#
Save the workspace (content + state + project metadata) to a SQLite file.

        Every reachable document (roots and their resolved imports) is serialized as
        JSON into the shared ``filecache`` table, with its state and import edges
        recorded in ``workspace_documents``; project metadata goes in
        ``workspace_meta``. Reusing ``filecache`` means no schema change to the
        support database.

        Args:
            path (str, optional): Destination path. Defaults to ``self.path``.

        Returns:
            bool: True on success.
methoddef unlock(self, doc: oscal.oscal_content.OSCAL, actor: Optional[str] = None) -> bool#
Release the write lock on ``doc``.

        Args:
            doc (OSCAL, required): The document to unlock.
            actor (str | None, optional): The actor; defaults to the current actor.
                A caller may only release its own lock (unless ``actor`` is None-held).

        Returns:
            bool: True when the document is unlocked afterward; False when the lock
                is held by a different actor and cannot be released.

oscal.metaschema_parser#

metaschema_parser — parse NIST resolved-metaschema XML into a structural index.

Parses OSCAL resolved-metaschema XML files into a dictionary representation of the
metaschema structure (assemblies, fields, flags, attributes, child elements, and
allowed-value constraints). The resulting index drives XML↔JSON conversion and
validation elsewhere in the library.

While there is some defensive coding, this module assumes metaschema files are
valid; it does not validate metaschema structure or content. It ignores unexpected
structures and logs a WARNING when it encounters expected but unhandled structures.

Module constants:
    SUPPRESS_XPATH_NOT_FOUND_WARNINGS (bool): Suppress warnings when an XPath yields
        no match.
    RUNAWAY_LIMIT (int): Maximum recursion/iteration count before aborting as a
        runaway.
    DEBUG_OBJECT (str): Name of a definition to trace for debugging ("" disables).
    PRUNE_JSON (bool): Remove None values and empty arrays from the resolved JSON output.
    OSCAL_DEFAULT_NAMESPACE (str): The NIST OSCAL namespace URI.
    METASCHEMA_DEFAULT_NAMESPACE (str): The NIST Metaschema namespace URI.
    METASCHEMA_TOP_IGNNORE (list): Top-level metaschema elements to ignore.
    METASCHEMA_TOP_KEEP (list): Top-level metaschema elements to process.
    METASCHEMA_PROPS_HANDLED (list): Metaschema ``prop`` names handled on definitions.
    METASCHEMA_RULE_PROPS_HANDLED (list): Metaschema ``prop`` names handled on rules.
    METASCHEMA_INDEX_PROPS_HANDLED (list): Metaschema ``prop`` names handled on indexes.
    METASCHEMA_ROP_NAMESPACE (list): Recognized metaschema property namespace URIs.
    METASCHEMA_ROOT_ELEMENT (str): Root element name of a metaschema document
        ("METASCHEMA").
    CONSTRAINT_ROOT_ELEMENT (str): Root element name of a meta-constraints document.
    CONSTRAINT_TOP_IGNORE (list): Top-level constraint elements to ignore.
    CONSTRAINT_TOP_KEEP (list): Top-level constraint elements to process.
    GREEN, BLUE, YELLOW, RED, ORANGE, MAGENTA, CYAN, PURPLE, BOLD, RESET (str):
        ANSI terminal escape codes used for colorized diagnostic output.

class MetaschemaParser#

Parses a single OSCAL resolved-metaschema XML document into a structural index.

    Holds the parsed metaschema tree and namespace/model context, resolves imported
    metaschemas, and walks assemblies, fields, and flags to build the nested index
    (nodes, attributes, allowed-value constraints) consumed by the converter and
    validator. Prefer the :meth:`create` classmethod to construct instances.
methoddef __init__(self, metaschema, support, oscal_version='', import_registry=None)#
Initialize a parser for one metaschema document.

        Args:
            metaschema (str, required): The resolved-metaschema XML content to parse.
            support (OSCALSupport, required): The OSCAL support object used to fetch
                imported metaschemas and store results.
            oscal_version (str, optional): The OSCAL version this metaschema belongs
                to. Defaults to "".
            import_registry (dict, optional): Shared registry of already-parsed imports
                (``href -> MetaschemaParser``), so a metaschema imported by more than one
                model is parsed once and reused. Endures across all models of one OSCAL
                version and is cleared when that version finishes. Defaults to ``None``
                (a fresh, isolated registry) — never a mutable default.
methoddef build_metaschema_tree(self)#
Build the full structural index for this metaschema's model.

        Recursively walks the root assembly to produce the node tree, applies
        constraints against a synthesized XML skeleton, prunes empty values, and
        annotates namespace conditions and JSON paths.

        Returns:
            dict: The metaschema index — model metadata plus a ``nodes`` tree — or an
                empty dict on error or when the root assembly cannot be found.
classmethodclassmethod create(cls, metaschema, support, oscal_version='', import_registry=None)#
Construct a ``MetaschemaParser`` (preferred factory over direct instantiation).

        Args:
            metaschema (str, required): The resolved-metaschema XML content to parse.
            support (OSCALSupport, required): The OSCAL support object.
            oscal_version (str, optional): The OSCAL version. Defaults to "".
            import_registry (dict, optional): Shared per-version import registry (see
                :meth:`__init__`). Defaults to ``None`` — a fresh registry per top-level
                parse. Recursive import parsing threads the parent's registry down.

        Returns:
            MetaschemaParser: A new parser instance.
methoddef get_markup_content(self, xExpr, context=None)#
Run an XPath query and return its markup content as a string.

        Handles results that are either plain strings or nodes containing HTML
        (markup) formatting, returning a string in either case.

        Args:
            xExpr (str, required): An XPath expression.
            context (Element, optional): Node to evaluate the expression against.
                Defaults to None (whole document).

        Returns:
            str: The matched content as a string (markup preserved as HTML).
methoddef graceful_accumulate(self, current_value, xExpr, context=None)#
Prepend a resolved markup value onto an accumulating list of values.

        Used where a field/assembly reference's values must be added to (rather than
        replace) any values already defined on the referenced define-field/assembly.

        Args:
            current_value (list, required): The existing accumulated values; wrapped in
                a list if not already one.
            xExpr (str, required): XPath expression yielding the markup value to add.
            context (Element, optional): Node to evaluate against. Defaults to None.

        Returns:
            list: ``current_value`` with the resolved value inserted at the front (when
                non-empty).
methoddef graceful_override(self, current_value, xExpr, context=None)#
Return an overriding value when present, otherwise keep the current value.

        Used where a field/assembly reference's value must replace any value already
        defined on the referenced define-field/assembly.

        Args:
            current_value (Any, required): The existing value to keep if no override
                is found.
            xExpr (str, required): XPath expression yielding the overriding value.
            context (Element, optional): Node to evaluate against. Defaults to None.

        Returns:
            Any: The resolved override value if non-empty, otherwise ``current_value``.
methoddef handle_attributes(self, metaschema_node, definition_obj: 'ET.Element', structure_type, name, parent)#
Map an XML definition's attributes onto a metaschema node.

        Translates attributes such as ``as-type`` (datatype), ``required``,
        ``min-occurs``/``max-occurs`` (cardinality), ``collapsible``, ``deprecated``,
        ``default``, and ``in-xml`` (XML wrapping) into node fields. Unhandled
        attributes are logged as warnings.

        Args:
            metaschema_node (dict, required): The node being built; updated in place.
            definition_obj (ET.Element, required): The XML definition element.
            structure_type (str, required): The definition's structure type.
            name (str, required): The definition name.
            parent (str, required): The parent path.

        Returns:
            dict: The updated ``metaschema_node``.
methoddef handle_children(self, name, structure_type, metaschema_node, context, handle_choice=0)#
Resolve the child model of an assembly (fields, assemblies, choices, any).

        Walks the assembly's ``model`` (or a specific ``choice`` group), recursing to
        build each child node and constructing synthetic nodes for ``choice``/``any``.

        Args:
            name (str, required): The definition name being processed.
            structure_type (str, required): "define-assembly" or "choice".
            metaschema_node (dict, required): The parent node (provides path/source).
            context (Element, required): The XML context to search within.
            handle_choice (int, optional): 1-based index of the choice group to process
                when ``structure_type`` is "choice". Defaults to 0.

        Returns:
            list: The resolved child node dicts.
methoddef handle_constraints(self, metaschema_node, definition_obj, structure_type, name, parent)#
Process ``<constraint><allowed-values>`` elements from a definition object.

        Targets are handled as follows: ``.`` or absent applies to the current node;
        ``@flag-name`` applies to the named flag child; complex Metapath targets are
        resolved against the XML skeleton (or stored with the unresolved target
        preserved). Multiple allowed-values sets for the same target are cumulative;
        ``allow-other`` conflicts resolve with 'yes' winning and emit a warning.

        Args:
            metaschema_node (dict, required): The node being built; updated in place.
            definition_obj (ET.Element, required): The XML definition element.
            structure_type (str, required): The definition's structure type.
            name (str, required): The definition name.
            parent (str, required): The parent path.

        Returns:
            dict: The updated ``metaschema_node``.
methoddef handle_flags(self, metaschema_node, definition_obj, structure_type, name, parent)#
Resolve the flags defined or referenced by a field or assembly.

        Finds each ``define-flag``/``flag`` child, recurses to build its node, and
        collects the results.

        Args:
            metaschema_node (dict, required): The parent node being built (used for
                path context).
            definition_obj (ET.Element, required): The field/assembly XML definition.
            structure_type (str, required): The parent's structure type.
            name (str, required): The parent definition name.
            parent (str, required): The parent path.

        Returns:
            list: The resolved flag node dicts (empty when none are present).
methoddef handle_group_as(self, metaschema_node, definition_obj: 'ET.Element', structure_type, name, parent)#
Apply a definition's ``group-as`` element to a metaschema node.

        Reads the ``group-as`` name and its ``in-xml``/``in-json`` grouping
        attributes and records them (and XML wrapping) on the node.

        Args:
            metaschema_node (dict, required): The node being built; updated in place.
            definition_obj (ET.Element, required): The XML definition element.
            structure_type (str, required): The definition's structure type.
            name (str, required): The definition name (for logging).
            parent (str, required): The parent path (used to build wrapped paths).

        Returns:
            dict: The updated ``metaschema_node``.
methoddef handle_props(self, metaschema_node, definition_obj, structure_type, name, parent)#
Map a definition's ``prop`` elements onto a metaschema node.

        Recognized props (``METASCHEMA_PROPS_HANDLED`` in the OSCAL namespace) are
        promoted to dedicated node keys; any other prop is appended to the node's
        ``props`` list as ``{"name", "value", "namespace"}``.

        Args:
            metaschema_node (dict, required): The node being built; updated in place.
            definition_obj (ET.Element, required): The XML definition element.
            structure_type (str, required): The definition's structure type.
            name (str, required): The definition name.
            parent (str, required): The parent path.

        Returns:
            dict: The updated ``metaschema_node``.
methoddef initialize_metaschema_index(self)#
Create a new, fully-keyed metaschema index-constraint dict with default values.

        Called before each index constraint is populated, to guarantee a consistent
        key set (id, level, name, target, handled props, etc.).

        Returns:
            dict: A new index dict with all expected keys initialized.
methoddef initialize_metaschema_node(self)#
Create a new, fully-keyed metaschema index node with default (empty) values.

        Called as each node is created, including the top-level node, to guarantee a
        consistent key set (path, name, datatype, cardinality, children, constraints,
        handled props, etc.).

        Returns:
            dict: A new node dict with all expected keys initialized.
methoddef initialize_metaschema_rule(self)#
Create a new, fully-keyed metaschema rule with default (empty) values.

        Called before each rule (e.g. an allowed-values constraint) is populated, to
        guarantee a consistent key set (id, level, datatype, allowed-values,
        allow-other, test, message, cardinality, etc.).

        Returns:
            dict: A new rule dict with all expected keys initialized.
methoddef look_in_imports(self, name, structure_type, parent='', ignore_local=False, already_searched=None)#
Search imported metaschemas for a definition by name and structure type.

        Args:
            name (str, required): The definition name to find.
            structure_type (str, required): The structure type to match
                (e.g. "define-assembly", "define-field", "define-flag").
            parent (str, optional): Parent path for the resolved node. Defaults to "".
            ignore_local (bool, optional): Passed through to recursion; ignore local
                definitions in the imported metaschema. Defaults to False.
            already_searched (list | None, optional): Definition names already visited,
                to prevent cycles. Defaults to None.

        Returns:
            dict | None: The resolved node from the imported metaschema, or None if not
                found.
methoddef recurse_metaschema(self, name, structure_type='define-assembly', parent='', ignore_local=False, already_searched=None, context=None, skip_children=False, use_name=None)#
Recursively build a metaschema index node and its descendants.

        Processes the XML definition for ``name`` and extracts a node dict describing
        its attributes, flags, and child elements, recursing into referenced
        definitions.

        Args:
            name (str, required): The definition/element name to process (e.g. a model
                or field name).
            structure_type (str, optional): The kind of definition — "define-assembly",
                "define-field", "define-flag", or an inline assembly/field/flag.
                Defaults to "define-assembly".
            parent (str, optional): Name of the parent definition, for logging/paths.
                Defaults to "".
            ignore_local (bool, optional): When True, ignore local (non-exported)
                definitions; set True when recursing into an imported metaschema so its
                private locals are not exposed. Defaults to False.
            already_searched (list | None, optional): Definition names already visited,
                to prevent infinite recursion. Defaults to None.
            context (Element, optional): XML context node to search within.
                Defaults to None.
            skip_children (bool, optional): When True, do not recurse into child
                elements. Defaults to False.
            use_name (str | None, optional): Override for the node's effective
                (use-)name. Defaults to None.

        Returns:
            dict: The metaschema index node for ``name`` (with nested children).
methoddef set_default_values(self, metaschema_node, definition_obj, structure_type, name, parent)#
Fill in default node values required by the metaschema specification.

        Applies spec defaults for any unset attributes — datatype ("string"),
        cardinality (0..1, or 1..1 for the root), ``json-collapsible``,
        ``deprecated``, ``default``, and XML wrapping for fields/assemblies.

        Args:
            metaschema_node (dict, required): The node being built; updated in place.
            definition_obj (ET.Element, required): The XML definition element.
            structure_type (str, required): The definition's structure type.
            name (str, required): The definition name.
            parent (str, required): The parent path; an empty value marks the root node.

        Returns:
            dict: The updated ``metaschema_node``.
methoddef setup_imports(self)#
Identify ``import`` elements and load each as a nested ``MetaschemaParser``.

        Imported metaschemas are fetched from the support database and stored in
        ``self.imports`` keyed by model name for later cross-metaschema lookups. Each
        import is also recorded as a child of this parser's ``import_inventory`` tree
        node, so the full import-relationship graph is captured.

        Duplicate imports (the same metaschema reached from more than one model, or via
        more than one path) are parsed only once: an :attr:`import_registry`, shared for
        the whole OSCAL version, is consulted first and its parsed object reused.

        Returns:
            None
methoddef str_node(self, node)#
Build a human-readable summary of a parsed metaschema index node.

        Args:
            node (dict, required): An index node produced by the parser, carrying
                keys such as ``formal-name``, ``use-name``, ``min-occurs``,
                ``max-occurs``, ``datatype``, ``children``, and ``constraints``.

        Returns:
            str: A multi-line, human-readable description of the node.
methoddef top_pass(self)#
Perform the first parsing pass: deserialize XML and read top-level metadata.

        Parses the metaschema content, then extracts the model name, schema name,
        OSCAL version, namespace, and JSON base URI, and sets up imports.

        Returns:
            bool: True if the XML was well-formed and parsed, False otherwise.
methoddef xpath(self, xExpr, context=None) -> 'ET.Element | list[ET.Element] | None'#
Run an XPath query and return the matching element(s).

        Args:
            xExpr (str, required): An XPath expression.
            context (Element, optional): Node to evaluate the expression against.
                When None, the expression runs against the whole document.
                Defaults to None.

        Returns:
            ET.Element | list[ET.Element] | None: A single element, a list of
                elements, or None on error / no match.
methoddef xpath_atomic(self, xExpr, context=None)#
Run an XPath query and return the first result as a string.

        Args:
            xExpr (str, required): An XPath expression.
            context (Element, optional): Node to evaluate the expression against.
                When None, the expression runs against the whole document.
                Defaults to None.

        Returns:
            str: The first matching result as a string, or "" on error / no match.
functiondef clean_none_values_recursive(dictionary)#
Recursively drop None values and empty containers from a dict.

    Removes key/value pairs whose value is None, and prunes empty nested dicts and
    lists (including dicts nested inside lists), returning a new cleaned dict.

    Args:
        dictionary (dict, required): The dictionary to clean.

    Returns:
        dict: A new dictionary with None values and empty containers removed.
functiondef parse_metaschema(support=None, oscal_version=None, save_to_fs=False) -> 'int'#
Parse and store the OSCAL metaschema index for one or all supported versions.

    Args:
        support (OSCALSupport, optional): The OSCAL support object. Currently the
            shared instance is fetched internally via ``get_support()`` regardless
            of this argument. Defaults to None.
        oscal_version (str, optional): The OSCAL version to parse. When None, all
            supported versions are processed. Defaults to None.
        save_to_fs (bool, optional): When True, also write each model index (and the
            parse report) to the local file system. Defaults to False (database only).

    Returns:
        int: 0 on success, 1 on error (process-style exit code).
functiondef parse_metaschema_specific(support, oscal_version, save_to_fs=False)#
Parse and store every model index for a specific OSCAL version.

    Each model index is stored in the support database as
    ``(version, model, "processed")``. When ``save_to_fs`` is True it is also written
    to ``support/<version>/<model>.json`` alongside the support database.

    Args:
        support (OSCALSupport, required): The OSCAL support object providing
            metaschema assets and asset storage.
        oscal_version (str, required): The OSCAL version to parse.
        save_to_fs (bool, optional): When True, also write each model index (and the
            parse report) to the local file system. Defaults to False (database only).

    Returns:
        bool: True if all models parsed and stored successfully, False otherwise.