8000 rfctr: Paragraph type-checks clean · python-openxml/python-docx@b61b63b · GitHub
[go: up one dir, main page]

Skip to content

Commit b61b63b

Browse files
committed
rfctr: Paragraph type-checks clean
1 parent 58c2453 commit b61b63b

15 files changed

+198
-79
lines changed

src/docx/opc/part.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
"""Open Packaging Convention (OPC) objects related to package parts."""
22

3+
from __future__ import annotations
4+
5+
from typing import TYPE_CHECKING
6+
37
from docx.opc.oxml import serialize_part_xml
48
from docx.opc.packuri import PackURI
59
from docx.opc.rel import Relationships
610
from docx.opc.shared import cls_method_fn, lazyproperty
711
from docx.oxml.parser import parse_xml
812

13+
if TYPE_CHECKING:
14+
from docx.opc.package import OpcPackage
15+
916

1017
class Part(object):
1118
"""Base class for package parts.
@@ -14,7 +21,13 @@ class Part(object):
1421
to implement specific part behaviors.
1522
"""
1623

17-
def __init__(self, partname, content_type, blob=None, package=None):
24+
def __init__(
25+
self,
26+
partname: str,
27+
content_type: str,
28+
blob: bytes | None = None,
29+
package: OpcPackage | None = None,
30+
):
1831
super(Part, self).__init__()
1932
self._partname = partname
2033
self._content_type = content_type
@@ -96,7 +109,7 @@ def partname(self, partname):
96109
raise TypeError(tmpl % type(partname).__name__)
97110
self._partname = partname
98111

99-
def part_related_by(self, reltype):
112+
def part_related_by(self, reltype: str) -> Part:
100113
"""Return part to which this part has a relationship of `reltype`.
101114
102115
Raises |KeyError| if no such relationship is found and |ValueError| if more than
@@ -105,9 +118,12 @@ def part_related_by(self, reltype):
105118
"""
106119
return self.rels.part_with_reltype(reltype)
107120

108-
def relate_to(self, target, reltype, is_external=False):
109-
"""Return rId key of relationship of `reltype` to `target`, from an existing
110-
relationship if there is one, otherwise a newly created one."""
121+
def relate_to(self, target: Part, reltype: str, is_external: bool = False) -> str:
122+
"""Return rId key of relationship of `reltype` to `target`.
123+
124+
The returned `rId` is from an existing relationship if there is one, otherwise a
125+
new relationship is created.
126+
"""
111127
if is_external:
112128
return self.rels.get_or_add_ext_rel(reltype, target)
113129
else:

