craceplot 0.1.0__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.
- craceplot/__init__.py +60 -0
- craceplot/_containers/__init__.py +0 -0
- craceplot/_containers/_core.py +1602 -0
- craceplot/_containers/_draw.py +104 -0
- craceplot/_inst/examples/acotsp/crace-3240078.stdout +838 -0
- craceplot/_inst/examples/acotsp/crace.stderr +8 -0
- craceplot/_inst/examples/acotsp/crace.test +216 -0
- craceplot/_inst/examples/acotsp/crace.train +622 -0
- craceplot/_inst/examples/cats200/crace-2814581.stdout +1665 -0
- craceplot/_inst/examples/cats200/crace.stderr +10 -0
- craceplot/_inst/examples/cats200/crace.test +1010 -0
- craceplot/_inst/examples/cats200/crace.train +655 -0
- craceplot/_plots/__init__.py +8 -0
- craceplot/_plots/_core.py +80 -0
- craceplot/_plots/parameters.py +1805 -0
- craceplot/_plots/quality.py +1406 -0
- craceplot/_scripts/__init__.py +4 -0
- craceplot/_scripts/_main.py +132 -0
- craceplot/_scripts/_utils.py +66 -0
- craceplot/_scripts/main +3 -0
- craceplot/_scripts/open_guide +12 -0
- craceplot/_settings/_description.py +168 -0
- craceplot/_settings/_options.json +299 -0
- craceplot/_utils/__init__.py +0 -0
- craceplot/_utils/_base.py +196 -0
- craceplot/_utils/_const.py +8 -0
- craceplot/_utils/_crace.py +42 -0
- craceplot/_utils/_format.py +191 -0
- craceplot/_vergit.py +2 -0
- craceplot/_version.py +24 -0
- craceplot/_vignettes/guide.ipynb +18943 -0
- craceplot-0.1.0.dist-info/METADATA +369 -0
- craceplot-0.1.0.dist-info/RECORD +61 -0
- craceplot-0.1.0.dist-info/WHEEL +5 -0
- craceplot-0.1.0.dist-info/entry_points.txt +2 -0
- craceplot-0.1.0.dist-info/licenses/LICENSE.md +21 -0
- craceplot-0.1.0.dist-info/top_level.txt +2 -0
- docs/_config.yml +51 -0
- docs/_static/css/custom.css +168 -0
- docs/_static/js/navbar.js +40 -0
- docs/_toc.yml +5 -0
- docs/index.md +4 -0
- docs/references/404.md +14 -0
- docs/references/authors.md +46 -0
- docs/references/citiation.bib +17 -0
- docs/references/functions/index.md +5 -0
- docs/references/functions/param_boxplot.ipynb +19 -0
- docs/references/index.md +44 -0
- docs/references/license.md +21 -0
- docs/references/others/favicon.ico +0 -0
- docs/references/others/favicon_io/about.txt +6 -0
- docs/references/others/favicon_io/android-chrome-192x192.png +0 -0
- docs/references/others/favicon_io/android-chrome-512x512.png +0 -0
- docs/references/others/favicon_io/apple-touch-icon.png +0 -0
- docs/references/others/favicon_io/favicon-16x16.png +0 -0
- docs/references/others/favicon_io/favicon-32x32.png +0 -0
- docs/references/others/favicon_io/site.webmanifest +1 -0
- docs/references/others/logo.png +0 -0
- docs/references/others/logo1.png +0 -0
- docs/references/plots/acotsp-parallelcoord-all.png +0 -0
- docs/requirements.txt +5 -0
|
File without changes
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# craceplot._core._utils.py
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import copy
|
|
5
|
+
import inspect
|
|
6
|
+
import numpy as np
|
|
7
|
+
from functools import wraps
|
|
8
|
+
from typing import get_origin, get_args, Union, Literal, TypeVar, ParamSpec
|
|
9
|
+
|
|
10
|
+
P = ParamSpec("P")
|
|
11
|
+
R = TypeVar("R")
|
|
12
|
+
|
|
13
|
+
dispatch_table_param = {}
|
|
14
|
+
methods_table_param = {}
|
|
15
|
+
def register_param(names):
|
|
16
|
+
if isinstance(names, str):
|
|
17
|
+
names = [names]
|
|
18
|
+
|
|
19
|
+
primary_name = names[0]
|
|
20
|
+
|
|
21
|
+
def decorator(func):
|
|
22
|
+
for name in names:
|
|
23
|
+
if name not in dispatch_table_param.keys():
|
|
24
|
+
dispatch_table_param[name] = func
|
|
25
|
+
if name not in methods_table_param.keys():
|
|
26
|
+
methods_table_param[name] = primary_name
|
|
27
|
+
return func
|
|
28
|
+
return decorator
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
dispatch_table_exps = {}
|
|
32
|
+
methods_table_exps = {}
|
|
33
|
+
def register_exps(names):
|
|
34
|
+
if isinstance(names, str):
|
|
35
|
+
names = [names]
|
|
36
|
+
|
|
37
|
+
primary_name = names[0]
|
|
38
|
+
|
|
39
|
+
def decorator(func):
|
|
40
|
+
for name in names:
|
|
41
|
+
if name not in dispatch_table_exps.keys():
|
|
42
|
+
dispatch_table_exps[name] = func
|
|
43
|
+
if name not in methods_table_exps.keys():
|
|
44
|
+
methods_table_exps[name] = primary_name
|
|
45
|
+
return func
|
|
46
|
+
return decorator
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def short_type(t):
|
|
50
|
+
import builtins
|
|
51
|
+
if t in vars(builtins).values(): # builtin types keep as-is
|
|
52
|
+
return t
|
|
53
|
+
|
|
54
|
+
class NewName:
|
|
55
|
+
__name__ = t.__name__
|
|
56
|
+
__qualname__ = t.__name__
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
del NewName.__module__
|
|
60
|
+
except Exception:
|
|
61
|
+
NewName.__module__ = None
|
|
62
|
+
|
|
63
|
+
return NewName
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def enforce_types(func):
|
|
67
|
+
sig = inspect.signature(func)
|
|
68
|
+
|
|
69
|
+
@wraps(func)
|
|
70
|
+
def wrapper(*args, **kwargs):
|
|
71
|
+
bound = sig.bind(*args, **kwargs)
|
|
72
|
+
bound.apply_defaults()
|
|
73
|
+
|
|
74
|
+
for name, value in bound.arguments.items():
|
|
75
|
+
if value is None:
|
|
76
|
+
continue
|
|
77
|
+
|
|
78
|
+
expected = sig.parameters[name].annotation
|
|
79
|
+
|
|
80
|
+
if expected is inspect._empty:
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
# Forward reference
|
|
84
|
+
if isinstance(expected, str):
|
|
85
|
+
if expected == "CraceResults":
|
|
86
|
+
from crace.containers.crace_results import CraceResults
|
|
87
|
+
expected = CraceResults
|
|
88
|
+
else:
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
check_type(value, expected, name)
|
|
92
|
+
|
|
93
|
+
return func(*args, **kwargs)
|
|
94
|
+
|
|
95
|
+
wrapper.__signature__ = sig
|
|
96
|
+
return wrapper
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def check_type(value, expected, name):
|
|
100
|
+
origin = get_origin(expected)
|
|
101
|
+
args = get_args(expected)
|
|
102
|
+
|
|
103
|
+
# ===== Union: try ALL branches =====
|
|
104
|
+
if origin is Union:
|
|
105
|
+
for t in args:
|
|
106
|
+
try:
|
|
107
|
+
check_type(value, t, name)
|
|
108
|
+
return # any branch ok
|
|
109
|
+
except Exception:
|
|
110
|
+
pass
|
|
111
|
+
raise TypeError(
|
|
112
|
+
f"Argument/Option '{name}' does not match any allowed type in {args}, "
|
|
113
|
+
f"got {value!r}({type(value)})"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# ===== Literal =====
|
|
117
|
+
if origin is Literal:
|
|
118
|
+
if value not in args:
|
|
119
|
+
raise ValueError(
|
|
120
|
+
f"Argument/Option '{name}' must be one of {args}, got {value!r}"
|
|
121
|
+
)
|
|
122
|
+
return
|
|
123
|
+
|
|
124
|
+
# --------------------------------
|
|
125
|
+
# list / list[T]
|
|
126
|
+
# --------------------------------
|
|
127
|
+
if origin is list or expected is list:
|
|
128
|
+
if not isinstance(value, list):
|
|
129
|
+
raise TypeError(f"Argument/Option '{name}' must be list, got {type(value)}")
|
|
130
|
+
|
|
131
|
+
if len(args) == 0:
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
elem_type = args[0]
|
|
135
|
+
|
|
136
|
+
for v in value:
|
|
137
|
+
check_type(v, elem_type, name)
|
|
138
|
+
|
|
139
|
+
return
|
|
140
|
+
|
|
141
|
+
# tuple[T, ...]
|
|
142
|
+
if origin is tuple:
|
|
143
|
+
if not isinstance(value, tuple):
|
|
144
|
+
raise TypeError(f"Argument/Option '{name}' must be tuple")
|
|
145
|
+
elem_types = args
|
|
146
|
+
if len(elem_types) == 2 and elem_types[1] is Ellipsis:
|
|
147
|
+
for v in value:
|
|
148
|
+
check_type(v, elem_types[0], name)
|
|
149
|
+
else:
|
|
150
|
+
for v, t in zip(value, elem_types):
|
|
151
|
+
check_type(v, t, name)
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
# ===== NoneType =====
|
|
155
|
+
if expected is type(None):
|
|
156
|
+
if value is not None:
|
|
157
|
+
raise TypeError(f"Argument/Option '{name}' must be None")
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
# ===== normal class =====
|
|
161
|
+
if isinstance(expected, type):
|
|
162
|
+
if not isinstance(value, expected):
|
|
163
|
+
raise TypeError(
|
|
164
|
+
f"Argument/Option '{name}' expected {expected}, got {type(value)}"
|
|
165
|
+
)
|
|
166
|
+
return
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def safe_copy(obj):
|
|
170
|
+
try:
|
|
171
|
+
return copy.deepcopy(obj)
|
|
172
|
+
except Exception:
|
|
173
|
+
return obj
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def parse_range_list(s: str):
|
|
177
|
+
"""
|
|
178
|
+
Parse '[start:stop]' or '[start:stop:step]' into list[int].
|
|
179
|
+
Python range semantics: stop is exclusive.
|
|
180
|
+
"""
|
|
181
|
+
re_format = re.compile(
|
|
182
|
+
r'^\s*\[?\s*(-?\d+)\s*:\s*(-?\d+)(?:\s*:\s*(-?\d+(?:\.\d+)?))?\s*\]?\s*$')
|
|
183
|
+
m = re_format.match(s)
|
|
184
|
+
print(s, m)
|
|
185
|
+
if not m:
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
start = int(m.group(1))
|
|
189
|
+
stop = int(m.group(2))
|
|
190
|
+
step = m.group(3)
|
|
191
|
+
|
|
192
|
+
if step is None:
|
|
193
|
+
return np.arange(start, stop+1).tolist()
|
|
194
|
+
else:
|
|
195
|
+
step = eval(step)
|
|
196
|
+
return np.arange(start, stop+step, step).tolist()
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
def _setup_crace(crace_main_custom_file=None):
|
|
5
|
+
if crace_main_custom_file is not None:
|
|
6
|
+
for name in list(sys.modules):
|
|
7
|
+
if name == "crace" or name.startswith("crace."):
|
|
8
|
+
del sys.modules[name]
|
|
9
|
+
crace_main_file = crace_main_custom_file
|
|
10
|
+
else:
|
|
11
|
+
crace_main_file = os.environ.get("CRACE_MAIN_FILE")
|
|
12
|
+
|
|
13
|
+
# case 1: if CRACE_MAIN_FILE exists
|
|
14
|
+
if crace_main_file and os.path.exists(crace_main_file):
|
|
15
|
+
# from .../crace/scripts/main.py → return .../crace
|
|
16
|
+
crace_home = os.path.dirname(os.path.dirname(os.path.dirname(crace_main_file)))
|
|
17
|
+
|
|
18
|
+
# insert crace home to path
|
|
19
|
+
if crace_home not in map(os.path.abspath, sys.path):
|
|
20
|
+
sys.path.insert(0, str(crace_home))
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
import crace
|
|
24
|
+
return crace
|
|
25
|
+
except ImportError as exc:
|
|
26
|
+
if crace_main_file:
|
|
27
|
+
raise RuntimeError(
|
|
28
|
+
f"Failed to import crace from CRACE_MAIN_FILE directory:\n{crace_home}"
|
|
29
|
+
) from exc
|
|
30
|
+
else:
|
|
31
|
+
raise RuntimeError(
|
|
32
|
+
"CRACE_MAIN_FILE is not set, and 'import crace' also failed.\n"
|
|
33
|
+
"Please set CRACE_MAIN_FILE to point to crace/scripts/main.py, \n"
|
|
34
|
+
"or provide the path to crace/scripts/main.py after --crace or -c in command line arguments."
|
|
35
|
+
) from exc
|
|
36
|
+
|
|
37
|
+
def get_crace(crace_main_file=None):
|
|
38
|
+
return _setup_crace(crace_main_file)
|
|
39
|
+
|
|
40
|
+
def get_CraceResults():
|
|
41
|
+
from crace.containers.crace_results import CraceResults
|
|
42
|
+
return CraceResults
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import pandas as pd
|
|
3
|
+
|
|
4
|
+
from craceplot._utils._const import WIDTH
|
|
5
|
+
|
|
6
|
+
_ESCAPE_RE = re.compile(r'\\([ntrfvab\\])')
|
|
7
|
+
_ESCAPE_MAP = {
|
|
8
|
+
"n": "\n",
|
|
9
|
+
"t": "\t",
|
|
10
|
+
"r": "\r",
|
|
11
|
+
"f": "\f",
|
|
12
|
+
"v": "\v",
|
|
13
|
+
"a": "\a",
|
|
14
|
+
"b": "\b",
|
|
15
|
+
"\\": "\\",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
def _decode_escape(text: str):
|
|
19
|
+
return _ESCAPE_RE.sub(lambda m: _ESCAPE_MAP[m.group(1)], text)
|
|
20
|
+
|
|
21
|
+
def _strip_ansi(s: str) -> str:
|
|
22
|
+
# CSI (colors, style, etc.)
|
|
23
|
+
s = re.sub(r'\x1b\[[0-9;]*m', '', s)
|
|
24
|
+
# OSC 8 hyperlinks: ESC ] 8 ;; ... ESC \
|
|
25
|
+
s = re.sub(r'\x1b]8;;.*?\x1b\\', '', s)
|
|
26
|
+
return s
|
|
27
|
+
|
|
28
|
+
def _ansi_wrap(text: str, width: int, hanging: int, space: bool=False):
|
|
29
|
+
"""
|
|
30
|
+
textwrap based on visible width (ANSI-aware)
|
|
31
|
+
preserve explicit newlines
|
|
32
|
+
hanging applies to wrapped lines inside each paragraph
|
|
33
|
+
"""
|
|
34
|
+
out = []
|
|
35
|
+
|
|
36
|
+
paras = text.split("\n")
|
|
37
|
+
|
|
38
|
+
for i, para in enumerate(paras):
|
|
39
|
+
if para.strip() == "":
|
|
40
|
+
out.append("")
|
|
41
|
+
continue
|
|
42
|
+
|
|
43
|
+
out.extend(_ansi_wrap_one_para(para, width, hanging, space, i==0))
|
|
44
|
+
|
|
45
|
+
return out
|
|
46
|
+
|
|
47
|
+
def _ansi_wrap_one_para(text: str, width: int, hanging: int, space: bool, init_line: bool):
|
|
48
|
+
base_indent = ""
|
|
49
|
+
|
|
50
|
+
if space:
|
|
51
|
+
text = text.replace(r"\t", "\t").expandtabs(2)
|
|
52
|
+
m = re.match(r"^ *", text)
|
|
53
|
+
base_indent = m.group(0)
|
|
54
|
+
text = text[len(base_indent):]
|
|
55
|
+
|
|
56
|
+
words = text.split()
|
|
57
|
+
lines = []
|
|
58
|
+
current = ""
|
|
59
|
+
first = True
|
|
60
|
+
|
|
61
|
+
available_width = width - len(base_indent) - hanging # available width
|
|
62
|
+
hanging_indent = base_indent + " " * hanging
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
for w in words:
|
|
66
|
+
|
|
67
|
+
test = w if not current else current + " " + w
|
|
68
|
+
|
|
69
|
+
if len(_strip_ansi(test)) > available_width:
|
|
70
|
+
if current:
|
|
71
|
+
if first and init_line:
|
|
72
|
+
lines.append(base_indent + current)
|
|
73
|
+
else:
|
|
74
|
+
lines.append(hanging_indent + current)
|
|
75
|
+
current = w
|
|
76
|
+
first = False
|
|
77
|
+
else:
|
|
78
|
+
current = test
|
|
79
|
+
|
|
80
|
+
if current:
|
|
81
|
+
if first and init_line:
|
|
82
|
+
lines.append(base_indent + current)
|
|
83
|
+
else:
|
|
84
|
+
lines.append(hanging_indent + current)
|
|
85
|
+
|
|
86
|
+
return lines
|
|
87
|
+
|
|
88
|
+
def _make_ascii_box(text: str, width: int, hanging: int):
|
|
89
|
+
"""replace xwarningbox to ascii format"""
|
|
90
|
+
# textwrap
|
|
91
|
+
lines = _ansi_wrap(text, width-2, hanging)
|
|
92
|
+
maxw = max(len(_strip_ansi(l)) for l in lines) if lines else 0
|
|
93
|
+
|
|
94
|
+
top = " +" + "-" * (maxw + 2) + "+"
|
|
95
|
+
box = [top]
|
|
96
|
+
for l in lines:
|
|
97
|
+
pad = maxw - len(_strip_ansi(l))
|
|
98
|
+
box.append(f" | {l}{' ' * pad} |")
|
|
99
|
+
box.append(top)
|
|
100
|
+
|
|
101
|
+
return "\n".join(box)
|
|
102
|
+
|
|
103
|
+
def _make_ascii_list(text: str, width: int, hanging: int):
|
|
104
|
+
"""
|
|
105
|
+
transform itemsize to ascii format
|
|
106
|
+
"""
|
|
107
|
+
ITEM_RE = re.compile(r"\\item\[\]\s*(.*?)(?=\\item\[\]|$)", re.DOTALL)
|
|
108
|
+
|
|
109
|
+
items = ITEM_RE.findall(text)
|
|
110
|
+
if not items:
|
|
111
|
+
return ""
|
|
112
|
+
|
|
113
|
+
output = []
|
|
114
|
+
for item in items:
|
|
115
|
+
item = item.strip()
|
|
116
|
+
|
|
117
|
+
wrapped = _ansi_wrap(item, width-4, hanging) # with prefix "- "
|
|
118
|
+
if wrapped:
|
|
119
|
+
# first line with prefix "- "
|
|
120
|
+
output.append(f" - {wrapped[0]}")
|
|
121
|
+
# other lines
|
|
122
|
+
for w in wrapped[1:]:
|
|
123
|
+
output.append(f" {w}")
|
|
124
|
+
else:
|
|
125
|
+
output.append("-")
|
|
126
|
+
|
|
127
|
+
return "\n".join(output)
|
|
128
|
+
|
|
129
|
+
def format_string(text: str, width: int=WIDTH, hanging: int=0, space: bool=False):
|
|
130
|
+
"""
|
|
131
|
+
- transform xwarningbox → ASCII frame
|
|
132
|
+
- transform itemize → ASCII list
|
|
133
|
+
- textwrap for other text
|
|
134
|
+
|
|
135
|
+
:param text: input string
|
|
136
|
+
:param width: max width of output string
|
|
137
|
+
:param hanging: hanging indent for wrapped lines
|
|
138
|
+
:param space: whether to preserve spaces and tabs at the beginning of each line
|
|
139
|
+
"""
|
|
140
|
+
output = []
|
|
141
|
+
pos = 0
|
|
142
|
+
|
|
143
|
+
# search patterns: warningbox -> itemize
|
|
144
|
+
for match in re.finditer(
|
|
145
|
+
r"(\\begin{xwarningbox}.*?\\end{xwarningbox}|"
|
|
146
|
+
r"\\begin{itemize}.*?\\end{itemize})",
|
|
147
|
+
text,
|
|
148
|
+
flags=re.DOTALL,
|
|
149
|
+
):
|
|
150
|
+
start, end = match.span()
|
|
151
|
+
before = text[pos:start]
|
|
152
|
+
|
|
153
|
+
# normal text in front
|
|
154
|
+
if before.strip():
|
|
155
|
+
output.append("\n".join(_ansi_wrap(before, width, hanging)))
|
|
156
|
+
|
|
157
|
+
block = match.group(1)
|
|
158
|
+
|
|
159
|
+
# xwarningbox
|
|
160
|
+
if block.startswith(r"\begin{xwarningbox}"):
|
|
161
|
+
content = re.search(r"\\begin{xwarningbox}\s*(.*?)\s*\\end{xwarningbox}", block, re.DOTALL).group(1)
|
|
162
|
+
output.append(_make_ascii_box(content.strip(), width, hanging))
|
|
163
|
+
|
|
164
|
+
# itemize
|
|
165
|
+
elif block.startswith(r"\begin{itemize}"):
|
|
166
|
+
content = re.search(r"\\begin{itemize}\s*(.*?)\s*\\end{itemize}", block, re.DOTALL).group(1)
|
|
167
|
+
output.append(_make_ascii_list(content.strip(), width, hanging))
|
|
168
|
+
|
|
169
|
+
pos = end
|
|
170
|
+
|
|
171
|
+
# normal text in the end
|
|
172
|
+
tail = text[pos:]
|
|
173
|
+
if tail.strip():
|
|
174
|
+
output.append("\n".join(_ansi_wrap(tail, width, hanging, space)))
|
|
175
|
+
|
|
176
|
+
return "\n".join(output)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class ConditionalReturn:
|
|
180
|
+
def __init__(self, **kwargs):
|
|
181
|
+
self.kwargs = kwargs if len(kwargs) != 0 else None
|
|
182
|
+
|
|
183
|
+
def __repr__(self):
|
|
184
|
+
if self.kwargs:
|
|
185
|
+
for k,v in self.kwargs.items():
|
|
186
|
+
if isinstance(v, pd.DataFrame) and 'title' in v.attrs and v.attrs['title']:
|
|
187
|
+
print(f"{v.attrs['title']} \n{v}\n")
|
|
188
|
+
elif isinstance(v, str):
|
|
189
|
+
print(f"{v}")
|
|
190
|
+
else: print(f'{k}: {v}')
|
|
191
|
+
return ""
|
craceplot/_vergit.py
ADDED
craceplot/_version.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.1.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = 'g120756444'
|