Skip to content

Add the six registered UIDs pydicom's dictionary is missing. - #79

Merged
xbmlz merged 1 commit into
mainfrom
uid-standard-additions
Aug 25, 2026
Merged

xbmlz merged 1 commit into
mainfrom
uid-standard-additions

Conversation

@xbmlz

@xbmlz xbmlz commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Closes #77.

What was wrong

uid's dictionary is generated from pydicom's _uid_dict.py. That is the right source for a port checked against pydicom, but pydicom is not the authority on which UIDs exist, and it is behind PS3.6 by six Storage SOP Classes. A consumer resolving a SOP Class UID it received over the wire got the raw digits back from UID.Name and nothing at all from uid.Lookup.

Six, not two

#77 named the two Waveform Presentation State classes. Rather than add those and stop, I diffed the whole registry — parsed Tables A-1 and A-2 out of part06.xml and compared against uid/dictionary_generated.go:

  • 496 registered UIDs against 490 generated ones
  • those six were the entire difference
  • no extra UIDs on the Go side, and no name, keyword or retired-flag disagreement anywhere else

So the generator was already sound; only these late additions were missing. The four beyond #77:

UID Name
1.2.840.10008.5.1.4.1.1.2.3 CT Image Storage - For Processing
1.2.840.10008.5.1.4.1.1.2.4 Enhanced CT Image Storage - For Processing
1.2.840.10008.5.1.4.1.1.2.5 Legacy Converted Enhanced CT Image Storage - For Processing
1.2.840.10008.5.1.4.1.1.601.5 Ultrasound Waveform Storage

All six are absent from pydicom both at the pinned submodule and at upstream main 0e98c4a (2026-08-01), so bumping the submodule would not have helped.

Keeping the mirror a mirror

They are supplied from a STANDARD_ADDITIONS table in generate_uid_dict.py rather than by editing the generated file, so a regeneration keeps them. The table is not allowed to become a quiet second source of truth — merging refuses to run when:

  1. pydicom now defines one of these UIDs (drop it from the table; pydicom owns it from then on)
  2. an addition's keyword is not a valid Go identifier
  3. …is reserved to MANUAL_CONSTS
  4. …is already taken by a pydicom entry
  5. …collides with another addition

2–5 each describe a case where the constant loop would skip the keyword, putting a UID in the dictionary while silently emitting no constant for it. All five checks run before anything is written, so a bad entry cannot leave a half-regenerated tree. I verified all five fire and that the happy path merges exactly six.

Verification

  • audit_uid_dict.py is the diff, kept as a tool so the numbers above are reproducible rather than something you have to take on trust. Exits non-zero on any discrepancy; currently reports 496 / 496, no discrepancies. Confirmed non-vacuous by deleting an entry and watching it report that one UID and exit 1.
  • TestUIDsAheadOfPydicom is the Go-side guard. These six are the only dictionary entries that do not come from the parse, which makes them the only ones a pydicom bump could drop with every other test still green — the count would just fall by six. It checks all three generated maps, because they come from separate loops and an entry can survive in one while vanishing from another. Verified by deleting one Dictionary entry: the test went red on Name/Type/Keyword while Lookup still passed, which is exactly that failure mode.
  • Not wired into CI on purpose: the audit depends on the network and on a document that changes without reference to this repository, so a new DICOM edition would turn an unrelated pull request red.

Also

The generator now runs gofmt on uid/generated.go. That filename does not match CI's *_generated.go exclusion, so it has to be formatted, and the constants are emitted one per line for gofmt to align. Without this a regeneration shows up as a ~1000-line whitespace diff on top of the real change — I hit exactly that while making this one. dictionary_generated.go is left unformatted, as it always has been, since it is excluded.

The diff to the generated files is 18 added lines and nothing removed: 6 Dictionary entries, 6 KeywordToUID entries, 6 constants.

Checks run locally

go build, go vet, staticcheck -checks=all (clean), gofmt, go test -count=1 -p 4 ./..., and the full suite under GOARCH=386 — all pass.

🤖 Generated with Claude Code

uid's dictionary is generated from pydicom's _uid_dict.py, which is the right
source for a port checked against pydicom -- but pydicom is not the authority on
which UIDs exist, and it is behind PS3.6 by six Storage SOP Classes. A consumer
resolving a SOP Class UID it received over the wire got the raw digits back from
UID.Name and nothing from uid.Lookup.

Rather than add the two named in #77 and stop, diff the whole registry: parse
Tables A-1 and A-2 out of part06.xml and compare against the generated
dictionary. 496 registered UIDs against 490 generated ones, and those six were
the entire difference -- no extra UIDs on the Go side, no name, keyword or
retired-flag disagreement anywhere else. So the four beyond #77 are:

    1.2.840.10008.5.1.4.1.1.2.3    CT Image Storage - For Processing
    1.2.840.10008.5.1.4.1.1.2.4    Enhanced CT Image Storage - For Processing
    1.2.840.10008.5.1.4.1.1.2.5    Legacy Converted Enhanced CT ... - For Processing
    1.2.840.10008.5.1.4.1.1.601.5  Ultrasound Waveform Storage

All six are absent from pydicom at the pinned submodule and at upstream main
0e98c4a (2026-08-01), so bumping the submodule would not have helped.

They are supplied from a STANDARD_ADDITIONS table in generate_uid_dict.py, not
by editing the generated file, so a regeneration keeps them. The table is not
allowed to become a quiet second source of truth: merging refuses to run once
pydicom defines any of these UIDs, and it rejects an addition whose keyword is
not a valid Go identifier, is reserved to MANUAL_CONSTS, is already taken by a
pydicom entry, or collides with another addition -- each of which would
otherwise put a UID in the dictionary and silently emit no constant for it. All
five refusals are checked before anything is written, so a bad entry cannot
leave a half-regenerated tree.

audit_uid_dict.py is the diff, kept as a tool so the claim above is
reproducible rather than something a reader has to take on trust. It exits
non-zero on any discrepancy. It is not wired into CI on purpose: it depends on
the network and on a document that changes without reference to this
repository, so a new DICOM edition would turn an unrelated pull request red.

TestUIDsAheadOfPydicom is the Go-side guard. These six are the only dictionary
entries that do not come from the parse, which makes them the only ones a
pydicom bump could drop with every other test still passing -- the count would
just fall by six. It checks all three generated maps, since they are emitted by
separate loops and an entry can survive in one and vanish from another;
verified by deleting one Dictionary entry and confirming the test goes red
while Lookup still passed.

The generator now also runs gofmt on uid/generated.go. That file does not match
CI's '*_generated.go' exclusion, so it has to be formatted, and the constants
are emitted one per line for gofmt to align -- without this a regeneration
shows up as a ~1000-line whitespace diff on top of the real change.

Closes #77

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@xbmlz
xbmlz merged commit 0ea4e2f into main Aug 25, 2026
6 checks passed
@xbmlz
xbmlz deleted the uid-standard-additions branch August 25, 2026 06:05
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.

uid: 字典缺两个 Waveform Presentation State Storage SOP Class

1 participant