This release has been yanked by its maintainers, and will be ignored by installers, except when explicitly specified.
Consider using release 0.7.1 instead.
Reason given by maintainers: Security: upgrade to iirds 0.3.1 or later (see SECURITY.md)
iirds
Read and write iiRDS packages in Python.
pip install iirds
import iirds
with iirds.open("machine-docs.iirds") as pkg:
print(pkg.version, pkg.variant) # "1.3" "unrestricted"
graph = pkg.graph # rdflib.Graph of the package metadata
data = pkg.read("content/topic1.xhtml")
iirds.pack("my-package-directory/") # → my-package-directory.iirds,
# mimetype first and stored,
# byte-identical on every run
Deliberately small: open a container, get the metadata as an RDF graph, read files, write a conformant container back. It does not validate — that is iirds-validate's job (185 rules, offline, CI-friendly), and keeping the two apart keeps this library at one dependency (rdflib).
Two things the pack() half gets right that generic ZIP tooling gets wrong:
the mimetype entry is first and stored uncompressed, and packing the same
directory twice produces byte-identical output (honouring
SOURCE_DATE_EPOCH), so "this archive came from that directory" is checkable
with a hash instead of taken on trust.
Stewardship
The iirds name on PyPI belongs to the standard's community more than to any
one project. Should the iiRDS Consortium want this name for an official
SDK, it will be transferred on request — until then it does real work
rather than squatting. iirds-sdk is an alias of this package and travels
under the same pledge.
This is an unofficial project, not affiliated with or endorsed by the iiRDS Consortium or tekom Deutschland e.V. "iiRDS" is used descriptively, to name the standard these functions read and write.
Queries
Package.instances_of(cls), is_instance(node, cls) and label_of(node)
(also module-level, taking any rdflib graph) answer "what is in this
package" with section-7 semantics: an instance of a class the package
itself declares beneath an iiRDS class is an instance of that class.
from iirds import IIRDS
with iirds.open("docs.iirds") as pkg:
for topic in pkg.instances_of(IIRDS["Topic"]):
print(pkg.label_of(topic))
The closure walks only the package's own rdfs:subClassOf declarations —
no ontology is bundled, so instances_of(IIRDS["InformationUnit"])
returns only what the package declares beneath it. One rdflib trap worth
knowing: Namespace subclasses str, so IIRDS.format is str.format;
always use bracket syntax (IIRDS["format"]).
Deliberately not here (so nobody waits for it): per-class conveniences
(topics() is instances_of(IIRDS["Topic"]) and 25 siblings would drift),
typed_exactly (one rdflib call on the public graph), a bundled ontology,
SPARQL wrappers, and anything that returns a verdict.
Files behind renditions
pkg.source_of(node) resolves a Rendition's iirds:source to the entry
it names — the helper a naive implementation gets wrong. The core
normalisation matches the validator verbatim (leading slashes stripped,
./ and internal ../ collapsed); folding backslashes and refusing a
path that escapes the package are the SDK's own added strictness.
pkg.open(node) returns a readable stream over that entry (streaming —
a two-gigabyte PDF is read, not loaded), raising when the node names
nothing or names an absent entry. The stream borrows the package's open
ZIP handle, so consume it while the Package is still open rather than
after close(). Resolution never judges existence: that split keeps
"what does this rendition say" apart from "is this package whole", which
is the validator's question.
Writing metadata
write_metadata(graph, destination=None) serialises a graph as
metadata.rdf — and self-verifies: the bytes are parsed back through
the same guarded reader every consumer uses and compared isomorphically
before being handed over, so "the validator can read what the SDK wrote"
is enforced at write time. Byte-stable across repeated writes of the
same Graph object, and no more: rdflib mints blank-node labels from a
process-global counter, so even identically-built graphs serialise
apart, and canonicalisation would be a different, heavier promise.
Composes with pack(): write the metadata into a directory, pack the
directory, open the result.
Untrusted input
A package arrives from a supplier, so open() treats its metadata the way
iirds-validate does — same
guards, same error strings, shared code:
- XML entity declarations are refused (a tame one is indistinguishable from the geometric kind until the parser is already inside it),
- metadata above 64 MiB uncompressed is refused before being read,
- a remote JSON-LD
@contextis refused wherever it nests — reading a package never touches the network, - a byte order mark decides the encoding, as XML says it should.
META-INF/metadata.jsonld is read and merged beside metadata.rdf
(isomorphic sources count once — blank nodes double under naive union).
pkg.metadata_sources, pkg.metadata_graphs and pkg.parse_errors say
what parsed, which document said what, and what was refused. pkg.graph
raises only when nothing parsed, because an empty graph is also what a
real, sparse package looks like.
API stability
0.x: the surface will grow; what is published is intended not to break. The API is small on purpose — additions are cheap, retractions are not.
Licence
Apache-2.0, © 2026 Wooyong Lee. Contributions need a Signed-off-by line
(DCO); see the workflow in .github/.
Release files for iirds 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| iirds-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Release files / iirds-0.3.0-py3-none-any.whl
| Download URL | iirds-0.3.0-py3-none-any.whl |
|---|---|
| Size | 18.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2f389adae8806dc80c4b03fc3ede2dfca5484fdf512bfa1e055c3867db910a8b
|
|
BLAKE2b-256 checksum How to use checksums |
ea73e1744298ca06d5657b5ffc68ecec31ed083e9a7124fcde2f58578fd837fa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.
Transparency log