Explore two CIViC bundles
Data files
Example files: civic-assertion-251-bundle.json · civic-assertion-9-bundle.json · civic-gks-bundle-v0.1.0.schema.json
Rendered from notebooks/civic/explore-civic-bundles.ipynb.
This notebook uses two small CIViC examples to show how ga4gh.gkm.bundles loads and explores GKM bundles. You will discover what each bundle contains, work with GKM Python objects, and follow relationships between them. Both examples use the same Python workflow even though CIViC defines their collection names and organization.
Set up
Import the bundles package
Locate the bundles and their schema
A bundle is a producer-defined JSON file containing related GKM objects grouped into named collections. A separate bundle schema describes that organization and identifies the GKM schema versions it uses. These examples include:
civic-aid-9-bundle.json, which contains a Tier II clinical-significance assertion under the AMP/ASCO/CAP Guidelines (2017).civic-aid-251-bundle.json, which contains a likely-oncogenic assertion under the ClinGen/CGC/VICC Guidelines for Oncogenicity (2022).civic-gks-bundle-v0.1.0.schema.json, which defines the CIViC-specific collections and identifies the GKM schemas they use.
The two bundles have the same organization, so they share one schema. The path setup works when Jupyter starts either at the repository root or in notebooks/civic.
bundle_dir = Path("bundles")
if not bundle_dir.is_dir():
bundle_dir = Path("notebooks/civic/bundles")
if not bundle_dir.is_dir():
message = "Run this notebook from the repository root or notebooks/civic"
raise FileNotFoundError(message)
aid_9_path = bundle_dir / "civic-assertion-9-bundle.json"
aid_251_path = bundle_dir / "civic-assertion-251-bundle.json"
schema_path = bundle_dir / "civic-gks-bundle-v0.1.0.schema.json"
Check GKM version compatibility
The bundle schema identifies the GKM product versions used by its collections. Check that those versions are compatible with the installed GKM Python libraries before interpreting the data.
This checks GKM versions; it does not perform full JSON Schema validation. load_bundle(..., schema=...) runs the same check automatically.
with schema_path.open(encoding="utf-8") as stream:
civic_schema = json.load(stream)
bundles.check_gkm_version_compatibility(civic_schema)
bundles.supported_gkm_versions()
Output:
{'gks-core': '1.1.0',
'vrs': '2.1.0-snapshot.2026-02.2',
'cat-vrs': '1.1.0-snapshot.2026-02.3',
'va-spec': '1.1.0-snapshot.2026-06.1'}
Load and inspect
Register and load the bundles
The bundle registry associates a short name with a bundle file, its schema, and producer information. This is useful when an application will load the same source more than once. Registration is local; it does not download or publish the bundle.
replace=True makes this cell safe to rerun. After registration, load_bundles() loads both examples by name and uses their registered schemas for the compatibility check.
for name, source in {
"civic-assertion-9": aid_9_path,
"civic-assertion-251": aid_251_path,
}.items():
bundles.registry.register(
bundles.BundleRegistration(
name=name,
source=source,
schema=schema_path,
producer="CIViC",
),
replace=True,
)
loaded = bundles.load_bundles("civic-assertion-9", "civic-assertion-251")
loaded
Output:
{'civic-assertion-9': Bundle(name='civic-assertion-9', collections=17),
'civic-assertion-251': Bundle(name='civic-assertion-251', collections=17)}
Output:
(Bundle(name='civic-assertion-9', collections=17),
Bundle(name='civic-assertion-251', collections=17))
Handle an unknown bundle
Package-specific errors share the BundleError base class and identify the operation that failed. For example, loading an unknown registered name raises BundleNotFoundError.
try:
bundles.load_bundle("civic-assertion-missing")
except bundles.BundleNotFoundError as error:
print(error)
Output:
Inspect the collections
The Starter Kit preserves the producer's collection names. For example, CIViC's sequenceReference collection maps sequence identifiers to sequence-reference objects. Names such as sequenceReference, evidence, and assertion come from CIViC's bundle schema. Use collection_names() to discover what a loaded bundle contains.
Output:
('sequenceReference',
'location',
'variant',
'feature',
'molecularProfile',
'disease',
'phenotype',
'conditionSet',
'therapy',
'therapyGroup',
'variantOrigin',
'source',
'method',
'organization',
'proposition',
'evidence',
'assertion')
Access a collection
Each named collection is represented by a BundleCollection, which maps the producer's object identifiers to objects. Collections support attribute access, mapping syntax, and the explicit collection() method.
sequence_references = aid_9.sequenceReference
assert isinstance(sequence_references, bundles.BundleCollection)
assert sequence_references is aid_9["sequenceReference"]
assert sequence_references is aid_9.collection("sequenceReference")
sequence_references
Output:
Handle an unknown collection
Attribute access with an unknown collection name raises BundleCollectionNotFoundError, which is also an AttributeError.
try:
aid_9.sequenceReferences # noqa: B018
except bundles.BundleCollectionNotFoundError as error:
print(error)
Output:
List object identifiers
Use keys() to see which object identifiers are available in the collection.
Output:
['SQ.6CnHhDq_bDCsuIBf0AzxtKq_lXYM7f0m',
'SQ.EJQv9rQmiD76iFmXsLwCy2dJHOFO3bpj',
'SQ.pnAqCRBrTsUoBghSD1yp_jXWSmlbdh4g']
Work with GKM objects
Use a typed GKM object
When an object is supported by an installed GKM reference library, ga4gh.gkm.bundles loads it as that library's Python model instead of leaving it as a raw dictionary. The example below retrieves a VRS SequenceReference by its identifier.
sequence_id = "SQ.6CnHhDq_bDCsuIBf0AzxtKq_lXYM7f0m"
sequence_reference = sequence_references[sequence_id]
sequence_reference.type, sequence_reference.refgetAccession
Output:
Handle an unknown identifier
Looking up an unknown identifier in a BundleCollection raises BundleObjectNotFoundError, which is also a KeyError.
missing_sequence_id = "SQ.not-found"
try:
sequence_references[missing_sequence_id]
except bundles.BundleObjectNotFoundError as error:
print(error)
Output:
Resolve a local reference
The assertion does not contain a second copy of its proposition. Its proposition field instead contains a local JSON Pointer such as #/proposition/..., which identifies a path within the same bundle. resolve() follows that reference.
assertion_9 = aid_9.assertion["civic.aid:9"]
proposition_9 = aid_9.resolve(assertion_9["proposition"])
resolved_proposition = {
"localReference": assertion_9["proposition"],
"resolvedObject": proposition_9.model_dump(mode="json", exclude_none=True),
}
print(json.dumps(resolved_proposition, indent=2))
Output:
{
"localReference": "#/proposition/civic.proposition:f0SrtLbW05PqfqLs-hOK4tZxI3xO3kMO",
"resolvedObject": {
"id": "civic.proposition:f0SrtLbW05PqfqLs-hOK4tZxI3xO3kMO",
"type": "VariantClinicalSignificanceProposition",
"subjectVariant": "#/molecularProfile/civic.mpid:1594",
"geneContextQualifier": "#/feature/civic.gid:154",
"alleleOriginQualifier": "#/variantOrigin/civic.variantOrigin:SOMATIC",
"predicate": "hasClinicalSignificanceFor",
"objectCondition": "#/disease/civic.did:2950"
}
}
Dereference related objects
dereference() replaces every reachable local pointer with inline data, producing a self-contained JSON-compatible view. The complete result can be large, so this example displays only a few fields from the expanded variant and condition.
inline_proposition_9 = aid_9.dereference(proposition_9)
inline_preview = {
"type": inline_proposition_9["type"],
"subjectVariant": {
"id": inline_proposition_9["subjectVariant"]["id"],
"name": inline_proposition_9["subjectVariant"]["name"],
},
"objectCondition": {
"id": inline_proposition_9["objectCondition"]["id"],
"name": inline_proposition_9["objectCondition"]["name"],
},
}
print(json.dumps(inline_preview, indent=2))
Output:
{
"type": "VariantClinicalSignificanceProposition",
"subjectVariant": {
"id": "civic.mpid:1594",
"name": "ACVR1 G328V"
},
"objectCondition": {
"id": "civic.did:2950",
"name": "Diffuse Midline Glioma, H3 K27-altered"
}
}
Compare assertions
The two bundles have the same CIViC-defined organization, but their assertions answer different scientific questions. A small helper can apply the same workflow to both and compare selected fields.
def assertion_summary(bundle: bundles.Bundle, assertion_id: str) -> dict[str, str]:
"""Summarize one assertion and its referenced proposition.
:param bundle: Bundle containing the assertion.
:param assertion_id: Collection key for the assertion.
:return: Selected assertion, classification, and proposition fields.
"""
assertion = bundle.assertion[assertion_id]
proposition = bundle.resolve(assertion["proposition"])
coding = assertion["classification"]["primaryCoding"]
return {
"assertion": assertion_id,
"classification_system": coding["system"],
"classification": coding["code"],
"proposition_type": str(proposition.type),
"predicate": str(proposition.predicate),
}
summaries = [
assertion_summary(aid_9, "civic.aid:9"),
assertion_summary(aid_251, "civic.aid:251"),
]
print(json.dumps(summaries, indent=2))
Output:
[
{
"assertion": "civic.aid:9",
"classification_system": "AMP/ASCO/CAP Guidelines, 2017",
"classification": "tier ii",
"proposition_type": "VariantClinicalSignificanceProposition",
"predicate": "hasClinicalSignificanceFor"
},
{
"assertion": "civic.aid:251",
"classification_system": "ClinGen/CGC/VICC Guidelines for Oncogenicity, 2022",
"classification": "likely oncogenic",
"proposition_type": "VariantOncogenicityProposition",
"predicate": "isOncogenicFor"
}
]
Prepare data for another tool
Suppose a review tool needs one assertion and its linked variant and condition, rather than the complete CIViC bundle. to_dict() provides JSON-compatible bundle values, which you can select and combine with the dereferenced proposition to create that smaller payload.
The resulting review_payload is application-specific data, not a CIViC bundle. This walkthrough keeps it in memory; an application could serialize it as JSON when needed.
Use Bundle.to_dict() for the complete bundle and model_dump() for an individual GKM Python model.
bundle_data = aid_9.to_dict()
review_payload = {
"assertion": bundle_data["assertion"]["civic.aid:9"],
"proposition": inline_proposition_9,
}
review_summary = {
"assertionId": review_payload["assertion"]["id"],
"classification": review_payload["assertion"]["classification"]["name"],
"variant": review_payload["proposition"]["subjectVariant"]["name"],
"condition": review_payload["proposition"]["objectCondition"]["name"],
}
print(json.dumps(review_summary, indent=2))
Output: