figpack 0.3.4__tar.gz → 0.3.6__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.
Files changed (80) hide show
  1. {figpack-0.3.4/figpack.egg-info → figpack-0.3.6}/PKG-INFO +2 -2
  2. {figpack-0.3.4 → figpack-0.3.6}/README.md +1 -1
  3. {figpack-0.3.4 → figpack-0.3.6}/figpack/__init__.py +1 -1
  4. figpack-0.3.6/figpack/core/zarr.py +129 -0
  5. figpack-0.3.6/figpack/figpack-figure-dist/assets/index-BQ9mn8rh.css +1 -0
  6. figpack-0.3.4/figpack/figpack-figure-dist/assets/index-P3Ni9vtI.js → figpack-0.3.6/figpack/figpack-figure-dist/assets/index-VDagO1Xz.js +61 -61
  7. {figpack-0.3.4 → figpack-0.3.6}/figpack/figpack-figure-dist/index.html +2 -2
  8. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/TimeseriesGraph.py +10 -0
  9. {figpack-0.3.4 → figpack-0.3.6/figpack.egg-info}/PKG-INFO +2 -2
  10. {figpack-0.3.4 → figpack-0.3.6}/figpack.egg-info/SOURCES.txt +2 -2
  11. {figpack-0.3.4 → figpack-0.3.6}/pyproject.toml +1 -1
  12. figpack-0.3.4/figpack/core/zarr.py +0 -61
  13. figpack-0.3.4/figpack/figpack-figure-dist/assets/index-V5m_wCvw.css +0 -1
  14. {figpack-0.3.4 → figpack-0.3.6}/LICENSE +0 -0
  15. {figpack-0.3.4 → figpack-0.3.6}/MANIFEST.in +0 -0
  16. {figpack-0.3.4 → figpack-0.3.6}/figpack/cli.py +0 -0
  17. {figpack-0.3.4 → figpack-0.3.6}/figpack/core/__init__.py +0 -0
  18. {figpack-0.3.4 → figpack-0.3.6}/figpack/core/_bundle_utils.py +0 -0
  19. {figpack-0.3.4 → figpack-0.3.6}/figpack/core/_file_handler.py +0 -0
  20. {figpack-0.3.4 → figpack-0.3.6}/figpack/core/_save_figure.py +0 -0
  21. {figpack-0.3.4 → figpack-0.3.6}/figpack/core/_server_manager.py +0 -0
  22. {figpack-0.3.4 → figpack-0.3.6}/figpack/core/_show_view.py +0 -0
  23. {figpack-0.3.4 → figpack-0.3.6}/figpack/core/_upload_bundle.py +0 -0
  24. {figpack-0.3.4 → figpack-0.3.6}/figpack/core/_view_figure.py +0 -0
  25. {figpack-0.3.4 → figpack-0.3.6}/figpack/core/_zarr_consolidate.py +0 -0
  26. {figpack-0.3.4 → figpack-0.3.6}/figpack/core/config.py +0 -0
  27. {figpack-0.3.4 → figpack-0.3.6}/figpack/core/extension_view.py +0 -0
  28. {figpack-0.3.4 → figpack-0.3.6}/figpack/core/figpack_extension.py +0 -0
  29. {figpack-0.3.4 → figpack-0.3.6}/figpack/core/figpack_view.py +0 -0
  30. {figpack-0.3.4 → figpack-0.3.6}/figpack/extensions.py +0 -0
  31. {figpack-0.3.4 → figpack-0.3.6}/figpack/figpack-figure-dist/assets/neurosift-logo-CLsuwLMO.png +0 -0
  32. {figpack-0.3.4 → figpack-0.3.6}/figpack/utils/__init__.py +0 -0
  33. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/Box.py +0 -0
  34. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/CaptionedView.py +0 -0
  35. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/DataFrame.py +0 -0
  36. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/Gallery.py +0 -0
  37. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/GalleryItem.py +0 -0
  38. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/Iframe.py +0 -0
  39. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/Image.py +0 -0
  40. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/LayoutItem.py +0 -0
  41. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/Markdown.py +0 -0
  42. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/MatplotlibFigure.py +0 -0
  43. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/MountainLayout.py +0 -0
  44. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/MountainLayoutItem.py +0 -0
  45. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/MultiChannelTimeseries.py +0 -0
  46. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/PlotlyExtension/PlotlyExtension.py +0 -0
  47. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/PlotlyExtension/__init__.py +0 -0
  48. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/PlotlyExtension/_plotly_extension.py +0 -0
  49. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/PlotlyExtension/plotly_view.js +0 -0
  50. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/Spectrogram.py +0 -0
  51. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/Splitter.py +0 -0
  52. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/TabLayout.py +0 -0
  53. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/TabLayoutItem.py +0 -0
  54. {figpack-0.3.4 → figpack-0.3.6}/figpack/views/__init__.py +0 -0
  55. {figpack-0.3.4 → figpack-0.3.6}/figpack-serve/worker/container/app.py +0 -0
  56. {figpack-0.3.4 → figpack-0.3.6}/figpack.egg-info/dependency_links.txt +0 -0
  57. {figpack-0.3.4 → figpack-0.3.6}/figpack.egg-info/entry_points.txt +0 -0
  58. {figpack-0.3.4 → figpack-0.3.6}/figpack.egg-info/requires.txt +0 -0
  59. {figpack-0.3.4 → figpack-0.3.6}/figpack.egg-info/top_level.txt +0 -0
  60. {figpack-0.3.4 → figpack-0.3.6}/setup.cfg +0 -0
  61. {figpack-0.3.4 → figpack-0.3.6}/tests/test_box.py +0 -0
  62. {figpack-0.3.4 → figpack-0.3.6}/tests/test_cli.py +0 -0
  63. {figpack-0.3.4 → figpack-0.3.6}/tests/test_core.py +0 -0
  64. {figpack-0.3.4 → figpack-0.3.6}/tests/test_dataframe.py +0 -0
  65. {figpack-0.3.4 → figpack-0.3.6}/tests/test_extension_system.py +0 -0
  66. {figpack-0.3.4 → figpack-0.3.6}/tests/test_figpack_view.py +0 -0
  67. {figpack-0.3.4 → figpack-0.3.6}/tests/test_file_handler.py +0 -0
  68. {figpack-0.3.4 → figpack-0.3.6}/tests/test_gallery.py +0 -0
  69. {figpack-0.3.4 → figpack-0.3.6}/tests/test_image.py +0 -0
  70. {figpack-0.3.4 → figpack-0.3.6}/tests/test_markdown.py +0 -0
  71. {figpack-0.3.4 → figpack-0.3.6}/tests/test_matplotlib_figure.py +0 -0
  72. {figpack-0.3.4 → figpack-0.3.6}/tests/test_multichannel_timeseries.py +0 -0
  73. {figpack-0.3.4 → figpack-0.3.6}/tests/test_plotly_figure.py +0 -0
  74. {figpack-0.3.4 → figpack-0.3.6}/tests/test_server_manager.py +0 -0
  75. {figpack-0.3.4 → figpack-0.3.6}/tests/test_spectrogram.py +0 -0
  76. {figpack-0.3.4 → figpack-0.3.6}/tests/test_splitter.py +0 -0
  77. {figpack-0.3.4 → figpack-0.3.6}/tests/test_tablayout.py +0 -0
  78. {figpack-0.3.4 → figpack-0.3.6}/tests/test_timeseries_graph.py +0 -0
  79. {figpack-0.3.4 → figpack-0.3.6}/tests/test_upload_bundle.py +0 -0
  80. {figpack-0.3.4 → figpack-0.3.6}/tests/test_view_figure.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: figpack
