jarvisplot 1.2.5__tar.gz → 1.3.1__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 (53) hide show
  1. {jarvisplot-1.2.5 → jarvisplot-1.3.1/JarvisPLOT.egg-info}/PKG-INFO +43 -11
  2. {jarvisplot-1.2.5/jarvisplot.egg-info → jarvisplot-1.3.1/JarvisPLOT.egg-info}/SOURCES.txt +7 -6
  3. {jarvisplot-1.2.5/jarvisplot.egg-info → jarvisplot-1.3.1/JarvisPLOT.egg-info}/requires.txt +1 -0
  4. {jarvisplot-1.2.5/jarvisplot.egg-info → jarvisplot-1.3.1}/PKG-INFO +43 -11
  5. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/README.md +39 -8
  6. jarvisplot-1.3.1/jarvisplot/Figure/data_pipelines.py +165 -0
  7. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/Figure/figure.py +369 -135
  8. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/Figure/load_data.py +226 -112
  9. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/Figure/preprocessor.py +791 -87
  10. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cache_store.py +115 -5
  11. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cli.py +6 -2
  12. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/core.py +265 -3
  13. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/data_loader.py +735 -7
  14. jarvisplot-1.3.1/jarvisplot/memtrace.py +240 -0
  15. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/pyproject.toml +4 -3
  16. jarvisplot-1.2.5/jarvisplot/Figure/data_pipelines.py +0 -87
  17. {jarvisplot-1.2.5/jarvisplot.egg-info → jarvisplot-1.3.1/JarvisPLOT.egg-info}/dependency_links.txt +0 -0
  18. {jarvisplot-1.2.5/jarvisplot.egg-info → jarvisplot-1.3.1/JarvisPLOT.egg-info}/entry_points.txt +0 -0
  19. {jarvisplot-1.2.5/jarvisplot.egg-info → jarvisplot-1.3.1/JarvisPLOT.egg-info}/top_level.txt +0 -0
  20. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/Figure/__init__.py +0 -0
  21. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/Figure/adapters.py +0 -0
  22. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/Figure/cards/std_axes_adapter_config.json +0 -0
  23. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/Figure/helper.py +0 -0
  24. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/Figure/method_registry.py +0 -0
  25. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/__init__.py +0 -0
  26. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/a4paper/1x1/ternary.json +0 -0
  27. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/a4paper/2x1/rect.json +0 -0
  28. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/a4paper/2x1/rect5x1.json +0 -0
  29. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/a4paper/2x1/rect_cmap.json +0 -0
  30. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/a4paper/2x1/ternary.json +0 -0
  31. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/a4paper/2x1/ternary_cmap.json +0 -0
  32. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/a4paper/4x1/rect.json +0 -0
  33. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/a4paper/4x1/rect_cmap.json +0 -0
  34. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/a4paper/4x1/ternary.json +0 -0
  35. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/a4paper/4x1/ternary_cmap.json +0 -0
  36. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/args.json +0 -0
  37. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/colors/colormaps.json +0 -0
  38. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/default/output.json +0 -0
  39. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/gambit/1x1/ternary.json +0 -0
  40. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/gambit/2x1/rect_cmap.json +0 -0
  41. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/gambit/2x1/ternary.json +0 -0
  42. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/gambit/2x1/ternary_cmap.json +0 -0
  43. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/icons/JarvisHEP.png +0 -0
  44. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/icons/gambit.png +0 -0
  45. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/icons/gambit_small.png +0 -0
  46. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/cards/style_preference.json +0 -0
  47. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/client.py +0 -0
  48. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/config.py +0 -0
  49. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/inner_func.py +0 -0
  50. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/utils/__init__.py +0 -0
  51. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/utils/cmaps.py +0 -0
  52. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/jarvisplot/utils/interpolator.py +0 -0
  53. {jarvisplot-1.2.5 → jarvisplot-1.3.1}/setup.cfg +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
- Name: jarvisplot
3
- Version: 1.2.5
4
- Summary: JarvisPLOT: YAML-driven plotting engine
2
+ Name: JarvisPLOT
3
+ Version: 1.3.1
4
+ Summary: Jarvis-PLOT: YAML-driven plotting engine
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
7
7
  Requires-Dist: loguru
@@ -10,6 +10,7 @@ Requires-Dist: jsonschema
10
10
  Requires-Dist: Jarvis-Operas>=1.1.4
11
11
  Requires-Dist: numpy
12
12
  Requires-Dist: pandas
13
+ Requires-Dist: polars
13
14
  Requires-Dist: matplotlib
14
15
  Requires-Dist: h5py
15
16
  Requires-Dist: sympy
@@ -17,19 +18,37 @@ Requires-Dist: scipy
17
18
  Requires-Dist: shapely
18
19
  Requires-Dist: deepmerge
19
20
 
20
- # JarvisPLOT
21
+ # Jarvis-PLOT
21
22
 
22
- JarvisPLOT is a lightweight, Python/Matplotlib-based plotting framework developed for **Jarvis-HEP**,
23
+ Jarvis-PLOT is a lightweight, Python/Matplotlib-based plotting framework developed for **Jarvis-HEP**,
23
24
  but it can also be used as a **standalone scientific plotting tool**.
24
25
 
25
26
  It provides a simple command-line interface (CLI) to generate publication-quality figures from YAML configuration files, with most layout and style decisions handled by predefined profiles and style cards.
26
27
 
27
28
  ---
28
29
 
29
- ## Installation
30
+ ## Installation
30
31
 
31
- ```bash
32
- pip install jarvisplot
32
+ ```bash
33
+ pip install Jarvis-PLOT
34
+ ```
35
+
36
+ The PyPI distribution name is now `Jarvis-PLOT`.
37
+ The Python import package and entrypoint remain unchanged:
38
+
39
+ ```python
40
+ import jarvisplot
41
+ ```
42
+
43
+ ```bash
44
+ jplot path/to/config.yaml
45
+ ```
46
+
47
+ If you have an older environment that still uses the historical package name, replace:
48
+
49
+ ```bash
50
+ pip uninstall jarvisplot
51
+ pip install Jarvis-PLOT
33
52
  ```
34
53
 
35
54
  ## Command-Line Usage
@@ -40,7 +59,7 @@ Display help information:
40
59
  jplot -h
41
60
  ```
42
61
 
43
- Run JarvisPLOT with one or more YAML configuration files:
62
+ Run Jarvis-PLOT with one or more YAML configuration files:
44
63
 
