Skip to main content

dimsechord

Pure-Python toolkit of composable building blocks for DICOM and DICOMweb services.

dimsechord wraps pydicom and pynetdicom behind a small, stable public API. It gives you the networking, caching, and conversion primitives needed to build PACS proxies, DICOMweb gateways, and imaging pipelines — and nothing else: there is no bundled HTTP framework and no opinionated request handlers, so you bring your own web layer.

Features

  • DIMSE SCU — C-FIND, C-STORE, C-MOVE and C-GET, exposed through an async DicomClient.
  • C-STORE SCP — receive incoming instances with StorageSCP, one listener per distinct port; multiple AETs may share a port. All transfer syntaxes are accepted by default, so compressed objects arrive verbatim.
  • C-STORE SCU session — StoreSession is a persistent, association-scoped C-STORE sender: the peer's DIMSE status passes through verbatim as an int (success, warning, and failure alike), and a presentation-context miss raises NoPresentationContextError without tearing down the association.
  • AssociationPool — manage multiple AE-Title identities with per-AET concurrency limits.
  • Two-tier cache — in-memory + disk, backed by a SQLite instance index.
  • Streaming pull-engine — move-to-self retrieval streamed instance by instance, with per-UID request coalescing.
  • Streaming query-engine — raw C-FIND pass-through via QueryEngine, streamed response by response under independent per-AET find leases.
  • DICOMweb conversion — DICOM ↔ DICOMweb JSON and multipart/related frame responses.

Runtime dependencies are just pydicom, pynetdicom, and cachetools.

Installation

pip install dimsechord

Requires Python 3.12+.

Quickstart

import asyncio

from dimsechord import (
    AssociationPool, DicomCache, DicomClient, DicomNode, PullEngine,
    SeriesQuery, StorageSCP, StudyQuery, convert_datasets_to_dicom_json,
)

PACS = DicomNode(aet="PACS", host="127.0.0.1", port=11112)


async def main() -> None:
    # 1. Find studies and series (C-FIND, QIDO-style).
    client = DicomClient(calling_aet="MYSCU")
    studies = await client.find_studies(StudyQuery(patient_id="12345"), PACS)
    series = await client.find_series(
        SeriesQuery(study_instance_uid=studies[0].study_instance_uid), PACS)

    # 2. Pull a series move-to-self and build DICOMweb JSON (WADO-style).
    pool = AssociationPool(aets=["MYDEST"])
    scp = StorageSCP()
    scp.start({"MYDEST": 11113})
    # Your PACS must route the AET "MYDEST" back to this SCP's host:port.
    cache = DicomCache(base_dir="./cache", index_path="./cache/index.db")
    engine = PullEngine(pool=pool, scp=scp, cache=cache, pacs=PACS)
    try:
        cached = await engine.ensure_series(
            studies[0].study_instance_uid, series[0].series_instance_uid)
        metadata = convert_datasets_to_dicom_json(
            list(cached.instances.values()), base_url="https://example.org/dicom-web")
        print(len(metadata), "instances")
    finally:
        scp.stop()
        cache.shutdown()


asyncio.run(main())

0.8.0: every wait on the C-MOVE-to-self retrieve path is now short and bounded, failing with a typed error instead of queuing for minutes — the new RetrieveBusyError (a PoolExhaustedError subclass) covers same-key coalescing. An abandoned move is aborted upstream immediately; its slot returns once the driver thread exits.

0.7.0: StoreSession is a persistent C-STORE SCU session with verbatim DIMSE status pass-through — success, warning, and failure codes all return as an int, never raised. A presentation-context miss raises the new NoPresentationContextError instead; build_storage_scp_contexts() is the new acceptor-side mirror of build_storage_scu_contexts().

0.6.0 (breaking): DicomCache's memory tier is now sized by bytes, not entry count — memory_max_entries (default 50) is replaced by memory_max_size_gb (default 1.0).

0.5.1: StorageSCP accepts all transfer syntaxes by default and the new build_storage_scu_contexts() builds forwarding-SCU contexts, so compressed objects pass through verbatim. C-GET now negotiates a curated storage-class set (see CHANGELOG).

0.5.0: the disk cache now records per-series completeness; series cached by earlier versions re-pull once on first read after upgrade (the SQLite schema migrates automatically).

0.4.0 (breaking): StorageSCP.start now takes an {AET: port} mapping (was start(aets, port)), binding one listener per port.

Public API

The supported surface is exactly what dimsechord/__init__.py exports (from dimsechord import …); it is frozen by tests/unit/test_public_api.py.

Everything else lives in underscore-prefixed modules (dimsechord._scu, dimsechord._cache, …) and is private — importing from those modules is unsupported and may break without notice. If you need something that is not exported, extend the public surface rather than reaching into a private module.

Documentation

License

MIT — see LICENSE.

Metadata

Release files for dimsechord 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dimsechord 0.8.0
File Size Uploaded
dimsechord-0.8.0.tar.gz 127.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dimsechord 0.8.0
File Interpreter ABI Platform
dimsechord-0.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 182.1 kB

Release files / dimsechord-0.8.0.tar.gz

Download URL dimsechord-0.8.0.tar.gz
Size 127.8 kB
Tags Source
SHA-256 checksum
How to use checksums
99b0cebb261f14c0029c30293bb213343a43728f43042336cb7de517c027c51a
BLAKE2b-256 checksum
How to use checksums
5c75bc21542ff3654b78d5b353dce5b7ad10255735e26f0ec2328b27b8b0744a
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 30, 2026.

Transparency log

Release files / dimsechord-0.8.0-py3-none-any.whl

Download URL dimsechord-0.8.0-py3-none-any.whl
Size 54.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
dc9dad3472696bb91382fa2ebb755a760082d9adc47e23176607701cc1c2dd8b
BLAKE2b-256 checksum
How to use checksums
ada5325b2d4da2a1600df2bd051723b3788220afc24d207f3e6493272320e551
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 30, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.8.0 This release

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page