diff --git a/CHANGELOG.md b/CHANGELOG.md index 16a2d5a68b..ca92ff585d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +## [0.35.4 - 2024-12-28] + +### Fixes + +* `Plottable._render` now typed as `RenderModesConcrete` +* Remote GFQL - Handle `output_type is None` + ## [0.35.3 - 2024-12-24] ### Docs diff --git a/graphistry/Plottable.py b/graphistry/Plottable.py index 99c160107b..9f10f42f68 100644 --- a/graphistry/Plottable.py +++ b/graphistry/Plottable.py @@ -54,7 +54,7 @@ class Plottable(object): _point_x : Optional[str] _point_y : Optional[str] _height : int - _render : bool + _render : RenderModesConcrete _url_params : dict _name : Optional[str] _description : Optional[str] @@ -497,26 +497,9 @@ def settings(self, url_params: Dict[str, Any] = {}, render: Optional[Union[bool, RenderModes]] = None ) -> 'Plottable': - """Specify iframe height and add URL parameter dictionary. - - The library takes care of URI component encoding for the dictionary. - - :param height: Height in pixels. - :type height: int - - :param url_params: Dictionary of querystring parameters to append to the URL. - :type url_params: dict - - :param render: Set default render mode from RenderModes types, where True/None is "auto" and False is "url" - :type render: Optional[Union[bool, RenderModes]] - - """ - - res = self.copy() - res._height = height or self._height - res._url_params = dict(self._url_params, **url_params) - res._render = self._render if render is None else render - return res + if 1 + 1: + raise RuntimeError('should not happen') + return self def to_cudf(self) -> 'Plottable': if 1 + 1: diff --git a/graphistry/PlotterBase.py b/graphistry/PlotterBase.py index 00bce624b1..ec78a5243b 100644 --- a/graphistry/PlotterBase.py +++ b/graphistry/PlotterBase.py @@ -1,4 +1,4 @@ -from graphistry.Plottable import Plottable, RenderModes +from graphistry.Plottable import Plottable, RenderModes, RenderModesConcrete from typing import Any, Callable, Dict, List, Optional, Union from graphistry.render.resolve_render_mode import resolve_render_mode import copy, hashlib, numpy as np, pandas as pd, pyarrow as pa, sys, uuid @@ -155,7 +155,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self._point_y : Optional[str] = None # Settings self._height : int = 500 - self._render : bool = True + self._render : RenderModesConcrete = resolve_render_mode(self, True) self._url_params : dict = {'info': 'true'} self._privacy : Optional[Privacy] = None # Metadata diff --git a/graphistry/compute/python_remote.py b/graphistry/compute/python_remote.py index 4803ea09f7..211cc59774 100644 --- a/graphistry/compute/python_remote.py +++ b/graphistry/compute/python_remote.py @@ -135,7 +135,7 @@ def task(g: Plottable) -> Dict[str, Any]: "engine": engine, **({"run_label": run_label} if run_label else {}), **({'format': format} if format != 'json' else {}), - **({'output_type': output_type} if output_type != 'json' else {}) + **({'output_type': output_type} if output_type is not None and output_type != 'json' else {}) } url = f"{self.base_url_server()}/api/v2/datasets/{dataset_id}/python" diff --git a/graphistry/render/resolve_render_mode.py b/graphistry/render/resolve_render_mode.py index 0fae06840e..0cadb46c17 100644 --- a/graphistry/render/resolve_render_mode.py +++ b/graphistry/render/resolve_render_mode.py @@ -1,17 +1,12 @@ +from functools import lru_cache from typing import Optional, Union from graphistry.Plottable import RENDER_MODE_CONCRETE_VALUES, Plottable, RenderModes, RenderModesConcrete from graphistry.util import in_databricks, in_ipython -def resolve_render_mode( - self: Plottable, - render: Optional[Union[bool, RenderModes]], -) -> RenderModesConcrete: - - # cascade - if render is None: - render = self._render +@lru_cache(10) +def resolve_cascaded(render: Union[bool, RenderModes]): # => RenderMode if isinstance(render, bool): @@ -33,3 +28,15 @@ def resolve_render_mode( return "browser" except Exception: return "url" + + +def resolve_render_mode( + self: Plottable, + render: Optional[Union[bool, RenderModes]], +) -> RenderModesConcrete: + + # cascade + if render is None: + render = self._render + + return resolve_cascaded(render) diff --git a/graphistry/tests/render/test_resolve_render_mode.py b/graphistry/tests/render/test_resolve_render_mode.py index 0adfaba689..a4c10dd097 100644 --- a/graphistry/tests/render/test_resolve_render_mode.py +++ b/graphistry/tests/render/test_resolve_render_mode.py @@ -5,7 +5,7 @@ from unittest.mock import Mock import graphistry.render -from graphistry.render.resolve_render_mode import resolve_render_mode +from graphistry.render.resolve_render_mode import resolve_cascaded, resolve_render_mode from graphistry.tests.common import NoAuthTestCase from graphistry.tests.test_compute import CGFull from graphistry.tests.test_plotter import Fake_Response @@ -24,12 +24,14 @@ def abc_g() -> CGFull: 's', 'd')) def test_resolve_concrete(abc_g): + resolve_cascaded.cache_clear() # concrete for mode in ['g', 'url', 'browser', 'ipython', 'databricks']: assert resolve_render_mode(abc_g, mode) == mode def test_resolve_sniffed(abc_g): + resolve_cascaded.cache_clear() # bool assert resolve_render_mode(abc_g, True) in ['url', 'ipython', 'databricks', 'browser'] @@ -39,6 +41,7 @@ def test_resolve_sniffed(abc_g): assert resolve_render_mode(abc_g, None) in ['url', 'ipython', 'databricks', 'browser'] def test_resolve_cascade(abc_g): + resolve_cascaded.cache_clear() assert resolve_render_mode(abc_g.settings(render='g'), None) == 'g' assert resolve_render_mode(abc_g.settings(render='g'), 'url') == 'url' @@ -50,6 +53,7 @@ def test_resolve_cascade(abc_g): class TestIPython(NoAuthTestCase): def test_no_ipython(self, mock_in_ipython): + resolve_cascaded.cache_clear() mock_in_ipython.return_value = False mode_render_true = resolve_render_mode(abc_g, True) @@ -62,6 +66,7 @@ def test_no_ipython(self, mock_in_ipython): self.assertEqual(mode_render_ipython, 'ipython') def test_ipython(self, mock_in_ipython): + resolve_cascaded.cache_clear() mock_in_ipython.return_value = True mode_render_true = resolve_render_mode(abc_g, True) @@ -78,6 +83,7 @@ def test_ipython(self, mock_in_ipython): class TestDatabricks(NoAuthTestCase): def test_no_databricks(self, mock_in_databricks): + resolve_cascaded.cache_clear() mock_in_databricks.return_value = False mode_render_true = resolve_render_mode(abc_g, True) @@ -90,6 +96,7 @@ def test_no_databricks(self, mock_in_databricks): self.assertEqual(mode_render_ipython, 'databricks') def test_ipython(self, mock_in_databricks): + resolve_cascaded.cache_clear() mock_in_databricks.return_value = True mode_render_true = resolve_render_mode(abc_g, True) diff --git a/graphistry/tests/test_ipython.py b/graphistry/tests/test_ipython.py index cf5bc90d27..016f1e23a8 100644 --- a/graphistry/tests/test_ipython.py +++ b/graphistry/tests/test_ipython.py @@ -1,6 +1,7 @@ import graphistry, IPython from common import NoAuthTestCase from mock import patch +from graphistry.render.resolve_render_mode import resolve_cascaded from graphistry.tests.test_plotter import Fake_Response, triangleEdges @@ -10,6 +11,7 @@ class TestPlotterReturnValue(NoAuthTestCase): @patch("graphistry.render.resolve_render_mode.in_ipython") def test_no_ipython(self, mock_in_ipython, mock_post, mock_open): + resolve_cascaded.cache_clear() mock_in_ipython.return_value = False url = graphistry.bind(source="src", destination="dst").plot(triangleEdges, render="browser") self.assertIn("fakedatasetname", url) @@ -19,6 +21,7 @@ def test_no_ipython(self, mock_in_ipython, mock_post, mock_open): @patch("graphistry.render.resolve_render_mode.in_ipython") def test_ipython(self, mock_in_ipython, mock_post, mock_open): + resolve_cascaded.cache_clear() mock_in_ipython.return_value = True # The setUpClass in NoAuthTestCase only run once, so, reset the _is_authenticated to True here