45
64
  ```bash
46
65
  jplot path/to/config.yaml
@@ -55,7 +74,7 @@ jplot path/to/config.yaml --rebuild-cache
55
74
  ### Project Workdir and Cache
56
75
 
57
76
  - You can set `project.workdir` in YAML.
58
- - If `output.dir` is omitted, JarvisPLOT now defaults to `<workdir>/plots/`.
77
+ - If `output.dir` is omitted, Jarvis-PLOT defaults to `<workdir>/plots/`.
59
78
  - Data cache is stored in `<workdir>/.cache/`.
60
79
  - Profiling pipelines are prebuilt once and reused from cache when source fingerprint and profile settings are unchanged.
61
80
  - Profiling uses a fast two-stage grid reduction (`pregrid` + render `bin`) for large datasets.
@@ -75,7 +94,7 @@ jplot ./bin/SUSYRun2_GEWMSSM.yaml
75
94
 
76
95
  - Figures are saved automatically to the output paths defined in the YAML configuration.
77
96
  - Common output formats include PNG and PDF (backend-dependent).
78
- - JarvisPLOT works in headless environments (SSH, batch jobs) without any GUI backend.
97
+ - Jarvis-PLOT works in headless environments (SSH, batch jobs) without any GUI backend.
79
98
 
80
99
  ---
81
100
 
@@ -102,6 +121,19 @@ jplot ./bin/SUSYRun2_GEWMSSM.yaml
102
121
  ### Documentation
103
122
  [https://pengxuan-zhu-phys.github.io/Jarvis-Docs/](https://pengxuan-zhu-phys.github.io/Jarvis-Docs/)
104
123
 
124
+ ### Developer Docs
125
+
126
+ Tracked architecture and maintenance docs now live in `docs/dev/`.
127
+
128
+ - `docs/dev/ARCHITECTURE_OVERVIEW.md`
129
+ - `docs/dev/DATAFLOW_ARCHITECTURE.md`
130
+ - `docs/dev/MEMORY_OPTIMIZATION_GUIDE.md`
131
+ - `docs/dev/DEVELOPER_RULES.md`
132
+ - `docs/dev/MEMTRACE_SYSTEM.md`
133
+ - `docs/dev/CONTRIBUTION_GUIDE.md`
134
+
135
+ These documents describe the 1.3.0 selection-table pipeline architecture that 1.3.1 consolidates. Future work should preserve the narrow-table dataflow and must not reintroduce wide-table profiling or cache payloads.
136
+
105
137
  ---
106
138
 
107
139
  ## License
@@ -1,5 +1,11 @@
1
1
  README.md
2
2
  pyproject.toml
3
+ JarvisPLOT.egg-info/PKG-INFO
4
+ JarvisPLOT.egg-info/SOURCES.txt
5
+ JarvisPLOT.egg-info/dependency_links.txt
6
+ JarvisPLOT.egg-info/entry_points.txt
7
+ JarvisPLOT.egg-info/requires.txt
8
+ JarvisPLOT.egg-info/top_level.txt
3
9
  jarvisplot/__init__.py
4
10
  jarvisplot/cache_store.py
5
11
  jarvisplot/cli.py
@@ -8,12 +14,7 @@ jarvisplot/config.py
8
14
  jarvisplot/core.py
9
15
  jarvisplot/data_loader.py
10
16
  jarvisplot/inner_func.py
11
- jarvisplot.egg-info/PKG-INFO
12
- jarvisplot.egg-info/SOURCES.txt
13
- jarvisplot.egg-info/dependency_links.txt
14
- jarvisplot.egg-info/entry_points.txt
15
- jarvisplot.egg-info/requires.txt
16
- jarvisplot.egg-info/top_level.txt
17
+ jarvisplot/memtrace.py
17
18
  jarvisplot/Figure/__init__.py
18
19
  jarvisplot/Figure/adapters.py
19
20
  jarvisplot/Figure/data_pipelines.py
@@ -4,6 +4,7 @@ jsonschema
4
4
  Jarvis-Operas>=1.1.4
5
5
  numpy
6
6
  pandas
7
+ polars
7
8
  matplotlib
8
9
  h5py
9
10
  sympy
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
- Name: jarvisplot
3
- Version: 1.2.5
4
- Summary: JarvisPLOT: YAML-driven plotting engine
2
+ Name: JarvisPLOT
3
+ Version: 1.3.1
4
+ Summary: Jarvis-PLOT: YAML-driven plotting engine
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
7
7
  Requires-Dist: loguru
@@ -10,6 +10,7 @@ Requires-Dist: jsonschema
10
10
  Requires-Dist: Jarvis-Operas>=1.1.4
11
11
  Requires-Dist: numpy
12
12
  Requires-Dist: pandas
13
+ Requires-Dist: polars
13
14
  Requires-Dist: matplotlib
14
15
  Requires-Dist: h5py
15
16
  Requires-Dist: sympy
@@ -17,19 +18,37 @@ Requires-Dist: scipy
17
18
  Requires-Dist: shapely
18
19
  Requires-Dist: deepmerge
19
20
 
20
- # JarvisPLOT
21
+ # Jarvis-PLOT
21
22
 
22
- JarvisPLOT is a lightweight, Python/Matplotlib-based plotting framework developed for **Jarvis-HEP**,
23
+ Jarvis-PLOT is a lightweight, Python/Matplotlib-based plotting framework developed for **Jarvis-HEP**,
23
24
  but it can also be used as a **standalone scientific plotting tool**.
24
25
 
25
26
  It provides a simple command-line interface (CLI) to generate publication-quality figures from YAML configuration files, with most layout and style decisions handled by predefined profiles and style cards.
26
27
 
27
28
  ---
28
29
 
29
- ## Installation
30
+ ## Installation
30
31
 
31
- ```bash
32
- pip install jarvisplot
32
+ ```bash
33
+ pip install Jarvis-PLOT
34
+ ```
35
+
36
+ The PyPI distribution name is now `Jarvis-PLOT`.
37
+ The Python import package and entrypoint remain unchanged:
38
+
39
+ ```python
40
+ import jarvisplot
41
+ ```
42
+
43
+ ```bash
44
+ jplot path/to/config.yaml
45
+ ```
46
+
47
+ If you have an older environment that still uses the historical package name, replace:
48
+
49
+ ```bash
50
+ pip uninstall jarvisplot
51
+ pip install Jarvis-PLOT
33
52
  ```
34
53
 
35
54
  ## Command-Line Usage
@@ -40,7 +59,7 @@ Display help information:
40
59
  jplot -h
41
60
  ```
42
61
 
43
- Run JarvisPLOT with one or more YAML configuration files:
62
+ Run Jarvis-PLOT with one or more YAML configuration files:
44
63
 
45
64
  ```bash
