Cobalt Python Library for Akoma Ntoso XML

Learn how to use the Cobalt Python library to work with Akoma Ntoso XML documents, metadata, FRBR URIs, Act documents, validation, and document parsing.

Published

Updated

Read time 8 min read

Reviewed byDeepak Prasad

Cobalt Python Library for Akoma Ntoso XML

Cobalt is a Python library from Laws.Africa for working with Akoma Ntoso XML. It helps you create and edit legislative-style XML, work with document metadata on the tree, and parse or build FRBR URIs. The Cobalt documentation stresses that most work happens on the XML document itself, so you still need a working mental model of Akoma Ntoso.

This guide sticks to patterns that match the official quickstart and API docs, and to examples that were run against Cobalt 9.0.1 on Python 3.13. Adjust attribute access if you pin an older major version.

Tested on: Python 3.13.3; Cobalt 9.0.1; kernel 6.14.0-37-generic.


What is Cobalt in Python?

Akoma Ntoso (often shortened to AKN) is an XML vocabulary for legal and legislative documents: acts, bills, judgments, and related structures share predictable tags and metadata so systems can exchange, cite, and version them reliably. Cobalt does not replace that standard; it gives you Python types and helpers so you are not hand-editing raw XML strings for every change.

Cobalt adds concrete document classes (Act, Bill, Judgment, and others) built on a shared Akoma Ntoso base. When you assign something like act.title or act.frbr_uri, Cobalt updates the correct elements in an lxml objectify tree behind the scenes. When you call to_xml(), you get the full document back as serialized Akoma Ntoso. When you pass XML into the same class (Act(xml)), Cobalt parses it back into that model so you can keep editing in Python instead of juggling XPath for routine metadata.

Legal URIs in this world follow FRBR-style rules. Cobalt exposes FrbrUri so you can parse URI strings, read their parts (country, doctype, date, number, and so on), and rebuild work, expression, or manifestation URI forms without writing your own parser. For quality gates, cobalt.schemas can validate a document object against the bundled Akoma Ntoso 3.0 schemas (strict or lenient), which is harder to wire up yourself from scratch.

Think of Cobalt as domain-aware XML tooling for legislative AKN: the same underlying XML and namespaces you would use anyway, with a maintained object layer and utilities aimed at how laws are modeled.

The PyPI package cobalt is marked Production/Stable; the source and issue tracker live at github.com/laws-africa/cobalt.


When should you use Cobalt?

Use Cobalt when:

  • You load, edit, or produce Akoma Ntoso legislative or legal XML.
  • You need reliable helpers for FRBR URIs and document metadata on real AKN trees.
  • You want schema validation (strict or lenient) without hand-wiring XSD paths yourself.
  • You are fine working close to the XML and reading the Akoma Ntoso model when something goes wrong.

Do not reach for Cobalt when:

  • You only need generic XML or HTML parsing (the standard library, lxml, or defusedxml may be simpler).
  • Your content is not legal or legislative Akoma Ntoso.
  • You want a general Python XML tutorial with no domain vocabulary.

Install Cobalt using pip

Install from PyPI into a virtual environment (recommended on modern Linux distributions that mark the system interpreter as externally managed). Pin dependencies in a pip requirements file when Cobalt is part of a larger project:

text
python3 -m venv .venv
source .venv/bin/activate
pip install cobalt

The same command appears in the Cobalt quickstart. To pin the version this article was checked against:

text
pip install cobalt==9.0.1

At the time of writing, the package metadata on PyPI required Python 3.9+; confirm the current requirement on the cobalt PyPI page before upgrading interpreters.


Create a simple Akoma Ntoso Act with Cobalt

The documented entry point is Act from the cobalt package: build an empty act, set human-readable metadata and an FRBR URI string, inspect URI parts, then emit XML.

python
from cobalt import Act

act = Act()
act.title = "Act 10 of 1980"
act.frbr_uri = "/za/act/1980-05-03/10"

print(act.frbr_uri.year)
print(act.frbr_uri.date)
print(act.frbr_uri.number)
print(act.frbr_uri.doctype)

