Skip to content

Commit 1f1dc72

Browse files
committed
Rework logic to actually use pathlib
1 parent f46f195 commit 1f1dc72

File tree

2 files changed

+73
-42
lines changed

2 files changed

+73
-42
lines changed

packages/python/plotly/plotly/io/_kaleido.py

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import absolute_import
22
from six import string_types
33
import os
4+
from pathlib import Path, PurePath
45
import json
56
import plotly
67
from plotly.io._utils import validate_coerce_fig_to_dict
@@ -215,14 +216,19 @@ def write_image(
215216
-------
216217
None
217218
"""
218-
# Check if file is a string
219-
# -------------------------
220-
file_is_str = isinstance(file, string_types)
219+
# Check if file can be interpreted as a pathlib object
220+
# ----------------------------------------------------
221+
if isinstance(file, string_types):
222+
path = Path(file)
223+
elif isinstance(file, PurePath):
224+
path = file
225+
else:
226+
path = None
221227

222228
# Infer format if not specified
223229
# -----------------------------
224-
if file_is_str and format is None:
225-
_, ext = os.path.splitext(file)
230+
if path is not None and format is None:
231+
ext = path.suffix
226232
if ext:
227233
format = ext.lstrip(".")
228234
else:
@@ -254,26 +260,16 @@ def write_image(
254260

255261
# Open file
256262
# ---------
257-
if file_is_str:
258-
with open(file, "wb") as f:
259-
f.write(img_data)
263+
if path is not None:
264+
file.write_bytes(img_data)
260265
else:
261-
# Handle a pathlib.Path object
262-
# ----------------------------
263-
try:
264-
file.write_bytes(img_data)
265-
return
266-
except AttributeError:
267-
pass
268266
# Handle an open file descriptor
269267
# ------------------------------
270268
try:
271269
file.write(img_data)
272270
return
273271
except AttributeError:
274272
pass
275-
# Handlers failed
276-
# ---------------
277273
raise ValueError(
278274
"""
279275
The 'file' argument '{file}' is not a string, pathlib.Path object, or file descriptor.

packages/python/plotly/plotly/tests/test_optional/test_kaleido/test_kaleido.py

Lines changed: 60 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import plotly.io as pio
22
import plotly.io.kaleido
33
import sys
4+
from pathlib import Path
45
from contextlib import contextmanager
56

67
if sys.version_info >= (3, 3):
@@ -11,6 +12,38 @@
1112
fig = {"layout": {"title": {"text": "figure title"}}}
1213

1314

15+
def make_writeable_mocks():
16+
17+
# A mock for a file descriptor
18+
# ----------------------------
19+
mock_file_descriptor = Mock()
20+
21+
# A file descriptor has no write_bytes method, unlike a pathlib Path.
22+
del mock_file_descriptor.write_bytes
23+
24+
# The expected write method for a file descriptor is .write
25+
mock_file_descriptor.active_write_function = mock_file_descriptor.write
26+
27+
# Since there is no filename, there should be no format detected.
28+
mock_file_descriptor.expected_format = None
29+
30+
# A mock for a pathlib path
31+
# -------------------------
32+
mock_pathlib_path = Mock(spec=Path)
33+
34+
# A pathlib Path object has no write method, unlike a file descriptor.
35+
del mock_pathlib_path.write
36+
37+
# The expected write method for a pathlib Path is .write_bytes
38+
mock_pathlib_path.active_write_function = mock_pathlib_path.write_bytes
39+
40+
# Mock a path with PNG suffix
41+
mock_pathlib_path.suffix = ".png"
42+
mock_pathlib_path.expected_format = "png"
43+
44+
return mock_file_descriptor, mock_pathlib_path
45+
46+
1447
@contextmanager
1548
def mocked_scope():
1649
# Code to acquire resource, e.g.:
@@ -44,16 +77,19 @@ def test_kaleido_engine_to_image():
4477

4578

4679
def test_kaleido_engine_write_image():
47-
writeable_mock = Mock()
48-
with mocked_scope() as scope:
49-
pio.write_image(fig, writeable_mock, engine="kaleido", validate=False)
80+
for writeable_mock in make_writeable_mocks():
81+
with mocked_scope() as scope:
82+
pio.write_image(fig, writeable_mock, engine="kaleido", validate=False)
5083

51-
scope.transform.assert_called_with(
52-
fig, format=None, width=None, height=None, scale=None
53-
)
84+
scope.transform.assert_called_with(
85+
fig,
86+
format=writeable_mock.expected_format,
87+
width=None,
88+
height=None,
89+
scale=None,
90+
)
5491

55-
count = writeable_mock.write.call_count + writeable_mock.write_bytes.call_count
56-
assert count == 1
92+
assert writeable_mock.active_write_function.call_count == 1
5793

5894

5995
def test_kaleido_engine_to_image_kwargs():
@@ -74,25 +110,24 @@ def test_kaleido_engine_to_image_kwargs():
74110

75111

76112
def test_kaleido_engine_write_image_kwargs():
77-
writeable_mock = Mock()
78-
with mocked_scope() as scope:
79-
pio.write_image(
80-
fig,
81-
writeable_mock,
82-
format="jpg",
83-
width=700,
84-
height=600,
85-
scale=2,
86-
engine="kaleido",
87-
validate=False,
113+
for writeable_mock in make_writeable_mocks():
114+
with mocked_scope() as scope:
115+
pio.write_image(
116+
fig,
117+
writeable_mock,
118+
format="jpg",
119+
width=700,
120+
height=600,
121+
scale=2,
122+
engine="kaleido",
123+
validate=False,
124+
)
125+
126+
scope.transform.assert_called_with(
127+
fig, format="jpg", width=700, height=600, scale=2
88128
)
89129

90-
scope.transform.assert_called_with(
91-
fig, format="jpg", width=700, height=600, scale=2
92-
)
93-
94-
count = writeable_mock.write.call_count + writeable_mock.write_bytes.call_count
95-
assert count == 1
130+
assert writeable_mock.active_write_function.call_count == 1
96131

97132

98133
def test_image_renderer():

0 commit comments

Comments
 (0)