1- import plotly .io as pio
2- import plotly .io .kaleido
3- from contextlib import contextmanager
41from io import BytesIO
52from pathlib import Path
6- from unittest .mock import Mock
7-
8- fig = {"layout" : {"title" : {"text" : "figure title" }}}
9-
10-
11- def make_writeable_mocks ():
12- """Produce some mocks which we will use for testing the `write_image()` function.
13-
14- These mocks should be passed as the `file=` argument to `write_image()`.
15-
16- The tests should verify that the method specified in the `active_write_function`
17- attribute is called once, and that scope.transform is called with the `format=`
18- argument specified by the `.expected_format` attribute.
19-
20- In total we provide two mocks: one for a writable file descriptor, and other for a
21- pathlib.Path object.
22- """
23-
24- # Part 1: A mock for a file descriptor
25- # ------------------------------------
26- mock_file_descriptor = Mock ()
3+ import tempfile
274
28- # A file descriptor has no write_bytes method, unlike a pathlib Path.
29- del mock_file_descriptor .write_bytes
30-
31- # The expected write method for a file descriptor is .write
32- mock_file_descriptor .active_write_function = mock_file_descriptor .write
33-
34- # Since there is no filename, there should be no format detected.
35- mock_file_descriptor .expected_format = None
36-
37- # Part 2: A mock for a pathlib path
38- # ---------------------------------
39- mock_pathlib_path = Mock (spec = Path )
40-
41- # A pathlib Path object has no write method, unlike a file descriptor.
42- del mock_pathlib_path .write
43-
44- # The expected write method for a pathlib Path is .write_bytes
45- mock_pathlib_path .active_write_function = mock_pathlib_path .write_bytes
46-
47- # Mock a path with PNG suffix
48- mock_pathlib_path .suffix = ".png"
49- mock_pathlib_path .expected_format = "png"
5+ from pdfrw import PdfReader
6+ from PIL import Image
7+ import plotly .io as pio
508
51- return mock_file_descriptor , mock_pathlib_path
9+ fig = { "data" : [], "layout" : { "title" : { "text" : "figure title" }}}
5210
5311
54- @contextmanager
55- def mocked_scope ():
56- # Code to acquire resource, e.g.:
57- scope_mock = Mock ()
58- original_scope = pio ._kaleido .scope
59- pio ._kaleido .scope = scope_mock
60- try :
61- yield scope_mock
62- finally :
63- pio ._kaleido .scope = original_scope
12+ def check_image (path_or_buffer , size = (700 , 500 ), format = "PNG" ):
13+ if format == "PDF" :
14+ img = PdfReader (path_or_buffer )
15+ # TODO: There is a conversion factor needed here
16+ # In Kaleido v0 the conversion factor is 0.75
17+ factor = 0.75
18+ expected_size = tuple (int (s * factor ) for s in size )
19+ actual_size = tuple (int (s ) for s in img .pages [0 ].MediaBox [2 :])
20+ assert actual_size == expected_size
21+ else :
22+ if isinstance (path_or_buffer , (str , Path )):
23+ with open (path_or_buffer , "rb" ) as f :
24+ img = Image .open (f )
25+ else :
26+ img = Image .open (path_or_buffer )
27+ assert img .size == size
28+ assert img .format == format
6429
6530
6631def test_kaleido_engine_to_image_returns_bytes ():
@@ -75,80 +40,66 @@ def test_kaleido_fulljson():
7540
7641
7742def test_kaleido_engine_to_image ():
78- with mocked_scope () as scope :
79- pio .to_image (fig , engine = "kaleido" , validate = False )
43+ bytes = pio .to_image (fig , engine = "kaleido" , validate = False )
8044
81- scope . transform . assert_called_with (
82- fig , format = None , width = None , height = None , scale = None
83- )
45+ # Check that image dimensions match default dimensions (700x500)
46+ # and format is default format (png)
47+ check_image ( BytesIO ( bytes ) )
8448
8549
86- def test_kaleido_engine_write_image ():
87- for writeable_mock in make_writeable_mocks ():
88- with mocked_scope () as scope :
89- pio .write_image (fig , writeable_mock , engine = "kaleido" , validate = False )
50+ def test_kaleido_engine_write_image (tmp_path ):
51+ path_str = tempfile .mkstemp (suffix = ".png" , dir = tmp_path )[1 ]
52+ path_path = Path (tempfile .mkstemp (suffix = ".png" , dir = tmp_path )[1 ])
9053
91- scope .transform .assert_called_with (
92- fig ,
93- format = writeable_mock .expected_format ,
94- width = None ,
95- height = None ,
96- scale = None ,
97- )
98-
99- assert writeable_mock .active_write_function .call_count == 1
54+ for out_path in [path_str , path_path ]:
55+ pio .write_image (fig , out_path , engine = "kaleido" , validate = False )
56+ check_image (out_path )
10057
10158
10259def test_kaleido_engine_to_image_kwargs ():
103- with mocked_scope () as scope :
104- pio .to_image (
60+ bytes = pio .to_image (
61+ fig ,
62+ format = "pdf" ,
63+ width = 700 ,
64+ height = 600 ,
65+ scale = 2 ,
66+ engine = "kaleido" ,
67+ validate = False ,
68+ )
69+ check_image (BytesIO (bytes ), size = (700 * 2 , 600 * 2 ), format = "PDF" )
70+
71+
72+ def test_kaleido_engine_write_image_kwargs (tmp_path ):
73+ path_str = tempfile .mkstemp (suffix = ".png" , dir = tmp_path )[1 ]
74+ path_path = Path (tempfile .mkstemp (suffix = ".png" , dir = tmp_path )[1 ])
75+
76+ for out_path in [path_str , path_path ]:
77+ pio .write_image (
10578 fig ,
106- format = "pdf" ,
79+ out_path ,
80+ format = "jpg" ,
10781 width = 700 ,
10882 height = 600 ,
10983 scale = 2 ,
11084 engine = "kaleido" ,
11185 validate = False ,
11286 )
113-
114- scope .transform .assert_called_with (
115- fig , format = "pdf" , width = 700 , height = 600 , scale = 2
116- )
117-
118-
119- def test_kaleido_engine_write_image_kwargs ():
120- for writeable_mock in make_writeable_mocks ():
121- with mocked_scope () as scope :
122- pio .write_image (
123- fig ,
124- writeable_mock ,
125- format = "jpg" ,
126- width = 700 ,
127- height = 600 ,
128- scale = 2 ,
129- engine = "kaleido" ,
130- validate = False ,
131- )
132-
133- scope .transform .assert_called_with (
134- fig , format = "jpg" , width = 700 , height = 600 , scale = 2
135- )
136-
137- assert writeable_mock .active_write_function .call_count == 1
87+ check_image (out_path , size = (700 * 2 , 600 * 2 ), format = "JPEG" )
13888
13989
14090def test_image_renderer ():
141- with mocked_scope () as scope :
142- pio .show (fig , renderer = "svg" , engine = "kaleido" , validate = False )
91+ # TODO: How to replicate this test using kaleido v1?
92+ # with mocked_scope() as scope:
93+ pio .show (fig , renderer = "svg" , engine = "kaleido" , validate = False )
14394
14495 renderer = pio .renderers ["svg" ]
145- scope .transform .assert_called_with (
146- fig ,
147- format = "svg" ,
148- width = None ,
149- height = None ,
150- scale = renderer .scale ,
151- )
96+ # scope.transform.assert_called_with(
97+ # fig,
98+ # format="svg",
99+ # width=None,
100+ # height=None,
101+ # scale=renderer.scale,
102+ # )
152103
153104
154105def test_bytesio ():
0 commit comments