46
65
  jplot path/to/config.yaml
@@ -55,7 +74,7 @@ jplot path/to/config.yaml --rebuild-cache
55
74
  ### Project Workdir and Cache
56
75
 
57
76
  - You can set `project.workdir` in YAML.
58
- - If `output.dir` is omitted, JarvisPLOT now defaults to `<workdir>/plots/`.
77
+ - If `output.dir` is omitted, Jarvis-PLOT defaults to `<workdir>/plots/`.
59
78
  - Data cache is stored in `<workdir>/.cache/`.
60
79
  - Profiling pipelines are prebuilt once and reused from cache when source fingerprint and profile settings are unchanged.
61
80
  - Profiling uses a fast two-stage grid reduction (`pregrid` + render `bin`) for large datasets.
@@ -75,7 +94,7 @@ jplot ./bin/SUSYRun2_GEWMSSM.yaml
75
94
 
76
95
  - Figures are saved automatically to the output paths defined in the YAML configuration.
77
96
  - Common output formats include PNG and PDF (backend-dependent).
78
- - JarvisPLOT works in headless environments (SSH, batch jobs) without any GUI backend.
97
+ - Jarvis-PLOT works in headless environments (SSH, batch jobs) without any GUI backend.
79
98
 
80
99
  ---
81
100
 
@@ -102,6 +121,19 @@ jplot ./bin/SUSYRun2_GEWMSSM.yaml
102
121
  ### Documentation
