Skip to content

Commit 692842f

Browse files
committed
refactor write_images to use kaleido write_fig function (WIP)
1 parent fe12aec commit 692842f

File tree

1 file changed

+144
-48
lines changed

1 file changed

+144
-48
lines changed

plotly/io/_kaleido.py

Lines changed: 144 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,44 @@ def __setattr__(self, name, value):
140140
scope = None
141141

142142

143+
def as_path_object(file: str | Path) -> Path | None:
144+
"""
145+
Cast the `file` argument, which may be either a string or a Path object,
146+
to a Path object.
147+
If `file` is neither a string nor a Path object, None will be returned.
148+
"""
149+
if isinstance(file, str):
150+
# Use the standard Path constructor to make a pathlib object.
151+
path = Path(file)
152+
elif isinstance(file, Path):
153+
# `file` is already a Path object.
154+
path = file
155+
else:
156+
# We could not make a Path object out of file. Either `file` is an open file
157+
# descriptor with a `write()` method or it's an invalid object.
158+
path = None
159+
return path
160+
161+
def infer_format(path: Path | None, format: str | None) -> str | None:
162+
if path is not None and format is None:
163+
ext = path.suffix
164+
if ext:
165+
format = ext.lstrip(".")
166+
else:
167+
raise ValueError(
168+
f"""
169+
Cannot infer image type from output path '{path}'.
170+
Please specify the type using the format parameter, or add a file extension.
171+
For example:
172+
173+
>>> import plotly.io as pio
174+
>>> pio.write_image(fig, file_path, format='png')
175+
"""
176+
)
177+
return format
178+
179+
180+
143181
def to_image(
144182
fig,
145183
format=None,
@@ -216,7 +254,6 @@ def to_image(
216254
"""
217255

218256
# Handle engine
219-
# -------------
220257
if engine is not None:
221258
warnings.warn(ENGINE_PARAM_DEPRECATION_MSG, DeprecationWarning, stacklevel=2)
222259
else:
@@ -394,7 +431,6 @@ def write_image(
394431
None
395432
"""
396433
# Show Kaleido deprecation warning if needed
397-
# ------------------------------------------
398434
if (
399435
engine in {None, "auto", "kaleido"}
400436
and kaleido_available()
@@ -407,38 +443,12 @@ def write_image(
407443
warnings.warn(ENGINE_PARAM_DEPRECATION_MSG, DeprecationWarning, stacklevel=2)
408444

409445
# Try to cast `file` as a pathlib object `path`.
410-
# ----------------------------------------------
411-
if isinstance(file, str):
412-
# Use the standard Path constructor to make a pathlib object.
413-
path = Path(file)
414-
elif isinstance(file, Path):
415-
# `file` is already a Path object.
416-
path = file
417-
else:
418-
# We could not make a Path object out of file. Either `file` is an open file
419-
# descriptor with a `write()` method or it's an invalid object.
420-
path = None
446+
path = as_path_object(file)
421447

422-
# Infer format if not specified
423-
# -----------------------------
424-
if path is not None and format is None:
425-
ext = path.suffix
426-
if ext:
427-
format = ext.lstrip(".")
428-
else:
429-
raise ValueError(
430-
f"""
431-
Cannot infer image type from output path '{file}'.
432-
Please add a file extension or specify the type using the format parameter.
433-
For example:
434-
435-
>>> import plotly.io as pio
436-
>>> pio.write_image(fig, file_path, format='png')
437-
"""
438-
)
448+
# Infer image format if not specified
449+
format = infer_format(path, format)
439450

440451
# Request image
441-
# -------------
442452
# Do this first so we don't create a file if image conversion fails
443453
img_data = to_image(
444454
fig,
@@ -451,7 +461,6 @@ def write_image(
451461
)
452462

453463
# Open file
454-
# ---------
455464
if path is None:
456465
# We previously failed to make sense of `file` as a pathlib object.
457466
# Attempt to write to `file` as an open file descriptor.
@@ -471,34 +480,121 @@ def write_image(
471480
path.write_bytes(img_data)
472481

473482

474-
def write_images(*args, **kwargs):
483+
def write_images(
484+
figs,
485+
file,
486+
format=None,
487+
scale=None,
488+
width=None,
489+
height=None,
490+
validate=True,
491+
):
475492
"""
476493
Write multiple images to files or writeable objects. This is much faster than
477-
calling write_image() multiple times.
494+
calling write_image() multiple times. This function can only be used with the Kaleido
495+
engine, v1.0.0 or greater.
478496
479497
Parameters
480498
----------
481-
Accepts the same parameters as pio.write_image(), but any parameter may be either
482-
a single value or a list of values. If more than one parameter is a list,
483-
all must be the same length.
499+
figs:
500+
Iterable of figure objects or dicts representing a figure
501+
502+
directory: str or writeable
503+
A string or pathlib.Path object representing a local directory path.
504+
505+
format: str or None
506+
The desired image format. One of
507+
- 'png'
508+
- 'jpg' or 'jpeg'
509+
- 'webp'
510+
- 'svg'
511+
- 'pdf'
512+
513+
If not specified, this will default to `plotly.io.defaults.default_format`.
514+
515+
width: int or None
516+
The width of the exported image in layout pixels. If the `scale`
517+
property is 1.0, this will also be the width of the exported image
518+
in physical pixels.
519+
520+
If not specified, will default to `plotly.io.defaults.default_width`.
521+
522+
height: int or None
523+
The height of the exported image in layout pixels. If the `scale`
524+
property is 1.0, this will also be the height of the exported image
525+
in physical pixels.
526+
527+
If not specified, will default to `plotly.io.defaults.default_height`.
528+
529+
scale: int or float or None
530+
The scale factor to use when exporting the figure. A scale factor
531+
larger than 1.0 will increase the image resolution with respect
532+
to the figure's layout pixel dimensions. Whereas as scale factor of
533+
less than 1.0 will decrease the image resolution.
534+
535+
If not specified, will default to `plotly.io.defaults.default_scale`.
536+
537+
validate: bool
538+
True if the figure should be validated before being converted to
539+
an image, False otherwise.
484540
485541
Returns
486542
-------
487543
None
488544
"""
489545

490-
# Get individual arguments
491-
individual_args, individual_kwargs = as_individual_args(*args, **kwargs)
546+
# Raise informative error message if Kaleido v1 is not installed
547+
if not kaleido_available():
548+
raise ValueError(
549+
"""
550+
The `write_images()` function requires the kaleido package,
551+
which can be installed using pip:
552+
$ pip install -U kaleido
553+
"""
554+
)
555+
elif kaleido_major() < 1:
556+
raise ValueError(
557+
f"""
558+
You have Kaleido version {Version(importlib_metadata.version("kaleido"))} installed.
559+
The `write_images()` function requires the kaleido package version 1 or greater,
560+
which can be installed using pip:
561+
$ pip install -U 'kaleido>=1.0.0'
562+
"""
563+
)
492564

493-
if kaleido_available() and kaleido_major() > 0:
494-
# Kaleido v1
495-
# TODO: Use a single shared kaleido instance for all images
496-
for a, kw in zip(individual_args, individual_kwargs):
497-
write_image(*a, **kw)
498-
else:
499-
# Kaleido v0, or orca
500-
for a, kw in zip(individual_args, individual_kwargs):
501-
write_image(*a, **kw)
565+
# Try to cast `file` as a pathlib object `path`.
566+
path = as_path_object(file)
567+
568+
# Infer image format if not specified
569+
format = infer_format(path, format)
570+
571+
# Convert figures to dicts (and validate if requested)
572+
# TODO: Keep same iterable type
573+
fig_dicts = [validate_coerce_fig_to_dict(fig, validate) for fig in figs]
574+
575+
kaleido.write_fig_sync(
576+
fig_dicts,
577+
directory=path,
578+
opts=dict(
579+
format=format or defaults.default_format,
580+
width=width or defaults.default_width,
581+
height=height or defaults.default_height,
582+
scale=scale or defaults.default_scale,
583+
),
584+
)
585+
586+
# # Get individual arguments
587+
# individual_args, individual_kwargs = as_individual_args(*args, **kwargs)
588+
589+
# if kaleido_available() and kaleido_major() > 0:
590+
# # Kaleido v1
591+
# # TODO: Use a single shared kaleido instance for all images
592+
# for a, kw in zip(individual_args, individual_kwargs):
593+
# write_image(*a, **kw)
594+
# else:
595+
# # Kaleido v0, or orca
596+
# for a, kw in zip(individual_args, individual_kwargs):
597+
# write_image(*a, **kw)
502598

503599

504600
def full_figure_for_development(fig, warn=True, as_dict=False):

0 commit comments

Comments
 (0)