Skip to content

FHIR Packages Client

FHIR Package Registry client utilities.

FHIRPackageRegistryClient

Path: fhircraft.fhir.packages.client.FHIRPackageRegistryClient

FHIRPackageRegistryClient(base_url: Optional[str] = None, timeout: float = 30.0, session: Optional[Session] = None)

Lightweight client for the FHIR Package Registry API.

Supports both packages.simplifier.net and packages.fhir.org endpoints.

Parameters:

Name Type Description Default
base_url Optional[str]

Base URL for the API. Defaults to packages.simplifier.net

None
timeout float

Request timeout in seconds

30.0
session Optional[Session]

Optional requests session to use

None

Methods:

Name Description
list_package_versions

List all versions for a package.

download_package

Download a specific package version.

get_latest_version

Get the latest version tag for a package.

download_latest_package

Download the latest version of a package.

load_resources_from_package

Load a FHIR package from the registry and add all structure definitions.

Source code in fhircraft/fhir/packages/client.py
def __init__(
    self,
    base_url: Optional[str] = None,
    timeout: float = 30.0,
    session: Optional[requests.Session] = None,
):
    """
    Initialize the FHIR Package Registry client.

    Args:
        base_url (Optional[str]): Base URL for the API. Defaults to packages.simplifier.net
        timeout (float): Request timeout in seconds
        session (Optional[requests.Session]): Optional requests session to use
    """
    self.base_url = base_url or self.FHIR_ORG_BASE_URL
    self.timeout = timeout
    self.session = session or requests.Session()
    self.history = set()  # To track loaded packages and avoid duplicates

    # Set default headers
    self.session.headers.update(
        {
            "User-Agent": "fhircraft-package-client/1.0.0",
            "Accept": "application/json",
        }
    )

list_package_versions

list_package_versions(package_name: str) -> PackageMetadata

List all versions for a package.

Parameters:

Name Type Description Default
package_name str

Name of the package (e.g., "hl7.fhir.us.core")

required

Returns:

Type Description
PackageMetadata

(PackageMetadata) Package metadata object with all available versions

Raises:

Type Description
PackageNotFoundError

If the package is not found

FHIRPackageRegistryError

For other API errors

Source code in fhircraft/fhir/packages/client.py
def list_package_versions(self, package_name: str) -> PackageMetadata:
    """
    List all versions for a package.

    Args:
        package_name (str): Name of the package (e.g., "hl7.fhir.us.core")

    Returns:
        (PackageMetadata) Package metadata object with all available versions

    Raises:
        PackageNotFoundError: If the package is not found
        FHIRPackageRegistryError: For other API errors
    """
    url = urljoin(self.base_url + "/", package_name)

    try:
        response = self.session.get(url, timeout=self.timeout)

        if response.status_code == 404:
            raise PackageNotFoundError(f"Package '{package_name}' not found")

        response.raise_for_status()

        try:
            return PackageMetadata.model_validate(response.json())
        except ValidationError as e:
            raise FHIRPackageRegistryError(f"Invalid response format: {e}")

    except requests.RequestException as e:
        raise FHIRPackageRegistryError(f"Request failed: {e}")

download_package

download_package(package_name: str, package_version: str, extract: bool = False) -> Union[bytes, TarFile]

Download a specific package version.

Parameters:

Name Type Description Default
package_name str

Name of the package

required
package_version str

Version of the package

required
extract bool

If True, return extracted TarFile object, otherwise raw bytes

False

Returns:

Type Description
Union[bytes, TarFile]

(TarFile) Raw tar.gz bytes or extracted TarFile object

Raises:

Type Description
PackageNotFoundError

If the package or version is not found

FHIRPackageRegistryError

For other API errors

Source code in fhircraft/fhir/packages/client.py
def download_package(
    self, package_name: str, package_version: str, extract: bool = False
) -> Union[bytes, tarfile.TarFile]:
    """
    Download a specific package version.

    Args:
        package_name (str): Name of the package
        package_version (str): Version of the package
        extract (bool): If True, return extracted TarFile object, otherwise raw bytes

    Returns:
        (TarFile) Raw tar.gz bytes or extracted TarFile object

    Raises:
        PackageNotFoundError: If the package or version is not found
        FHIRPackageRegistryError: For other API errors
    """
    url = urljoin(self.base_url + "/", f"{package_name}/{package_version}")

    try:
        # Use different Accept header for binary download
        headers = {"Accept": "application/tar+gzip"}
        response = self.session.get(url, headers=headers, timeout=self.timeout)

        if response.status_code == 404:
            raise PackageNotFoundError(
                f"Package '{package_name}' version '{package_version}' not found"
            )

        response.raise_for_status()
        self.history.add(f"{package_name}@{package_version}")
        if extract:
            # Return extracted tarfile
            return tarfile.open(fileobj=io.BytesIO(response.content), mode="r:gz")
        else:
            # Return raw bytes
            return response.content

    except requests.RequestException as e:
        raise FHIRPackageRegistryError(f"Download failed: {e}")

