8000 rfctr: add types to Run and its tests · python-openxml/python-docx@45bf74b · GitHub
[go: up one dir, main page]

Skip to content
  • Insights
  • Commit 45bf74b

    Browse files
    committed
    rfctr: add types to Run and its tests
    1 parent fd54be1 commit 45bf74b

    File tree

    6 files changed

    +145
    -61
    lines changed

    6 files changed

    +145
    -61
    lines changed

    src/docx/oxml/text/run.py

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

    33
    from __future__ import annotations
    44

    5+
    from typing import Callable
    6+
    57
    from docx.oxml.ns import qn
    68
    from docx.oxml.simpletypes import ST_BrClear, ST_BrType
    79
    from docx.oxml.text.font import CT_RPr
    @@ -14,12 +16,16 @@
    1416
    class CT_R(BaseOxmlElement):
    1517
    """`<w:r>` element, containing the properties and text for a run."""
    1618

    19+
    add_br: Callable[[], CT_Br]
    20+
    get_or_add_rPr: Callable[[], CT_RPr]
    21+
    _add_t: Callable[..., CT_Text]
    22+
    1723
    rPr = ZeroOrOne("w:rPr")
    18-
    t = ZeroOrMore("w:t")
    1924
    br = ZeroOrMore("w:br")
    2025
    cr = ZeroOrMore("w:cr")
    21-
    tab = ZeroOrMore("w:tab")
    2226
    drawing = ZeroOrMore("w:drawing")
    27+
    t = ZeroOrMore("w:t")
    28+
    tab = ZeroOrMore("w:tab")
    2329

    2430
    def add_t(self, text: str) -> CT_Text:
    2531
    """Return a newly added `<w:t>` element containing `text`."""
    @@ -44,7 +50,7 @@ def clear_content(self):
    4450
    self.remove(child)
    4551

    4652
    @property
    47-
    def style(self):
    53+
    def style(self) -> str | None:
    4854
    """String contained in `w:val` attribute of `w:rStyle` grandchild.
    4955
    5056
    |None| if that element is not present.
    @@ -64,7 +70,7 @@ def style(self, style):
    6470
    rPr.style = style
    6571

    6672
    @property
    67-
    def text(self):
    73+
    def text(self) -> str:
    6874
    """The textual content of this run.
    6975
    7076
    Inner-content child elements like `w:tab` are translated to their text
    @@ -82,7 +88,7 @@ def text(self):
    8288
    return text
    8389

    8490
    @text.setter
    85-
    def text(self, text):
    91+
    def text(self, text: str):
    8692
    self.clear_content()
    8793
    _RunContentAppender.append_to_run_from_text(self, text)
    8894

    @@ -98,7 +104,7 @@ def _insert_rPr(self, rPr: CT_RPr) -> CT_RPr:
    98104
    class CT_Br(BaseOxmlElement):
    99105
    """`<w:br>` element, indicating a line, page, or column break in a run."""
    100106

    101-
    type = OptionalAttribute("w:type", ST_BrType)
    107+
    type = OptionalAttribute("w:type", ST_BrType, default="textWrapping")
    102108
    clear = OptionalAttribute("w:clear", ST_BrClear)
    103109

    104110

    119125
    appended.
    120126
    """
    121127

    122-
    def __init__(self, r):
    128+
    def __init__(self, r: CT_R):
    123129
    self._r = r
    124130
    self._bfr = []
    125131

    126132
    @classmethod
    127-
    def append_to_run_from_text(cls, r, text):
    133+
    def append_to_run_from_text(cls, r: CT_R, text: str):
    128134
    """Append inner-content elements for `text` to `r` element."""
    129135
    appender = cls(r)
    130136
    appender.add_text(text)
    131137

    132-
    def add_text(self, text):
    138+
    def add_text(self, text: str):
    133139
    """Append inner-content elements for `text` to the `w:r` element."""
    134140
    for char in text:
    135141
    self.add_char(char)
    136142
    self.flush()
    137143

    138-
    def add_char(self, char):
    144+
    def add_char(self, char: str):
    139145
    """Process next character of input through finite state maching (FSM).
    140146
    141147
    There are two possible states, buffer pending and not pending, but those are

    src/docx/text/run.py

    Lines changed: 28 additions & 16 deletions
    Original file line numberDiff line numberDiff line change
    @@ -1,26 +1,33 @@
    11
    """Run-related proxy objects for python-docx, Run in particular."""
    22

    3+
    from __future__ import annotations
    4+
    5+
    from typing import IO
    6+
    7+
    from docx import types as t
    38
    from docx.enum.style import WD_STYLE_TYPE
    49
    from docx.enum.text import WD_BREAK
    10+
    from docx.oxml.text.run import CT_R, CT_Text
    511
    from docx.shape import InlineShape
    6-
    from docx.shared import Parented
    12+
    from docx.shared import Length, Parented
    13+
    from docx.styles.style import CharacterStyle
    714
    from docx.text.font import Font
    815

    916

    1017
    class Run(Parented):
    11-
    """Proxy object wrapping ``<w:r>`` element.
    18+
    """Proxy object wrapping `<w:r>` element.
    1219
    1320
    Several of the properties on Run take a tri-state value, |True|, |False|, or |None|.
    1421
    |True| and |False| correspond to on and off respectively. |None| indicates the
    1522
    property is not specified directly on the run and its effective value is taken from
    1623
    the style hierarchy.
    1724
    """
    1825

    19-
    def __init__(self, r, parent):
    26+
    def __init__(self, r: CT_R, parent: t.StoryChild):
    2027
    super(Run, self).__init__(parent)
    2128
    self._r = self._element = self.element = r
    2229

    23-
    def add_break(self, break_type: WD_BREAK = WD_BREAK.LINE):
    30+
    def add_break(self, break_type: WD_BREAK = WD_BREAK.LINE): # pyright: ignore
    2431
    """Add a break element of `break_type` to this run.
    2532
    2633
    `break_type` can take the values `WD_BREAK.LINE`, `WD_BREAK.PAGE`, and
    @@ -41,7 +48,12 @@ def add_break(self, break_type: WD_BREAK = WD_BREAK.LINE):
    4148
    if clear is not None:
    4249
    br.clear = clear
    4350

    44-
    def add_picture(self, image_path_or_stream, width=None, height=None):
    51+
    def add_picture(
    52+
    self,
    53+
    image_path_or_stream: str | IO[bytes],
    54+
    width: Length | None = None,
    55+
    height: Length | None = None,
    56+
    ) -> InlineShape:
    4557
    """Return an |InlineShape| instance containing the image identified by
    4658
    `image_path_or_stream`, added to the end of this run.
    4759
    @@ -62,7 +74,7 @@ def add_tab(self):
    6274
    tab character."""
    6375
    self._r._add_tab()
    6476

    65-
    def add_text(self, text):
    77+
    def add_text(self, text: str):
    6678
    """Returns a newly appended |_Text| object (corresponding to a new ``<w:t>``
    6779
    child element) to the run, containing `text`.
    6880
    @@ -73,15 +85,15 @@ def add_text(self, text):
    7385
    return _Text(t)
    7486

    7587
    @property
    76-
    def bold(self):
    88+
    def bold(self) -> bool:
    7789
    """Read/write.
    7890
    7991
    Causes the text of the run to appear in bold.
    8092
    """
    8193
    return self.font.bold
    8294

    8395
    @bold.setter
    84-
    def bold(self, value):
    96+
    def bold(self, value: bool):
    8597
    self.font.bold = value
    8698

    8799
    def clear(self):
    @@ -99,19 +111,19 @@ def font(self):
    99111
    return Font(self._element)
    100112

    101113
    @property
    102-
    def italic(self):
    114+
    def italic(self) -> bool:
    103115
    """Read/write tri-state value.
    104116
    105117
    When |True|, causes the text of the run to appear in italics.
    106118
    """
    107119
    return self.font.italic
    108120

    109121
    @italic.setter
    110-
    def italic(self, value):
    122+
    def italic(self, value: bool):
    111123
    self.font.italic = value
    112124

    113125
    @property
    114-
    def style(self):
    126+
    def style(self) -> CharacterStyle | None:
    115127
    """Read/write.
    116128
    117129
    A |_CharacterStyle| object representing the character style applied to this run.
    @@ -123,7 +135,7 @@ def style(self):
    123135
    return self.part.get_style(style_id, WD_STYLE_TYPE.CHARACTER)
    124136

    125137
    @style.setter
    126-
    def style(self, style_or_name):
    138+
    def style(self, style_or_name: str | CharacterStyle | None):
    127139
    style_id = self.part.get_style_id(style_or_name, WD_STYLE_TYPE.CHARACTER)
    128140
    self._r.style = style_id
    129141

    @@ -146,11 +158,11 @@ def text(self) -> str:
    146158
    return self._r.text
    147159

    148160
    @text.setter
    149-
    def text(self, text):
    161+
    def text(self, text: str):
    150162
    self._r.text = text
    151163

    152164
    @property
    153-
    def underline(self):
    165+
    def underline(self) -> bool:
    154166
    """The underline style for this |Run|, one of |None|, |True|, |False|, or a
    155167
    value from :ref:`WdUnderline`.
    156168
    @@ -165,13 +177,13 @@ def underline(self):
    165177
    return self.font.underline
    166178

    167179
    @underline.setter
    168-
    def underline(self, value):
    180+
    def underline(self, value: bool):
    169181
    self.font.underline = value
    170182

    171183

    172184
    class _Text(object):
    173185
    """Proxy object wrapping `<w:t>` element."""
    174186

    175-
    def __init__(self, t_elm):
    187+
    def __init__(self, t_elm: CT_Text):
    176188
    super(_Text, self).__init__()
    177189
    self._t = t_elm

    src/docx/types.py

    Lines changed: 19 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,19 @@
    1+
    """Abstract types used by `python-docx`."""
    2+
    3+
    from __future__ import annotations
    4+
    5+
    from typing_extensions import Protocol
    6+
    7+
    from docx.parts.story import StoryPart
    8+
    9+
    10+
    class StoryChild(Protocol):
    11+
    """An object that can fulfill the `parent` role in a `Parented` class.
    12+
    13+
    This type is for objects that have a story part like document or header as their
    14+
    root part.
    15+
    """
    16+
    17+
    @property
    18+
    def part(self) -> StoryPart:
    19+
    ...

    tests/text/test_run.py

    Lines changed: 5 additions & 4 deletions
    Original file line numberDiff line numberDiff line change
    @@ -75,13 +75,14 @@ def it_can_add_text(self, add_text_fixture, Text_):
    7575
    (WD_BREAK.LINE, "w:r/w:br"),
    7676
    (WD_BREAK.PAGE, "w:r/w:br{w:type=page}"),
    7777
    (WD_BREAK.COLUMN, "w:r/w:br{w:type=column}"),
    78-
    (WD_BREAK.LINE_CLEAR_LEFT, "w:r/w:br{w:type=textWrapping, w:clear=left}"),
    79-
    (WD_BREAK.LINE_CLEAR_RIGHT, "w:r/w:br{w:type=textWrapping, w:clear=right}"),
    80-
    (WD_BREAK.LINE_CLEAR_ALL, "w:r/w:br{w:type=textWrapping, w:clear=all}"),
    78+
    (WD_BREAK.LINE_CLEAR_LEFT, "w:r/w:br{w:clear=left}"),
    79+
    (WD_BREAK.LINE_CLEAR_RIGHT, "w:r/w:br{w:clear=right}"),
    80+
    (WD_BREAK.LINE_CLEAR_ALL, "w:r/w:br{w:clear=all}"),
    8181
    ],
    8282
    )
    8383
    def it_can_add_a_break(self, break_type: WD_BREAK, expected_cxml: str):
    84-
    run = Run(element("w:r"), None)
    84+
    r = cast(CT_R, element("w:r"))
    85+
    run = Run(r, None) # pyright:ignore[reportGeneralTypeIssues]
    8586
    expected_xml = xml(expected_cxml)
    8687

    8788
    run.add_break(break_type)

    tests/unitutil/file.py

    Lines changed: 8 additions & 6 deletions
    Original file line numberDiff line numberDiff line change
    @@ -1,28 +1,30 @@
    11
    """Utility functions for loading files for unit testing."""
    22

    3+
    from __future__ import annotations
    4+
    35
    import os
    46

    57
    _thisdir = os.path.split(__file__)[0]
    68
    test_file_dir = os.path.abspath(os.path.join(_thisdir, "..", "test_files"))
    79

    810

    9-
    def abspath(relpath):
    11+
    def abspath(relpath: str) -> str:
    1012
    thisdir = os.path.split(__file__)[0]
    1113
    return os.path.abspath(os.path.join(thisdir, relpath))
    1214

    1315

    14-
    def absjoin(*paths):
    16+
    def absjoin(*paths: str) -> str:
    1517
    return os.path.abspath(os.path.join(*paths))
    1618

    1719

    18-
    def docx_path(name):
    20+
    def docx_path(name: str):
    1921
    """
    2022
    Return the absolute path to test .docx file with root name `name`.
    2123
    """
    2224
    return absjoin(test_file_dir, "%s.docx" % name)
    2325

    2426

    25-
    def snippet_seq(name, offset=0, count=1024):
    27+
    def snippet_seq(name: str, offset: int = 0, count: int = 1024):
    2628
    """
    2729
    Return a tuple containing the unicode text snippets read from the snippet
    2830
    file having `name`. Snippets are delimited by a blank line. If specified,
    @@ -36,7 +38,7 @@ def snippet_seq(name, offset=0, count=1024):
    3638
    return tuple(snippets[start:end])
    3739

    3840

    39-
    def snippet_text(snippet_file_name):
    41+
    def snippet_text(snippet_file_name: str):
    4042
    """
    4143
    Return the unicode text read from the test snippet file having
    4244
    `snippet_file_name`.
    @@ -49,7 +51,7 @@ def snippet_text(snippet_file_name):
    4951
    return snippet_bytes.decode("utf-8")
    5052

    5153

    52-
    def test_file(name):
    54+
    def test_file(name: str):
    5355
    """
    5456
    Return the absolute path to test file having `name`.
    5557
    """

    0 commit comments

    Comments
     (0)
    0