src/docx/oxml/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
CT_ShapeProperties,
2222
CT_Transform2D,
2323
)
24+
from docx.oxml.shared import CT_DecimalNumber, CT_OnOff, CT_String
2425
from docx.oxml.text.hyperlink import CT_Hyperlink
2526
from docx.oxml.text.pagebreak import CT_LastRenderedPageBreak
2627
from docx.oxml.text.run import (
@@ -68,13 +69,13 @@
6869
register_element_cls("w:t", CT_Text)
6970

7071
# ---------------------------------------------------------------------------
71-
# other custom element class mappings
72-
73-
from .shared import CT_DecimalNumber, CT_OnOff, CT_String # noqa
72+
# header/footer-related mappings
7473

7574
register_element_cls("w:evenAndOddHeaders", CT_OnOff)
7675
register_element_cls("w:titlePg", CT_OnOff)
7776

77+
# ---------------------------------------------------------------------------
78+
# other custom element class mappings
7879

7980
from .coreprops import CT_CoreProperties # noqa
8081

src/docx/oxml/shared.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
"""Objects shared by modules in the docx.oxml subpackage."""
22

3+
from __future__ import annotations
4+
5+
from typing import cast
6+
37
from docx.oxml.ns import qn
48
from docx.oxml.parser import OxmlElement
59
from docx.oxml.simpletypes import ST_DecimalNumber, ST_OnOff, ST_String
@@ -33,15 +37,19 @@ class CT_OnOff(BaseOxmlElement):
3337

3438

3539
class CT_String(BaseOxmlElement):
36-
"""Used for ``<w:pStyle>`` and ``<w:tblStyle>`` elements and others, containing a
37-
style name in its ``val`` attribute."""
40+
"""Used for `w:pStyle` and `w:tblStyle` elements and others.
41+
42+
In those cases, it containing a style name in its `val` attribute.
43+
"""
3844

39-
val = RequiredAttribute("w:val", ST_String)
45+
val: str = RequiredAttribute( # pyright: ignore[reportGeneralTypeIssues]
46+
"w:val", ST_String
47+
)
4048

4149
@classmethod
42-
def new(cls, nsptagname, val):
50+
def new(cls, nsptagname: str, val: str):
4351
"""Return a new ``CT_String`` element with tagname `nsptagname` and ``val``
4452
attribute set to `val`."""
45-
elm = OxmlElement(nsptagname)
53+
elm = cast(CT_String, OxmlElement(nsptagname))
4654
elm.val = val
4755
return elm

src/docx/oxml/styles.py

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Custom element classes related to the styles part."""
22

3+
from __future__ import annotations
4+
35
from docx.enum.style import WD_STYLE_TYPE
46
from docx.oxml.simpletypes import ST_DecimalNumber, ST_OnOff, ST_String
57
from docx.oxml.xmlchemy import (
@@ -126,8 +128,14 @@ class CT_Style(BaseOxmlElement):
126128
rPr = ZeroOrOne("w:rPr", successors=_tag_seq[18:])
127129
del _tag_seq
128130

129-
type = OptionalAttribute("w:type", WD_STYLE_TYPE)
130-
styleId = OptionalAttribute("w:styleId", ST_String)
131+
type: WD_STYLE_TYPE | None = (
132+
OptionalAttribute( # pyright: ignore[reportGeneralTypeIssues]
133+
"w:type", WD_STYLE_TYPE
134+
)
135+
)
136+
styleId: str | None = OptionalAttribute( # pyright: ignore[reportGeneralTypeIssues]
137+
"w:styleId", ST_String
138+
)
131139
default = OptionalAttribute("w:default", ST_OnOff)
132140
customStyle = OptionalAttribute("w:customStyle", ST_OnOff)
133141

@@ -293,23 +301,21 @@ def default_for(self, style_type):
293301
# spec calls for last default in document order
294302
return default_styles_for_type[-1]
295303

296-
def get_by_id(self, styleId):
297-
"""Return the ``<w:style>`` child element having ``styleId`` attribute matching
298-
`styleId`, or |None| if not found."""
299-
xpath = 'w:style[@w:styleId="%s"]' % styleId
300-
try:
301-
return self.xpath(xpath)[0]
302-
except IndexError:
303-
return None
304+
def get_by_id(self, styleId: str) -> CT_Style | None:
305+
"""`w:style` child where @styleId = `styleId`.
304306
305-
def get_by_name(self, name):
306-
"""Return the ``<w:style>`` child element having ``<w:name>`` child element with
307-
value `name`, or |None| if not found."""
307+
|None| if not found.
308+
"""
309+
xpath = f'w:style[@w:styleId="{styleId}"]'
310+
return next(iter(self.xpath(xpath)), None)
311+
312+
def get_by_name(self, name: str) -> CT_Style | None:
313+
"""`w:style` child with `w:name` grandchild having value `name`.
314+
315+
|None| if not found.
316+
"""
308317
xpath = 'w:style[w:name/@w:val="%s"]' % name
309-
try:
310-
return self.xpath(xpath)[0]
311-
except IndexError:
312-
return None
318+
return next(iter(self.xpath(xpath)), None)
313319

314320
def _iter_styles(self):
315321
"""Generate each of the `w:style` child elements in document order."""

src/docx/oxml/text/paragraph.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1+
# pyright: reportPrivateUsage=false
2+
13
"""Custom element classes related to paragraphs (CT_P)."""
24

35
from __future__ import annotations
46

5-
from typing import TYPE_CHECKING, Callable, List
7+
from typing import TYPE_CHECKING, Callable, List, cast
68

79
from docx.oxml.parser import OxmlElement
810
from docx.oxml.xmlchemy import BaseOxmlElement, ZeroOrMore, ZeroOrOne
911

1012
if TYPE_CHECKING:
1113
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
14+
from docx.oxml.section import CT_SectPr
1215
from docx.oxml.text.hyperlink import CT_Hyperlink
1316
from docx.oxml.text.pagebreak import CT_LastRenderedPageBreak
1417
from docx.oxml.text.parfmt import CT_PPr
@@ -18,6 +21,7 @@
1821
class CT_P(BaseOxmlElement):
1922
"""`<w:p>` element, containing the properties and text for a paragraph."""
2023

24+
add_r: Callable[[], CT_R]
2125
get_or_add_pPr: Callable[[], CT_PPr]
2226
hyperlink_lst: List[CT_Hyperlink]
2327
r_lst: List[CT_R]
@@ -28,7 +32,7 @@ class CT_P(BaseOxmlElement):
2832

2933
def add_p_before(self) -> CT_P:
3034
"""Return a new `<w:p>` element inserted directly prior to this one."""
31-
new_p = OxmlElement("w:p")
35+
new_p = cast(CT_P, OxmlElement("w:p"))
3236
self.addprevious(new_p)
3337
return new_p
3438

@@ -66,7 +70,7 @@ def lastRenderedPageBreaks(self) -> List[CT_LastRenderedPageBreak]:
6670
"./w:r/w:lastRenderedPageBreak | ./w:hyperlink/w:r/w:lastRenderedPageBreak"
6771
)
6872

69-
def set_sectPr(self, sectPr):
73+
def set_sectPr(self, sectPr: CT_SectPr):
7074
"""Unconditionally replace or add `sectPr` as grandchild in correct sequence."""
7175
pPr = self.get_or_add_pPr()
7276
pPr._remove_sectPr()
@@ -84,7 +88,7 @@ def style(self) -> str | None:
8488
return pPr.style
8589

8690
@style.setter
87-
def style(self, style):
91+
def style(self, style: str | None):
8892
pPr = self.get_or_add_pPr()
8993
pPr.style = style
9094

src/docx/oxml/text/parfmt.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from __future__ import annotations
44

5+
from typing import TYPE_CHECKING, Callable
6+
57
from docx.enum.text import (
68
WD_ALIGN_PARAGRAPH,
79
WD_LINE_SPACING,
@@ -18,6 +20,10 @@
1820
)
1921
from docx.shared import Length
2022

23+
if TYPE_CHECKING:
24+
from docx.oxml.section import CT_SectPr
25+
from docx.oxml.shared import CT_String
26+
2127

2228
class CT_Ind(BaseOxmlElement):
2329
"""``<w:ind>`` element, specifying paragraph indentation."""
@@ -37,6 +43,11 @@ class CT_Jc(BaseOxmlElement):
3743
class CT_PPr(BaseOxmlElement):
3844
"""``<w:pPr>`` element, containing the properties for a paragraph."""
3945

46+
get_or_add_pStyle: Callable[[], CT_String]
47+
_i 10000 nsert_sectPr: Callable[[CT_SectPr], None]
48+
_remove_pStyle: Callable[[], None]
49+
_remove_sectPr: Callable[[], None]
50+
4051
_tag_seq = (
4152
"w:pStyle",
4253
"w:keepNext",
@@ -75,7 +86,9 @@ class CT_PPr(BaseOxmlElement):
7586
"w:sectPr",
7687
"w:pPrChange",
7788
)
78-
pStyle = ZeroOrOne("w:pStyle", successors=_tag_seq[1:])
89+
pStyle: CT_String | None = ZeroOrOne( # pyright: ignore[reportGeneralTypeIssues]
90+
"w:pStyle", successors=_tag_seq[1:]
91+
)
7992
keepNext = ZeroOrOne("w:keepNext", successors=_tag_seq[2:])
8093
keepLines = ZeroOrOne("w:keepLines", successors=_tag_seq[3:])
8194
pageBreakBefore = ZeroOrOne("w:pageBreakBefore", successors=_tag_seq[4:])
@@ -273,20 +286,18 @@ def spacing_lineRule(self, value):
273286
self.get_or_add_spacing().lineRule = value
274287

275288
@property
276-
def style(self):
277-
"""String contained in <w:pStyle> child, or None if that element is not
278-
present."""
289+
def style(self) -> str | None:
290+
"""String contained in `./w:pStyle/@val`, or None if child is not present."""
279291
pStyle = self.pStyle
280292
if pStyle is None:
281293
return None
282294
return pStyle.val
283295

284296
@style.setter
285-
def style(self, style):
286-
"""Set val attribute of <w:pStyle> child element to `style`, adding a new
287-
element if necessary.
297+
def style(self, style: str | None):
298+
"""Set `./w:pStyle/@val` `style`, adding a new element if necessary.
288299
289-
If `style` is |None|, remove the <w:pStyle> element if present.
300+
If `style` is |None|, remove `./w:pStyle` when present.
290301
"""
291302
if style is None:
292303
self._remove_pStyle()

src/docx/oxml/xmlchemy.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,7 @@ class BaseOxmlElement(metaclass=MetaOxmlElement):
623623
Adds standardized behavior to all classes in one place.
624624
"""
625625

626+
addprevious: Callable[[BaseOxmlElement], None]
626627
attrib: Dict[str, str]
627628
append: Callable[[ElementBase], None]
628629
find: Callable[[str], ElementBase | None]

src/docx/parts/document.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
"""|DocumentPart| and closely related objects."""
22

3+
from __future__ import annotations
4+
5+
from typing import TYPE_CHECKING, cast
6+
37
from docx.document import Document
8+
from docx.enum.style import WD_STYLE_TYPE
49
from docx.opc.constants import RELATIONSHIP_TYPE as RT
510
from docx.parts.hdrftr import FooterPart, HeaderPart
611
from docx.parts.numbering import NumberingPart
@@ -10,6 +15,9 @@
1015
from docx.shape import InlineShapes
1116
from docx.shared import lazyproperty
1217

18+
if TYPE_CHECKING:
19+
from docx.styles.style import BaseStyle
20+
1321

1422
class DocumentPart(StoryPart):
1523
"""Main document part of a WordprocessingML (WML) package, aka a .docx file.
@@ -51,7 +59,7 @@ def footer_part(self, rId: str):
5159
"""Return |FooterPart| related by `rId`."""
5260
return self.related_parts[rId]
5361

54-
def get_style(self, style_id, style_type):
62+
def get_style(self, style_id: str | None, style_type: WD_STYLE_TYPE) -> BaseStyle:
5563
"""Return the style in this document matching `style_id`.
5664
5765
Returns the default style for `style_type` if `style_id` is |None| or does not
@@ -124,14 +132,16 @@ def _settings_part(self):
124132
return settings_part
125133

126134
@property
127-
def _styles_part(self):
135+
def _styles_part(self) -> StylesPart:
128136
"""Instance of |StylesPart| for this document.
129137
130138
Creates an empty styles part if one is not present.
131139
"""
132140
try:
133-
return self.part_related_by(RT.STYLES)
141+
return cast(StylesPart, self.part_related_by(RT.STYLES))
134142
except KeyError:
135-
styles_part = StylesPart.default(self.package)
143+
package = self.package
144+
assert package is not None
145+
styles_part = StylesPart.default(package)
136146
self.relate_to(styles_part, RT.STYLES)
137147
return styles_part

0 commit comments

Comments
 (0)
0