figpack 0.2.2__tar.gz → 0.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of figpack might be problematic. Click here for more details.

Files changed (71) hide show
  1. {figpack-0.2.2/figpack.egg-info → figpack-0.2.3}/PKG-INFO +1 -1
  2. {figpack-0.2.2 → figpack-0.2.3}/figpack/__init__.py +1 -1
  3. {figpack-0.2.2 → figpack-0.2.3}/figpack/core/_show_view.py +19 -4
  4. {figpack-0.2.2 → figpack-0.2.3}/figpack/core/_upload_bundle.py +19 -9
  5. {figpack-0.2.2 → figpack-0.2.3}/figpack/core/figpack_view.py +28 -2
  6. {figpack-0.2.2 → figpack-0.2.3/figpack.egg-info}/PKG-INFO +1 -1
  7. {figpack-0.2.2 → figpack-0.2.3}/pyproject.toml +1 -1
  8. figpack-0.2.3/tests/test_figpack_view.py +51 -0
  9. figpack-0.2.2/tests/test_figpack_view.py +0 -106
  10. {figpack-0.2.2 → figpack-0.2.3}/LICENSE +0 -0
  11. {figpack-0.2.2 → figpack-0.2.3}/MANIFEST.in +0 -0
  12. {figpack-0.2.2 → figpack-0.2.3}/README.md +0 -0
  13. {figpack-0.2.2 → figpack-0.2.3}/figpack/cli.py +0 -0
  14. {figpack-0.2.2 → figpack-0.2.3}/figpack/core/__init__.py +0 -0
  15. {figpack-0.2.2 → figpack-0.2.3}/figpack/core/_bundle_utils.py +0 -0
  16. {figpack-0.2.2 → figpack-0.2.3}/figpack/core/_server_manager.py +0 -0
  17. {figpack-0.2.2 → figpack-0.2.3}/figpack/core/config.py +0 -0
  18. {figpack-0.2.2 → figpack-0.2.3}/figpack/figpack-gui-dist/assets/index-Cmae55E4.css +0 -0
  19. {figpack-0.2.2 → figpack-0.2.3}/figpack/figpack-gui-dist/assets/index-DUR9Dmwh.js +0 -0
  20. {figpack-0.2.2 → figpack-0.2.3}/figpack/figpack-gui-dist/assets/neurosift-logo-CLsuwLMO.png +0 -0
  21. {figpack-0.2.2 → figpack-0.2.3}/figpack/figpack-gui-dist/index.html +0 -0
  22. {figpack-0.2.2 → figpack-0.2.3}/figpack/franklab/__init__.py +0 -0
  23. {figpack-0.2.2 → figpack-0.2.3}/figpack/franklab/views/TrackAnimation.py +0 -0
  24. {figpack-0.2.2 → figpack-0.2.3}/figpack/franklab/views/__init__.py +0 -0
  25. {figpack-0.2.2 → figpack-0.2.3}/figpack/spike_sorting/__init__.py +0 -0
  26. {figpack-0.2.2 → figpack-0.2.3}/figpack/spike_sorting/views/AutocorrelogramItem.py +0 -0
  27. {figpack-0.2.2 → figpack-0.2.3}/figpack/spike_sorting/views/Autocorrelograms.py +0 -0
  28. {figpack-0.2.2 → figpack-0.2.3}/figpack/spike_sorting/views/AverageWaveforms.py +0 -0
  29. {figpack-0.2.2 → figpack-0.2.3}/figpack/spike_sorting/views/CrossCorrelogramItem.py +0 -0
  30. {figpack-0.2.2 → figpack-0.2.3}/figpack/spike_sorting/views/CrossCorrelograms.py +0 -0
  31. {figpack-0.2.2 → figpack-0.2.3}/figpack/spike_sorting/views/SpikeAmplitudes.py +0 -0
  32. {figpack-0.2.2 → figpack-0.2.3}/figpack/spike_sorting/views/SpikeAmplitudesItem.py +0 -0
  33. {figpack-0.2.2 → figpack-0.2.3}/figpack/spike_sorting/views/UnitSimilarityScore.py +0 -0
  34. {figpack-0.2.2 → figpack-0.2.3}/figpack/spike_sorting/views/UnitsTable.py +0 -0
  35. {figpack-0.2.2 → figpack-0.2.3}/figpack/spike_sorting/views/UnitsTableColumn.py +0 -0
  36. {figpack-0.2.2 → figpack-0.2.3}/figpack/spike_sorting/views/UnitsTableRow.py +0 -0
  37. {figpack-0.2.2 → figpack-0.2.3}/figpack/spike_sorting/views/__init__.py +0 -0
  38. {figpack-0.2.2 → figpack-0.2.3}/figpack/views/Box.py +0 -0
  39. {figpack-0.2.2 → figpack-0.2.3}/figpack/views/Image.py +0 -0
  40. {figpack-0.2.2 → figpack-0.2.3}/figpack/views/LayoutItem.py +0 -0
  41. {figpack-0.2.2 → figpack-0.2.3}/figpack/views/Markdown.py +0 -0
  42. {figpack-0.2.2 → figpack-0.2.3}/figpack/views/MatplotlibFigure.py +0 -0
  43. {figpack-0.2.2 → figpack-0.2.3}/figpack/views/MultiChannelTimeseries.py +0 -0
  44. {figpack-0.2.2 → figpack-0.2.3}/figpack/views/PlotlyFigure.py +0 -0
  45. {figpack-0.2.2 → figpack-0.2.3}/figpack/views/Splitter.py +0 -0
  46. {figpack-0.2.2 → figpack-0.2.3}/figpack/views/TabLayout.py +0 -0
  47. {figpack-0.2.2 → figpack-0.2.3}/figpack/views/TabLayoutItem.py +0 -0
  48. {figpack-0.2.2 → figpack-0.2.3}/figpack/views/TimeseriesGraph.py +0 -0
  49. {figpack-0.2.2 → figpack-0.2.3}/figpack/views/__init__.py +0 -0
  50. {figpack-0.2.2 → figpack-0.2.3}/figpack.egg-info/SOURCES.txt +0 -0
  51. {figpack-0.2.2 → figpack-0.2.3}/figpack.egg-info/dependency_links.txt +0 -0
  52. {figpack-0.2.2 → figpack-0.2.3}/figpack.egg-info/entry_points.txt +0 -0
  53. {figpack-0.2.2 → figpack-0.2.3}/figpack.egg-info/requires.txt +0 -0
  54. {figpack-0.2.2 → figpack-0.2.3}/figpack.egg-info/top_level.txt +0 -0
  55. {figpack-0.2.2 → figpack-0.2.3}/setup.cfg +0 -0
  56. {figpack-0.2.2 → figpack-0.2.3}/tests/test_average_waveforms.py +0 -0
  57. {figpack-0.2.2 → figpack-0.2.3}/tests/test_box.py +0 -0
  58. {figpack-0.2.2 → figpack-0.2.3}/tests/test_cli.py +0 -0
  59. {figpack-0.2.2 → figpack-0.2.3}/tests/test_core.py +0 -0
  60. {figpack-0.2.2 → figpack-0.2.3}/tests/test_image.py +0 -0
  61. {figpack-0.2.2 → figpack-0.2.3}/tests/test_markdown.py +0 -0
  62. {figpack-0.2.2 → figpack-0.2.3}/tests/test_matplotlib_figure.py +0 -0
  63. {figpack-0.2.2 → figpack-0.2.3}/tests/test_multichannel_timeseries.py +0 -0
  64. {figpack-0.2.2 → figpack-0.2.3}/tests/test_plotly_figure.py +0 -0
  65. {figpack-0.2.2 → figpack-0.2.3}/tests/test_show_view.py +0 -0
  66. {figpack-0.2.2 → figpack-0.2.3}/tests/test_spike_sorting_correlograms.py +0 -0
  67. {figpack-0.2.2 → figpack-0.2.3}/tests/test_splitter.py +0 -0
  68. {figpack-0.2.2 → figpack-0.2.3}/tests/test_tablayout.py +0 -0
  69. {figpack-0.2.2 → figpack-0.2.3}/tests/test_timeseries_graph.py +0 -0
  70. {figpack-0.2.2 → figpack-0.2.3}/tests/test_track_animation.py +0 -0
  71. {figpack-0.2.2 → figpack-0.2.3}/tests/test_units_table.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: figpack
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: A Python package for creating shareable, interactive visualizations in the browser
5
5
  Author-email: Jeremy Magland <jmagland@flatironinstitute.org>
