Skip to content

Commit bb948ae

Browse files
Fix text replacement until end of paragraph, improve tests (#9)
1 parent c483683 commit bb948ae

4 files changed

Lines changed: 60 additions & 26 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "cmi_docx"
3-
version = "0.1.3"
3+
version = "0.1.4"
44
description = ".docx utilities"
55
authors = ["Reinder Vos de Wael <reinder.vosdewael@childmind.org>"]
66
license = "LGPL-2.1"

src/cmi_docx/document.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,13 @@ def find_in_runs(self, needle: str) -> list[run.FindRun]:
3636
needle: The text to find.
3737
3838
Returns:
39-
The indices of the text in the document.
39+
The locations of the text in the runs.
4040
"""
41-
run_finds: list[run.FindRun] = []
42-
for document_paragraph in self.all_paragraphs:
43-
run_finds.extend(
44-
paragraph.ExtendParagraph(document_paragraph).find_in_runs(needle)
45-
)
46-
return run_finds
41+
return [
42+
finder
43+
for para in self.all_paragraphs
44+
for finder in paragraph.ExtendParagraph(para).find_in_runs(needle)
45+
]
4746

4847
def replace(self, needle: str, replace: str) -> None:
4948
"""Finds and replaces text in a Word document.

src/cmi_docx/paragraph.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,18 @@ def find_in_runs(self, needle: str) -> list[run.FindRun]:
6464
Returns:
6565
The indices of the text in the paragraph.
6666
"""
67+
if len(needle) == 0:
68+
return []
69+
6770
run_finds: list[run.FindRun] = []
6871
run_lengths = [len(run.text) for run in self.paragraph.runs]
6972
cumulative_run_lengths = list(itertools.accumulate(run_lengths))
7073
for occurence in self.find_in_paragraph(needle).character_indices:
7174
start_run = bisect.bisect_right(cumulative_run_lengths, occurence[0])
7275
end_run = bisect.bisect_right(
73-
cumulative_run_lengths, occurence[1], lo=start_run
76+
cumulative_run_lengths[:-1], occurence[1], lo=start_run
7477
)
78+
7579
start_index = (
7680
occurence[0] - cumulative_run_lengths[start_run - 1]
7781
if start_run > 0

tests/test_document.py

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Tests for the document module."""
22

33
import docx
4+
import pytest
45

56
from cmi_docx import document
67

@@ -35,28 +36,58 @@ def test_find_in_runs() -> None:
3536
assert actual[2].character_indices == (14, 19)
3637

3738

38-
def test_replace() -> None:
39+
@pytest.mark.parametrize(
40+
("runs", "needle", "replace", "expected"),
41+
[
42+
(
43+
["Hello, world!"],
44+
"Hello",
45+
"Goodbye",
46+
"Goodbye, world!",
47+
),
48+
(
49+
["Hello, world!", " Hello!"],
50+
"Hello",
51+
"Goodbye",
52+
"Goodbye, world! Goodbye!",
53+
),
54+
(
55+
["Hello {{", "FULL_NAME}}"],
56+
"{{FULL_NAME}}",
57+
'Shizuka "Lea" Sakai',
58+
'Hello Shizuka "Lea" Sakai',
59+
),
60+
(
61+
["This is James", " Bond ", "007!"],
62+
"James Bond 007",
63+
"Patrick",
64+
"This is Patrick!",
65+
),
66+
(
67+
["This is Alec", " Travelyan ", "006!"],
68+
"This is Alec Travelyan 006!",
69+
"",
70+
"",
71+
),
72+
(
73+
["This", " is ", "Patrick!"],
74+
"",
75+
"Nonsense",
76+
"This is Patrick!",
77+
),
78+
],
79+
)
80+
def test_replace(runs: list[str], needle: str, replace: str, expected: str) -> None:
3981
"""Test replacing text in a document."""
4082
doc = docx.Document()
41-
doc.add_paragraph("Hello, world!")
42-
extend_document = document.ExtendDocument(doc)
43-
44-
extend_document.replace("Hello", "Goodbye")
45-
46-
assert doc.paragraphs[0].text == "Goodbye, world!"
47-
48-
49-
def test_replace_across_runs() -> None:
50-
"""Test replacing text across runs in a document."""
51-
doc = docx.Document()
52-
paragraph = doc.add_paragraph("Hello, world!")
53-
paragraph.add_run(" Maintain, World!")
54-
paragraph.add_run(" Goodbye, World!")
83+
paragraph = doc.add_paragraph(runs[0])
84+
for run in runs[1:]:
85+
paragraph.add_run(run)
5586
extend_document = document.ExtendDocument(doc)
5687

57-
extend_document.replace("world! Maintain, World! Goodbye", "Goodbye")
88+
extend_document.replace(needle, replace)
5889

59-
assert doc.paragraphs[0].text == "Hello, Goodbye, World!"
90+
assert doc.paragraphs[0].text == expected
6091

6192

6293
def test_insert_paragraph_by_object() -> None:

0 commit comments

Comments
 (0)