docx_plus.core.oxml¶
Element-construction and XPath helpers. All XML construction in the library goes through these — see the invariants, invariant 2.
docx_plus.core.oxml ¶
Element-construction helpers — the single chokepoint for OOXML I/O.
Capability modules must construct elements through :func:el and :func:sub
rather than calling lxml.etree directly. SPEC §9.2.
el ¶
Create a namespaced element with attributes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tag
|
str
|
Element name in |
required |
**attrs
|
str
|
Attributes. Keys may be namespaced ( |
{}
|
Elements in the main document namespaces are built declaring
:data:~docx_plus.core.ns.BUILD_NSMAP. An element in any other
namespace — w15 and w16cid, which appear only in the comment
side-parts — declares just its own prefix, so extension parts stay
free of irrelevant declarations and document.xml stays free of
the extension prefixes.
Returns:
| Type | Description |
|---|---|
_Element
|
A fresh detached :class: |
Example
style = el("w:style", **{"w:type": "paragraph", "w:styleId": "Foo"}) style.tag.endswith("}style") True
Source code in docx_plus/core/oxml.py
sub ¶
Create a namespaced child of parent and append it.
Equivalent to :func:el followed by parent.append(...). Returned for
chained use.
Source code in docx_plus/core/oxml.py
xpath ¶
Run an XPath query with the library's namespace map pre-bound.
Uses :class:lxml.etree.XPath so the call works equally on raw lxml
elements and on python-docx's :class:BaseOxmlElement subclasses (whose
own xpath method does not accept a namespaces= kwarg).
Values that vary per call (style ids, numbering ids, etc.) should be
passed as **variables and referenced with $name in the
expression — this lets lxml escape them as XPath variables rather than
splicing the caller's string into the query, which avoids quote-handling
bugs and the obvious injection class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node
|
_Element
|
Context node. |
required |
expr
|
str
|
XPath expression that may reference |
required |
**variables
|
Any
|
Values for |
{}
|
Returns:
| Type | Description |
|---|---|
list[Any]
|
The XPath result list (elements, attribute strings, etc.). Returned |
list[Any]
|
as |
Example
safe variable binding instead of f-string interpolation¶
xpath(styles_root, "./w:style[@w:styleId=$sid]", sid=style_id)¶
Source code in docx_plus/core/oxml.py
remove ¶
Detach node from its parent if it has one. No-op if already detached.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node
|
_Element
|
Element to remove. |
required |
build_complex_field ¶
Append the 5-run complex-field sequence to p_element.
The sequence is::
<w:r><w:fldChar w:fldCharType="begin"/></w:r>
<w:r><w:instrText xml:space="preserve">INSTRUCTION</w:instrText></w:r>
<w:r><w:fldChar w:fldCharType="separate"/></w:r>
<w:r><w:t xml:space="preserve">INITIAL_TEXT</w:t></w:r>
<w:r><w:fldChar w:fldCharType="end"/></w:r>
xml:space="preserve" on the instruction and the result text
keeps leading / trailing whitespace from being normalised by Word's
XML reader.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
p_element
|
_Element
|
The underlying |
required |
instruction
|
str
|
The field instruction text (e.g. |
required |
initial_text
|
str
|
The result text shown before Word recalculates the
field. Use |
required |
Returns:
| Type | Description |
|---|---|
_Element
|
The begin |
_Element
|
field. |
Source code in docx_plus/core/oxml.py
build_bookmark ¶
build_bookmark(
start_anchor: _Element, end_anchor: _Element, *, bookmark_id: int, name: str
) -> tuple[etree._Element, etree._Element]
Bracket start_anchor..end_anchor with a bookmark pair.
Emits <w:bookmarkStart> immediately before start_anchor and
<w:bookmarkEnd> immediately after end_anchor, both carrying
bookmark_id. Pass the same element twice to bookmark a single run.
Lives in core because two capability packages need it and SPEC
§9.1 forbids the sibling import: bookmarks owns the public
:func:~docx_plus.bookmarks.add_bookmark, while publishing needs
the same emission to make a caption referenceable — a REF field
can only point at a bookmark, never at the caption's SEQ field.
Neither the name nor the id is validated here; the caller owns that
(bookmarks checks the name against the ECMA-376 grammar and
allocates the id from a registry).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_anchor
|
_Element
|
Element the |
required |
end_anchor
|
_Element
|
Element the |
required |
bookmark_id
|
int
|
Numeric id shared by the pair. |
required |
name
|
str
|
Bookmark name written on the start element. |
required |
Returns:
| Type | Description |
|---|---|
tuple[_Element, _Element]
|
The |
Source code in docx_plus/core/oxml.py
insert_before_first_anchor ¶
insert_before_first_anchor(
parent: _Element, new_element: _Element, anchor_tags: tuple[str, ...]
) -> None
Insert new_element before the first anchor_tags match in parent.
Falls back to appending at the end if none of the anchors exist. The
pattern keeps schema-strict child ordering even when parent has a
sparse / partial set of children — most real settings.xml files
do, so capability modules building optional settings.xml children
use this to land in the right schema position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parent
|
_Element
|
The element to insert into. |
required |
new_element
|
_Element
|
The element to insert. |
required |
anchor_tags
|
tuple[str, ...]
|
Sequence of |
required |
Source code in docx_plus/core/oxml.py
ordered_insert ¶
Insert child into parent at the position dictated by order.
Removes any existing element in parent with the same tag first, so
calling twice with the same tag replaces the previous instance. The
new element is placed immediately before the first existing sibling
whose local name comes later in order; a tag absent from order,
or one with no later sibling present, is appended.
The stronger sibling of :func:insert_before_first_anchor: that one
takes an explicit list of later siblings and never replaces, this one
derives the insertion point from a full child-order tuple and is
idempotent. Reach for this when the parent's complete schema sequence
is known — w:style, w:pPr, w:rPr, w:lvl.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parent
|
_Element
|
The element to insert into. |
required |
child
|
_Element
|
The element to insert. Any existing sibling with the same tag is removed first. |
required |
order
|
tuple[str, ...]
|
Local names in schema order. |
required |
Source code in docx_plus/core/oxml.py
body_document_for ¶
Return the main-body :class:~docx.document.Document owning proxy.
python-docx proxies (Run, Paragraph, …) inherit .part from
Parented. For a proxy in the main document body, part is the
DocumentPart, which exposes a .document property. A header /
footer proxy is parented to a part that does not, so this raises a
clear :class:ValueError naming the operation that needs the body.
Shared by the comments and notes packages (a core utility,
so the duplication SPEC §9.1 would otherwise force is avoided).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
proxy
|
Any
|
A python-docx proxy exposing |
required |
operation
|
str
|
Human-readable name of the caller, woven into the error
message when |
'this operation'
|
Returns:
| Type | Description |
|---|---|
Document
|
The owning :class: |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |