docx_plus.core.ids¶
Per-document id allocators. One registry per namespace per edit session —
OOXML reuses w:id across several disjoint uniqueness domains, and
bookmark id 7 does not collide with comment id 7.
Most namespaces get their registry in the capability package that owns
them (comments.CommentIdRegistry, notes.FootnoteIdRegistry,
numbering.NumIdRegistry). Three live here instead:
IdRegistry— SDT content-control ids, the original v0.1 case.ParaIdRegistry—w14:paraId, which is hex-rendered and unique across the whole package rather than within one part, because threaded comments key their parent/child links off it.BookmarkIdRegistry/BookmarkNameRegistry— moved here in v0.5 because two capability packages need them:bookmarksownsadd_bookmark, andpublishinghas to bookmark a caption to make it referenceable. SPEC §9.1 forbids the sibling import. Both are re-exported fromdocx_plus.bookmarks.registry.
Allocation comes in two flavours. next() mints a random 31-bit
value, which is right for an opaque handle. next_sequential() takes the
lowest free integer, which is what Word and python-docx do for
numbering — a numbering.xml full of nine-digit ids is needlessly
unreadable. _MIN_ID exists because w:abstractNumId legitimately
starts at 0, unlike every w:id.
docx_plus.core.ids ¶
Per-document registries of issued w:id values.
OOXML uses w:id for several disjoint namespaces — SDT controls,
bookmarks, comments, footnotes, endnotes. Each namespace has its own
uniqueness requirement; bookmark id 7 does not collide with comment
id 7. v0.1 only minted SDT ids and shipped :class:IdRegistry for
that purpose. v0.2 adds further namespaces (comments, bookmarks, notes)
and refactors the shared next/reserve/issued mechanics into
:class:_IdRegistryBase. Each namespace-specific registry is a tiny
subclass that overrides :meth:_seed_from_document with the right
discovery query.
:class:ParaIdRegistry is the one registry not backed by w:id: it
mints w14:paraId values, which are hex-rendered and unique across the
whole package rather than within a single part. It reuses the same
31-bit allocator because that happens to be exactly the range Word
accepts for a paraId.
:class:BookmarkNameRegistry is the odd one out entirely — bookmarks are
addressed by name, and nothing else in the format is. It lives here
because two capability packages need it: bookmarks owns the public
add_bookmark, and publishing has to bookmark a caption to make it
referenceable, since a REF field can only point at a bookmark.
BookmarkIdRegistry sits alongside it for the same reason (it was in
bookmarks/registry.py through v0.4 and is still re-exported there).
SPEC §3, IMPLEMENTATION.md §7.
IdRegistry ¶
Bases: _IdRegistryBase
Tracks issued SDT w:id values for one document-edit session.
On construction, the registry scans the document body and settings part
for existing w:id values on w:sdt descendants and seeds itself
with them, so :meth:next cannot collide with values already in the file.
Source code in docx_plus/core/ids.py
ParaIdRegistry ¶
Bases: _IdRegistryBase
Tracks issued w14:paraId values for one document-edit session.
w14:paraId identifies a paragraph across the whole package rather
than within one part: threaded comments key their parent/child links
off the paraId of a comment body's last paragraph
(w15:commentEx), so a collision between a body paragraph and a
comment paragraph would corrupt the thread graph. The registry
therefore seeds from the document body and every part that can
carry paragraphs with a paraId — comments, footnotes, endnotes.
Word writes paraId as 8 uppercase hex digits — the rendering
:meth:~_IdRegistryBase.next_hex provides — so this subclass adds
only the seeding query.
Source code in docx_plus/core/ids.py
BookmarkIdRegistry ¶
Bases: _IdRegistryBase
Tracks issued bookmark w:id values for one document-edit session.
The body-side <w:bookmarkStart> / <w:bookmarkEnd> elements
both carry the id on a direct @w:id attribute, not as a
<w:id w:val=...> child the way SDTs do.
Source code in docx_plus/core/ids.py
BookmarkNameRegistry ¶
Tracks bookmark names in use, and mints Word-style hidden ones.
Bookmarks are the one thing in the format addressed by name rather
than by id, and nothing prevents a document from carrying two with
the same w:name. That is worth guarding: a duplicate makes a
REF ambiguous and makes
:func:~docx_plus.bookmarks.delete_bookmark remove both.
Deliberately not an :class:_IdRegistryBase subclass — the keys are
strings, and most of them (sec_1, intro) have no numeric part
to allocate from at all.
Lifecycle: one instance per document-edit session, like every other registry here (SPEC §9.4).
Scan doc for bookmark names already in use.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
doc
|
Document
|
A python-docx :class: |
required |
Source code in docx_plus/core/ids.py
__contains__ ¶
reserve ¶
Claim name, asserting it is not already taken.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The bookmark name to claim. |
required |
Returns:
| Type | Description |
|---|---|
str
|
|
Raises:
| Type | Description |
|---|---|
DuplicateBookmarkNameError
|
If |
Source code in docx_plus/core/ids.py
next_ref_name ¶
Mint an unused hidden name in Word's _Ref + 9-digit form.
Use this for anchors the user never names themselves — a caption being made referenceable, for instance. The leading underscore keeps it out of Word's Bookmark dialog.
Returns:
| Type | Description |
|---|---|
str
|
A fresh name such as |
Raises:
| Type | Description |
|---|---|
RegistryExhaustedError
|
If the space is exhausted (effectively impossible — included for completeness). |
Source code in docx_plus/core/ids.py
DuplicateIdError ¶
Bases: DocxPlusError, ValueError
Raised when an ID is reserved twice.
Subclasses ValueError so existing except ValueError: clauses still
catch it; also subclasses :class:DocxPlusError per SPEC §9.7.
IdRangeError ¶
Bases: DocxPlusError, ValueError
Raised when a reserved ID falls outside the 31-bit positive range.
Subclasses ValueError for backward compatibility; also subclasses
:class:DocxPlusError per SPEC §9.7.
DuplicateBookmarkNameError ¶
Bases: DocxPlusError, ValueError
Raised when a bookmark name is already in use in the document.
Subclasses ValueError so existing except ValueError: clauses
still catch it; also subclasses :class:DocxPlusError per SPEC §9.7.