103
122
  [https://pengxuan-zhu-phys.github.io/Jarvis-Docs/](https://pengxuan-zhu-phys.github.io/Jarvis-Docs/)
104
123
 
124
+ ### Developer Docs
125
+
126
+ Tracked architecture and maintenance docs now live in `docs/dev/`.
127
+
128
+ - `docs/dev/ARCHITECTURE_OVERVIEW.md`
129
+ - `docs/dev/DATAFLOW_ARCHITECTURE.md`
130
+ - `docs/dev/MEMORY_OPTIMIZATION_GUIDE.md`
131
+ - `docs/dev/DEVELOPER_RULES.md`
132
+ - `docs/dev/MEMTRACE_SYSTEM.md`
133
+ - `docs/dev/CONTRIBUTION_GUIDE.md`
134
+
135
+ These documents describe the 1.3.0 selection-table pipeline architecture that 1.3.1 consolidates. Future work should preserve the narrow-table dataflow and must not reintroduce wide-table profiling or cache payloads.
136
+
105
137
  ---
106
138
 
107
139
  ## License
@@ -1,16 +1,34 @@
1
- # JarvisPLOT
1
+ # Jarvis-PLOT
2
2
 
3
- JarvisPLOT is a lightweight, Python/Matplotlib-based plotting framework developed for **Jarvis-HEP**,
3
+ Jarvis-PLOT is a lightweight, Python/Matplotlib-based plotting framework developed for **Jarvis-HEP**,
4
4
  but it can also be used as a **standalone scientific plotting tool**.
5
5
 
6
6
  It provides a simple command-line interface (CLI) to generate publication-quality figures from YAML configuration files, with most layout and style decisions handled by predefined profiles and style cards.
7
7
 
8
8
  ---
9
9
 
10
- ## Installation
10
+ ## Installation
11
11
 
12
- ```bash
13
- pip install jarvisplot
12
+ ```bash
13
+ pip install Jarvis-PLOT
14
+ ```
15
+
16
+ The PyPI distribution name is now `Jarvis-PLOT`.
17
+ The Python import package and entrypoint remain unchanged:
18
+
19
+ ```python
20
+ import jarvisplot
21
+ ```
22
+
23
+ ```bash
24
+ jplot path/to/config.yaml
25
+ ```
26
+
27
+ If you have an older environment that still uses the historical package name, replace:
28
+
29
+ ```bash
30
+ pip uninstall jarvisplot
31
+ pip install Jarvis-PLOT
14
32
  ```
15
33
 
16
34
  ## Command-Line Usage
@@ -21,7 +39,7 @@ Display help information:
21
39
  jplot -h
22
40
  ```
23
41
 
24
- Run JarvisPLOT with one or more YAML configuration files:
42
+ Run Jarvis-PLOT with one or more YAML configuration files:
25
43
 
26
44
  ```bash
27
45
  jplot path/to/config.yaml
@@ -36,7 +54,7 @@ jplot path/to/config.yaml --rebuild-cache
36
54
  ### Project Workdir and Cache
37
55
 
38
56
  - You can set `project.workdir` in YAML.
39
- - If `output.dir` is omitted, JarvisPLOT now defaults to `<workdir>/plots/`.
57
+ - If `output.dir` is omitted, Jarvis-PLOT defaults to `<workdir>/plots/`.
40
58
  - Data cache is stored in `<workdir>/.cache/`.
41
59
  - Profiling pipelines are prebuilt once and reused from cache when source fingerprint and profile settings are unchanged.
42
60
  - Profiling uses a fast two-stage grid reduction (`pregrid` + render `bin`) for large datasets.
@@ -56,7 +74,7 @@ jplot ./bin/SUSYRun2_GEWMSSM.yaml
56
74
 
57
75
  - Figures are saved automatically to the output paths defined in the YAML configuration.
58
76
  - Common output formats include PNG and PDF (backend-dependent).
59
- - JarvisPLOT works in headless environments (SSH, batch jobs) without any GUI backend.
77
+ - Jarvis-PLOT works in headless environments (SSH, batch jobs) without any GUI backend.
60
78
 
61
79
  ---
62
80
 
@@ -83,6 +101,19 @@ jplot ./bin/SUSYRun2_GEWMSSM.yaml
83
101
  ### Documentation
84
102
  [https://pengxuan-zhu-phys.github.io/Jarvis-Docs/](https://pengxuan-zhu-phys.github.io/Jarvis-Docs/)
85
103
 
104
+ ### Developer Docs
105
+
106
+ Tracked architecture and maintenance docs now live in `docs/dev/`.
107
+
108
+ - `docs/dev/ARCHITECTURE_OVERVIEW.md`
109
+ - `docs/dev/DATAFLOW_ARCHITECTURE.md`
110
+ - `docs/dev/MEMORY_OPTIMIZATION_GUIDE.md`
111
+ - `docs/dev/DEVELOPER_RULES.md`
112
+ - `docs/dev/MEMTRACE_SYSTEM.md`
113
+ - `docs/dev/CONTRIBUTION_GUIDE.md`
114
+
115
+ These documents describe the 1.3.0 selection-table pipeline architecture that 1.3.1 consolidates. Future work should preserve the narrow-table dataflow and must not reintroduce wide-table profiling or cache payloads.
116
+
86
117
  ---
87
118
 
88
119
  ## License
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env python3
2
+
3
+ #!/usr/bin/env python3
4
+ from __future__ import annotations
5
+ from typing import Any, Callable, Dict, Optional
6
+ import threading
7
+
8
+ class SharedContent:
9
+ """
10
+ Session-level variable shared storage for all Figures (supports lazy evaluation and updates).
11
+ - register(name, compute_fn): Register a lazy evaluation function.
12
+ - get(name): Return cached value if available; otherwise, compute using compute_fn and cache the result.
13
+ - update(name, value): Explicitly write or overwrite a value.
14
+ - invalidate(name=None): Invalidate a specific entry or all entries.
15
+ - stats(): Diagnostic information.
16
+ """
17
+ def __init__(self, seed: Optional[int] = None, logger: Any = None):
18
+ self._logger = logger
19
+ self._seed = seed
20
+ self._store: Dict[str, Any] = {}
21
+ self._registry: Dict[str, Callable[[SharedContent], Any]] = {}
22
+ self._release_registry: Dict[str, Callable[[], None]] = {}
23
+ self._remaining_uses: Dict[str, int] = {}
24
+ self._lock = threading.RLock()
25
+
26
+ # ---- 懒计算接口 ----
27
+ def register(
28
+ self,
29
+ name: str,
30
+ compute_fn: Callable[[SharedContent], Any],
31
+ release_fn: Optional[Callable[[], None]] = None,
32
+ ) -> None:
33
+ with self._lock:
34
+ self._registry[name] = compute_fn
35
+ if release_fn is not None:
36
+ self._release_registry[name] = release_fn
37
+ if self._logger:
38
+ self._logger.debug(f"SharedContent: register -> {name}")
39
+
40
+ def get(self, name: str) -> Any:
41
+ with self._lock:
42
+ if name in self._store:
43
+ return self._store[name]
44
+ if name in self._registry:
45
+ if self._logger:
46
+ self._logger.debug(f"SharedContent: MISS -> {name}; computing...")
47
+ val = self._registry[name](self)
48
+ self._store[name] = val
49
+ return val
50
+ if self._logger:
51
+ self._logger.debug(f"SharedContent: MISS (no registry) -> {name}; returning None")
52
+ return None
53
+
54
+ def update(self, name: str, value: Any, release_fn: Optional[Callable[[], None]] = None) -> None:
55
+ with self._lock:
56
+ self._store[name] = value
57
+ if release_fn is not None:
58
+ self._release_registry[name] = release_fn
59
+ if self._logger:
60
+ self._logger.debug(f"SharedContent: update -> {name}")
61
+
62
+ def invalidate(self, name: Optional[str] = None) -> None:
63
+ with self._lock:
64
+ if name is None:
65
+ for key, release_fn in list(self._release_registry.items()):
66
+ try:
67
+ release_fn()
68
+ except Exception:
69
+ pass
70
+ self._store.clear()
71
+ if self._logger:
72
+ self._logger.debug("SharedContent: invalidate ALL")
73
+ else:
74
+ self._store.pop(name, None)
75
+ release_fn = self._release_registry.get(name)
76
+ if release_fn is not None:
77
+ try:
78
+ release_fn()
79
+ except Exception:
80
+ pass
81
+ if self._logger:
82
+ self._logger.debug(f"SharedContent: invalidate -> {name}")
83
+
84
+ def set_usage_plan(self, counts: Dict[str, int]) -> None:
85
+ with self._lock:
86
+ self._remaining_uses = {}
87
+ for name, count in counts.items():
88
+ try:
89
+ ival = int(count)
90
+ except Exception:
91
+ continue
92
+ if ival >= 0:
93
+ self._remaining_uses[str(name)] = ival
94
+ if self._logger:
95
+ self._logger.debug(
96
+ "SharedContent: usage plan loaded -> {}".format(
97
+ ", ".join(f"{k}:{v}" for k, v in sorted(self._remaining_uses.items()))
98
+ )
99
+ )
100
+
101
+ def remaining_uses(self, name: str) -> int:
102
+ with self._lock:
103
+ return int(self._remaining_uses.get(str(name), 0))
104
+
105
+ def consume(self, name: str, amount: int = 1) -> int:
106
+ key = str(name)
107
+ with self._lock:
108
+ if key not in self._remaining_uses:
109
+ return 0
110
+ try:
111
+ dec = max(int(amount), 0)
112
+ except Exception:
113
+ dec = 0
114
+ remain = max(int(self._remaining_uses.get(key, 0)) - dec, 0)
115
+ self._remaining_uses[key] = remain
116
+ if self._logger:
117
+ self._logger.debug(f"SharedContent: consume -> {key}, remain={remain}")
118
+ if remain == 0:
119
+ self.invalidate(key)
120
+ return remain
121
+
122
+ def stats(self) -> Dict[str, int]:
123
+ with self._lock:
124
+ return {
125
+ "cached": len(self._store),
126
+ "registered": len(self._registry),
127
+ "tracked": len(self._remaining_uses),
128
+ }
129
+
130
+ class DataContext:
131
+ """
132
+ Inject it into the facade of each Figure to isolate the Figure from the core implementation.
133
+ Figures use it to get, update, register, and invalidate shared content.
134
+ """
135
+ def __init__(self, shared: SharedContent):
136
+ self._shared = shared
137
+
138
+ def get(self, name: str) -> Any:
139
+ return self._shared.get(name)
140
+
141
+ def update(self, name: str, value: Any, release_fn: Optional[Callable[[], None]] = None) -> None:
142
+ self._shared.update(name, value, release_fn=release_fn)
143
+
144
+ def register(
145
+ self,
146
+ name: str,
147
+ compute_fn: Callable[[SharedContent], Any],
148
+ release_fn: Optional[Callable[[], None]] = None,
149
+ ) -> None:
150
+ self._shared.register(name, compute_fn, release_fn=release_fn)
151
+
152
+ def invalidate(self, name: Optional[str] = None) -> None:
153
+ self._shared.invalidate(name)
154
+
155
+ def set_usage_plan(self, counts: Dict[str, int]) -> None:
156
+ self._shared.set_usage_plan(counts)
157
+
158
+ def remaining_uses(self, name: str) -> int:
159
+ return self._shared.remaining_uses(name)
160
+
161
+ def consume(self, name: str, amount: int = 1) -> int:
162
+ return self._shared.consume(name, amount=amount)
163
+
164
+ def stats(self) -> Dict[str, int]:
165
+ return self._shared.stats()