8000 hlink: add Hyperlink.address · python-openxml/python-docx@16e3f10 · GitHub
[go: up one dir, main page]

Skip to content

Commit 16e3f10

Browse files
committed
hlink: add Hyperlink.address
1 parent 7868f3e commit 16e3f10

File tree

5 files changed

+118
-2
lines changed

5 files changed

+118
-2
lines changed

features/hlk-props.feature

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ Feature: Access hyperlink properties
44
I need properties on Hyperlink
55

66

7-
@wip
87
Scenario: Hyperlink.address has the URL of the hyperlink
98
Given a hyperlink
109
Then hyperlink.address is the URL of the hyperlink

src/docx/oxml/text/hyperlink.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,24 @@
22

33
from __future__ import annotations
44

5-
from docx.oxml.xmlchemy import BaseOxmlElement
5+
from typing import List
6+
7+
from docx.oxml.simpletypes import ST_OnOff, XsdString
8+
from docx.oxml.text.run import CT_R
9+
from docx.oxml.xmlchemy import (
10+
BaseOxmlElement,
11+
OptionalAttribute,
12+
RequiredAttribute,
13+
ZeroOrMore,
14+
)
615

716

817
class CT_Hyperlink(BaseOxmlElement):
918
"""`<w:hyperlink>` element, containing the text and address for a hyperlink."""
19+
20+
r_lst: List[CT_R]
21+
22+
rId = RequiredAttribute("r:id", XsdString)
23+
history = OptionalAttribute("w:history", ST_OnOff, default=True)
24+
25+
r = ZeroOrMore("w:r")

src/docx/text/hyperlink.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,13 @@ def __init__(self, hyperlink: CT_Hyperlink, parent: t.StoryChild):
2424
super().__init__(parent)
2525
self._parent = parent
2626
self._hyperlink = self._element = hyperlink
27+
28+
@property
29+
def address(self) -> str:
30+
"""The "URL" of the hyperlink (but not necessarily a web link).
31+
32+
While commonly a web link like "https://google.com" the hyperlink address can
33+
take a variety of forms including "internal links" to bookmarked locations
34+
within the document.
35+
"""
36+
return self._parent.part.rels[self._hyperlink.rId].target_ref

tests/oxml/text/test_hyperlink.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""Test suite for the docx.oxml.text.hyperlink module."""
2+
3+
from typing import cast
4+
5+
import pytest
6+
7+
from docx.oxml.text.hyperlink import CT_Hyperlink
8+
from docx.oxml.text.run import CT_R
9+
10+
from ...unitutil.cxml import element
11+
12+
13+
class DescribeCT_Hyperlink:
14+
"""Unit-test suite for the CT_Hyperlink (<w:hyperlink>) element."""
15+
16+
def it_has_a_relationship_that_contains_the_hyperlink_address(self):
17+
cxml = 'w:hyperlink{r:id=rId6}/w:r/w:t"post"'
18+
hyperlink = cast(CT_Hyperlink, element(cxml))
19+
20+
rId = hyperlink.rId
21+
22+
assert rId == "rId6"
23+
24+
@pytest.mark.parametrize(
25+
("cxml", "expected_value"),
26+
[
27+
# -- default (when omitted) is True, somewhat surprisingly --
28+
("w:hyperlink{r:id=rId6}", True),
29+
("w:hyperlink{r:id=rId6,w:history=0}", False),
30+
("w:hyperlink{r:id=rId6,w:history=1}", True),
31+
],
32+
)
33+
def it_knows_whether_it_has_been_clicked_on_aka_visited(
34+
self, cxml: str, expected_value: bool
35+
):
36+
hyperlink = cast(CT_Hyperlink, element(cxml))
37+
assert hyperlink.history is expected_value
38+
39+
def it_has_zero_or_more_runs_containing_the_hyperlink_text(self):
40+
cxml = 'w:hyperlink{r:id=rId6,w:history=1}/(w:r/w:t"blog",w:r/w:t" post")'
41+
hyperlink = cast(CT_Hyperlink, element(cxml))
42+
43+
rs = hyperlink.r_lst
44+
45+
assert [type(r) for r in rs] == [CT_R, CT_R]
46+
assert rs[0].text == "blog"
47+
assert rs[1].text == " post"

tests/text/test_hyperlink.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""Test suite for the docx.text.hyperlink module."""
2+
3+
from typing import cast
4+
5+
import pytest
6+
7+
from docx import types as t
8+
from docx.opc.rel import _Relationship # pyright: ignore[reportPrivateUsage]
9+
from docx.oxml.text.hyperlink import CT_Hyperlink
10+
from docx.parts.story import StoryPart
11+
from docx.text.hyperlink import Hyperlink
12+
13+
from ..unitutil.cxml import element
14+
from ..unitutil.mock import FixtureRequest, Mock, instance_mock
15+
16+
17+
class DescribeHyperlink:
18+
"""Unit-test suite for the docx.text.hyperlink.Hyperlink object."""
19+
20+
def it_knows_the_hyperlink_URL(self, fake_parent: t.StoryChild):
21+
cxml = 'w:hyperlink{r:id=rId6}/w:r/w:t"post"'
22+
hlink = cast(CT_Hyperlink, element(cxml))
23+
hyperlink = Hyperlink(hlink, fake_parent)
24+
25+
assert hyperlink.address == "https://google.com/"
26+
27+
# -- fixtures --------------------------------------------------------------------
28+
29+
@pytest.fixture
30+
def fake_parent(self, story_part: Mock, rel: Mock) -> t.StoryChild:
31+
class StoryChild:
32+
@property
33+
def part(self) -> StoryPart:
34+
return story_part
35+
36+
return StoryChild()
37+
38+
@pytest.fixture
39+
def rel(self, request: FixtureRequest):
40+
return instance_mock(request, _Relationship, target_ref="https://google.com/")
41+
42+
@pytest.fixture
43+
def story_part(self, request: FixtureRequest, rel: Mock):
44+
return instance_mock(request, StoryPart, rels={"rId6": rel})

0 commit comments

Comments
 (0)
0