From 1da32c6fdb7ce946be476b85b83d743e720e08cc Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 30 Sep 2023 09:15:18 -0400 Subject: [PATCH 1/7] Support pdf export with vl-convert engine --- altair/utils/mimebundle.py | 21 +++++++++++++++------ tests/vegalite/v5/test_api.py | 18 ++++++------------ 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/altair/utils/mimebundle.py b/altair/utils/mimebundle.py index a0964899d..f35ecee7e 100644 --- a/altair/utils/mimebundle.py +++ b/altair/utils/mimebundle.py @@ -145,6 +145,20 @@ def _spec_to_mimebundle_with_engine(spec, format, mode, **kwargs): return {"image/png": png}, { "image/png": {"width": w / factor, "height": h / factor} } + elif format == "pdf": + scale = kwargs.get("scale_factor", 1) + if mode == "vega": + pdf = vlc.vega_to_pdf( + spec, + scale=scale, + ) + else: + pdf = vlc.vegalite_to_pdf( + spec, + vl_version=vl_version, + scale=scale, + ) + return {"application/pdf": pdf} else: # This should be validated above # but raise exception for the sake of future development @@ -192,18 +206,13 @@ def _validate_normalize_engine(engine, format): raise ValueError( "The 'vl-convert' conversion engine requires the vl-convert-python package" ) - if format == "pdf": - raise ValueError( - "The 'vl-convert' conversion engine does not support the {fmt!r} format.\n" - "Use the 'altair_saver' engine instead".format(fmt=format) - ) elif normalized_engine == "altairsaver": if altair_saver is None: raise ValueError( "The 'altair_saver' conversion engine requires the altair_saver package" ) elif normalized_engine is None: - if vlc is not None and format != "pdf": + if vlc is not None: normalized_engine = "vlconvert" elif altair_saver is not None: normalized_engine = "altairsaver" diff --git a/tests/vegalite/v5/test_api.py b/tests/vegalite/v5/test_api.py index 60dedc339..c61d466d6 100644 --- a/tests/vegalite/v5/test_api.py +++ b/tests/vegalite/v5/test_api.py @@ -322,23 +322,15 @@ def test_save(format, engine, basic_chart): return elif engine == "vl-convert": - if vlc is None and format != "bogus": - with pytest.raises(ValueError) as err: - basic_chart.save(out, format=format, engine=engine) - assert "vl-convert-python" in str(err.value) - return - elif format == "pdf": + if format == "bogus": with pytest.raises(ValueError) as err: basic_chart.save(out, format=format, engine=engine) - assert ( - f"The 'vl-convert' conversion engine does not support the '{format}' format" - in str(err.value) - ) + assert f"Unsupported format: '{format}'" in str(err.value) return - elif format not in ("png", "svg"): + elif vlc is None: with pytest.raises(ValueError) as err: basic_chart.save(out, format=format, engine=engine) - assert f"Unsupported format: '{format}'" in str(err.value) + assert "vl-convert-python" in str(err.value) return basic_chart.save(out, format=format, engine=engine) @@ -353,6 +345,8 @@ def test_save(format, engine, basic_chart): assert content.startswith(" Date: Sat, 30 Sep 2023 09:24:28 -0400 Subject: [PATCH 2/7] fix typo --- altair/utils/_vegafusion_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/altair/utils/_vegafusion_data.py b/altair/utils/_vegafusion_data.py index 83bb8d066..920082b58 100644 --- a/altair/utils/_vegafusion_data.py +++ b/altair/utils/_vegafusion_data.py @@ -182,7 +182,7 @@ def compile_with_vegafusion(vegalite_spec: dict) -> dict: def using_vegafusion() -> bool: - """Check whether the vegafusion data transfomer is enabled""" + """Check whether the vegafusion data transformer is enabled""" # Local import to avoid circular ImportError from altair import data_transformers From 73919228fc369b5ba103239dd140d9db4c79ff98 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Mon, 30 Oct 2023 07:35:36 -0400 Subject: [PATCH 3/7] Bump minimum vl-convert to 1.0.0 --- altair/utils/_importers.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/altair/utils/_importers.py b/altair/utils/_importers.py index 6ee41351d..d8bf20047 100644 --- a/altair/utils/_importers.py +++ b/altair/utils/_importers.py @@ -29,7 +29,7 @@ def import_vegafusion() -> ModuleType: def import_vl_convert() -> ModuleType: - min_version = "0.14.0" + min_version = "1.0.0" try: version = importlib_version("vl-convert-python") if Version(version) < Version(min_version): diff --git a/pyproject.toml b/pyproject.toml index 527d03f09..af321ba55 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,7 +64,7 @@ dev = [ "pytest-cov", "m2r", "vega_datasets", - "vl-convert-python>=0.14.0", + "vl-convert-python>=1.0.0", "mypy", "pandas-stubs", "types-jsonschema", From f63f85a0587392e5ff48aadd508e14568e48292b Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Mon, 30 Oct 2023 07:40:13 -0400 Subject: [PATCH 4/7] Update docs to remove PDF caveat --- doc/user_guide/saving_charts.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/user_guide/saving_charts.rst b/doc/user_guide/saving_charts.rst index c9b32f24b..e265c85d0 100644 --- a/doc/user_guide/saving_charts.rst +++ b/doc/user_guide/saving_charts.rst @@ -172,9 +172,8 @@ or:: pip install vl-convert-python Unlike altair_saver_, vl-convert_ does not require any external dependencies. -However, it only supports saving charts to PNG and SVG formats. To save directly to -PDF, altair_saver_ is still required. See the vl-convert documentation for information -on other `limitations `_. +See the vl-convert documentation for information and for known +`limitations `_. altair_saver ^^^^^^^^^^^^ From 7edf7f51fa469ab5d9dc60fd3ff89413c8394111 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Mon, 30 Oct 2023 07:49:20 -0400 Subject: [PATCH 5/7] Add altair_saver deprecation warning --- altair/utils/mimebundle.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/altair/utils/mimebundle.py b/altair/utils/mimebundle.py index f35ecee7e..1aa14cb3c 100644 --- a/altair/utils/mimebundle.py +++ b/altair/utils/mimebundle.py @@ -1,6 +1,8 @@ +from .deprecation import AltairDeprecationWarning from .html import spec_to_html from ._importers import import_vl_convert import struct +import warnings def spec_to_mimebundle( @@ -164,6 +166,12 @@ def _spec_to_mimebundle_with_engine(spec, format, mode, **kwargs): # but raise exception for the sake of future development raise ValueError("Unexpected format {fmt!r}".format(fmt=format)) elif normalized_engine == "altairsaver": + warnings.warn( + "The altair_saver export engine is deprecated and will be removed in a future version.\n" + "Please migrate to the vl-convert engine", + AltairDeprecationWarning, + stacklevel=1 + ) import altair_saver return altair_saver.render(spec, format, mode=mode, **kwargs) From 1ceaa0f1ecb65b882d65ce56d60cbad7aa3e52d1 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Mon, 30 Oct 2023 07:52:40 -0400 Subject: [PATCH 6/7] fmt --- altair/utils/mimebundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/altair/utils/mimebundle.py b/altair/utils/mimebundle.py index 1aa14cb3c..fa36ed4b8 100644 --- a/altair/utils/mimebundle.py +++ b/altair/utils/mimebundle.py @@ -170,7 +170,7 @@ def _spec_to_mimebundle_with_engine(spec, format, mode, **kwargs): "The altair_saver export engine is deprecated and will be removed in a future version.\n" "Please migrate to the vl-convert engine", AltairDeprecationWarning, - stacklevel=1 + stacklevel=1, ) import altair_saver From 07ae53a7dbd9d786e400cf194eee8539aa7e39f0 Mon Sep 17 00:00:00 2001 From: Mattijn van Hoek Date: Wed, 1 Nov 2023 19:51:53 +0100 Subject: [PATCH 7/7] raise RunTimeError when package version is too low --- altair/utils/_importers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/altair/utils/_importers.py b/altair/utils/_importers.py index d8bf20047..388d379f5 100644 --- a/altair/utils/_importers.py +++ b/altair/utils/_importers.py @@ -8,7 +8,7 @@ def import_vegafusion() -> ModuleType: try: version = importlib_version("vegafusion") if Version(version) < Version(min_version): - raise ImportError( + raise RuntimeError( f"The vegafusion package must be version {min_version} or greater. " f"Found version {version}" ) @@ -33,7 +33,7 @@ def import_vl_convert() -> ModuleType: try: version = importlib_version("vl-convert-python") if Version(version) < Version(min_version): - raise ImportError( + raise RuntimeError( f"The vl-convert-python package must be version {min_version} or greater. " f"Found version {version}" ) @@ -58,7 +58,7 @@ def import_pyarrow_interchange() -> ModuleType: version = importlib_version("pyarrow") if Version(version) < Version(min_version): - raise ImportError( + raise RuntimeError( f"The pyarrow package must be version {min_version} or greater. " f"Found version {version}" )