Repository: vega/altair

Issue:
save using vl-convert doesn't work with json data_transformer
In altair 5.0.0rc1, vl-convert produces images without any data points when the json transformer is enabled.

Example code:
```python
import altair as alt
from vega_datasets import data

alt.data_transformers.enable('json')

chart = alt.Chart(
    data.iris()
).mark_point().encode(
    x='sepalWidth', y='sepalLength'
)
chart.save('test.png', engine='vl-convert', scale_factor=2)
```
In my jupyter notebook, I see
![visualization (8)](https://user-images.githubusercontent.com/6425077/223539893-2d18e1ad-c163-4310-8f4d-c760b5bd9281.png)

but the created `test.png` looks like:
![test](https://user-images.githubusercontent.com/6425077/223539980-90bc2e59-26d8-4b01-81d6-6449d170a788.png)



Discussion:
Thank you @sid-kap for reporting this bug! I can reproduce it.

@jonmmease would you mind having a look? I noticed that the fix in #2724 for the data-server package might not actually work as at that point the Altair chart got already converted to a dictionary here https://github.com/altair-viz/altair/blob/master/altair/utils/save.py#L124. So we might want to move the temporary disabling of any data transformer (introduced in #2724) to that line anyway when `.to_dict` is called so that all data is inlined. But as this issue is about json files, I'm wondering if vl-convert does not work with data from URLs in any case and if this is the expected behavior?
Thanks for the ping @binste. My first thought is that this issue is that the URLs reference JSON files on the local file system. vl-convert supports data URLs that reference remote JSON files, but I believe the issue is that it doesn't currently support urls to local files.

In any case, I think your suggestion to move the disabling of data transformers makes sense.
Thanks for the quick turnaround! I'll create a PR to fix this.

Resolving patch:
diff --git a/altair/utils/mimebundle.py b/altair/utils/mimebundle.py
--- a/altair/utils/mimebundle.py
+++ b/altair/utils/mimebundle.py
@@ -1,5 +1,4 @@
 from .html import spec_to_html
-from ..vegalite.v5.data import data_transformers
 
 
 def spec_to_mimebundle(
@@ -102,26 +101,23 @@ def _spec_to_mimebundle_with_engine(spec, format, mode, **kwargs):
         # Compute VlConvert's vl_version string (of the form 'v5_2')
         # from SCHEMA_VERSION (of the form 'v5.2.0')
         vl_version = "_".join(SCHEMA_VERSION.split(".")[:2])
-        # Turn off the data transformer until the following issue is resolved
-        # https://github.com/vega/vl-convert/issues/31#
-        with data_transformers.enable("default"), data_transformers.disable_max_rows():
-            if format == "vega":
-                vg = vlc.vegalite_to_vega(spec, vl_version=vl_version)
-                return {"application/vnd.vega.v5+json": vg}
-            elif format == "svg":
-                svg = vlc.vegalite_to_svg(spec, vl_version=vl_version)
-                return {"image/svg+xml": svg}
-            elif format == "png":
-                png = vlc.vegalite_to_png(
-                    spec,
-                    vl_version=vl_version,
-                    scale=kwargs.get("scale_factor", 1.0),
-                )
-                return {"image/png": png}
-            else:
-                # This should be validated above
-                # but raise exception for the sake of future development
-                raise ValueError("Unexpected format {fmt!r}".format(fmt=format))
+        if format == "vega":
+            vg = vlc.vegalite_to_vega(spec, vl_version=vl_version)
+            return {"application/vnd.vega.v5+json": vg}
+        elif format == "svg":
+            svg = vlc.vegalite_to_svg(spec, vl_version=vl_version)
+            return {"image/svg+xml": svg}
+        elif format == "png":
+            png = vlc.vegalite_to_png(
+                spec,
+                vl_version=vl_version,
+                scale=kwargs.get("scale_factor", 1.0),
+            )
+            return {"image/png": png}
+        else:
+            # This should be validated above
+            # but raise exception for the sake of future development
+            raise ValueError("Unexpected format {fmt!r}".format(fmt=format))
     elif normalized_engine == "altairsaver":
         import altair_saver
 
diff --git a/altair/utils/save.py b/altair/utils/save.py
--- a/altair/utils/save.py
+++ b/altair/utils/save.py
@@ -3,6 +3,7 @@
 import warnings
 
 from .mimebundle import spec_to_mimebundle
+from ..vegalite.v5.data import data_transformers
 
 
 def write_file_or_filename(fp, content, mode="w"):
@@ -121,46 +122,52 @@ def save(
 
     format = set_inspect_format_argument(format, fp, inline)
 
-    spec = chart.to_dict()
-
-    mode = set_inspect_mode_argument(mode, embed_options, spec, vegalite_version)
-
-    if format == "json":
-        json_spec = json.dumps(spec, **json_kwds)
-        write_file_or_filename(fp, json_spec, mode="w")
-    elif format == "html":
-        if inline:
-            kwargs["template"] = "inline"
-        mimebundle = spec_to_mimebundle(
-            spec=spec,
-            format=format,
-            mode=mode,
-            vega_version=vega_version,
-            vegalite_version=vegalite_version,
-            vegaembed_version=vegaembed_version,
-            embed_options=embed_options,
-            json_kwds=json_kwds,
-            **kwargs,
-        )
-        write_file_or_filename(fp, mimebundle["text/html"], mode="w")
-    elif format in ["png", "svg", "pdf", "vega"]:
-        mimebundle = spec_to_mimebundle(
-            spec=spec,
-            format=format,
-            mode=mode,
-            vega_version=vega_version,
-            vegalite_version=vegalite_version,
-            vegaembed_version=vegaembed_version,
-            webdriver=webdriver,
-            scale_factor=scale_factor,
-            engine=engine,
-            **kwargs,
-        )
-        if format == "png":
-            write_file_or_filename(fp, mimebundle["image/png"], mode="wb")
-        elif format == "pdf":
-            write_file_or_filename(fp, mimebundle["application/pdf"], mode="wb")
+    # Temporarily turn off any data transformers so that all data is inlined
+    # when calling chart.to_dict. This is relevant for vl-convert which cannot access
+    # local json files which could be created by a json data transformer. Furthermore,
+    # we don't exit the with statement until this function completed due to the issue
+    # described at https://github.com/vega/vl-convert/issues/31
+    with data_transformers.enable("default"), data_transformers.disable_max_rows():
+        spec = chart.to_dict()
+
+        mode = set_inspect_mode_argument(mode, embed_options, spec, vegalite_version)
+
+        if format == "json":
+            json_spec = json.dumps(spec, **json_kwds)
+            write_file_or_filename(fp, json_spec, mode="w")
+        elif format == "html":
+            if inline:
+                kwargs["template"] = "inline"
+            mimebundle = spec_to_mimebundle(
+                spec=spec,
+                format=format,
+                mode=mode,
+                vega_version=vega_version,
+                vegalite_version=vegalite_version,
+                vegaembed_version=vegaembed_version,
+                embed_options=embed_options,
+                json_kwds=json_kwds,
+                **kwargs,
+            )
+            write_file_or_filename(fp, mimebundle["text/html"], mode="w")
+        elif format in ["png", "svg", "pdf", "vega"]:
+            mimebundle = spec_to_mimebundle(
+                spec=spec,
+                format=format,
+                mode=mode,
+                vega_version=vega_version,
+                vegalite_version=vegalite_version,
+                vegaembed_version=vegaembed_version,
+                webdriver=webdriver,
+                scale_factor=scale_factor,
+                engine=engine,
+                **kwargs,
+            )
+            if format == "png":
+                write_file_or_filename(fp, mimebundle["image/png"], mode="wb")
+            elif format == "pdf":
+                write_file_or_filename(fp, mimebundle["application/pdf"], mode="wb")
+            else:
+                write_file_or_filename(fp, mimebundle["image/svg+xml"], mode="w")
         else:
-            write_file_or_filename(fp, mimebundle["image/svg+xml"], mode="w")
-    else:
-        raise ValueError("Unsupported format: '{}'".format(format))
+            raise ValueError("Unsupported format: '{}'".format(format))


Test patch: