pynetdicom: DICOM Networking Protocol
pynetdicom is a Python implementation of the DICOM networking protocol, allowing developers to create DICOM Service Class Users (SCUs) and Service Class Providers (SCPs). It handles DICOM association negotiation, message exchange, and event management. The current version is 3.0.4, and it generally follows a release cadence tied to bug fixes and minor feature enhancements, with major versions introducing significant architectural changes.
Common errors
-
Association Rejected (0x01)
cause The remote AE rejected the association. Common reasons include unsupported presentation contexts, incorrect AE titles, or security issues (e.g., TLS mismatch).fixCheck that your `ae.add_supported_context()` calls match what the remote AE supports. Ensure AE titles are correct and formatted as `bytes`. Verify TLS/SSL settings if applicable. Check the SCP's logs for a more specific reason for rejection. -
TypeError: a bytes-like object is required, not 'str'
cause An AE title (either for the local AE or the remote AE in `ae.associate`) was provided as a Python string instead of a `bytes` object.fixEncode your AE titles to bytes: `AE(ae_title=b'MY_AE_TITLE')` or `ae.associate(..., ae_title=my_string.encode('utf-8'))`. -
ValueError: No Presentation Context for Verification SOP Class
cause You attempted to send a C-ECHO (or similar service) without having an accepted presentation context for the corresponding SOP Class.fixBefore associating, add the required presentation context: `ae.add_supported_context(VerificationPresentationContexts[0])` for C-ECHO, or similar for other services. -
RuntimeWarning: You have not called ae.shutdown(). This can lead to uncleaned resources and your program hanging.
cause The `AE` object was garbage collected or the program exited without explicitly calling its `shutdown()` method, leaving internal threads or resources open.fixAlways call `ae.shutdown()` when your `AE` instance is no longer needed, typically at the end of your script or within a `finally` block.
Warnings
- breaking pynetdicom v3.0.0 and later no longer bundle pydicom. If you need to work with actual DICOM datasets (e.g., sending/receiving images), you must explicitly install `pydicom`.
- breaking The event handling API changed significantly in v3.0.0. Instead of overriding internal `_handle_*` methods, event handlers are now registered using `AE.register_event_handler` with `evt.EVT_*` constants.
- gotcha DICOM AE Titles must be `bytes` objects (e.g., `b'MY_AE'`), not strings. Using a string will result in a `TypeError`.
- gotcha Incorrect or missing Presentation Contexts are a common cause of 'Association Rejected' errors. Each DICOM service (C-ECHO, C-STORE, etc.) requires a specific Presentation Context to be proposed and accepted.
- gotcha Failure to call `ae.shutdown()` after an `AE` object is no longer needed can lead to lingering server threads, preventing the program from exiting cleanly or consuming resources.
Install
-
pip install pynetdicom -
pip install pynetdicom[scu] -
pip install pynetdicom[scp] -
pip install pynetdicom[tls]
Imports
- AE
from pynetdicom.apps import AE
from pynetdicom import AE
- evt
ae._handle_association_release
from pynetdicom import evt
- VerificationPresentationContexts
from pynetdicom.sop_class import VerificationSOPClass
from pynetdicom.presentation import VerificationPresentationContexts
- StoragePresentationContexts
from pynetdicom.presentation import StoragePresentationContexts
Quickstart
from pynetdicom import AE, VerificationPresentationContexts
import logging
import os
# Configure logging to see association details (optional but recommended)
logging.basicConfig(level=logging.INFO)
# Initialise the Application Entity (AE) for the SCU
# AE Titles must be bytes objects (e.g., b'MY_AE_TITLE')
scu_ae_title = os.environ.get('PYNETDICOM_AE_TITLE_SCU', 'PYNETDICOM_SCU').encode('utf-8')
ae = AE(ae_title=scu_ae_title)
# Add a supported presentation context for the Verification SOP Class (C-ECHO)
# This tells the AE what services it can request/provide.
ae.add_supported_context(VerificationPresentationContexts[0])
# Define the target SCP's details (can be read from environment variables for flexibility)
target_ip = os.environ.get('PYNETDICOM_TARGET_IP', '127.0.0.1')
target_port = int(os.environ.get('PYNETDICOM_TARGET_PORT', '11112'))
target_ae_title = os.environ.get('PYNETDICOM_TARGET_AE_TITLE', 'ANY_SCP_AE').encode('utf-8')
print(f"\nAttempting to associate {scu_ae_title.decode()} with {target_ae_title.decode()} at {target_ip}:{target_port}...")
print("NOTE: Ensure a DICOM SCP is running at this address and listening on the specified port.")
# Attempt to establish an association with the remote AE (SCP)
assoc = ae.associate(target_ip, target_port, ae_title=target_ae_title)
if assoc.is_established:
print('\nAssociation established with peer.')
# Send a C-ECHO request to verify connection
status = assoc.send_c_echo()
print(f'C-ECHO response status: {status}')
# Release the association gracefully
assoc.release()
print('Association released.')
else:
print('\nAssociation rejected, aborted or never connected.')
# Clean up any resources (e.g., server threads) associated with the AE
ae.shutdown()