jarvisplot 1.0.1__py3-none-any.whl
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.
- jarvisplot/Figure/adapters.py +773 -0
- jarvisplot/Figure/cards/std_axes_adapter_config.json +23 -0
- jarvisplot/Figure/data_pipelines.py +87 -0
- jarvisplot/Figure/figure.py +1573 -0
- jarvisplot/Figure/helper.py +217 -0
- jarvisplot/Figure/load_data.py +252 -0
- jarvisplot/__init__.py +0 -0
- jarvisplot/cards/a4paper/1x1/ternary.json +6 -0
- jarvisplot/cards/a4paper/2x1/rect.json +106 -0
- jarvisplot/cards/a4paper/2x1/rect5x1.json +344 -0
- jarvisplot/cards/a4paper/2x1/rect_cmap.json +181 -0
- jarvisplot/cards/a4paper/2x1/ternary.json +139 -0
- jarvisplot/cards/a4paper/2x1/ternary_cmap.json +189 -0
- jarvisplot/cards/a4paper/4x1/rect.json +106 -0
- jarvisplot/cards/a4paper/4x1/rect_cmap.json +174 -0
- jarvisplot/cards/a4paper/4x1/ternary.json +139 -0
- jarvisplot/cards/a4paper/4x1/ternary_cmap.json +189 -0
- jarvisplot/cards/args.json +50 -0
- jarvisplot/cards/colors/colormaps.json +140 -0
- jarvisplot/cards/default/output.json +11 -0
- jarvisplot/cards/gambit/1x1/ternary.json +6 -0
- jarvisplot/cards/gambit/2x1/rect_cmap.json +200 -0
- jarvisplot/cards/gambit/2x1/ternary.json +139 -0
- jarvisplot/cards/gambit/2x1/ternary_cmap.json +205 -0
- jarvisplot/cards/icons/JarvisHEP.png +0 -0
- jarvisplot/cards/icons/gambit.png +0 -0
- jarvisplot/cards/icons/gambit_small.png +0 -0
- jarvisplot/cards/style_preference.json +23 -0
- jarvisplot/cli.py +64 -0
- jarvisplot/client.py +6 -0
- jarvisplot/config.py +69 -0
- jarvisplot/core.py +237 -0
- jarvisplot/data_loader.py +441 -0
- jarvisplot/inner_func.py +162 -0
- jarvisplot/utils/__init__.py +0 -0
- jarvisplot/utils/cmaps.py +258 -0
- jarvisplot/utils/interpolator.py +377 -0
- jarvisplot-1.0.1.dist-info/METADATA +80 -0
- jarvisplot-1.0.1.dist-info/RECORD +42 -0
- jarvisplot-1.0.1.dist-info/WHEEL +5 -0
- jarvisplot-1.0.1.dist-info/entry_points.txt +2 -0
- jarvisplot-1.0.1.dist-info/top_level.txt +1 -0
jarvisplot/inner_func.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from sympy.core import numbers as SCNum
|
|
5
|
+
import sympy
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
_AllSCNum = (
|
|
9
|
+
SCNum.Float,
|
|
10
|
+
SCNum.Number,
|
|
11
|
+
SCNum.Rational,
|
|
12
|
+
SCNum.Integer,
|
|
13
|
+
SCNum.Infinity,
|
|
14
|
+
SCNum.AlgebraicNumber,
|
|
15
|
+
SCNum.RealNumber,
|
|
16
|
+
SCNum.Zero,
|
|
17
|
+
SCNum.One,
|
|
18
|
+
SCNum.NegativeOne,
|
|
19
|
+
SCNum.NegativeInfinity,
|
|
20
|
+
SCNum.Exp1,
|
|
21
|
+
SCNum.Pi,
|
|
22
|
+
float,
|
|
23
|
+
int,
|
|
24
|
+
np.float16,
|
|
25
|
+
np.float32,
|
|
26
|
+
np.float64,
|
|
27
|
+
np.int8,
|
|
28
|
+
np.int16,
|
|
29
|
+
np.int32,
|
|
30
|
+
np.int64
|
|
31
|
+
)
|
|
32
|
+
_Inner_FCs = {
|
|
33
|
+
# Natural Logarithm
|
|
34
|
+
"log": sympy.log,
|
|
35
|
+
"exp": sympy.exp,
|
|
36
|
+
"ln": sympy.ln,
|
|
37
|
+
# Triangle Function
|
|
38
|
+
"sin": sympy.sin,
|
|
39
|
+
"cos": sympy.cos,
|
|
40
|
+
"tan": sympy.tan,
|
|
41
|
+
"sec": sympy.sec,
|
|
42
|
+
"csc": sympy.csc,
|
|
43
|
+
"cot": sympy.cot,
|
|
44
|
+
"sinc": sympy.sinc,
|
|
45
|
+
"asin": sympy.asin,
|
|
46
|
+
"acos": sympy.acos,
|
|
47
|
+
"atan": sympy.atan,
|
|
48
|
+
"asec": sympy.asec,
|
|
49
|
+
"acsc": sympy.acsc,
|
|
50
|
+
"acot": sympy.acot,
|
|
51
|
+
"atan2":sympy.atan2,
|
|
52
|
+
# Hyperbolic Function
|
|
53
|
+
"sinh": sympy.sinh,
|
|
54
|
+
"cosh": sympy.cosh,
|
|
55
|
+
"tanh": sympy.tanh,
|
|
56
|
+
"sech": sympy.sech,
|
|
57
|
+
"csch": sympy.csch,
|
|
58
|
+
"coth": sympy.coth,
|
|
59
|
+
"asinh": sympy.asinh,
|
|
60
|
+
"acosh": sympy.acosh,
|
|
61
|
+
"atanh": sympy.atanh,
|
|
62
|
+
"acoth": sympy.acoth,
|
|
63
|
+
"asech": sympy.asech,
|
|
64
|
+
"acsch": sympy.acsch,
|
|
65
|
+
# General Math
|
|
66
|
+
"sqrt": sympy.sqrt,
|
|
67
|
+
"Min": sympy.Min,
|
|
68
|
+
"Max": sympy.Max,
|
|
69
|
+
"root": sympy.root,
|
|
70
|
+
"Abs": sympy.Abs,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
_Constant = {
|
|
74
|
+
"Pi": sympy.pi,
|
|
75
|
+
"E": sympy.E,
|
|
76
|
+
"Inf": np.inf
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# External function hooks (e.g. user-defined / lazy-loaded interpolators)
|
|
80
|
+
# These are injected into the expression runtime via `update_funcs`.
|
|
81
|
+
#
|
|
82
|
+
# Usage:
|
|
83
|
+
# - core/context code can call `set_external_funcs({...})` once after building ctx.
|
|
84
|
+
# - or provide a getter with `set_external_funcs_getter(lambda: {...})` if the set may change.
|
|
85
|
+
# - values must be callables (LazyCallable is OK).
|
|
86
|
+
_EXTERNAL_FCS = {}
|
|
87
|
+
_EXTERNAL_FCS_GETTER = None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def set_external_funcs(funcs: dict) -> None:
|
|
91
|
+
"""Register external functions to be injected by `update_funcs`.
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
funcs:
|
|
96
|
+
Mapping of name -> callable (LazyCallable is acceptable).
|
|
97
|
+
"""
|
|
98
|
+
global _EXTERNAL_FCS
|
|
99
|
+
_EXTERNAL_FCS = dict(funcs) if funcs is not None else {}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def set_external_funcs_getter(getter) -> None:
|
|
103
|
+
"""Register a callable that returns a dict of external functions.
|
|
104
|
+
|
|
105
|
+
The getter should return a `dict[str, callable]`.
|
|
106
|
+
"""
|
|
107
|
+
global _EXTERNAL_FCS_GETTER
|
|
108
|
+
_EXTERNAL_FCS_GETTER = getter
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def clear_external_funcs() -> None:
|
|
112
|
+
"""Clear any registered external functions/getter."""
|
|
113
|
+
global _EXTERNAL_FCS, _EXTERNAL_FCS_GETTER
|
|
114
|
+
_EXTERNAL_FCS = {}
|
|
115
|
+
_EXTERNAL_FCS_GETTER = None
|
|
116
|
+
|
|
117
|
+
def Gauss(xx, mean, err):
|
|
118
|
+
prob = sympy.exp(-0.5 * ((xx - mean) / err)**2)
|
|
119
|
+
return prob
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# def Gauss(xx, mean, err):
|
|
123
|
+
# from math import sqrt, pi, exp
|
|
124
|
+
# # prob = 1./ (err * sqrt(2 * pi)) * exp(-0.5*((xx - mean)/err)**2)
|
|
125
|
+
# prob = exp(-0.5*((xx - mean)/err)**2)
|
|
126
|
+
# return prob
|
|
127
|
+
|
|
128
|
+
def Normal(xx, mean, err):
|
|
129
|
+
# from math import sqrt, pi, exp
|
|
130
|
+
prob = 1./ (err * sympy.sqrt(2 * sympy.pi)) * sympy.exp(-0.5*((xx - mean)/err)**2)
|
|
131
|
+
return prob
|
|
132
|
+
|
|
133
|
+
def LogGauss(xx, mean, err):
|
|
134
|
+
prob = -0.5*((xx - mean)/err)**2
|
|
135
|
+
return prob
|
|
136
|
+
|
|
137
|
+
def update_funcs(funcs):
|
|
138
|
+
funcs['sympy'] = sympy
|
|
139
|
+
funcs['Gauss'] = Gauss
|
|
140
|
+
funcs['LogGauss'] = LogGauss
|
|
141
|
+
funcs['Normal'] = Normal
|
|
142
|
+
funcs['Heaviside'] = sympy.Heaviside
|
|
143
|
+
|
|
144
|
+
# Built-in functions
|
|
145
|
+
funcs.update(_Inner_FCs)
|
|
146
|
+
|
|
147
|
+
# External functions (e.g. lazy-loaded interpolators). External takes priority.
|
|
148
|
+
try:
|
|
149
|
+
if _EXTERNAL_FCS_GETTER is not None:
|
|
150
|
+
ext = _EXTERNAL_FCS_GETTER() or {}
|
|
151
|
+
funcs.update(ext)
|
|
152
|
+
elif _EXTERNAL_FCS:
|
|
153
|
+
funcs.update(_EXTERNAL_FCS)
|
|
154
|
+
except Exception:
|
|
155
|
+
# Never fail expression evaluation because external injection failed.
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
return funcs
|
|
159
|
+
|
|
160
|
+
def update_const(vars):
|
|
161
|
+
vars.update(_Constant)
|
|
162
|
+
return vars
|
|
File without changes
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
JarvisPLOT colormap loader: JSON-only, no built-ins.
|
|
5
|
+
Public API:
|
|
6
|
+
- setup(json_path: str|None = None, force: bool = True) -> dict
|
|
7
|
+
- register_from_json(json_path: str|os.PathLike, force: bool = True) -> dict
|
|
8
|
+
- list_available() -> list[str]
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
from typing import Any, Dict, List, Tuple, Optional, Union
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
|
|
16
|
+
import matplotlib as mpl
|
|
17
|
+
import matplotlib.cm as mcm
|
|
18
|
+
from matplotlib.colors import ListedColormap, LinearSegmentedColormap, Colormap
|
|
19
|
+
|
|
20
|
+
import inspect
|
|
21
|
+
import matplotlib.pyplot as plt
|
|
22
|
+
def _register(name: str, cmap: Colormap, force: bool) -> bool:
|
|
23
|
+
"""
|
|
24
|
+
Try multiple registration paths depending on Matplotlib version:
|
|
25
|
+
1) mpl.colormaps.register(...), passing 'name' and 'override' only if supported
|
|
26
|
+
2) plt.register_cmap(name=..., cmap=...)
|
|
27
|
+
3) update legacy cm.cmap_d if present
|
|
28
|
+
Returns True on success.
|
|
29
|
+
"""
|
|
30
|
+
name = str(name)
|
|
31
|
+
# Path 1: modern registry
|
|
32
|
+
try:
|
|
33
|
+
reg = getattr(mpl.colormaps, "register", None)
|
|
34
|
+
if reg is not None:
|
|
35
|
+
kwargs = {}
|
|
36
|
+
try:
|
|
37
|
+
sig = inspect.signature(reg)
|
|
38
|
+
if "name" in sig.parameters:
|
|
39
|
+
kwargs["name"] = name
|
|
40
|
+
else:
|
|
41
|
+
try:
|
|
42
|
+
cmap.name = name
|
|
43
|
+
except Exception:
|
|
44
|
+
pass
|
|
45
|
+
if "override" in sig.parameters:
|
|
46
|
+
kwargs["override"] = bool(force)
|
|
47
|
+
except Exception:
|
|
48
|
+
# If signature introspection fails, try safest call (cmap only).
|
|
49
|
+
try:
|
|
50
|
+
cmap.name = name
|
|
51
|
+
except Exception:
|
|
52
|
+
pass
|
|
53
|
+
# Call register
|
|
54
|
+
if kwargs:
|
|
55
|
+
reg(cmap, **kwargs)
|
|
56
|
+
else:
|
|
57
|
+
reg(cmap)
|
|
58
|
+
ok = True
|
|
59
|
+
else:
|
|
60
|
+
ok = False
|
|
61
|
+
except Exception:
|
|
62
|
+
ok = False
|
|
63
|
+
# Path 2: pyplot register_cmap
|
|
64
|
+
if not ok:
|
|
65
|
+
try:
|
|
66
|
+
plt.register_cmap(name=name, cmap=cmap)
|
|
67
|
+
ok = True
|
|
68
|
+
except Exception:
|
|
69
|
+
ok = False
|
|
70
|
+
# Path 3: legacy dict
|
|
71
|
+
if not ok and hasattr(mcm, "cmap_d"):
|
|
72
|
+
try:
|
|
73
|
+
mcm.cmap_d[name] = cmap
|
|
74
|
+
ok = True
|
|
75
|
+
except Exception:
|
|
76
|
+
ok = False
|
|
77
|
+
# final visibility sanity check
|
|
78
|
+
try:
|
|
79
|
+
visible = (name in list(mpl.colormaps)) or (hasattr(mcm, "cmap_d") and name in mcm.cmap_d)
|
|
80
|
+
except Exception:
|
|
81
|
+
visible = False
|
|
82
|
+
return bool(ok and visible)
|
|
83
|
+
|
|
84
|
+
class CmapSpecError(Exception):
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
def _norm_color_list(seq):
|
|
88
|
+
"""
|
|
89
|
+
Normalize a list of colors into formats Matplotlib accepts.
|
|
90
|
+
Accepts:
|
|
91
|
+
- hex strings "#RRGGBB" / "#RRGGBBAA"
|
|
92
|
+
- RGB/RGBA tuples or lists in 0..1 or 0..255
|
|
93
|
+
Returns a new list; raises CmapSpecError on invalid entries.
|
|
94
|
+
"""
|
|
95
|
+
out = []
|
|
96
|
+
for c in list(seq):
|
|
97
|
+
if isinstance(c, str):
|
|
98
|
+
out.append(c)
|
|
99
|
+
continue
|
|
100
|
+
if isinstance(c, (list, tuple)):
|
|
101
|
+
vals = list(c)
|
|
102
|
+
if not vals:
|
|
103
|
+
raise CmapSpecError("empty color tuple")
|
|
104
|
+
if all(isinstance(v, (int, float)) for v in vals):
|
|
105
|
+
# If any component >1, assume 0..255 and scale
|
|
106
|
+
mx = max(abs(float(v)) for v in vals)
|
|
107
|
+
if mx > 1.0:
|
|
108
|
+
vals = [float(v)/255.0 for v in vals]
|
|
109
|
+
out.append(tuple(vals))
|
|
110
|
+
continue
|
|
111
|
+
raise CmapSpecError(f"unsupported color value: {c!r}")
|
|
112
|
+
return out
|
|
113
|
+
|
|
114
|
+
JsonLike = Union[Dict[str, Any], List[Dict[str, Any]]]
|
|
115
|
+
|
|
116
|
+
def _read_json(path: Path) -> Optional[JsonLike]:
|
|
117
|
+
try:
|
|
118
|
+
with path.open("r", encoding="utf-8") as f:
|
|
119
|
+
return json.load(f)
|
|
120
|
+
except Exception:
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
def _to_linear(name: str, colors: List[Any]) -> Optional[LinearSegmentedColormap]:
|
|
124
|
+
try:
|
|
125
|
+
# Accept either ["#hex", ...] or [[pos, color], ...]
|
|
126
|
+
if colors and isinstance(colors[0], (list, tuple)) and len(colors[0]) == 2 and not isinstance(colors[0][1], (list, tuple)) and not (isinstance(colors[0][1], str) and colors[0][1].startswith("#") or isinstance(colors[0][1], str)):
|
|
127
|
+
# The above heuristic is too brittle; simplify by explicit shape check below
|
|
128
|
+
pass
|
|
129
|
+
except Exception:
|
|
130
|
+
pass
|
|
131
|
+
try:
|
|
132
|
+
if colors and isinstance(colors[0], (list, tuple)) and len(colors[0]) == 2:
|
|
133
|
+
pts: List[Tuple[float, Any]] = []
|
|
134
|
+
for p, c in colors:
|
|
135
|
+
try:
|
|
136
|
+
pp = float(p)
|
|
137
|
+
except Exception:
|
|
138
|
+
continue
|
|
139
|
+
pp = max(0.0, min(1.0, pp))
|
|
140
|
+
pts.append((pp, c))
|
|
141
|
+
pts.sort(key=lambda t: t[0])
|
|
142
|
+
# Normalize color payload
|
|
143
|
+
pts = [(p, _norm_color_list([c])[0]) for (p, c) in pts]
|
|
144
|
+
return LinearSegmentedColormap.from_list(name, pts)
|
|
145
|
+
# Plain list of colors
|
|
146
|
+
return LinearSegmentedColormap.from_list(name, _norm_color_list(list(colors)))
|
|
147
|
+
except Exception:
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
def _to_listed(name: str, colors: List[Any]) -> Optional[ListedColormap]:
|
|
151
|
+
try:
|
|
152
|
+
return ListedColormap(_norm_color_list(list(colors)), name=name)
|
|
153
|
+
except Exception:
|
|
154
|
+
return None
|
|
155
|
+
|
|
156
|
+
def _build(spec: Dict[str, Any]) -> Colormap:
|
|
157
|
+
name = spec.get("name")
|
|
158
|
+
colors = spec.get("colors")
|
|
159
|
+
if not isinstance(name, str) or not name.strip():
|
|
160
|
+
raise CmapSpecError("missing or invalid 'name'")
|
|
161
|
+
if not isinstance(colors, (list, tuple)) or not colors:
|
|
162
|
+
raise CmapSpecError(f"cmap {name!r}: missing or invalid 'colors'")
|
|
163
|
+
t = str(spec.get("type", "listed")).lower()
|
|
164
|
+
if t in ("linear", "segmented", "continuous"):
|
|
165
|
+
cm = _to_linear(name, list(colors))
|
|
166
|
+
else:
|
|
167
|
+
cm = _to_listed(name, list(colors))
|
|
168
|
+
if cm is None:
|
|
169
|
+
raise CmapSpecError(f"failed to build colormap {name!r}")
|
|
170
|
+
return cm
|
|
171
|
+
|
|
172
|
+
def register_from_json(json_path: Union[str, os.PathLike], force: bool = True) -> Dict[str, Any]:
|
|
173
|
+
p = Path(json_path).expanduser().resolve()
|
|
174
|
+
reg: List[str] = []
|
|
175
|
+
fail: List[str] = []
|
|
176
|
+
err: Dict[str, str] = {}
|
|
177
|
+
try:
|
|
178
|
+
with p.open("r", encoding="utf-8") as f:
|
|
179
|
+
data = json.load(f)
|
|
180
|
+
except Exception as e:
|
|
181
|
+
return {"registered": [], "failed": [], "errors": {str(p): f"read json failed: {e}"}, "path": str(p)}
|
|
182
|
+
# Accept three shapes: dict with 'colormaps', single dict, or top-level list
|
|
183
|
+
specs: List[Dict[str, Any]] = []
|
|
184
|
+
aliases: Dict[str, str] = {}
|
|
185
|
+
if isinstance(data, dict):
|
|
186
|
+
if "name" in data and "colors" in data:
|
|
187
|
+
specs.append(data)
|
|
188
|
+
else:
|
|
189
|
+
cmaps_list = data.get("colormaps")
|
|
190
|
+
if isinstance(cmaps_list, list):
|
|
191
|
+
for item in cmaps_list:
|
|
192
|
+
if isinstance(item, dict):
|
|
193
|
+
specs.append(item)
|
|
194
|
+
aliases = data.get("aliases", {}) or {}
|
|
195
|
+
elif isinstance(data, list):
|
|
196
|
+
for item in data:
|
|
197
|
+
if isinstance(item, dict):
|
|
198
|
+
specs.append(item)
|
|
199
|
+
# Build + register
|
|
200
|
+
for spec in specs:
|
|
201
|
+
try:
|
|
202
|
+
name = spec.get("name")
|
|
203
|
+
colors = spec.get("colors")
|
|
204
|
+
ctype = str(spec.get("type", "listed")).lower()
|
|
205
|
+
if not isinstance(name, str) or not name or not isinstance(colors, (list, tuple)) or not colors:
|
|
206
|
+
raise ValueError("invalid spec (need 'name' and non-empty 'colors')")
|
|
207
|
+
if ctype in ("linear", "segmented", "continuous"):
|
|
208
|
+
cm = _to_linear(name, list(colors))
|
|
209
|
+
else:
|
|
210
|
+
cm = _to_listed(name, list(colors))
|
|
211
|
+
if cm is None:
|
|
212
|
+
raise ValueError("failed to build colormap")
|
|
213
|
+
if _register(name, cm, force=force):
|
|
214
|
+
reg.append(name)
|
|
215
|
+
try:
|
|
216
|
+
cm_r = cm.reversed()
|
|
217
|
+
if _register(f"{name}_r", cm_r, force=force):
|
|
218
|
+
reg.append(f"{name}_r")
|
|
219
|
+
except Exception:
|
|
220
|
+
pass
|
|
221
|
+
else:
|
|
222
|
+
fail.append(name)
|
|
223
|
+
except Exception as e:
|
|
224
|
+
nm = spec.get("name", "<missing-name>")
|
|
225
|
+
fail.append(nm)
|
|
226
|
+
err[nm] = str(e)
|
|
227
|
+
# aliases (if any): only when target is visible
|
|
228
|
+
for alias, target in (aliases.items() if isinstance(aliases, dict) else []):
|
|
229
|
+
try:
|
|
230
|
+
base = mpl.colormaps.get(target)
|
|
231
|
+
except Exception:
|
|
232
|
+
base = None
|
|
233
|
+
if base is None and hasattr(mcm, "cmap_d"):
|
|
234
|
+
base = mcm.cmap_d.get(target)
|
|
235
|
+
if base is None:
|
|
236
|
+
fail.append(alias)
|
|
237
|
+
err[alias] = f"alias target not found: {target}"
|
|
238
|
+
elif _register(alias, base, force=force):
|
|
239
|
+
reg.append(alias)
|
|
240
|
+
else:
|
|
241
|
+
fail.append(alias)
|
|
242
|
+
return {"registered": reg, "failed": fail, "errors": err, "path": str(p)}
|
|
243
|
+
|
|
244
|
+
def setup(json_path: Optional[Union[str, os.PathLike]] = None, force: bool = True) -> Dict[str, Any]:
|
|
245
|
+
src = json_path
|
|
246
|
+
if not src:
|
|
247
|
+
return {"registered": [], "failed": [], "errors": {}, "path": None}
|
|
248
|
+
return register_from_json(src, force=force)
|
|
249
|
+
|
|
250
|
+
def list_available() -> List[str]:
|
|
251
|
+
try:
|
|
252
|
+
return list(mpl.colormaps)
|
|
253
|
+
except Exception:
|
|
254
|
+
if hasattr(mcm, "cmap_d"):
|
|
255
|
+
return sorted(mcm.cmap_d.keys())
|
|
256
|
+
return []
|
|
257
|
+
|
|
258
|
+
__all__ = ["setup", "register_from_json", "list_available"]
|