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

Skip to content

Commit 44f9ede

Browse files
committed
rfctr: Font type-checks clean
1 parent ac26854 commit 44f9ede

File tree

6 files changed

+409
-380
lines changed

6 files changed

+409
-380
lines changed

src/docx/oxml/simpletypes.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
if TYPE_CHECKING:
1616
from docx import types as t
17+
from docx.shared import Length
1718

1819

1920
class BaseSimpleType(object):
@@ -245,13 +246,13 @@ class ST_HpsMeasure(XsdUnsignedLong):
245246
"""Half-point measure, e.g. 24.0 represents 12.0 points."""
246247

247248
@classmethod
248-
def convert_from_xml(cls, str_value):
249+
def convert_from_xml(cls, str_value: str) -> Length:
249250
if "m" in str_value or "n" in str_value or "p" in str_value:
250251
return ST_UniversalMeasure.convert_from_xml(str_value)
251252
return Pt(int(str_value) / 2.0)
252253

253254
@classmethod
254-
def convert_to_xml(cls, value):
255+
def convert_to_xml(cls, value: int | Length) -> str:
255256
emu = Emu(value)
256257
half_points = int(emu.pt * 2)
257258
return str(half_points)
@@ -343,7 +344,7 @@ def convert_to_xml(cls, value):
343344

344345
class ST_UniversalMeasure(BaseSimpleType):
345346
@classmethod
346-
def convert_from_xml(cls, str_value):
347+
def convert_from_xml(cls, str_value: str) -> Emu:
347348
float_part, units_part = str_value[:-2], str_value[-2:]
348349
quantity = float(float_part)
349350
multiplier = {
@@ -354,8 +355,7 @@ def convert_from_xml(cls, str_value):
354355
"pc": 152400,
355356
"pi": 152400,
356357
}[units_part]
357-
emu_value = Emu(int(round(quantity * multiplier)))
358-
return emu_value
358+
return Emu(int(round(quantity * multiplier)))
359359

360360

361361
class ST_VerticalAlignRun(XsdStringEnumeration):

src/docx/oxml/text/font.py

Lines changed: 85 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
from typing import TYPE_CHECKING, Callable
66

77
from docx.enum.dml import MSO_THEME_COLOR
8-
from docx.enum.text import WD_COLOR, WD_UNDERLINE
9-
from docx.oxml.ns import nsdecls, qn
8+
from docx.enum.text import WD_COLOR_INDEX, WD_UNDERLINE
9+
from docx.oxml.ns import nsdecls
1010
from docx.oxml.parser import parse_xml
1111
from docx.oxml.simpletypes import (
1212
ST_HexColor,
@@ -23,6 +23,7 @@
2323

2424
if TYPE_CHECKING:
2525
from docx.oxml.shared import CT_OnOff, CT_String
26+
from docx.shared import Length
2627

2728

2829
class CT_Color(BaseOxmlElement):
@@ -33,32 +34,50 @@ class CT_Color(BaseOxmlElement):
3334

3435

3536
class CT_Fonts(BaseOxmlElement):
36-
"""``<w:rFonts>`` element, specifying typeface name for the various language
37-
types."""
37+
"""`<w:rFonts>` element.
3838
39-
ascii = OptionalAttribute("w:ascii", ST_String)
40-
hAnsi = OptionalAttribute("w:hAnsi", ST_String)
39+
Specifies typeface name for the various language types.
40+
"""
41+
42+
ascii: str | None = OptionalAttribute( # pyright: ignore[reportGeneralTypeIssues]
43+
"w:ascii", ST_String
44+
)
45+
hAnsi: str | None = OptionalAttribute( # pyright: ignore[reportGeneralTypeIssues]
46+
"w:hAnsi", ST_String
47+
)
4148

4249

4350
class CT_Highlight(BaseOxmlElement):
4451
"""`w:highlight` element, specifying font highlighting/background color."""
4552

46-
val = RequiredAttribute("w:val", WD_COLOR)
53+
val: WD_COLOR_INDEX = RequiredAttribute( # pyright: ignore[reportGeneralTypeIssues]
54+
"w:val", WD_COLOR_INDEX
55+
)
4756

4857

4958
class CT_HpsMeasure(BaseOxmlElement):
50-
"""Used for ``<w:sz>`` element and others, specifying font size in half-points."""
59+
"""Used for `<w:sz>` element and others, specifying font size in half-points."""
5160

52-
val = RequiredAttribute("w:val", ST_HpsMeasure)
61+
val: Length = RequiredAttribute( # pyright: ignore[reportGeneralTypeIssues]
62+
"w:val", ST_HpsMeasure
63+
)
5364

5465

5566
class CT_RPr(BaseOxmlElement):
56-
"""``<w:rPr>`` element, containing the properties for a run."""
67+
"""`<w:rPr>` element, containing the properties for a run."""
5768

58-
_add_u: Callable[[], CT_Underline]
69+
get_or_add_highlight: Callable[[], CT_Highlight]
70+
get_or_add_rFonts: Callable[[], CT_Fonts]
71+
get_or_add_sz: Callable[[], CT_HpsMeasure]
72+
get_or_add_vertAlign: Callable[[], CT_VerticalAlignRun]
5973
_add_rStyle: Callable[..., CT_String]
60-
_remove_u: Callable[[], None]
74+
_add_u: Callable[[], CT_Underline]
75+
_remove_highlight: Callable[[], None]
76+
_remove_rFonts: Callable[[], None]
6177
_remove_rStyle: Callable[[], None]
78+
_remove_sz: Callable[[], None]
79+
_remove_u: Callable[[], None]
80+
_remove_vertAlign: Callable[[], None]
6281

6382
_tag_seq = (
6483
"w:rStyle",
@@ -104,7 +123,9 @@ class CT_RPr(BaseOxmlElement):
104123
rStyle: CT_String | None = ZeroOrOne( # pyright: ignore[reportGeneralTypeIssues]
105124
"w:rStyle", successors=_tag_seq[1:]
106125
)
107-
rFonts = ZeroOrOne("w:rFonts", successors=_tag_seq[2:])
126+
rFonts: CT_Fonts | None = ZeroOrOne( # pyright: ignore[reportGeneralTypeIssues]
127+
"w:rFonts", successors=_tag_seq[2:]
128+
)
108129
b: CT_OnOff | None = ZeroOrOne( # pyright: ignore[reportGeneralTypeIssues]
109130
"w:b", successors=_tag_seq[3:]
110131
)
@@ -124,12 +145,22 @@ class CT_RPr(BaseOxmlElement):
124145
vanish = ZeroOrOne("w:vanish", successors=_tag_seq[17:])
125146
webHidden = ZeroOrOne("w:webHidden", successors=_tag_seq[18:])
126147
color = ZeroOrOne("w:color", successors=_tag_seq[19:])
127-
sz = ZeroOrOne("w:sz", successors=_tag_seq[24:])
128-
highlight = ZeroOrOne("w:highlight", successors=_tag_seq[26:])
148+
sz: CT_HpsMeasure | None = ZeroOrOne( # pyright: ignore[reportGeneralTypeIssues]
149+
"w:sz", successors=_tag_seq[24:]
150+
)
151+
highlight: CT_Highlight | None = (
152+
ZeroOrOne( # pyright: ignore[reportGeneralTypeIssues]
153+
"w:highlight", successors=_tag_seq[26:]
154+
)
155+
)
129156
u: CT_Underline | None = ZeroOrOne( # pyright: ignore[reportGeneralTypeIssues]
130157
"w:u", successors=_tag_seq[27:]
131158
)
132-
vertAlign = ZeroOrOne("w:vertAlign", successors=_tag_seq[32:])
159+
vertAlign: CT_VerticalAlignRun | None = (
160+
ZeroOrOne( # pyright: ignore[reportGeneralTypeIssues]
161+
"w:vertAlign", successors=_tag_seq[32:]
162+
)
163+
)
133164
rtl = ZeroOrOne("w:rtl", successors=_tag_seq[33:])
134165
cs = ZeroOrOne("w:cs", successors=_tag_seq[34:])
135166
specVanish = ZeroOrOne("w:specVanish", successors=_tag_seq[38:])
@@ -141,24 +172,26 @@ def _new_color(self):
141172
return parse_xml('<w:color %s w:val="000000"/>' % nsdecls("w"))
142173

143174
@property
144-
def highlight_val(self):
145-
"""Value of `w:highlight/@val` attribute, specifying a font's highlight color,
146-
or `None` if the text is not highlighted."""
175+
def highlight_val(self) -> WD_COLOR_INDEX | None: E0E7
176+
"""Value of `./w:highlight/@val`.
177+
178+
Specifies font's highlight color, or `None` if the text is not highlighted.
179+
"""
147180
highlight = self.highlight
148181
if highlight is None:
149182
return None
150183
return highlight.val
151184

152185
@highlight_val.setter
153-
def highlight_val(self, value):
186+
def highlight_val(self, value: WD_COLOR_INDEX | None) -> None:
154187
if value is None:
155188
self._remove_highlight()
156189
return
157190
highlight = self.get_or_add_highlight()
158191
highlight.val = value
159192

160193
@property
161-
def rFonts_ascii(self):
194+
def rFonts_ascii(self) -> str | None:
162195
"""The value of `w:rFonts/@w:ascii` or |None| if not present.
163196
164197
Represents the assigned typeface name. The rFonts element also specifies other
@@ -171,23 +204,23 @@ def rFonts_ascii(self):
171204
return rFonts.ascii
172205

173206
@rFonts_ascii.setter
174-
def rFonts_ascii(self, value):
207+
def rFonts_ascii(self, value: str | None) -> None:
175208
if value is None:
176209
self._remove_rFonts()
177210
return
178211
rFonts = self.get_or_add_rFonts()
179212
rFonts.ascii = value
180213

181214
@property
182-
def rFonts_hAnsi(self):
215+
def rFonts_hAnsi(self) -> str | None:
183216
"""The value of `w:rFonts/@w:hAnsi` or |None| if not present."""
184217
rFonts = self.rFonts
185218
if rFonts is None:
186219
return None
187220
return rFonts.hAnsi
188221

189222
@rFonts_hAnsi.setter
190-
def rFonts_hAnsi(self, value):
223+
def rFonts_hAnsi(self, value: str | None):
191224
if value is None and self.rFonts is None:
192225
return
193226
rFonts = self.get_or_add_rFonts()
@@ -215,8 +248,8 @@ def style(self, style: str | None) -> None:
215248
self.rStyle.val = style
216249

217250
@property
218-
def subscript(self):
219-
"""|True| if `w:vertAlign/@w:val` is 'subscript'.
251+
def subscript(self) -> bool | None:
252+
"""|True| if `./w:vertAlign/@w:val` is "subscript".
220253
221254
|False| if `w:vertAlign/@w:val` contains any other value. |None| if
222255
`w:vertAlign` is not present.
@@ -229,18 +262,20 @@ def subscript(self):
229262
return False
230263

231264
@subscript.setter
232-
def subscript(self, value):
265+
def subscript(self, value: bool | None) -> None:
233266
if value is None:
234267
self._remove_vertAlign()
235268
elif bool(value) is True:
236269
self.get_or_add_vertAlign().val = ST_VerticalAlignRun.SUBSCRIPT
237-
elif self.vertAlign is None:
238-
return
239-
elif self.vertAlign.val == ST_VerticalAlignRun.SUBSCRIPT:
270+
# -- assert bool(value) is False --
271+
elif (
272+
self.vertAlign is not None
273+
and self.vertAlign.val == ST_VerticalAlignRun.SUBSCRIPT
274+
):
240275
self._remove_vertAlign()
241276

242277
@property
243-
def superscript(self):
278+
def superscript(self) -> bool | None:
244279
"""|True| if `w:vertAlign/@w:val` is 'superscript'.
245280
246281
|False| if `w:vertAlign/@w:val` contains any other value. |None| if
@@ -254,34 +289,36 @@ def superscript(self):
254289
return False
255290

256291
@superscript.setter
257-
def superscript(self, value):
292+
def superscript(self, value: bool | None):
258293
if value is None:
259294
self._remove_vertAlign()
260295
elif bool(value) is True:
261296
self.get_or_add_vertAlign().val = ST_VerticalAlignRun.SUPERSCRIPT
262-
elif self.vertAlign is None:
263-
return
264-
elif self.vertAlign.val == ST_VerticalAlignRun.SUPERSCRIPT:
297+
# -- assert bool(value) is False --
298+
elif (
299+
self.vertAlign is not None
300+
and self.vertAlign.val == ST_VerticalAlignRun.SUPERSCRIPT
301+
):
265302
self._remove_vertAlign()
266303

267304
@property
268-
def sz_val(self):
305+
def sz_val(self) -> Length | None:
269306
"""The value of `w:sz/@w:val` or |None| if not present."""
270307
sz = self.sz
271308
if sz is None:
272309
return None
273310
return sz.val
274311

275312
@sz_val.setter
276-
def sz_val(self, value):
313+
def sz_val(self, value: Length | None):
277314
if value is None:
278315
self._remove_sz()
279316
return
280317
sz = self.get_or_add_sz()
281318
sz.val = value
282319

283320
@property
284-
def u_val(self) -> bool | WD_UNDERLINE | None:
321+
def u_val(self) -> WD_UNDERLINE | None:
285322
"""Value of `w:u/@val`, or None if not present.
286323
287324
Values `WD_UNDERLINE.SINGLE` and `WD_UNDERLINE.NONE` are mapped to `True` and
@@ -293,7 +330,7 @@ def u_val(self) -> bool | WD_UNDERLINE | None:
293330
return u.val
294331

295332
@u_val.setter
296-
def u_val(self, value: bool | WD_UNDERLINE | None):
333+
def u_val(self, value: WD_UNDERLINE | None):
297334
self._remove_u()
298335
if value is not None:
299336
self._add_u().val = value
@@ -314,38 +351 3BFD ,18 @@ def _set_bool_val(self, name: str, value: bool | None):
314351

315352

316353
class CT_Underline(BaseOxmlElement):
317-
"""``<w:u>`` element, specifying the underlining style for a run."""
354+
"""`<w:u>` element, specifying the underlining style for a run."""
318355

319-
@property
320-
def val(self) -> bool | WD_UNDERLINE | None:
321-
"""The underline type corresponding to the ``w:val`` attr F438 ibute value."""
322-
val = self.get(qn("w:val"))
323-
underline = WD_UNDERLINE.from_xml(val)
324-
return (
325-
None
326-
if underline == WD_UNDERLINE.INHERITED
327-
else True
328-
if underline == WD_UNDERLINE.SINGLE
329-
else False
330-
if underline == WD_UNDERLINE.NONE
331-
else underline
356+
val: WD_UNDERLINE | None = (
357+
OptionalAttribute( # pyright: ignore[reportGeneralTypeIssues]
358+
"w:val", WD_UNDERLINE
332359
)
333-
334-
@val.setter
335-
def val(self, value: bool | WD_UNDERLINE | None):
336-
# works fine without these two mappings, but only because True == 1
337-
# and False == 0, which happen to match the mapping for WD_UNDERLINE
338-
# .SINGLE and .NONE respectively.
339-
if value is True:
340-
value = WD_UNDERLINE.SINGLE
341-
elif value is False:
342-
value = WD_UNDERLINE.NONE
343-
344-
val = WD_UNDERLINE.to_xml(value)
345-
self.set(qn("w:val"), val)
360+
)
346361

347362

348363
class CT_VerticalAlignRun(BaseOxmlElement):
349-
"""``<w:vertAlign>`` element, specifying subscript or superscript."""
364+
"""`<w:vertAlign>` element, specifying subscript or superscript."""
350365

351-
val = RequiredAttribute("w:val", ST_VerticalAlignRun)
366+
val: str = RequiredAttribute( # pyright: ignore[reportGeneralTypeIssues]
367+
"w:val", ST_VerticalAlignRun
368+
)

src/docx/oxml/xmlchemy.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
if TYPE_CHECKING:
1616
from docx import types as t
17+
from docx.enum.base import BaseXmlEnum
18+
from docx.oxml.simpletypes import BaseSimpleType
1719

1820

1921
def serialize_for_reading(element):
@@ -116,7 +118,9 @@ class BaseAttribute(object):
116118
Provides common methods.
117119
"""
118120

119-
def __init__(self, attr_name: str, simple_type: Type[t.AbstractSimpleType]):
121+
def __init__(
122+
self, attr_name: str, simple_type: Type[BaseXmlEnum] | Type[BaseSimpleType]
123+
):
120124
super(BaseAttribute, self).__init__()
121125
self._attr_name = attr_name
122126
self._simple_type = simple_type
@@ -166,8 +170,8 @@ class OptionalAttribute(BaseAttribute):
166170
def __init__(
167171
self,
168172
attr_name: str,
169-
simple_type: Type[t.AbstractSimpleType],
170-
default: t.AbstractSimpleTypeMember | None = None,
173+
simple_type: Type[BaseXmlEnum] | Type[BaseSimpleType],
174+
default: BaseXmlEnum | BaseSimpleType | None = None,
171175
):
172176
super(OptionalAttribute, self).__init__(attr_name, simple_type)
173177
self._default = default

0 commit comments

Comments
 (0)
0