Skip to content

Conversation

@jamesobutler
Copy link
Collaborator

@jamesobutler jamesobutler commented Nov 13, 2025

This closes #119.

Pydicom warning:
get_frame_offsets is deprecated and will be removed in v4.0

See instructions at https://github.com/pydicom/pydicom/blob/main/doc/release_notes/v3.0.0.rst that pydicom get_frame_offsets usage should be replaced with parse_basic_offsets.

This PR maintains compatibility with pydicom >=2.2 which is supported per the pyproject.toml:

"pydicom>=2.2",

I discovered this deprecation usage while using pydicom 3 and latest dicomweb-client in the context of 3D Slicer as part of updating python packages to latest (Slicer/Slicer#8844).

cc: @fedorov

@fedorov
Copy link
Member

fedorov commented Nov 13, 2025

@jamesobutler thank you so much for the contribution!

I asked Copilot about the failing test, and this was the (most relevant, I think, part of the) response:

This is a static type-checker error (mypy), not a runtime ImportError. The code does try/except ImportError but mypy still resolves the attribute from the pydicom stubs used in CI and complains when it can't find parse_basic_offsets in those stubs.

Replace the direct conditional import with a safe getattr-based resolution. This avoids mypy trying to validate module attributes at import time and keeps runtime compatibility with both older and newer pydicom.

from typing import Any, Callable, Optional
import pydicom.encaps as _pydicom_encaps  # type: ignore

# Resolve functions dynamically (use getattr so mypy does not require the attribute in stubs)
_parse_basic_offsets: Optional[Callable[..., Any]] = getattr(
    _pydicom_encaps, 'parse_basic_offsets', None
)
_get_frame_offsets: Optional[Callable[..., Any]] = getattr(
    _pydicom_encaps, 'get_frame_offsets', None
)

if _parse_basic_offsets is not None:
    parse_basic_offsets = _parse_basic_offsets  # type: ignore
    _use_parse_basic_offsets = True
elif _get_frame_offsets is not None:
    get_frame_offsets = _get_frame_offsets  # type: ignore
    _use_parse_basic_offsets = False
else:
    # Should not happen for a standard pydicom install. Make the error explicit.
    raise ImportError(
        'Neither parse_basic_offsets nor get_frame_offsets available in pydicom.encaps'
    )

Or we could just require latest pydicom?

@pieper @CPBridge what would you recommend?

@jamesobutler jamesobutler force-pushed the pydicom-deprecation-replace branch from d01e29c to 9e866aa Compare November 13, 2025 03:34
@jamesobutler
Copy link
Collaborator Author

jamesobutler commented Nov 13, 2025

The mypy check should no longer fail following dropping of end-of-life Python 3.9. The Python 3.9 CI install of the application is pulling pydicom 2.4.4 because pydicom 3 requires Python 3.10+. I have added testing for Python 3.14 following the drop of Python 3.9. Whenever dicomweb-client last dropped python version support, the justification was because they were end-of-life versions as well (see #113).

I would recommend a v0.61.0 release considering the python requirements will have changed. Similarly in dicomweb-client v0.60.0 there was a bump in the python requirement.

@CPBridge
Copy link
Collaborator

CPBridge commented Nov 13, 2025

I do not think it's a good idea to try and support pydicom across the major version change 2.x to 3.x. I would favour requiring pydicom>=3.0.0 and removing the import hackery

@CPBridge
Copy link
Collaborator

I am also on board with dropping 3.9 support now that it is EOL

@CPBridge
Copy link
Collaborator

@jamesobutler I merged #121, could you please remove those changes from this PR

@jamesobutler
Copy link
Collaborator Author

Yes, I'll rebase this branch upon integration of #122.

@fedorov
Copy link
Member

fedorov commented Nov 13, 2025

I would favour requiring pydicom>=3.0.0 and removing the import hackery

I was thinking the same, but wanted to hear from Chris or Steve, since they have a lot more experience with python.
@jamesobutler let's do pydicom>=3.0.0!

@jamesobutler jamesobutler force-pushed the pydicom-deprecation-replace branch 2 times, most recently from 2ac8e62 to b4af343 Compare November 13, 2025 15:48
@jamesobutler
Copy link
Collaborator Author

jamesobutler commented Nov 13, 2025

Todo:

  • Handle addition deprecations when using pydicom >=3 such as DeprecationWarning: 'Dataset.is_little_endian' will be removed in v4.0, set the Transfer Syntax UID or use the 'little_endian' argument with Dataset.save_as() or dcmwrite() instead

@jamesobutler jamesobutler force-pushed the pydicom-deprecation-replace branch from b4af343 to 5e9aea8 Compare November 17, 2025 00:25
Observed warning:

UserWarning: message sent by origin server in response to GET request of Retrieve Instance transaction was not compliant with the DICOM standard, message body shall have Content-Type 'multipart/related; type="application/dicom"' rather than "application/dicom"
@jamesobutler jamesobutler force-pushed the pydicom-deprecation-replace branch 2 times, most recently from b016910 to f3c170d Compare November 17, 2025 03:15
@jamesobutler jamesobutler force-pushed the pydicom-deprecation-replace branch from f3c170d to 6d4d870 Compare November 17, 2025 03:18
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
26.7% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Copy link
Collaborator

@CPBridge CPBridge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @jamesobutler, looks good to me

@fedorov
Copy link
Member

fedorov commented Nov 17, 2025

Tests are failing!

@CPBridge I think you mentioned we should not use 3.0.0 earlier. Should we set minimum required pydicom to 3.0.1?

@CPBridge
Copy link
Collaborator

Yes just looking into the tests. I am not concerned about the code duplication detected by sonarcloud since it is in the tests (I will try to figure out if there's a way to exclude them). I might need to look further into the failed unit test though. But yes I hadn't realized there's also a unit test failure related to the offset parsing. I will revoke my approval

Copy link
Collaborator

@CPBridge CPBridge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revoking approval until tests are fixed

@CPBridge
Copy link
Collaborator

CPBridge commented Nov 17, 2025

Should we set minimum required pydicom to 3.0.1?

We could consider it. Although the nature of the bugs in 3.0.0 that I know about wouldn't really be relevant to dicomweb-client so I don't think it makes a big difference in practice either way

@jamesobutler
Copy link
Collaborator Author

jamesobutler commented Nov 17, 2025

I would suggest adding branch protection rules to the master branch if they don't already exist. Select "Require status checks to pass before merging" and then use the search fields to add "unit tests / build 3.10" and the other python version status checks as well as any others (SonarCloud Code Analysis?) you want to enforce before integration. Then save the update branch protection rules.

{BC2495C9-3B49-42A4-971F-57F852E89ADB}

@CPBridge
Copy link
Collaborator

There are already branch protections in place for master. I would not have tried to merge before the checks passed anyway. I was just a bit trigger happy when approving after looking at the code changes (which look fine, I guess the error is related to different behaviour of the new offset parsing function)

@jamesobutler
Copy link
Collaborator Author

jamesobutler commented Nov 17, 2025

@CPBridge To avoid a human accidentally overlooking failing tests, adding the required status checks to the master branch is still a good idea. I see that currently only the Python 3.10 unit test is set as required, when it is more likely that we want all Python versions to be passing and therefore required status checks prior to integration.
{7786CAD7-B3A6-4CAE-9EC3-A2541FED48F7}

@CPBridge
Copy link
Collaborator

CPBridge commented Nov 17, 2025

The unit tests are now all required, I would prefer not to require sonarcloud as it generates some noise

@fedorov
Copy link
Member

fedorov commented Nov 17, 2025

The branch was protected. I added one rule.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pydicom get_frame_offsets is deprecated

3 participants