3
- Version: 0.3.4
3
+ Version: 0.3.6
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
@@ -133,7 +133,7 @@ If you use figpack in your research, please cite it:
133
133
 
134
134
  Or in APA format:
135
135
 
136
- > Magland, J. (2025). figpack (Version 0.3.4) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.17419621
136
+ > Magland, J. (2025). figpack (Version 0.3.6) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.17419621
137
137
 
138
138
  ## Contributing
139
139
 
@@ -57,7 +57,7 @@ If you use figpack in your research, please cite it:
57
57
 
58
58
  Or in APA format:
59
59
 
60
- > Magland, J. (2025). figpack (Version 0.3.4) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.17419621
60
+ > Magland, J. (2025). figpack (Version 0.3.6) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.17419621
61
61
 
62
62
  ## Contributing
63
63
 
@@ -2,7 +2,7 @@
2
2
  figpack - A Python package for creating shareable, interactive visualizations in the browser
3
3
  """
4
4
 
5
- __version__ = "0.3.4"
5
+ __version__ = "0.3.6"
6
6
 
7
7
  from .cli import view_figure
8
8
  from .core import FigpackView, FigpackExtension, ExtensionView
@@ -0,0 +1,129 @@
1
+ from typing import Any, Dict
2
+ import numpy as np
3
+
4
+
5
+ _UNSPECIFIED = object()
6
+
7
+
8
+ class Group:
9
+ def __init__(self, zarr_group):
10
+ self._zarr_group = zarr_group
11
+
12
+ def create_group(self, name: str) -> "Group":
13
+ return Group(self._zarr_group.create_group(name))
14
+
15
+ def create_dataset(
16
+ self,
17
+ name: str,
18
+ *,
19
+ data=_UNSPECIFIED,
20
+ dtype=_UNSPECIFIED,
21
+ chunks=_UNSPECIFIED,
22
+ compressor=_UNSPECIFIED,
23
+ ) -> None:
24
+ kwargs = {}
25
+ if data is not _UNSPECIFIED:
26
+ kwargs["data"] = data
27
+ if dtype is not _UNSPECIFIED:
28
+ kwargs["dtype"] = dtype
29
+ if chunks is not _UNSPECIFIED:
30
+ kwargs["chunks"] = chunks
31
+ else:
32
+ if data is not _UNSPECIFIED:
33
+ chunks2 = _get_optimal_chunk_size(data.shape, data.dtype)
34
+ if chunks2 is not _UNSPECIFIED:
35
+ kwargs["chunks"] = chunks2
36
+ if compressor is not _UNSPECIFIED:
37
+ kwargs["compressor"] = compressor
38
+ if _check_zarr_version() == 2:
39
+ self._zarr_group.create_dataset(name, **kwargs)
40
+ elif _check_zarr_version() == 3:
41
+ self._zarr_group.create_array(name, **kwargs) # type: ignore
42
+ else:
43
+ raise RuntimeError("Unsupported Zarr version")
44
+
45
+ @property
46
+ def attrs(self) -> Dict[str, Any]:
47
+ return self._zarr_group.attrs # type: ignore
48
+
49
+ def __getitem__(self, key: str) -> Any:
50
+ return self._zarr_group[key]
51
+
52
+ # implement in operator
53
+ def __contains__(self, key: str) -> bool:
54
+ return key in self._zarr_group
55
+
56
+ def __iter__(self):
57
+ return iter(self._zarr_group)
58
+
59
+ def __reversed__(self):
60
+ return reversed(self._zarr_group)
61
+
62
+
63
+ def _check_zarr_version():
64
+ import zarr
65
+
66
+ version = zarr.__version__
67
+ major_version = int(version.split(".")[0])
68
+ return major_version
69
+
70
+
71
+ def _get_optimal_chunk_size(shape: tuple, dtype) -> tuple:
72
+ """Compute an optimal chunk shape targeting ~5 MB per chunk,
73
+ chunking along only one dimension and avoiding mostly empty chunks.
74
+ """
75
+ target_bytes = 5 * 1024 * 1024 # 5 MB
76
+
77
+ if np.prod(shape) <= 1:
78
+ # all dimensions are zero or one
79
+ # return shape of unspecified to let zarr choose on this edge case
80
+ return _UNSPECIFIED
81
+
82
+ # Ensure dtype is a numpy dtype to get itemsize
83
+ if isinstance(dtype, str):
84
+ dtype = np.dtype(dtype)
85
+
86
+ itemsize = np.dtype(dtype).itemsize
87
+
88
+ # Total array size in bytes
89
+ total_bytes = np.prod(shape) * itemsize
90
+ if total_bytes <= target_bytes:
91
+ # Entire array fits comfortably in one chunk
92
+ return shape
93
+
94
+ # Choose the first non-singleton dimension to chunk along
95
+ for axis, dim in enumerate(shape):
96
+ if dim > 1:
97
+ break
98
+ else:
99
+ return shape # all singleton
100
+
101
+ # Compute how many elements fit in target_bytes
102
+ target_elements = target_bytes // itemsize
103
+
104
+ # Compute product of all other dimensions (fixed)
105
+ other_size = np.prod(shape) // shape[axis]
106
+
107
+ # Determine chunk length along chosen axis
108
+ chunk_len = max(1, min(shape[axis], target_elements // other_size))
109
+
110
+ # Construct chunk shape
111
+ chunk_shape = list(shape)
112
+ chunk_shape[axis] = chunk_len
113
+
114
+ return tuple([int(c) for c in chunk_shape])
115
+
116
+
117
+ if __name__ == "__main__":
118
+ # Test the _get_optimal_chunk_size function for a variety of shapes and dtypes
119
+ test_cases = [
120
+ ((1000, 1000), np.float32),
121
+ ((5000, 2000), np.uint8),
122
+ ((100, 100, 100), np.float64),
123
+ ((10, 30, 30, 1500), np.int16),
124
+ ((1, 1, 1), np.float32),
125
+ ]
126
+
127
+ for shape, dtype in test_cases:
128
+ chunk_size = _get_optimal_chunk_size(shape, dtype)
129
+ print(f"Optimal chunk size for shape {shape} and dtype {dtype}: {chunk_size}")
@@ -0,0 +1 @@
1
+ .unitEntryBase{padding-right:13px;padding-top:10px}.unselectedUnitEntry{border:1px transparent solid;font-weight:400;color:#000;background-color:#fff}.selectedUnitEntry{border:1px solid blue;font-weight:700;color:#000;background-color:#b5d1ff}.unitIdsColumnLabelStyle{min-width:200px;font-weight:700;padding:7px 5px}.unitLabelsStyle{padding-right:3px;color:#333}.plotUnitLabel{text-align:left;margin-left:20px;margin-top:5px;font-weight:700}.plotWrapperButtonParent{display:inline-block}.plotWrapperStyleButton{padding-top:50px}.plotUnselectedStyle{border:3px solid rgb(202,224,231)}.plotSelectedStyle{border:3px solid #9999ff;background-color:#fff}.plotUnselectableStyle{border:3px solid transparent}.plotCurrentStyle{border:3px solid blue;background-color:#fff}.plotLabelStyle{font-weight:700;text-align:center;overflow:hidden;white-space:nowrap}:root{font-family:system-ui,Avenir,Helvetica,Arial,sans-serif;line-height:1.5;font-weight:400;color:#213547;background-color:#fff;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#747bff}body{margin:0;overflow:hidden}h1{font-size:3.2em;line-height:1.1}button{border-radius:8px;border:1px solid transparent;padding:.6em 1.2em;font-size:1em;font-weight:500;font-family:inherit;background-color:#f9f9f9;cursor:pointer;transition:border-color .25s}button:hover{border-color:#646cff}button:focus,button:focus-visible{outline:4px auto -webkit-focus-ring-color}.status-bar{position:fixed;left:0;right:0;height:30px;background-color:#f5f5f5;color:#333;display:flex;align-items:center;padding:0 12px;font-size:12px;border-top:1px solid #ddd;z-index:1000}.status-bar.warning{background-color:#fff3cd;color:#856404;border-top:1px solid #ffeaa7}.status-bar.error,.status-bar.expired{background-color:#f8d7da;color:#721c24;border-top:1px solid #f5c6cb}.manage-button{background-color:#e0e0e0;color:#333;border:1px solid #ccc;padding:4px 8px;border-radius:4px;cursor:pointer;font-size:11px;height:22px;margin-left:12px;transition:background-color .2s}.manage-button:hover{background-color:#d0d0d0}.about-button{background-color:#e0e0e0;color:#333;border:1px solid #ccc;padding:4px 8px;border-radius:4px;cursor:pointer;font-size:11px;height:22px;margin-left:12px;transition:background-color .2s}.about-button:hover{background-color:#d0d0d0}.about-dialog-overlay{position:fixed;inset:0;background-color:#00000080;display:flex;justify-content:center;align-items:center;z-index:2000}.about-dialog{background-color:#fff;color:#333;border-radius:8px;max-width:600px;max-height:80vh;width:90%;box-shadow:0 4px 20px #00000026;overflow:hidden}.about-dialog-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid #ddd}.about-dialog-header h2{margin:0;font-size:18px;font-weight:600}.about-dialog-close{background:none;border:none;color:#333;font-size:24px;cursor:pointer;padding:0;width:30px;height:30px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:background-color .2s}.about-dialog-close:hover{background-color:#f0f0f0}.about-dialog-content{padding:20px;overflow-y:auto;max-height:calc(80vh - 80px)}.about-dialog-description{line-height:1.6}.about-dialog-description h1,.about-dialog-description h2,.about-dialog-description h3{margin-top:0;margin-bottom:12px}.about-dialog-description p{margin-bottom:12px}.about-dialog-description code{background-color:#f5f5f5;color:#333;padding:2px 4px;border-radius:3px;font-family:Courier New,monospace;font-size:.9em}.about-dialog-description pre{background-color:#f5f5f5;color:#333;padding:12px;border-radius:4px;overflow-x:auto;margin-bottom:12px}.about-dialog-description ul,.about-dialog-description ol{margin-bottom:12px;padding-left:20px}.about-dialog-description blockquote{border-left:4px solid #ccc;padding-left:16px;margin:12px 0;font-style:italic}.manage-edits-view{display:flex;align-items:center;margin-left:12px}.edits-notification{color:#e65100;font-weight:500;margin-right:8px}.save-changes-button{background-color:#ff9800;color:#fff;border:1px solid #f57c00;padding:4px 8px;border-radius:4px;cursor:pointer;font-size:11px;height:22px;transition:background-color .2s}.save-changes-button:hover{background-color:#f57c00}.dialog-overlay{position:fixed;inset:0;background-color:#00000080;display:flex;justify-content:center;align-items:center;z-index:2000}.dialog-content{background-color:#fff;color:#333;border-radius:8px;max-width:500px;width:90%;box-shadow:0 4px 20px #00000026;overflow:hidden}.dialog-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid #ddd}.dialog-header h3{margin:0;font-size:18px;font-weight:600}.dialog-close{background:none;border:none;color:#333;font-size:24px;cursor:pointer;padding:0;width:30px;height:30px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:background-color .2s}.dialog-close:hover{background-color:#f0f0f0}.dialog-body{padding:20px}.dialog-body p{margin:0 0 12px;line-height:1.5}.dialog-footer{padding:16px 20px;border-top:1px solid #ddd;display:flex;justify-content:flex-end}.dialog-button{background-color:#e0e0e0;color:#333;border:1px solid #ccc;padding:8px 16px;border-radius:4px;cursor:pointer;font-size:14px;transition:background-color .2s}.dialog-button:hover{background-color:#d0d0d0}.dialog-fullscreen{position:fixed;inset:0;background-color:#fff;color:#333;z-index:2000;display:flex;flex-direction:column}.dialog-content-fullscreen{display:flex;flex-direction:column;height:100%;overflow:hidden}.dialog-content-fullscreen .dialog-header{flex-shrink:0}.dialog-content-fullscreen .dialog-body{flex:1;overflow-y:auto;padding:20px}.dialog-content-fullscreen .dialog-footer{flex-shrink:0}