zs-yaml 0.9.1.dev2__tar.gz → 0.9.2.dev10__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.
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/.github/workflows/ci.yml +12 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/CHANGELOG.md +15 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/PKG-INFO +1 -1
- zs_yaml-0.9.2.dev10/examples/perf/.gitignore +4 -0
- zs_yaml-0.9.2.dev10/examples/perf/benchmark.py +138 -0
- zs_yaml-0.9.2.dev10/examples/perf/generate_perf_yaml.py +51 -0
- zs_yaml-0.9.2.dev10/examples/perf/perf.zs +20 -0
- zs_yaml-0.9.2.dev10/examples/perf/perf_reference.bin +0 -0
- zs_yaml-0.9.2.dev10/examples/perf/perf_reference_dict.sha256 +1 -0
- zs_yaml-0.9.2.dev10/examples/perf/test_fast_path.py +83 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/zs_yaml/_version.py +3 -3
- zs_yaml-0.9.2.dev10/zs_yaml/convert.py +616 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/zs_yaml.egg-info/PKG-INFO +1 -1
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/zs_yaml.egg-info/SOURCES.txt +7 -0
- zs_yaml-0.9.1.dev2/zs_yaml/convert.py +0 -342
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/.gitignore +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/LICENSE +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/README.md +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/api_docs_review.md +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/doc/zs-yaml.png +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/doc/zs-yaml.svg +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/examples/extern_compression/extern_compression.zs +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/examples/extern_compression/payload.yaml +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/examples/extern_compression/test_compression.py +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/examples/team/addresses.yaml +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/examples/team/custom_transformations.py +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/examples/team/hobbies.yaml +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/examples/team/skills.yaml +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/examples/team/team.zs +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/examples/team/team1.yaml +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/examples/team/test_all_conversions.py +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/examples/team/test_pyobj_to_yaml.py +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/examples/team/work_experience.yaml +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/pyproject.toml +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/setup.cfg +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/zs_yaml/__init__.py +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/zs_yaml/built_in_transformations.py +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/zs_yaml/main.py +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/zs_yaml/yaml_transformer.py +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/zs_yaml.egg-info/dependency_links.txt +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/zs_yaml.egg-info/entry_points.txt +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/zs_yaml.egg-info/requires.txt +0 -0
- {zs_yaml-0.9.1.dev2 → zs_yaml-0.9.2.dev10}/zs_yaml.egg-info/top_level.txt +0 -0
|
@@ -77,6 +77,18 @@ jobs:
|
|
|
77
77
|
echo "Compression tests failed"
|
|
78
78
|
exit 1
|
|
79
79
|
fi
|
|
80
|
+
- name: Generate schema API for perf example
|
|
81
|
+
run: |
|
|
82
|
+
cd examples/perf
|
|
83
|
+
zserio perf.zs -withTypeInfoCode -python zs_gen_api
|
|
84
|
+
- name: Perf regression - byte-identical reference
|
|
85
|
+
run: |
|
|
86
|
+
cd examples/perf
|
|
87
|
+
python benchmark.py
|
|
88
|
+
- name: Perf regression - fast-path structural assertion
|
|
89
|
+
run: |
|
|
90
|
+
cd examples/perf
|
|
91
|
+
python test_fast_path.py
|
|
80
92
|
- name: Build package
|
|
81
93
|
run: |
|
|
82
94
|
python -m build
|
|
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.10.0] - 2026-05-07
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Conversion is significantly faster: `yaml_to_bin` ~10x and `bin_to_dict` ~3x on the perf benchmark (5,000 records). Drop-in upgrade — output is byte-identical to 0.9.x. Fixes #21
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- `examples/perf` benchmark plus CI guards to catch future performance regressions
|
|
17
|
+
|
|
18
|
+
## [0.9.1] - 2026-04-27
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- Improved API documentation: clearer top-level entry points and a landing-page overview
|
|
22
|
+
|
|
8
23
|
## [0.9.0] - 2026-04-23
|
|
9
24
|
|
|
10
25
|
### Added
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Benchmark yaml_to_bin and bin_to_dict on the perf example.
|
|
3
|
+
|
|
4
|
+
Generates the perf YAML on demand, runs yaml_to_bin N times, then runs
|
|
5
|
+
bin_to_dict N times. Reports median wall time for each direction and,
|
|
6
|
+
if reference artifacts are present, verifies byte-identical output.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python benchmark.py [--runs 3] [--records 5000] [--points-per-record 5]
|
|
10
|
+
"""
|
|
11
|
+
import argparse
|
|
12
|
+
import hashlib
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
import statistics
|
|
16
|
+
import sys
|
|
17
|
+
import time
|
|
18
|
+
|
|
19
|
+
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
20
|
+
sys.path.insert(0, os.path.abspath(os.path.join(HERE, "..", "..")))
|
|
21
|
+
sys.path.insert(0, os.path.join(HERE, "zs_gen_api"))
|
|
22
|
+
|
|
23
|
+
from zs_yaml.convert import yaml_to_bin, bin_to_dict # noqa: E402
|
|
24
|
+
|
|
25
|
+
from generate_perf_yaml import generate # noqa: E402
|
|
26
|
+
|
|
27
|
+
YAML_PATH = os.path.join(HERE, "perf.yaml")
|
|
28
|
+
BIN_PATH = os.path.join(HERE, "perf.bin")
|
|
29
|
+
REFERENCE_BIN = os.path.join(HERE, "perf_reference.bin")
|
|
30
|
+
REFERENCE_DICT_HASH = os.path.join(HERE, "perf_reference_dict.sha256")
|
|
31
|
+
|
|
32
|
+
SCHEMA_MODULE = "perf.api"
|
|
33
|
+
SCHEMA_TYPE = "Dataset"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def ensure_yaml(records: int, points_per_record: int) -> None:
|
|
37
|
+
if not os.path.exists(YAML_PATH):
|
|
38
|
+
print(f"Generating {YAML_PATH}...")
|
|
39
|
+
generate(records, points_per_record, YAML_PATH)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def run_yaml_to_bin() -> float:
|
|
43
|
+
if os.path.exists(BIN_PATH):
|
|
44
|
+
os.remove(BIN_PATH)
|
|
45
|
+
t0 = time.perf_counter()
|
|
46
|
+
yaml_to_bin(YAML_PATH, BIN_PATH)
|
|
47
|
+
return time.perf_counter() - t0
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def run_bin_to_dict():
|
|
51
|
+
t0 = time.perf_counter()
|
|
52
|
+
data, _ = bin_to_dict(BIN_PATH, SCHEMA_MODULE, SCHEMA_TYPE)
|
|
53
|
+
return time.perf_counter() - t0, data
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _report(label, timings):
|
|
57
|
+
median = statistics.median(timings)
|
|
58
|
+
print(f"{label} median: {median:.3f}s "
|
|
59
|
+
f"(min {min(timings):.3f}s, max {max(timings):.3f}s, runs={len(timings)})")
|
|
60
|
+
return median
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def main() -> int:
|
|
64
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
65
|
+
parser.add_argument("--runs", type=int, default=3)
|
|
66
|
+
parser.add_argument("--records", type=int, default=5000)
|
|
67
|
+
parser.add_argument("--points-per-record", type=int, default=5)
|
|
68
|
+
parser.add_argument("--write-reference", action="store_true",
|
|
69
|
+
help="Overwrite reference artifacts with freshly produced output")
|
|
70
|
+
args = parser.parse_args()
|
|
71
|
+
|
|
72
|
+
ensure_yaml(args.records, args.points_per_record)
|
|
73
|
+
|
|
74
|
+
yaml_size = os.path.getsize(YAML_PATH)
|
|
75
|
+
print(f"Input: {YAML_PATH} ({yaml_size / (1024 * 1024):.2f} MiB)")
|
|
76
|
+
|
|
77
|
+
# Forward: yaml -> bin
|
|
78
|
+
print("\n[yaml_to_bin]")
|
|
79
|
+
yaml_timings = []
|
|
80
|
+
for i in range(args.runs):
|
|
81
|
+
elapsed = run_yaml_to_bin()
|
|
82
|
+
yaml_timings.append(elapsed)
|
|
83
|
+
print(f" run {i + 1}: {elapsed:.3f}s")
|
|
84
|
+
_report("yaml_to_bin", yaml_timings)
|
|
85
|
+
bin_size = os.path.getsize(BIN_PATH)
|
|
86
|
+
print(f"Output bin: {bin_size / (1024 * 1024):.2f} MiB")
|
|
87
|
+
|
|
88
|
+
# Reverse: bin -> dict
|
|
89
|
+
print("\n[bin_to_dict]")
|
|
90
|
+
bin_timings = []
|
|
91
|
+
last_dict = None
|
|
92
|
+
for i in range(args.runs):
|
|
93
|
+
elapsed, last_dict = run_bin_to_dict()
|
|
94
|
+
bin_timings.append(elapsed)
|
|
95
|
+
print(f" run {i + 1}: {elapsed:.3f}s")
|
|
96
|
+
_report("bin_to_dict", bin_timings)
|
|
97
|
+
|
|
98
|
+
dict_hash = hashlib.sha256(
|
|
99
|
+
json.dumps(last_dict, sort_keys=True, separators=(",", ":")).encode()
|
|
100
|
+
).hexdigest()
|
|
101
|
+
|
|
102
|
+
if args.write_reference:
|
|
103
|
+
import shutil
|
|
104
|
+
shutil.copyfile(BIN_PATH, REFERENCE_BIN)
|
|
105
|
+
print(f"\nWrote reference bin -> {REFERENCE_BIN}")
|
|
106
|
+
with open(REFERENCE_DICT_HASH, "w") as f:
|
|
107
|
+
f.write(dict_hash + "\n")
|
|
108
|
+
print(f"Wrote reference dict hash -> {REFERENCE_DICT_HASH}")
|
|
109
|
+
return 0
|
|
110
|
+
|
|
111
|
+
status = 0
|
|
112
|
+
print()
|
|
113
|
+
if os.path.exists(REFERENCE_BIN):
|
|
114
|
+
with open(BIN_PATH, "rb") as a, open(REFERENCE_BIN, "rb") as b:
|
|
115
|
+
if a.read() == b.read():
|
|
116
|
+
print("bin matches perf_reference.bin byte-for-byte.")
|
|
117
|
+
else:
|
|
118
|
+
print("MISMATCH: bin differs from perf_reference.bin")
|
|
119
|
+
status = 1
|
|
120
|
+
else:
|
|
121
|
+
print("No perf_reference.bin yet; run with --write-reference to pin one.")
|
|
122
|
+
|
|
123
|
+
if os.path.exists(REFERENCE_DICT_HASH):
|
|
124
|
+
with open(REFERENCE_DICT_HASH) as f:
|
|
125
|
+
expected_hash = f.read().strip()
|
|
126
|
+
if dict_hash == expected_hash:
|
|
127
|
+
print("dict matches perf_reference_dict.sha256.")
|
|
128
|
+
else:
|
|
129
|
+
print(f"MISMATCH: dict hash {dict_hash} != expected {expected_hash}")
|
|
130
|
+
status = 1
|
|
131
|
+
else:
|
|
132
|
+
print("No perf_reference_dict.sha256 yet; run with --write-reference to pin one.")
|
|
133
|
+
|
|
134
|
+
return status
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
if __name__ == "__main__":
|
|
138
|
+
sys.exit(main())
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Deterministically generate a sizeable perf YAML for benchmarking.
|
|
3
|
+
|
|
4
|
+
Produces examples/perf/perf.yaml with a Dataset containing a parameterized
|
|
5
|
+
number of records, each with a short nested array of Points. Matches the
|
|
6
|
+
"large array of small records with nested small arrays" pattern from
|
|
7
|
+
issue #21 - the shape where the JSON tokenizer cost dominates.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
python generate_perf_yaml.py [--records N] [--points-per-record M] [--out PATH]
|
|
11
|
+
"""
|
|
12
|
+
import argparse
|
|
13
|
+
import os
|
|
14
|
+
|
|
15
|
+
DEFAULT_RECORDS = 5000
|
|
16
|
+
DEFAULT_POINTS_PER_RECORD = 5
|
|
17
|
+
DEFAULT_OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "perf.yaml")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def generate(records: int, points_per_record: int, out_path: str) -> None:
|
|
21
|
+
with open(out_path, "w") as f:
|
|
22
|
+
f.write("_meta:\n")
|
|
23
|
+
f.write(" schema_module: perf.api\n")
|
|
24
|
+
f.write(" schema_type: Dataset\n")
|
|
25
|
+
f.write("\n")
|
|
26
|
+
f.write('name: "perf-dataset"\n')
|
|
27
|
+
f.write("records:\n")
|
|
28
|
+
for i in range(records):
|
|
29
|
+
f.write(f" - id: {i}\n")
|
|
30
|
+
f.write(f' label: "record-{i:08d}"\n')
|
|
31
|
+
f.write(" points:\n")
|
|
32
|
+
for j in range(points_per_record):
|
|
33
|
+
x = (i * 31 + j) & 0x7FFFFFFF
|
|
34
|
+
y = (i * 17 - j) & 0x7FFFFFFF
|
|
35
|
+
f.write(f" - x: {x}\n")
|
|
36
|
+
f.write(f" y: {y}\n")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def main() -> None:
|
|
40
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
41
|
+
parser.add_argument("--records", type=int, default=DEFAULT_RECORDS)
|
|
42
|
+
parser.add_argument("--points-per-record", type=int, default=DEFAULT_POINTS_PER_RECORD)
|
|
43
|
+
parser.add_argument("--out", default=DEFAULT_OUT)
|
|
44
|
+
args = parser.parse_args()
|
|
45
|
+
generate(args.records, args.points_per_record, args.out)
|
|
46
|
+
size = os.path.getsize(args.out)
|
|
47
|
+
print(f"Wrote {args.out} ({size / (1024 * 1024):.2f} MiB, {args.records} records x {args.points_per_record} points)")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
if __name__ == "__main__":
|
|
51
|
+
main()
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fd4029aa09b63ec6edbf4af9d823566ff665438f2099113025fb0fa5bb8160b4
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Structural perf-regression guard.
|
|
3
|
+
|
|
4
|
+
The 0.10 perf rewrite replaced `zserio.creator.ZserioTreeCreator` and
|
|
5
|
+
`zserio.walker.Walker` with a per-compound typeinfo descriptor cache.
|
|
6
|
+
Those two classes must never be instantiated on the `yaml_to_bin` /
|
|
7
|
+
`bin_to_dict` hot paths. This test asserts that invariant directly so a
|
|
8
|
+
future change that re-introduces the slow path fails CI - even on noisy
|
|
9
|
+
GitHub-hosted runners where wall-clock measurements are unreliable.
|
|
10
|
+
"""
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
import tempfile
|
|
14
|
+
from unittest.mock import patch
|
|
15
|
+
|
|
16
|
+
import zserio.creator
|
|
17
|
+
import zserio.walker
|
|
18
|
+
|
|
19
|
+
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
20
|
+
sys.path.insert(0, os.path.abspath(os.path.join(HERE, "..", "..")))
|
|
21
|
+
sys.path.insert(0, os.path.join(HERE, "zs_gen_api"))
|
|
22
|
+
|
|
23
|
+
from zs_yaml.convert import yaml_to_bin, bin_to_dict # noqa: E402
|
|
24
|
+
|
|
25
|
+
from generate_perf_yaml import generate # noqa: E402
|
|
26
|
+
|
|
27
|
+
YAML_PATH = os.path.join(HERE, "perf.yaml")
|
|
28
|
+
SCHEMA_MODULE = "perf.api"
|
|
29
|
+
SCHEMA_TYPE = "Dataset"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _ensure_yaml():
|
|
33
|
+
if not os.path.exists(YAML_PATH):
|
|
34
|
+
generate(records=200, points_per_record=3, out_path=YAML_PATH)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _run_with_patches(fn):
|
|
38
|
+
with patch.object(zserio.creator, "ZserioTreeCreator") as creator, \
|
|
39
|
+
patch.object(zserio.walker, "Walker") as walker:
|
|
40
|
+
fn()
|
|
41
|
+
return creator.call_count, walker.call_count
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_yaml_to_bin_skips_slow_path():
|
|
45
|
+
_ensure_yaml()
|
|
46
|
+
with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as tmp:
|
|
47
|
+
bin_path = tmp.name
|
|
48
|
+
try:
|
|
49
|
+
creator_calls, walker_calls = _run_with_patches(
|
|
50
|
+
lambda: yaml_to_bin(YAML_PATH, bin_path)
|
|
51
|
+
)
|
|
52
|
+
finally:
|
|
53
|
+
os.unlink(bin_path)
|
|
54
|
+
assert creator_calls == 0, f"ZserioTreeCreator instantiated {creator_calls}x - slow path re-introduced"
|
|
55
|
+
assert walker_calls == 0, f"Walker instantiated {walker_calls}x - slow path re-introduced"
|
|
56
|
+
print("yaml_to_bin: no ZserioTreeCreator, no Walker - fast path confirmed")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_bin_to_dict_skips_slow_path():
|
|
60
|
+
_ensure_yaml()
|
|
61
|
+
with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as tmp:
|
|
62
|
+
bin_path = tmp.name
|
|
63
|
+
try:
|
|
64
|
+
yaml_to_bin(YAML_PATH, bin_path)
|
|
65
|
+
creator_calls, walker_calls = _run_with_patches(
|
|
66
|
+
lambda: bin_to_dict(bin_path, SCHEMA_MODULE, SCHEMA_TYPE)
|
|
67
|
+
)
|
|
68
|
+
finally:
|
|
69
|
+
os.unlink(bin_path)
|
|
70
|
+
assert creator_calls == 0, f"ZserioTreeCreator instantiated {creator_calls}x - slow path re-introduced"
|
|
71
|
+
assert walker_calls == 0, f"Walker instantiated {walker_calls}x - slow path re-introduced"
|
|
72
|
+
print("bin_to_dict: no ZserioTreeCreator, no Walker - fast path confirmed")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def main():
|
|
76
|
+
test_yaml_to_bin_skips_slow_path()
|
|
77
|
+
test_bin_to_dict_skips_slow_path()
|
|
78
|
+
print("\nAll fast-path assertions passed.")
|
|
79
|
+
return 0
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if __name__ == "__main__":
|
|
83
|
+
sys.exit(main())
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.9.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 9,
|
|
21
|
+
__version__ = version = '0.9.2.dev10'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 9, 2, 'dev10')
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g9846aef5d'
|