diff options
Diffstat (limited to 'venv/lib/python3.8/site-packages/plotly/io/_kaleido.py')
-rw-r--r-- | venv/lib/python3.8/site-packages/plotly/io/_kaleido.py | 872 |
1 files changed, 872 insertions, 0 deletions
diff --git a/venv/lib/python3.8/site-packages/plotly/io/_kaleido.py b/venv/lib/python3.8/site-packages/plotly/io/_kaleido.py new file mode 100644 index 0000000..6775c33 --- /dev/null +++ b/venv/lib/python3.8/site-packages/plotly/io/_kaleido.py @@ -0,0 +1,872 @@ +import os +import json +from pathlib import Path +from typing import Union, List +import importlib.metadata as importlib_metadata +from packaging.version import Version +import warnings + +import plotly +from plotly.io._utils import validate_coerce_fig_to_dict, broadcast_args_to_dicts +from plotly.io._defaults import defaults + +ENGINE_SUPPORT_TIMELINE = "September 2025" +ENABLE_KALEIDO_V0_DEPRECATION_WARNINGS = True + +PLOTLY_GET_CHROME_ERROR_MSG = """ + +Kaleido requires Google Chrome to be installed. + +Either download and install Chrome yourself following Google's instructions for your operating system, +or install it from your terminal by running: + + $ plotly_get_chrome + +""" + +KALEIDO_DEPRECATION_MSG = f""" +Support for Kaleido versions less than 1.0.0 is deprecated and will be removed after {ENGINE_SUPPORT_TIMELINE}. +Please upgrade Kaleido to version 1.0.0 or greater (`pip install 'kaleido>=1.0.0'` or `pip install 'plotly[kaleido]'`). +""" +ORCA_DEPRECATION_MSG = f""" +Support for the Orca engine is deprecated and will be removed after {ENGINE_SUPPORT_TIMELINE}. +Please install Kaleido (`pip install 'kaleido>=1.0.0'` or `pip install 'plotly[kaleido]'`) to use the Kaleido engine. +""" +ENGINE_PARAM_DEPRECATION_MSG = f""" +Support for the 'engine' argument is deprecated and will be removed after {ENGINE_SUPPORT_TIMELINE}. +Kaleido will be the only supported engine at that time. +""" + +_KALEIDO_AVAILABLE = None +_KALEIDO_MAJOR = None + + +def kaleido_scope_default_warning_func(x): + return f""" +Use of plotly.io.kaleido.scope.{x} is deprecated and support will be removed after {ENGINE_SUPPORT_TIMELINE}. +Please use plotly.io.defaults.{x} instead. +""" + + +def bad_attribute_error_msg_func(x): + return f""" +Attribute plotly.io.defaults.{x} is not valid. +Also, use of plotly.io.kaleido.scope.* is deprecated and support will be removed after {ENGINE_SUPPORT_TIMELINE}. +Please use plotly.io.defaults.* instead. +""" + + +def kaleido_available() -> bool: + """ + Returns True if any version of Kaleido is installed, otherwise False. + """ + global _KALEIDO_AVAILABLE + global _KALEIDO_MAJOR + if _KALEIDO_AVAILABLE is not None: + return _KALEIDO_AVAILABLE + try: + import kaleido # noqa: F401 + + _KALEIDO_AVAILABLE = True + except ImportError: + _KALEIDO_AVAILABLE = False + return _KALEIDO_AVAILABLE + + +def kaleido_major() -> int: + """ + Returns the major version number of Kaleido if it is installed, + otherwise raises a ValueError. + """ + global _KALEIDO_MAJOR + if _KALEIDO_MAJOR is not None: + return _KALEIDO_MAJOR + if not kaleido_available(): + raise ValueError("Kaleido is not installed.") + else: + _KALEIDO_MAJOR = Version(importlib_metadata.version("kaleido")).major + return _KALEIDO_MAJOR + + +try: + if kaleido_available() and kaleido_major() < 1: + # Kaleido v0 + import kaleido + from kaleido.scopes.plotly import PlotlyScope + + # Show a deprecation warning if the old method of setting defaults is used + class PlotlyScopeWrapper(PlotlyScope): + def __setattr__(self, name, value): + if name in defaults.__dict__: + if ENABLE_KALEIDO_V0_DEPRECATION_WARNINGS: + warnings.warn( + kaleido_scope_default_warning_func(name), + DeprecationWarning, + stacklevel=2, + ) + super().__setattr__(name, value) + + def __getattr__(self, name): + if hasattr(defaults, name): + if ENABLE_KALEIDO_V0_DEPRECATION_WARNINGS: + warnings.warn( + kaleido_scope_default_warning_func(name), + DeprecationWarning, + stacklevel=2, + ) + return super().__getattr__(name) + + # Ensure the new method of setting defaults is backwards compatible with Kaleido v0 + # DefaultsBackwardsCompatible sets the attributes on `scope` object at the same time + # as they are set on the `defaults` object + class DefaultsBackwardsCompatible(defaults.__class__): + def __init__(self, scope): + self._scope = scope + super().__init__() + + def __setattr__(self, name, value): + if not name == "_scope": + if ( + hasattr(self._scope, name) + and getattr(self._scope, name) != value + ): + setattr(self._scope, name, value) + super().__setattr__(name, value) + + scope = PlotlyScopeWrapper() + defaults = DefaultsBackwardsCompatible(scope) + # Compute absolute path to the 'plotly/package_data/' directory + root_dir = os.path.dirname(os.path.abspath(plotly.__file__)) + package_dir = os.path.join(root_dir, "package_data") + scope.plotlyjs = os.path.join(package_dir, "plotly.min.js") + if scope.mathjax is None: + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", message=r".*scope\.mathjax.*", category=DeprecationWarning + ) + scope.mathjax = ( + "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js" + ) + else: + # Kaleido v1 + import kaleido + + # Show a deprecation warning if the old method of setting defaults is used + class DefaultsWrapper: + def __getattr__(self, name): + if hasattr(defaults, name): + if ENABLE_KALEIDO_V0_DEPRECATION_WARNINGS: + warnings.warn( + kaleido_scope_default_warning_func(name), + DeprecationWarning, + stacklevel=2, + ) + return getattr(defaults, name) + else: + raise AttributeError(bad_attribute_error_msg_func(name)) + + def __setattr__(self, name, value): + if hasattr(defaults, name): + if ENABLE_KALEIDO_V0_DEPRECATION_WARNINGS: + warnings.warn( + kaleido_scope_default_warning_func(name), + DeprecationWarning, + stacklevel=2, + ) + setattr(defaults, name, value) + else: + raise AttributeError(bad_attribute_error_msg_func(name)) + + scope = DefaultsWrapper() + +except ImportError: + PlotlyScope = None + scope = None + + +def as_path_object(file: Union[str, Path]) -> Union[Path, None]: + """ + Cast the `file` argument, which may be either a string or a Path object, + to a Path object. + If `file` is neither a string nor a Path object, None will be returned. + """ + if isinstance(file, str): + # Use the standard Path constructor to make a pathlib object. + path = Path(file) + elif isinstance(file, Path): + # `file` is already a Path object. + path = file + else: + # We could not make a Path object out of file. Either `file` is an open file + # descriptor with a `write()` method or it's an invalid object. + path = None + return path + + +def infer_format(path: Union[Path, None], format: Union[str, None]) -> Union[str, None]: + if path is not None and format is None: + ext = path.suffix + if ext: + format = ext.lstrip(".") + else: + raise ValueError( + f""" +Cannot infer image type from output path '{path}'. +Please specify the type using the format parameter, or add a file extension. +For example: + + >>> import plotly.io as pio + >>> pio.write_image(fig, file_path, format='png') +""" + ) + return format + + +def to_image( + fig: Union[dict, plotly.graph_objects.Figure], + format: Union[str, None] = None, + width: Union[int, None] = None, + height: Union[int, None] = None, + scale: Union[int, float, None] = None, + validate: bool = True, + # Deprecated + engine: Union[str, None] = None, +) -> bytes: + """ + Convert a figure to a static image bytes string + + Parameters + ---------- + fig: + Figure object or dict representing a figure + + format: str or None + The desired image format. One of + - 'png' + - 'jpg' or 'jpeg' + - 'webp' + - 'svg' + - 'pdf' + - 'eps' (deprecated) (Requires the poppler library to be installed and on the PATH) + + If not specified, will default to: + - `plotly.io.defaults.default_format` if engine is "kaleido" + - `plotly.io.orca.config.default_format` if engine is "orca" (deprecated) + + width: int or None + The width of the exported image in layout pixels. If the `scale` + property is 1.0, this will also be the width of the exported image + in physical pixels. + + If not specified, will default to: + - `plotly.io.defaults.default_width` if engine is "kaleido" + - `plotly.io.orca.config.default_width` if engine is "orca" (deprecated) + + height: int or None + The height of the exported image in layout pixels. If the `scale` + property is 1.0, this will also be the height of the exported image + in physical pixels. + + If not specified, will default to: + - `plotly.io.defaults.default_height` if engine is "kaleido" + - `plotly.io.orca.config.default_height` if engine is "orca" (deprecated) + + scale: int or float or None + The scale factor to use when exporting the figure. A scale factor + larger than 1.0 will increase the image resolution with respect + to the figure's layout pixel dimensions. Whereas as scale factor of + less than 1.0 will decrease the image resolution. + + If not specified, will default to: + - `plotly.io.defaults.default_scale` if engine is "kaleido" + - `plotly.io.orca.config.default_scale` if engine is "orca" (deprecated) + + validate: bool + True if the figure should be validated before being converted to + an image, False otherwise. + + engine (deprecated): str + Image export engine to use. This parameter is deprecated and Orca engine support will be + dropped in the next major Plotly version. Until then, the following values are supported: + - "kaleido": Use Kaleido for image export + - "orca": Use Orca for image export + - "auto" (default): Use Kaleido if installed, otherwise use Orca + + Returns + ------- + bytes + The image data + """ + + # Handle engine + if engine is not None: + if ENABLE_KALEIDO_V0_DEPRECATION_WARNINGS: + warnings.warn( + ENGINE_PARAM_DEPRECATION_MSG, DeprecationWarning, stacklevel=2 + ) + else: + engine = "auto" + + if engine == "auto": + if kaleido_available(): + # Default to kaleido if available + engine = "kaleido" + else: + # See if orca is available + from ._orca import validate_executable + + try: + validate_executable() + engine = "orca" + except Exception: + # If orca not configured properly, make sure we display the error + # message advising the installation of kaleido + engine = "kaleido" + + if engine == "orca": + if ENABLE_KALEIDO_V0_DEPRECATION_WARNINGS: + warnings.warn(ORCA_DEPRECATION_MSG, DeprecationWarning, stacklevel=2) + # Fall back to legacy orca image export path + from ._orca import to_image as to_image_orca + + return to_image_orca( + fig, + format=format, + width=width, + height=height, + scale=scale, + validate=validate, + ) + elif engine != "kaleido": + raise ValueError(f"Invalid image export engine specified: {repr(engine)}") + + # Raise informative error message if Kaleido is not installed + if not kaleido_available(): + raise ValueError( + """ +Image export using the "kaleido" engine requires the Kaleido package, +which can be installed using pip: + + $ pip install --upgrade kaleido +""" + ) + + # Convert figure to dict (and validate if requested) + fig_dict = validate_coerce_fig_to_dict(fig, validate) + + # Request image bytes + if kaleido_major() > 0: + # Kaleido v1 + # Check if trying to export to EPS format, which is not supported in Kaleido v1 + if format == "eps": + raise ValueError( + f""" +EPS export is not supported by Kaleido v1. Please use SVG or PDF instead. +You can also downgrade to Kaleido v0, but support for Kaleido v0 will be removed after {ENGINE_SUPPORT_TIMELINE}. +To downgrade to Kaleido v0, run: + $ pip install 'kaleido<1.0.0' +""" + ) + from kaleido.errors import ChromeNotFoundError + + try: + kopts = {} + if defaults.plotlyjs: + kopts["plotlyjs"] = defaults.plotlyjs + if defaults.mathjax: + kopts["mathjax"] = defaults.mathjax + + # TODO: Refactor to make it possible to use a shared Kaleido instance here + img_bytes = kaleido.calc_fig_sync( + fig_dict, + opts=dict( + format=format or defaults.default_format, + width=width or defaults.default_width, + height=height or defaults.default_height, + scale=scale or defaults.default_scale, + ), + topojson=defaults.topojson, + kopts=kopts, + ) + except ChromeNotFoundError: + raise RuntimeError(PLOTLY_GET_CHROME_ERROR_MSG) + + else: + # Kaleido v0 + if ENABLE_KALEIDO_V0_DEPRECATION_WARNINGS: + warnings.warn(KALEIDO_DEPRECATION_MSG, DeprecationWarning, stacklevel=2) + img_bytes = scope.transform( + fig_dict, format=format, width=width, height=height, scale=scale + ) + + return img_bytes + + +def write_image( + fig: Union[dict, plotly.graph_objects.Figure], + file: Union[str, Path], + format: Union[str, None] = None, + scale: Union[int, float, None] = None, + width: Union[int, None] = None, + height: Union[int, None] = None, + validate: bool = True, + # Deprecated + engine: Union[str, None] = "auto", +): + """ + Convert a figure to a static image and write it to a file or writeable + object + + Parameters + ---------- + fig: + Figure object or dict representing a figure + + file: str or writeable + A string representing a local file path or a writeable object + (e.g. a pathlib.Path object or an open file descriptor) + + format: str or None + The desired image format. One of + - 'png' + - 'jpg' or 'jpeg' + - 'webp' + - 'svg' + - 'pdf' + - 'eps' (deprecated) (Requires the poppler library to be installed and on the PATH) + + If not specified and `file` is a string then this will default to the + file extension. If not specified and `file` is not a string then this + will default to: + - `plotly.io.defaults.default_format` if engine is "kaleido" + - `plotly.io.orca.config.default_format` if engine is "orca" (deprecated) + + width: int or None + The width of the exported image in layout pixels. If the `scale` + property is 1.0, this will also be the width of the exported image + in physical pixels. + + If not specified, will default to: + - `plotly.io.defaults.default_width` if engine is "kaleido" + - `plotly.io.orca.config.default_width` if engine is "orca" (deprecated) + + height: int or None + The height of the exported image in layout pixels. If the `scale` + property is 1.0, this will also be the height of the exported image + in physical pixels. + + If not specified, will default to: + - `plotly.io.defaults.default_height` if engine is "kaleido" + - `plotly.io.orca.config.default_height` if engine is "orca" (deprecated) + + scale: int or float or None + The scale factor to use when exporting the figure. A scale factor + larger than 1.0 will increase the image resolution with respect + to the figure's layout pixel dimensions. Whereas as scale factor of + less than 1.0 will decrease the image resolution. + + If not specified, will default to: + - `plotly.io.defaults.default_scale` if engine is "kaleido" + - `plotly.io.orca.config.default_scale` if engine is "orca" (deprecated) + + validate: bool + True if the figure should be validated before being converted to + an image, False otherwise. + + engine (deprecated): str + Image export engine to use. This parameter is deprecated and Orca engine support will be + dropped in the next major Plotly version. Until then, the following values are supported: + - "kaleido": Use Kaleido for image export + - "orca": Use Orca for image export + - "auto" (default): Use Kaleido if installed, otherwise use Orca + + Returns + ------- + None + """ + # Show Kaleido deprecation warning if needed + if ENABLE_KALEIDO_V0_DEPRECATION_WARNINGS: + if ( + engine in {None, "auto", "kaleido"} + and kaleido_available() + and kaleido_major() < 1 + ): + warnings.warn(KALEIDO_DEPRECATION_MSG, DeprecationWarning, stacklevel=2) + if engine == "orca": + warnings.warn(ORCA_DEPRECATION_MSG, DeprecationWarning, stacklevel=2) + if engine not in {None, "auto"}: + warnings.warn( + ENGINE_PARAM_DEPRECATION_MSG, DeprecationWarning, stacklevel=2 + ) + + # Try to cast `file` as a pathlib object `path`. + path = as_path_object(file) + + # Infer image format if not specified + format = infer_format(path, format) + + # Request image + # Do this first so we don't create a file if image conversion fails + img_data = to_image( + fig, + format=format, + scale=scale, + width=width, + height=height, + validate=validate, + engine=engine, + ) + + # Open file + if path is None: + # We previously failed to make sense of `file` as a pathlib object. + # Attempt to write to `file` as an open file descriptor. + try: + file.write(img_data) + return + except AttributeError: + pass + raise ValueError( + f""" +The 'file' argument '{file}' is not a string, pathlib.Path object, or file descriptor. +""" + ) + else: + # We previously succeeded in interpreting `file` as a pathlib object. + # Now we can use `write_bytes()`. + path.write_bytes(img_data) + + +def write_images( + fig: Union[ + List[Union[dict, plotly.graph_objects.Figure]], + Union[dict, plotly.graph_objects.Figure], + ], + file: Union[List[Union[str, Path]], Union[str, Path]], + format: Union[List[Union[str, None]], Union[str, None]] = None, + scale: Union[List[Union[int, float, None]], Union[int, float, None]] = None, + width: Union[List[Union[int, None]], Union[int, None]] = None, + height: Union[List[Union[int, None]], Union[int, None]] = None, + validate: Union[List[bool], bool] = True, +) -> None: + """ + Write multiple images to files or writeable objects. This is much faster than + calling write_image() multiple times. This function can only be used with the Kaleido + engine, v1.0.0 or greater. + + This function accepts the same arguments as write_image() (minus the `engine` argument), + except that any of the arguments may be either a single value or an iterable of values. + If multiple arguments are iterable, they must all have the same length. + + Parameters + ---------- + fig: + List of figure objects or dicts representing a figure. + Also accepts a single figure or dict representing a figure. + + file: str, pathlib.Path, or list of (str or pathlib.Path) + List of str or pathlib.Path objects representing local file paths to write to. + Can also be a single str or pathlib.Path object if fig argument is + a single figure or dict representing a figure. + + format: str, None, or list of (str or None) + The image format to use for exported images. + Supported formats are: + - 'png' + - 'jpg' or 'jpeg' + - 'webp' + - 'svg' + - 'pdf' + + Use a list to specify formats for each figure or dict in the list + provided to the `fig` argument. + Specify format as a `str` to apply the same format to all exported images. + If not specified, and the corresponding `file` argument has a file extension, then `format` will default to the + file extension. Otherwise, will default to `plotly.io.defaults.default_format`. + + width: int, None, or list of (int or None) + The width of the exported image in layout pixels. If the `scale` + property is 1.0, this will also be the width of the exported image + in physical pixels. + + Use a list to specify widths for each figure or dict in the list + provided to the `fig` argument. + Specify width as an `int` to apply the same width to all exported images. + If not specified, will default to `plotly.io.defaults.default_width`. + + height: int, None, or list of (int or None) + The height of the exported image in layout pixels. If the `scale` + property is 1.0, this will also be the height of the exported image + in physical pixels. + + Use a list to specify heights for each figure or dict in the list + provided to the `fig` argument. + Specify height as an `int` to apply the same height to all exported images. + If not specified, will default to `plotly.io.defaults.default_height`. + + scale: int, float, None, or list of (int, float, or None) + The scale factor to use when exporting the figure. A scale factor + larger than 1.0 will increase the image resolution with respect + to the figure's layout pixel dimensions. Whereas as scale factor of + less than 1.0 will decrease the image resolution. + + Use a list to specify scale for each figure or dict in the list + provided to the `fig` argument. + Specify scale as an `int` or `float` to apply the same scale to all exported images. + If not specified, will default to `plotly.io.defaults.default_scale`. + + validate: bool or list of bool + True if the figure should be validated before being converted to + an image, False otherwise. + + Use a list to specify validation setting for each figure in the list + provided to the `fig` argument. + Specify validate as a boolean to apply the same validation setting to all figures. + + Returns + ------- + None + """ + + # Raise informative error message if Kaleido v1 is not installed + if not kaleido_available(): + raise ValueError( + """ +The `write_images()` function requires the Kaleido package, +which can be installed using pip: + + $ pip install --upgrade kaleido +""" + ) + elif kaleido_major() < 1: + raise ValueError( + f""" +You have Kaleido version {Version(importlib_metadata.version("kaleido"))} installed. +The `write_images()` function requires the Kaleido package version 1.0.0 or greater, +which can be installed using pip: + + $ pip install 'kaleido>=1.0.0' +""" + ) + + # Broadcast arguments into correct format for passing to Kaleido + arg_dicts = broadcast_args_to_dicts( + fig=fig, + file=file, + format=format, + scale=scale, + width=width, + height=height, + validate=validate, + ) + + # For each dict: + # - convert figures to dicts (and validate if requested) + # - try to cast `file` as a Path object + for d in arg_dicts: + d["fig"] = validate_coerce_fig_to_dict(d["fig"], d["validate"]) + d["file"] = as_path_object(d["file"]) + + # Reshape arg_dicts into correct format for passing to Kaleido + # We call infer_format() here rather than above so that the `file` argument + # has already been cast to a Path object. + # Also insert defaults for any missing arguments as needed + kaleido_specs = [ + dict( + fig=d["fig"], + path=d["file"], + opts=dict( + format=infer_format(d["file"], d["format"]) or defaults.default_format, + width=d["width"] or defaults.default_width, + height=d["height"] or defaults.default_height, + scale=d["scale"] or defaults.default_scale, + ), + topojson=defaults.topojson, + ) + for d in arg_dicts + ] + + from kaleido.errors import ChromeNotFoundError + + try: + kopts = {} + if defaults.plotlyjs: + kopts["plotlyjs"] = defaults.plotlyjs + if defaults.mathjax: + kopts["mathjax"] = defaults.mathjax + kaleido.write_fig_from_object_sync( + kaleido_specs, + kopts=kopts, + ) + except ChromeNotFoundError: + raise RuntimeError(PLOTLY_GET_CHROME_ERROR_MSG) + + +def full_figure_for_development( + fig: Union[dict, plotly.graph_objects.Figure], + warn: bool = True, + as_dict: bool = False, +) -> Union[plotly.graph_objects.Figure, dict]: + """ + Compute default values for all attributes not specified in the input figure and + returns the output as a "full" figure. This function calls Plotly.js via Kaleido + to populate unspecified attributes. This function is intended for interactive use + during development to learn more about how Plotly.js computes default values and is + not generally necessary or recommended for production use. + + Parameters + ---------- + fig: + Figure object or dict representing a figure + + warn: bool + If False, suppress warnings about not using this in production. + + as_dict: bool + If True, output is a dict with some keys that go.Figure can't parse. + If False, output is a go.Figure with unparseable keys skipped. + + Returns + ------- + plotly.graph_objects.Figure or dict + The full figure + """ + + # Raise informative error message if Kaleido is not installed + if not kaleido_available(): + raise ValueError( + """ +Full figure generation requires the Kaleido package, +which can be installed using pip: + + $ pip install --upgrade kaleido +""" + ) + + if warn: + warnings.warn( + "full_figure_for_development is not recommended or necessary for " + "production use in most circumstances. \n" + "To suppress this warning, set warn=False" + ) + + if kaleido_available() and kaleido_major() > 0: + # Kaleido v1 + bytes = kaleido.calc_fig_sync( + fig, + opts=dict(format="json"), + ) + fig = json.loads(bytes.decode("utf-8")) + else: + # Kaleido v0 + if ENABLE_KALEIDO_V0_DEPRECATION_WARNINGS: + warnings.warn( + f"Support for Kaleido versions less than 1.0.0 is deprecated and will be removed after {ENGINE_SUPPORT_TIMELINE}. " + + "Please upgrade Kaleido to version 1.0.0 or greater (`pip install 'kaleido>=1.0.0'`).", + DeprecationWarning, + ) + fig = json.loads(scope.transform(fig, format="json").decode("utf-8")) + + if as_dict: + return fig + else: + import plotly.graph_objects as go + + return go.Figure(fig, skip_invalid=True) + + +def get_chrome() -> None: + """ + Install Google Chrome for Kaleido (Required for Plotly image export). + This function can be run from the command line using the command `plotly_get_chrome` + defined in pyproject.toml + """ + + usage = """ +Usage: plotly_get_chrome [-y] [--path PATH] + +Installs Google Chrome for Plotly image export. + +Options: + -y Skip confirmation prompt + --path PATH Specify the path to install Chrome. Must be a path to an existing directory. + --help Show this message and exit. +""" + + if not kaleido_available() or kaleido_major() < 1: + raise ValueError( + """ +This command requires Kaleido v1.0.0 or greater. +Install it using `pip install 'kaleido>=1.0.0'` or `pip install 'plotly[kaleido]'`." +""" + ) + + # Handle command line arguments + import sys + + cli_args = sys.argv + + # Handle "-y" flag + cli_yes = "-y" in cli_args + if cli_yes: + cli_args.remove("-y") + + # Handle "--path" flag + chrome_install_path = None + user_specified_path = False + if "--path" in cli_args: + path_index = cli_args.index("--path") + 1 + if path_index < len(cli_args): + chrome_install_path = cli_args[path_index] + cli_args.remove("--path") + cli_args.remove(chrome_install_path) + chrome_install_path = Path(chrome_install_path) + user_specified_path = True + else: + from choreographer.cli.defaults import default_download_path + + chrome_install_path = default_download_path + + # If install path was chosen by user, make sure there is an existing directory + # located at chrome_install_path; otherwise fail + if user_specified_path: + if not chrome_install_path.exists(): + raise ValueError( + f""" +The specified install path '{chrome_install_path}' does not exist. +Please specify a path to an existing directory using the --path argument, +or omit the --path argument to use the default download path. +""" + ) + # Make sure the path is a directory + if not chrome_install_path.is_dir(): + raise ValueError( + f""" +The specified install path '{chrome_install_path}' already exists but is not a directory. +Please specify a path to an existing directory using the --path argument, +or omit the --path argument to use the default download path. +""" + ) + + # If any arguments remain, command syntax was incorrect -- print usage and exit + if len(cli_args) > 1: + print(usage) + sys.exit(1) + + if not cli_yes: + print( + f""" +Plotly will install a copy of Google Chrome to be used for generating static images of plots. +Chrome will be installed at: {chrome_install_path}""" + ) + response = input("Do you want to proceed? [y/n] ") + if not response or response[0].lower() != "y": + print("Cancelled") + return + print("Installing Chrome for Plotly...") + exe_path = kaleido.get_chrome_sync(path=chrome_install_path) + print("Chrome installed successfully.") + print(f"The Chrome executable is now located at: {exe_path}") + + +__all__ = ["to_image", "write_image", "scope", "full_figure_for_development"] |