docx_plus.styles.sweep¶
The document-wide cascade sweep. Resolves every paragraph and run against a single shared cache, in document order.
resolve_effective_formatting
answers "what does this paragraph render as", rebuilding the theme, the
styles part, and each basedOn chain on every call.
iter_resolved_paragraphs
answers the same question about every paragraph at once, resolving those
document-level inputs once for the whole walk — roughly 5x faster per target
on a text-heavy document, and the read half any whole-document analysis
needs.
Pass include_baseline=True to also resolve each target with its own direct
formatting excluded, populating .baseline. That is the comparison behind
"is this direct property doing anything?" — see
stop_below for what the baseline is a resolve of.
docx_plus.styles.sweep ¶
Document-wide cascade sweep: iter_resolved_paragraphs.
:func:~docx_plus.styles.resolve_effective_formatting resolves one target
at a time and rebuilds every document-level lookup on each call — the theme,
the styles part, each basedOn chain. That is the right shape for asking
about a single paragraph and the wrong one for asking about all of them:
profiling put load_theme alone at 39% of per-call cost.
This module walks a whole document against one shared cache, in document order, yielding each paragraph's resolved formatting alongside its runs'. It is the read half any whole-document analysis needs — the effective formatting of everything, cheaply enough to then compare targets against each other.
ResolvedParagraph
dataclass
¶
ResolvedParagraph(
paragraph: Paragraph,
index: int,
formatting: ResolvedFormatting,
runs: tuple[ResolvedRun, ...],
table_depth: int,
baseline: ResolvedFormatting | None = None,
spacing: ParagraphSpacing | None = None,
part: SweepPart = "body",
)
A paragraph's resolved formatting, its runs', and where it sits.
Attributes:
| Name | Type | Description |
|---|---|---|
paragraph |
Paragraph
|
The python-docx :class: |
index |
int
|
0-based position in the sweep's own ordering — see the note
on :func: |
formatting |
ResolvedFormatting
|
The paragraph's fully-resolved formatting. Run-level properties here reflect the paragraph mark, not any one run. |
runs |
tuple[ResolvedRun, ...]
|
One :class: |
table_depth |
int
|
0 for a body-level paragraph, 1 inside a table, 2 inside a table nested in a table, and so on. |
baseline |
ResolvedFormatting | None
|
The same paragraph resolved with
Note this is not the same as deleting the paragraph's
|
spacing |
ParagraphSpacing | None
|
The paragraph's effective vertical spacing against its
neighbours, as :func: |
part |
SweepPart
|
Which part of the document the paragraph came from. Always
|
text
property
¶
The paragraph's text, for convenience when scanning content.
Covers exactly the runs in :attr:runs — both include the inside of
a <w:hyperlink> and both exclude <w:ins> / <w:del> /
<w:sdt>, matching python-docx's own Paragraph.text. A rule
may therefore index into this string and expect a run to answer for
every offset.
ResolvedRun
dataclass
¶
ResolvedRun(
run: Run,
index: int,
formatting: ResolvedFormatting,
baseline: ResolvedFormatting | None = None,
)
A run's resolved formatting, with its position in the paragraph.
Attributes:
| Name | Type | Description |
|---|---|---|
run |
Run
|
The python-docx :class: |
index |
int
|
0-based position within the owning paragraph. |
formatting |
ResolvedFormatting
|
The run's fully-resolved formatting. |
baseline |
ResolvedFormatting | None
|
The same run resolved with |
iter_resolved_paragraphs ¶
iter_resolved_paragraphs(
doc: Document,
*,
include_provenance: bool = False,
include_runs: bool = True,
include_tables: bool = True,
include_baseline: bool = False,
include_spacing: bool = False,
parts: Iterable[SweepPart] | Literal["all"] = ("body",),
) -> Iterator[ResolvedParagraph]
Resolve every paragraph in doc, sharing one cascade cache.
Yields lazily in document order, so a caller can stop early or stream a
large document without materialising every result. Wrap in list()
for the whole set.
Equivalent to calling
:func:~docx_plus.styles.resolve_effective_formatting on each paragraph
and run — the same walk over the same code path — but with the theme,
the styles part, and every basedOn chain resolved once for the whole
document rather than once per target.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
doc
|
Document
|
The python-docx :class: |
required |
include_provenance
|
bool
|
Populate each result's |
False
|
include_runs
|
bool
|
Resolve each paragraph's runs as well. Set False when only paragraph-level properties matter — runs are usually the bulk of the work in a text-heavy document. |
True
|
include_tables
|
bool
|
Descend into table cells (including nested tables). Set False to sweep body-level paragraphs only. |
True
|
include_baseline
|
bool
|
Also resolve each target with its own direct
formatting layer excluded, populating |
False
|
include_spacing
|
bool
|
Also compute each paragraph's
:class: |
False
|
parts
|
Iterable[SweepPart] | Literal['all']
|
Which parts to sweep, in :data: |
('body',)
|
Yields:
| Name | Type | Description |
|---|---|---|
One |
ResolvedParagraph
|
class: |
Raises:
| Type | Description |
|---|---|
StyleCascadeError
|
If a |
Note
index counts the paragraphs this sweep yields, in document
order. With include_tables=True (the default) that includes
table-cell paragraphs, which doc.paragraphs omits — so the two
indexings diverge at the first table. Pass include_tables=False
to get indices that line up with doc.paragraphs, and note the
CLI's inspect command numbers from 1 rather than 0.
Note
Headers, footers, footnotes, endnotes, and comments are swept only
when asked for via parts. A paragraph from a footnotes,
endnotes, or comments part is wrapped in a
:class:~docx.text.paragraph.Paragraph whose .part is that
side part rather than a python-docx story part, so its
.style accessor does not work; .text, .runs and the
resolved formatting do.
Raises:
| Type | Description |
|---|---|
InvalidSweepPartError
|
If |
Example
from docx import Document from docx_plus.styles import iter_resolved_paragraphs doc = Document() _ = doc.add_paragraph("Hello") for resolved in iter_resolved_paragraphs(doc): ... print(resolved.index, resolved.formatting.font_size) 0 11.0
Source code in docx_plus/styles/sweep.py
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 | |