get_latest_version

get_latest_version(package_name: str) -> Optional[str]

Get the latest version tag for a package.

Parameters:

Name Type Description Default
package_name str

Name of the package

required

Returns:

Type Description
Optional[str]

(str | None) Latest version string or None if not available

Source code in fhircraft/fhir/packages/client.py
def get_latest_version(self, package_name: str) -> Optional[str]:
    """
    Get the latest version tag for a package.

    Args:
        package_name (str): Name of the package

    Returns:
        (str | None) Latest version string or None if not available
    """
    metadata = self.list_package_versions(package_name)
    return metadata.dist_tags.latest if metadata.dist_tags else None

download_latest_package

download_latest_package(package_name: str, extract: bool = False) -> Union[bytes, TarFile]

Download the latest version of a package.

Parameters:

Name Type Description Default
package_name str

Name of the package

required
extract bool

If True, return extracted TarFile object, otherwise raw bytes

False

Returns:

Type Description
Union[bytes, TarFile]

(Union[bytes, tarfile.TarFile]) Raw tar.gz bytes or extracted TarFile object

Raises:

Type Description
PackageNotFoundError

If the package is not found or has no latest version

FHIRPackageRegistryError

For other API errors

Source code in fhircraft/fhir/packages/client.py
def download_latest_package(
    self, package_name: str, extract: bool = False
) -> Union[bytes, tarfile.TarFile]:
    """
    Download the latest version of a package.

    Args:
        package_name (str): Name of the package
        extract (bool): If True, return extracted TarFile object, otherwise raw bytes

    Returns:
        (Union[bytes, tarfile.TarFile]) Raw tar.gz bytes or extracted TarFile object

    Raises:
        PackageNotFoundError: If the package is not found or has no latest version
        FHIRPackageRegistryError: For other API errors
    """
    latest_version = self.get_latest_version(package_name)
    if not latest_version:
        raise PackageNotFoundError(
            f"No latest version found for package '{package_name}'"
        )

    return self.download_package(package_name, latest_version, extract=extract)

load_resources_from_package

load_resources_from_package(target_resource: str, package_name: str, package_version: Optional[str] = None, install_dependencies: bool = True, fail_if_exists: bool = False) -> list[dict]

Load a FHIR package from the registry and add all structure definitions.

Parameters:

Name Type Description Default
package_name str

Name of the package (e.g., "hl7.fhir.us.core")

required
package_version Optional[str]

Version of the package (defaults to latest)

None
install_dependencies bool

If True, checks and installs any dependencies of the package

True
fail_if_exists bool

If True, raise error if package already loaded

False

Raises:

Type Description
PackageNotFoundError

If package or version not found

FHIRPackageRegistryError

If download fails

RuntimeError

If package processing fails

Source code in fhircraft/fhir/packages/client.py
def load_resources_from_package(
    self,
    target_resource: str,
    package_name: str,
    package_version: Optional[str] = None,
    install_dependencies: bool = True,
    fail_if_exists: bool = False,
) -> list[dict]:
    """
    Load a FHIR package from the registry and add all structure definitions.

    Args:
        package_name: Name of the package (e.g., "hl7.fhir.us.core")
        package_version: Version of the package (defaults to latest)
        install_dependencies: If True, checks and installs any dependencies of the package
        fail_if_exists: If True, raise error if package already loaded

    Raises:
        PackageNotFoundError: If package or version not found
        FHIRPackageRegistryError: If download fails
        RuntimeError: If package processing fails
    """

    # Determine version to load
    target_version = package_version
    if not target_version:
        try:
            target_version = self.get_latest_version(package_name)
        except (PackageNotFoundError, FHIRPackageRegistryError) as e:
            raise PackageNotFoundError(
                f"Failed to get latest version for package {package_name}: {e}"
            )

    if not target_version:
        raise PackageNotFoundError(
            f"No latest version found for package {package_name}"
        )

    # Check if already loaded
    package_key = f"{package_name}@{target_version}"
    try:
        # Download and extract package
        result = self.download_package(package_name, target_version, extract=True)

        # Ensure we got a TarFile object (should be guaranteed when extract=True)
        if not isinstance(result, tarfile.TarFile):
            raise RuntimeError(
                f"Expected TarFile object but got {type(result)} when downloading package"
            )

        try:
            results, errors = self._process_package_tar(
                target_resource,
                result,
                install_dependencies,
            )

            if len(results) == 0:
                raise RuntimeError(
                    f"No valid resources ({target_resource}) found in package"
                )

            if errors:
                # Log errors but don't fail if we got some definitions
                error_summary = f"Loaded {len(results)} {target_resource} resources with {len(errors)} errors"
                print(f"Warning: {error_summary}")
                for error in errors[:5]:  # Show first 5 errors
                    print(f"  - {error}")
                if len(errors) > 5:
                    print(f"  ... and {len(errors) - 5} more errors")

            return results

        except (PackageNotFoundError, FHIRPackageRegistryError) as e:
            raise e
    except Exception as e:
        raise RuntimeError(f"Failed to process package {package_key}: {e}")

FHIRPackageRegistryError

Path: fhircraft.fhir.packages.client.FHIRPackageRegistryError

Bases: Exception

Base exception for FHIR Package Registry client errors.

PackageNotFoundError

Path: fhircraft.fhir.packages.client.PackageNotFoundError

Bases: FHIRPackageRegistryError

Raised when a package is not found.

download_latest_package

download_latest_package(package_name: str, base_url: Optional[str] = None, extract: bool = False) -> Union[bytes, TarFile]

Convenience function to download the latest version of a package.

Parameters:

Name Type Description Default
package_name str

Name of the package

required
base_url Optional[str]

Optional base URL (defaults to packages.simplifier.net)

None
extract bool

If True, return extracted TarFile object, otherwise raw bytes

False

Returns:

Type Description
Union[bytes, TarFile]

(Union[bytes, tarfile.TarFile]) Raw tar.gz bytes or extracted TarFile object

Source code in fhircraft/fhir/packages/client.py
def download_latest_package(
    package_name: str, base_url: Optional[str] = None, extract: bool = False
) -> Union[bytes, tarfile.TarFile]:
    """
    Convenience function to download the latest version of a package.

    Args:
        package_name (str): Name of the package
        base_url (Optional[str]): Optional base URL (defaults to packages.simplifier.net)
        extract (bool): If True, return extracted TarFile object, otherwise raw bytes

    Returns:
        (Union[bytes, tarfile.TarFile]) Raw tar.gz bytes or extracted TarFile object
    """
    client = FHIRPackageRegistryClient(base_url=base_url)
    return client.download_latest_package(package_name, extract=extract)

download_package

download_package(package_name: str, package_version: str, base_url: Optional[str] = None, extract: bool = False) -> Union[bytes, TarFile]

Convenience function to download a package.

Parameters:

Name Type Description Default
package_name str

Name of the package

required
package_version str

Version of the package

required
base_url Optional[str]

Optional base URL (defaults to packages.simplifier.net)

None
extract bool

If True, return extracted TarFile object, otherwise raw bytes

False

Returns:

Type Description
Union[bytes, TarFile]

(Union[bytes, tarfile.TarFile]) Raw tar.gz bytes or extracted TarFile object

Source code in fhircraft/fhir/packages/client.py
def download_package(
    package_name: str,
    package_version: str,
    base_url: Optional[str] = None,
    extract: bool = False,
) -> Union[bytes, tarfile.TarFile]:
    """
    Convenience function to download a package.

    Args:
        package_name (str): Name of the package
        package_version (str): Version of the package
        base_url (Optional[str]): Optional base URL (defaults to packages.simplifier.net)
        extract (bool): If True, return extracted TarFile object, otherwise raw bytes

    Returns:
        (Union[bytes, tarfile.TarFile]) Raw tar.gz bytes or extracted TarFile object
    """
    client = FHIRPackageRegistryClient(base_url=base_url)
    return client.download_package(package_name, package_version, extract=extract)

get_package_metadata

get_package_metadata(package_name: str, base_url: Optional[str] = None) -> PackageMetadata

Convenience function to get package metadata.

Parameters:

Name Type Description Default
package_name str

Name of the package

required
base_url Optional[str]

Optional base URL (defaults to packages.simplifier.net)

None

Returns:

Type Description
PackageMetadata

(PackageMetadata) Metadata of the package

Source code in fhircraft/fhir/packages/client.py
def get_package_metadata(
    package_name: str, base_url: Optional[str] = None
) -> PackageMetadata:
    """
    Convenience function to get package metadata.

    Args:
        package_name (str): Name of the package
        base_url (Optional[str]): Optional base URL (defaults to packages.simplifier.net)

    Returns:
        (PackageMetadata) Metadata of the package
    """
    client = FHIRPackageRegistryClient(base_url=base_url)
    return client.list_package_versions(package_name)