xml = act.to_xml()
print(xml[:200])
Output

to_xml() returns UTF-8 bytes; decode with xml.decode("utf-8") when you need a Unicode string (for example writing text mode or embedding in JSON). Running the example prints the year, full date, number, and document type from the parsed URI, then the opening bytes of the generated Akoma Ntoso XML. Assigning frbr_uri accepts a string; Cobalt exposes it as a cobalt.uri.FrbrUri instance with the attributes above.


Read and update document metadata

High-level properties such as title map onto the Akoma Ntoso metadata in the tree. After you change them, call to_xml() again (or pass the document into validation) to see the updated serialization.

python
from cobalt import Act

act = Act()
act.title = "Act 10 of 1980"
act.frbr_uri = "/za/act/1980-05-03/10"

act.title = "Amended short title"
print(act.title)
Output

To load an existing document, pass the XML (string or bytes) into the same document class (here Act) so Cobalt picks the correct structure and helpers.

python
from cobalt import Act

act = Act()
act.title = "Act 10 of 1980"
act.frbr_uri = "/za/act/1980-05-03/10"
xml = act.to_xml()

reloaded = Act(xml)
print(reloaded.title)
Output

For a dynamic document type string (for example data-driven pipelines), StructuredDocument.for_document_type from cobalt.akn resolves the right class before parsing.

python
from cobalt import Act
from cobalt.akn import StructuredDocument

act = Act()
act.title = "Act 10 of 1980"
act.frbr_uri = "/za/act/1980-05-03/10"
xml = act.to_xml()

Cls = StructuredDocument.for_document_type("act")
same = Cls(xml)
print(type(same).__name__, same.title)
Output

Work with FRBR URIs

FrbrUri (in cobalt.uri) is the dedicated type for Akoma Ntoso 3.0 FRBR URIs. Parse a URI string with FrbrUri.parse, read its segments, then derive work, expression, or manifestation forms with work_uri(), expression_uri(), and manifestation_uri() as described in the FRBR URIs section of the Cobalt API docs.

python
from cobalt.uri import FrbrUri

uri = FrbrUri.parse("/akn/za/act/2009/1")

print(uri.country)
print(uri.doctype)
print(uri.date)
print(uri.number)
print(uri.work_uri())
print(uri.expression_uri())
print(uri.manifestation_uri())
Output

Invalid strings raise ValueError with an “Invalid FRBR URI” message, which is the practical signal that the path does not match what the parser expects.


Parse existing Akoma Ntoso XML

Use the concrete document class that matches the root element of your file (for example Act, Bill, Judgment). Passing XML into the constructor parses it with Cobalt’s Akoma Ntoso-aware rules; malformed or non-AKN input raises errors from the stack rather than silently succeeding.

python
from cobalt import Act

with open("act.xml", "r", encoding="utf-8") as f:
    xml = f.read()

act = Act(xml)
print(act.title)
Output

The base AkomaNtosoDocument wrapper exists for generic XML access, but typed subclasses expose properties like title and frbr_uri that line up with the Akoma Ntoso layout.


Validate Akoma Ntoso XML

Cobalt ships strict and lenient Akoma Ntoso 3.0 schemas. The API documented under “Schemas and validation” provides validate and assert_validates in cobalt.schemas: the first returns a (validates, errors) tuple; the second raises lxml.etree.DocumentInvalid when validation fails.

python
from cobalt import Act
from cobalt.schemas import validate, assert_validates

act = Act()
act.title = "Act 10 of 1980"
act.frbr_uri = "/za/act/1980-05-03/10"

ok, errors = validate(act, strict=False)
print(ok, errors)

assert_validates(act, strict=False)
Output

Use strict=True when you also need the stricter checks described in the docs (for example around duplicate eId values, depending on schema). For production pipelines, decide whether you need strict or lenient mode based on how clean your upstream XML is.


Supported Akoma Ntoso document types