6
6
  License: Apache-2.0
@@ -2,4 +2,4 @@
2
2
  figpack - A Python package for creating shareable, interactive visualizations in the browser
3
3
  """
4
4
 
5
- __version__ = "0.2.2"
5
+ __version__ = "0.2.3"
@@ -38,6 +38,19 @@ def _is_in_notebook() -> bool:
38
38
  return False
39
39
 
40
40
 
41
+ def _is_in_colab():
42
+ try:
43
+ import google.colab # type: ignore
44
+
45
+ return True
46
+ except ImportError:
47
+ return False
48
+
49
+
50
+ def _is_in_jupyterhub():
51
+ return "JUPYTERHUB_USER" in os.environ
52
+
53
+
41
54
  def _display_inline_iframe(url: str, height: int) -> None:
42
55
  """
43
56
  Display an iframe inline in a Jupyter notebook.
@@ -75,6 +88,7 @@ def _show_view(
75
88
  port: Union[int, None] = None,
76
89
  allow_origin: Union[str, None] = None,
77
90
  upload: bool = False,
91
+ ephemeral: bool = False,
78
92
  title: Union[str, None] = None,
79
93
  description: Union[str, None] = None,
80
94
  inline: Union[bool, None] = None,
@@ -91,16 +105,17 @@ def _show_view(
91
105
  with tempfile.TemporaryDirectory(prefix="figpack_upload_") as tmpdir:
92
106
  prepare_figure_bundle(view, tmpdir, title=title, description=description)
93
107
 
94
- # Check for required environment variable
108
+ # Check for API key - required for regular uploads, optional for ephemeral
95
109
  api_key = os.environ.get("FIGPACK_API_KEY")
96
- if not api_key:
110
+ if not ephemeral and not api_key:
97
111
  raise EnvironmentError(
98
112
  "FIGPACK_API_KEY environment variable must be set to upload views."
99
113
  )
100
114
 
101
115
  # Upload the bundle
102
- print("Starting upload...")
103
- figure_url = _upload_bundle(tmpdir, api_key, title=title)
116
+ figure_url = _upload_bundle(
117
+ tmpdir, api_key, title=title, ephemeral=ephemeral
118
+ )
104
119
 
105
120
  if use_inline:
106
121
  # For uploaded figures, display the remote URL inline and continue
@@ -133,33 +133,45 @@ def _create_or_get_figure(
133
133
  total_files: int = None,
134
134
  total_size: int = None,
135
135
  title: str = None,
136
+ ephemeral: bool = False,
136
137
  ) -> dict:
137
138
  """
138
139
  Create a new figure or get existing figure information
139
140
 
140
141
  Args:
141
142
  figure_hash: The hash of the figure
142
- api_key: The API key for authentication
143
+ api_key: The API key for authentication (required for non-ephemeral)
143
144
  total_files: Optional total number of files
144
145
  total_size: Optional total size of files
145
146
  title: Optional title for the figure
147
+ ephemeral: Whether to create an ephemeral figure
146
148
 
147
149
  Returns:
148
150
  dict: Figure information from the API
149
151
  """
152
+ # Validate API key requirement
153
+ if not ephemeral and api_key is None:
154
+ raise ValueError("API key is required for non-ephemeral figures")
155
+
150
156
  payload = {
151
157
  "figureHash": figure_hash,
152
- "apiKey": api_key,
153
158
  "figpackVersion": __version__,
154
159
  }
155
160
 
161
+ # API key is optional for ephemeral figures
162
+ if api_key is not None:
163
+ payload["apiKey"] = api_key
164
+
156
165
  if total_files is not None:
157
166
  payload["totalFiles"] = total_files
158
167
  if total_size is not None:
159
168
  payload["totalSize"] = total_size
160
169
  if title is not None:
161
170
  payload["title"] = title
171
+ if ephemeral:
172
+ payload["ephemeral"] = True
162
173
 
174
+ # Use the same endpoint for both regular and ephemeral figures
163
175
  response = requests.post(f"{FIGPACK_API_BASE_URL}/api/figures/create", json=payload)
164
176
 
165
177
  if not response.ok:
@@ -212,16 +224,16 @@ def _finalize_figure(figure_url: str, api_key: str) -> dict:
212
224
  return response_data
213
225
 
214
226
 
215
- def _upload_bundle(tmpdir: str, api_key: str, title: str = None) -> str:
227
+ def _upload_bundle(
228
+ tmpdir: str, api_key: str, title: str = None, ephemeral: bool = False
229
+ ) -> str:
216
230
  """
217
231
  Upload the prepared bundle to the cloud using the new database-driven approach
218
232
  """
219
233
  tmpdir_path = pathlib.Path(tmpdir)
220
234
 
221
235
  # Compute deterministic figure ID based on file contents
222
- print("Computing deterministic figure ID...")
223
236
  figure_hash = _compute_deterministic_figure_hash(tmpdir_path)
224
- print(f"Figure hash: {figure_hash}")
225
237
 
226
238
  # Collect all files to upload
227
239
  all_files = []
@@ -239,17 +251,15 @@ def _upload_bundle(tmpdir: str, api_key: str, title: str = None) -> str:
239
251
 
240
252
  # Find available figure ID and create/get figure in database with metadata
241
253
  result = _create_or_get_figure(
242
- figure_hash, api_key, total_files, total_size, title=title
254
+ figure_hash, api_key, total_files, total_size, title=title, ephemeral=ephemeral
243
255
  )
244
256
  figure_info = result.get("figure", {})
245
257
  figure_url = figure_info.get("figureUrl")
246
258
 
247
259
  if figure_info["status"] == "completed":
248
- print(f"Figure already exists at: {figure_url}")
260
+ print(f"Figure already exists. No upload needed.")
249
261
  return figure_url
250
262
 
251
- print(f"Using figure URL: {figure_url}")
252
-
253
263
  files_to_upload = all_files
254
264
  total_files_to_upload = len(files_to_upload)
255
265
 
@@ -18,7 +18,8 @@ class FigpackView:
18
18
  port: Union[int, None] = None,
19
19
  open_in_browser: bool = False,
20
20
  allow_origin: Union[str, None] = None,
21
- upload: bool = False,
21
+ upload: Union[bool, None] = None,
22
+ ephemeral: Union[bool, None] = None,
22
23
  _dev: bool = False,
23
24
  title: Union[str, None] = None,
24
25
  description: Union[str, None] = None,
@@ -33,13 +34,37 @@ class FigpackView:
33
34
  open_in_browser: Whether to open in browser automatically
34
35
  allow_origin: CORS allow origin header
35
36
  upload: Whether to upload the figure
37
+ ephemeral: Whether to upload as ephemeral figure (None=auto-detect, True=force ephemeral, False=force regular)
36
38
  _dev: Development mode flag
37
39
  title: Title for the browser tab and figure
38
40
  description: Description text (markdown supported) for the figure
39
41
  inline: Whether to display inline in notebook (None=auto-detect, True=force inline, False=force browser)
40
42
  inline_height: Height in pixels for inline iframe display (default: 600)
41
43
  """
42
- from ._show_view import _show_view
44
+ from ._show_view import (
45
+ _show_view,
46
+ _is_in_notebook,
47
+ _is_in_colab,
48
+ _is_in_jupyterhub,
49
+ )
50
+
51
+ if ephemeral is None and upload is None:
52
+ # If we haven't specified both, then let's check if we're in a notebook in a non-local environment
53
+ if _is_in_notebook():
54
+ if _is_in_colab():
55
+ # if we are in a notebook and in colab, we should show as uploaded ephemeral
56
+ print("Detected Google Colab notebook environment.")
57
+ upload = True
58
+ ephemeral = True
59
+ if _is_in_jupyterhub():
60
+ # if we are in a notebook and in jupyterhub, we should show as uploaded ephemeral
61
+ print("Detected JupyterHub notebook environment.")
62
+ upload = True
63
+ ephemeral = True
64
+
65
+ # Validate ephemeral parameter
66
+ if ephemeral and not upload:
67
+ raise ValueError("ephemeral=True requires upload=True to be set")
43
68
 
44
69
  if _dev:
45
70
  if port is None:
@@ -61,6 +86,7 @@ class FigpackView:
61
86
  open_in_browser=open_in_browser,
62
87
  allow_origin=allow_origin,
63
88
  upload=upload,
89
+ ephemeral=ephemeral,
64
90
  title=title,
65
91
  description=description,
66
92
  inline=inline,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: figpack
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: A Python package for creating shareable, interactive visualizations in the browser
5
5
  Author-email: Jeremy Magland <jmagland@flatironinstitute.org>
6
6
  License: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "figpack"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "A Python package for creating shareable, interactive visualizations in the browser"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -0,0 +1,51 @@
1
+ """
2
+ Tests for the FigpackView base class
3
+ """
4
+
5
+ import pytest
6
+ import zarr
7
+ from unittest.mock import patch
8
+ from figpack.core.figpack_view import FigpackView
9
+
10
+
11
+ class TestFigpackView:
12
+ """Test cases for FigpackView class"""
13
+
14
+ def test_write_to_zarr_group_abstract(self):
15
+ """Test that _write_to_zarr_group raises NotImplementedError"""
16
+ view = FigpackView()
17
+ group = zarr.group()
18
+
19
+ with pytest.raises(NotImplementedError) as exc_info:
20
+ view._write_to_zarr_group(group)
21
+
22
+ assert "Subclasses must implement _write_to_zarr_group" in str(exc_info.value)
23
+
24
+ def test_show_dev_mode_constraints(self):
25
+ """Test development mode constraints in show method"""
26
+ view = FigpackView()
27
+
28
+ # Test that allow_origin cannot be set with _dev=True
29
+ with pytest.raises(ValueError) as exc_info:
30
+ view.show(_dev=True, allow_origin="http://example.com")
31
+ assert "Cannot set allow_origin when _dev is True" in str(exc_info.value)
32
+
33
+ # Test that upload cannot be used with _dev=True
34
+ with pytest.raises(ValueError) as exc_info:
35
+ view.show(_dev=True, upload=True)
36
+ assert "Cannot upload when _dev is True" in str(exc_info.value)
37
+
38
+ def test_show_with_title_and_description(self):
39
+ """Test show method with title and description"""
40
+ view = FigpackView()
41
+ title = "Test Figure"
42
+ description = "A test description"
43
+
44
+ with patch("figpack.core._show_view._show_view") as mock_show:
45
+ view.show(title=title, description=description)
46
+
47
+ mock_show.assert_called_once()
48
+ args = mock_show.call_args
49
+
50
+ assert args[1]["title"] == title
51
+ assert args[1]["description"] == description
@@ -1,106 +0,0 @@
1
- """
2
- Tests for the FigpackView base class
3
- """
4
-
5
- import pytest
6
- import zarr
7
- from unittest.mock import patch
8
- from figpack.core.figpack_view import FigpackView
9
-
10
-
11
- class TestFigpackView:
12
- """Test cases for FigpackView class"""
13
-
14
- def test_write_to_zarr_group_abstract(self):
15
- """Test that _write_to_zarr_group raises NotImplementedError"""
16
- view = FigpackView()
17
- group = zarr.group()
18
-
19
- with pytest.raises(NotImplementedError) as exc_info:
20
- view._write_to_zarr_group(group)
21
-
22
- assert "Subclasses must implement _write_to_zarr_group" in str(exc_info.value)
23
-
24
- def test_show_dev_mode_constraints(self):
25
- """Test development mode constraints in show method"""
26
- view = FigpackView()
27
-
28
- # Test that allow_origin cannot be set with _dev=True
29
- with pytest.raises(ValueError) as exc_info:
30
- view.show(_dev=True, allow_origin="http://example.com")
31
- assert "Cannot set allow_origin when _dev is True" in str(exc_info.value)
32
-
33
- # Test that upload cannot be used with _dev=True
34
- with pytest.raises(ValueError) as exc_info:
35
- view.show(_dev=True, upload=True)
36
- assert "Cannot upload when _dev is True" in str(exc_info.value)
37
-
38
- def test_show_dev_mode_default_values(self, capfd):
39
- """Test that development mode sets correct default values"""
40
- view = FigpackView()
41
-
42
- # Mock _show_view to avoid actual server startup
43
- with patch("figpack.core._show_view._show_view") as mock_show:
44
- view.show(_dev=True)
45
-
46
- # Verify _show_view was called with correct parameters
47
- mock_show.assert_called_once()
48
- args = mock_show.call_args
49
-
50
- assert args[1]["port"] == 3004
51
- assert args[1]["allow_origin"] == "http://localhost:5173"
52
- assert args[1]["upload"] is False
53
- assert args[1]["open_in_browser"] is False
54
-
55
- # Verify correct message was printed
56
- captured = capfd.readouterr()
57
- assert "For development, run figpack-gui in dev mode" in captured.out
58
-
59
- def test_show_regular_usage(self):
60
- """Test show method with standard parameters"""
61
- view = FigpackView()
62
- port = 8080
63
-
64
- with patch("figpack.core._show_view._show_view") as mock_show:
65
- # Test with various combinations of parameters
66
- view.show(port=port, open_in_browser=True, allow_origin="http://test.com")
67
-
68
- mock_show.assert_called_once()
69
- args = mock_show.call_args
70
-
71
- assert args[1]["port"] == port
72
- assert args[1]["open_in_browser"] is True
73
- assert args[1]["allow_origin"] == "http://test.com"
74
- assert args[1]["upload"] is False
75
-
76
- def test_show_with_title_and_description(self):
77
- """Test show method with title and description"""
78
- view = FigpackView()
79
- title = "Test Figure"
80
- description = "A test description"
81
-
82
- with patch("figpack.core._show_view._show_view") as mock_show:
83
- view.show(title=title, description=description)
84
-
85
- mock_show.assert_called_once()
86
- args = mock_show.call_args
87
-
88
- assert args[1]["title"] == title
89
- assert args[1]["description"] == description
90
-
91
- def test_show_with_default_values(self):
92
- """Test show method uses correct default values"""
93
- view = FigpackView()
94
-
95
- with patch("figpack.core._show_view._show_view") as mock_show:
96
- view.show()
97
-
98
- mock_show.assert_called_once()
99
- args = mock_show.call_args
100
-
101
- assert args[1]["port"] is None
102
- assert args[1]["open_in_browser"] is False
103
- assert args[1]["allow_origin"] is None
104
- assert args[1]["upload"] is False
105
- assert args[1]["title"] is None
106
- assert args[1]["description"] is None
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes