Skip to content

Commit 4151eb9

Browse files
author
Steve Canny
committed
img: add ImagePart.from_image()
Also, changing the call of from_image() to require partname required some tweaks to the ImageParts._add_image_part() tests.
1 parent bb8e602 commit 4151eb9

File tree

4 files changed

+70
-10
lines changed

4 files changed

+70
-10
lines changed

docx/package.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ def _add_image_part(self, image):
7979
Return an |ImagePart| instance newly created from image and appended
8080
to the collection.
8181
"""
82-
image_part = ImagePart.from_image(image)
82+
partname = self._next_image_partname
83+
image_part = ImagePart.from_image(image, partname)
8384
self.append(image_part)
8485
return image_part
8586

@@ -92,3 +93,11 @@ def _get_by_sha1(self, sha1):
9293
if image_part.sha1 == sha1:
9394
return image_part
9495
return None
96+
97+
@property
98+
def _next_image_partname(self):
99+
"""
100+
The next available image partname, starting from
101+
``/word/media/image1.{ext}`` where unused numbers are reused.
102+
"""
103+
raise NotImplementedError

docx/parts/image.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@ def __init__(self, blob, filename):
2222
self._blob = blob
2323
self._filename = filename
2424

25+
@property
26+
def blob(self):
27+
"""
28+
The bytes of the image 'file'
29+
"""
30+
return self._blob
31+
32+
@property
33+
def content_type(self):
34+
"""
35+
The MIME type of the image, e.g. 'image/png'.
36+
"""
37+
raise NotImplementedError
38+
2539
@property
2640
def filename(self):
2741
"""
@@ -73,11 +87,12 @@ def filename(self):
7387
raise NotImplementedError
7488

7589
@classmethod
76-
def from_image(cls, image):
90+
def from_image(cls, image, partname):
7791
"""
78-
Return an |ImagePart| instance newly created from *image*.
92+
Return an |ImagePart| instance newly created from *image* and
93+
assigned *partname*.
7994
"""
80-
raise NotImplementedError
95+
return ImagePart(partname, image.content_type, image.blob, image)
8196

8297
@property
8398
def height(self):

tests/parts/test_image.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
from docx.package import Package
1515
from docx.parts.image import Image, ImagePart
1616

17-
from ..unitutil import instance_mock, method_mock, test_file
17+
from ..unitutil import (
18+
initializer_mock, instance_mock, method_mock, test_file
19+
)
1820

1921

2022
class DescribeImage(object):
@@ -52,12 +54,32 @@ def it_is_used_by_PartFactory_to_construct_image_part(self, load_fixture):
5254
)
5355
assert part is image_part_
5456

57+
def it_can_construct_from_image_instance(self, from_image_fixture):
58+
image_, partname_, ImagePart__init__ = from_image_fixture
59+
image_part = ImagePart.from_image(image_, partname_)
60+
ImagePart__init__.assert_called_once_with(
61+
partname_, image_.content_type, image_.blob, image_
62+
)
63+
assert isinstance(image_part, ImagePart)
64+
5565
# fixtures -------------------------------------------------------
5666

5767
@pytest.fixture
5868
def blob_(self, request):
5969
return instance_mock(request, str)
6070

71+
@pytest.fixture
72+
def from_image_fixture(self, image_, partname_, ImagePart__init__):
73+
return image_, partname_, ImagePart__init__
74+
75+
@pytest.fixture
76+
def image_(self, request):
77+
return instance_mock(request, Image)
78+
79+
@pytest.fixture
80+
def ImagePart__init__(self, request):
81+
return initializer_mock(request, ImagePart)
82+
6183
@pytest.fixture
6284
def image_part_(self, request):
6385
return instance_mock(request, ImagePart)

tests/test_package.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88

99
import pytest
1010

11+
from docx.opc.packuri import PackURI
1112
from docx.package import ImageParts, Package
1213
from docx.parts.image import Image, ImagePart
1314

14-
from .unitutil import class_mock, docx_path, instance_mock, method_mock
15+
from .unitutil import (
16+
docx_path, class_mock, instance_mock, method_mock, property_mock
17+
)
1518

1619

1720
class DescribePackage(object):
@@ -38,11 +41,11 @@ def it_can_add_a_new_image_part(self, add_image_part_fixture):
3841

3942
def it_can_really_add_a_new_image_part(
4043
self, really_add_image_part_fixture):
41-
image_parts, image_, ImagePart_, image_part_ = (
44+
image_parts, image_, ImagePart_, partname_, image_part_ = (
4245
really_add_image_part_fixture
4346
)
4447
image_part = image_parts._add_image_part(image_)
45-
ImagePart_.from_image.assert_called_once_with(image_)
48+
ImagePart_.from_image.assert_called_once_with(image_, partname_)
4649
assert image_part in image_parts
4750
assert image_part is image_part_
4851

@@ -101,9 +104,20 @@ def new_image_part_(self, request):
101104
return instance_mock(request, ImagePart)
102105

103106
@pytest.fixture
104-
def really_add_image_part_fixture(self, image_, ImagePart_, image_part_):
107+
def _next_image_partname_(self, request):
108+
return property_mock(request, ImageParts, '_next_image_partname')
109+
110+
@pytest.fixture
111+
def partname_(self, request):
112+
return instance_mock(request, PackURI)
113+
114+
@pytest.fixture
115+
def really_add_image_part_fixture(
116+
self, _next_image_partname_, partname_, image_, ImagePart_,
117+
image_part_):
105118
image_parts = ImageParts()
106-
return image_parts, image_, ImagePart_, image_part_
119+
_next_image_partname_.return_value = partname_
120+
return image_parts, image_, ImagePart_, partname_, image_part_
107121

108122
@pytest.fixture
109123
def sha1(self):

0 commit comments

Comments
 (0)