Cobalt maps Akoma Ntoso document types to Python classes. The following document_type strings work with StructuredDocument.for_document_type in Cobalt 9.0.1 (each resolves to the listed class under the cobalt subpackages):

document_type (string) Typical class
act Act
bill Bill
judgment Judgment
debate Debate
statement Statement
doc Document
portion Portion
amendment Amendment
documentCollection Collection
officialGazette OfficialGazette
amendmentList AmendmentList
debateReport DebateReport

Import the concrete classes from their modules when you want explicit imports (for example from cobalt.hierarchical import Act) instead of resolving by string.


Cobalt vs Bluebell

Laws.Africa also maintains Bluebell, a plain-text authoring format that compiles to Akoma Ntoso 3.0 XML. Cobalt and Bluebell solve different jobs:

Tool Best for
Cobalt Reading, editing, validating, and serializing existing Akoma Ntoso XML in Python; FRBR URI and metadata helpers on the real document tree.
Bluebell Authoring structured legislation from indented plain text without hand-writing XML; parsers emit Akoma Ntoso XML for downstream tools.

Bluebell is documented on laws.africa/bluebell and ships as its own installable tooling; Cobalt is the library you embed when Python code must manipulate AKN XML directly.


Common mistakes when using Cobalt

  • Skipping Akoma Ntoso basics and expecting Cobalt to hide the entire standard—most operations still reflect the underlying XML layout.
  • Wrong FRBR URI strings for frbr_uri assignment or FrbrUri.parse, which raises ValueError until the path matches the expected grammar.
  • Parsing with the wrong document class, which loads XML but leaves you without the typed helpers you need (always match the root document type).
  • Editing the raw lxml tree without checking Akoma Ntoso rules, then being surprised when validation fails or identifiers no longer line up with FRBR metadata.
  • Returning a hard-coded document class in your own helper functions when subclasses should be supported; mirror Cobalt’s style and instantiate through the resolved type (for example the class returned by for_document_type) when you build similar utilities.
  • Treating validation as optional for untrusted input; call validate or assert_validates at system boundaries when malformed AKN would break downstream consumers.

Should you keep using Cobalt?

If your stack depends on Akoma Ntoso in Python, Cobalt remains a sensible default: it is actively maintained on GitHub, published on PyPI with a stable classifier, and documented on Read the Docs with quickstart and API sections. Watch the GitHub repository for release notes when you upgrade major versions, and re-run your validation suite after bumps because schema bundles and parsing rules can tighten.

If your project never deals with legislative XML, you do not need Cobalt in the dependency tree—lighter XML libraries will stay easier to reason about.


Summary

Cobalt is the Laws.Africa–maintained Python library for Akoma Ntoso XML: create documents with classes like Act, assign title and string frbr_uri values, inspect structured FRBR data through FrbrUri, round-trip with to_xml() and typed constructors, and gate quality with cobalt.schemas.validate / assert_validates. Pair Cobalt with real Akoma Ntoso knowledge, use Bluebell when you need authoring-from-text instead of in-place XML editing, and pin a Python and Cobalt version that matches your deployment.


References


Frequently Asked Questions

1. What is the Cobalt Python library?

Cobalt is a lightweight library from Laws.Africa for working with Akoma Ntoso XML in Python: document classes (such as Act), metadata on the XML tree, FRBR URI helpers, and schema validation helpers.

2. How do you install Cobalt?

Use pip in your environment: pip install cobalt. PyPI lists Cobalt as Production/Stable and requires Python 3.9 or newer (check the package page for the current minimum).

3. What is FrbrUri in Cobalt?

FrbrUri in cobalt.uri models Akoma Ntoso 3.0 FRBR URIs: parse strings with FrbrUri.parse, read components like country and doctype, and build work, expression, or manifestation URI strings with work_uri(), expression_uri(), and manifestation_uri().

4. How do you validate Akoma Ntoso XML with Cobalt?

Import validate and assert_validates from cobalt.schemas: validate(document, strict=False) returns a (validates, errors) tuple; assert_validates(document, strict=False) raises if the document fails the schema check.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …