noodle-bucket 2.7.3__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.
- noodle_bucket-2.7.3/.git_archival.txt +3 -0
- noodle_bucket-2.7.3/.gitattributes +1 -0
- noodle_bucket-2.7.3/.gitignore +44 -0
- noodle_bucket-2.7.3/LICENSE +21 -0
- noodle_bucket-2.7.3/PKG-INFO +115 -0
- noodle_bucket-2.7.3/README.md +79 -0
- noodle_bucket-2.7.3/bucket/__init__.py +25 -0
- noodle_bucket-2.7.3/bucket/__main__.py +311 -0
- noodle_bucket-2.7.3/bucket/axis.py +383 -0
- noodle_bucket-2.7.3/bucket/axisutils.py +244 -0
- noodle_bucket-2.7.3/bucket/base.py +64 -0
- noodle_bucket-2.7.3/bucket/bucket.py +92 -0
- noodle_bucket-2.7.3/bucket/common/chain.py +96 -0
- noodle_bucket-2.7.3/bucket/common/exceptions.py +8 -0
- noodle_bucket-2.7.3/bucket/common/types.py +67 -0
- noodle_bucket-2.7.3/bucket/context.py +36 -0
- noodle_bucket-2.7.3/bucket/covergroup.py +316 -0
- noodle_bucket-2.7.3/bucket/coverpoint.py +388 -0
- noodle_bucket-2.7.3/bucket/covertop.py +228 -0
- noodle_bucket-2.7.3/bucket/goal.py +25 -0
- noodle_bucket-2.7.3/bucket/link.py +50 -0
- noodle_bucket-2.7.3/bucket/rw/__init__.py +24 -0
- noodle_bucket-2.7.3/bucket/rw/archive.py +442 -0
- noodle_bucket-2.7.3/bucket/rw/common.py +907 -0
- noodle_bucket-2.7.3/bucket/rw/console.py +131 -0
- noodle_bucket-2.7.3/bucket/rw/html.py +112 -0
- noodle_bucket-2.7.3/bucket/rw/json.py +185 -0
- noodle_bucket-2.7.3/bucket/rw/point.py +108 -0
- noodle_bucket-2.7.3/bucket/rw/report.py +79 -0
- noodle_bucket-2.7.3/bucket/rw/sql.py +365 -0
- noodle_bucket-2.7.3/pyproject.toml +75 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.git_archival.txt export-subst
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
|
|
4
|
+
# Distribution / packaging
|
|
5
|
+
uv.lock
|
|
6
|
+
|
|
7
|
+
# pyenv
|
|
8
|
+
.python-version
|
|
9
|
+
|
|
10
|
+
# ZSH autocomplete
|
|
11
|
+
bin/zsh/.zcompdump
|
|
12
|
+
|
|
13
|
+
example_file_store.db
|
|
14
|
+
example_*.bktgz
|
|
15
|
+
example_*.db
|
|
16
|
+
|
|
17
|
+
# test coverage
|
|
18
|
+
.coverage
|
|
19
|
+
|
|
20
|
+
dist/
|
|
21
|
+
dev-dist/
|
|
22
|
+
# Demo report written at the repo root; anchored so the pattern doesn't also
|
|
23
|
+
# exclude viewer/index.html (hatchling applies these patterns to builds).
|
|
24
|
+
/index.html
|
|
25
|
+
|
|
26
|
+
# macOS
|
|
27
|
+
.DS_Store
|
|
28
|
+
*.iconset/
|
|
29
|
+
|
|
30
|
+
# Node modules
|
|
31
|
+
electron/node_modules/
|
|
32
|
+
|
|
33
|
+
# VSCode settings (do not check in)
|
|
34
|
+
.vscode/settings.json
|
|
35
|
+
|
|
36
|
+
# Local Copilot instructions (do not check in)
|
|
37
|
+
.github/copilot-instructions.md
|
|
38
|
+
.vscode/settings.json
|
|
39
|
+
|
|
40
|
+
# Example coverage files (do not check in)
|
|
41
|
+
example*.bktgz
|
|
42
|
+
|
|
43
|
+
# Stress example local output
|
|
44
|
+
/output/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023-2025 Noodle-Bytes
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: noodle-bucket
|
|
3
|
+
Version: 2.7.3
|
|
4
|
+
Summary: Functional coverage written in Python
|
|
5
|
+
Project-URL: Homepage, https://github.com/Noodle-Bytes/bucket
|
|
6
|
+
Project-URL: Documentation, https://noodle-bytes.github.io/bucket/docs/
|
|
7
|
+
Project-URL: Repository, https://github.com/Noodle-Bytes/bucket
|
|
8
|
+
Project-URL: Coverage viewer, https://noodle-bytes.github.io/bucket/
|
|
9
|
+
Project-URL: Issues, https://github.com/Noodle-Bytes/bucket/issues
|
|
10
|
+
Author-email: Stuart Alldred <stuartalldred@gmail.com>, Edward Kotarski <edktrsk@gmail.com>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: cocotb,functional-coverage,verification
|
|
14
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
|
|
24
|
+
Classifier: Topic :: Software Development :: Testing
|
|
25
|
+
Requires-Python: >=3.11
|
|
26
|
+
Requires-Dist: click>=8.1.3
|
|
27
|
+
Requires-Dist: pydantic>=2.8.2
|
|
28
|
+
Requires-Dist: rich>=13.3.4
|
|
29
|
+
Requires-Dist: sqlalchemy>=2.0.40
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: mkdocs>=1.6.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pre-commit>=3.8.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest<9,>=8.0.1; extra == 'dev'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
<!--
|
|
38
|
+
~ SPDX-License-Identifier: MIT
|
|
39
|
+
~ Copyright (c) 2023-2026 Noodle-Bytes. All Rights Reserved
|
|
40
|
+
-->
|
|
41
|
+
|
|
42
|
+
<!--
|
|
43
|
+
~ SPDX-License-Identifier: MIT
|
|
44
|
+
~ Copyright (c) 2023-2024 Vypercore. All Rights Reserved
|
|
45
|
+
-->
|
|
46
|
+
<div align="center">
|
|
47
|
+
<img alt="Bucket logo" src="https://raw.githubusercontent.com/Noodle-Bytes/bucket/main/.github/images/Logo-192x192.png">
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
# Bucket
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
Bucket is a python library to create nicer functional coverpoints. There are several benefits writing the coverage in python, such as:
|
|
54
|
+
|
|
55
|
+
* Easier integration into cocotb and other python testbenches
|
|
56
|
+
* No commercial EDA license required
|
|
57
|
+
* Vendor independence - easily collect coverage from multiple tools/sources (eg. models, log parsers, etc)
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install noodle-bucket
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
This installs the Python library (`import bucket`) and the `bucket` CLI.
|
|
66
|
+
It is enough to write coverpoints, collect coverage, and export `.bktgz` /
|
|
67
|
+
SQL / JSON / console output. View results in the [hosted viewer](https://noodle-bytes.github.io/bucket/)
|
|
68
|
+
or the desktop app.
|
|
69
|
+
|
|
70
|
+
Generating standalone HTML (`bucket write html` / `bucket write report`)
|
|
71
|
+
needs a source checkout of this repository and Node.js; it is not part of
|
|
72
|
+
the pip package.
|
|
73
|
+
|
|
74
|
+
## Documentation
|
|
75
|
+
|
|
76
|
+
The user documentation lives in the `docs/` directory and can be read directly on GitHub, starting from [`docs/index.md`](docs/index.md).
|
|
77
|
+
|
|
78
|
+
For a richer browsing experience, you can also run the MkDocs site locally:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
./bin/shell
|
|
82
|
+
mkdocs serve
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Then open `http://127.0.0.1:8000/` in your browser.
|
|
86
|
+
|
|
87
|
+
## Viewer
|
|
88
|
+
|
|
89
|
+
A copy of the viewer is hosted at: https://noodle-bytes.github.io/bucket/
|
|
90
|
+
|
|
91
|
+
## Support
|
|
92
|
+
|
|
93
|
+
This library is being actively built, and is expected to change while it matures and key features are added.
|
|
94
|
+
We are not providing external support for use of this library, however we are aiming to make it as easy to use as possible.
|
|
95
|
+
|
|
96
|
+
As per the licence:
|
|
97
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
98
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
99
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
100
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
101
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
102
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
103
|
+
SOFTWARE.
|
|
104
|
+
|
|
105
|
+
## Contributions
|
|
106
|
+
|
|
107
|
+
**We will be introducing a contributor licence agreement in the near future. In the meantime, if you want to contribute please get in touch.**
|
|
108
|
+
|
|
109
|
+
Please feel free to contribute to the project, following these guidelines:
|
|
110
|
+
|
|
111
|
+
* Please contribute by creating a fork and submitting a pull request.
|
|
112
|
+
* Pull requests should be as small as possible to resolve the issue they are trying to address.
|
|
113
|
+
* Pull requests must respect the goals of the library, as stated in the documentation.
|
|
114
|
+
* Pull requests should take care not to make performance worse except for cases which require bug fixes.
|
|
115
|
+
* Pull requests should update the documentation for any added/changed functionality.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
~ SPDX-License-Identifier: MIT
|
|
3
|
+
~ Copyright (c) 2023-2026 Noodle-Bytes. All Rights Reserved
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
<!--
|
|
7
|
+
~ SPDX-License-Identifier: MIT
|
|
8
|
+
~ Copyright (c) 2023-2024 Vypercore. All Rights Reserved
|
|
9
|
+
-->
|
|
10
|
+
<div align="center">
|
|
11
|
+
<img alt="Bucket logo" src="https://raw.githubusercontent.com/Noodle-Bytes/bucket/main/.github/images/Logo-192x192.png">
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
# Bucket
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
Bucket is a python library to create nicer functional coverpoints. There are several benefits writing the coverage in python, such as:
|
|
18
|
+
|
|
19
|
+
* Easier integration into cocotb and other python testbenches
|
|
20
|
+
* No commercial EDA license required
|
|
21
|
+
* Vendor independence - easily collect coverage from multiple tools/sources (eg. models, log parsers, etc)
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install noodle-bucket
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This installs the Python library (`import bucket`) and the `bucket` CLI.
|
|
30
|
+
It is enough to write coverpoints, collect coverage, and export `.bktgz` /
|
|
31
|
+
SQL / JSON / console output. View results in the [hosted viewer](https://noodle-bytes.github.io/bucket/)
|
|
32
|
+
or the desktop app.
|
|
33
|
+
|
|
34
|
+
Generating standalone HTML (`bucket write html` / `bucket write report`)
|
|
35
|
+
needs a source checkout of this repository and Node.js; it is not part of
|
|
36
|
+
the pip package.
|
|
37
|
+
|
|
38
|
+
## Documentation
|
|
39
|
+
|
|
40
|
+
The user documentation lives in the `docs/` directory and can be read directly on GitHub, starting from [`docs/index.md`](docs/index.md).
|
|
41
|
+
|
|
42
|
+
For a richer browsing experience, you can also run the MkDocs site locally:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
./bin/shell
|
|
46
|
+
mkdocs serve
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Then open `http://127.0.0.1:8000/` in your browser.
|
|
50
|
+
|
|
51
|
+
## Viewer
|
|
52
|
+
|
|
53
|
+
A copy of the viewer is hosted at: https://noodle-bytes.github.io/bucket/
|
|
54
|
+
|
|
55
|
+
## Support
|
|
56
|
+
|
|
57
|
+
This library is being actively built, and is expected to change while it matures and key features are added.
|
|
58
|
+
We are not providing external support for use of this library, however we are aiming to make it as easy to use as possible.
|
|
59
|
+
|
|
60
|
+
As per the licence:
|
|
61
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
62
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
63
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
64
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
65
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
66
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
67
|
+
SOFTWARE.
|
|
68
|
+
|
|
69
|
+
## Contributions
|
|
70
|
+
|
|
71
|
+
**We will be introducing a contributor licence agreement in the near future. In the meantime, if you want to contribute please get in touch.**
|
|
72
|
+
|
|
73
|
+
Please feel free to contribute to the project, following these guidelines:
|
|
74
|
+
|
|
75
|
+
* Please contribute by creating a fork and submitting a pull request.
|
|
76
|
+
* Pull requests should be as small as possible to resolve the issue they are trying to address.
|
|
77
|
+
* Pull requests must respect the goals of the library, as stated in the documentation.
|
|
78
|
+
* Pull requests should take care not to make performance worse except for cases which require bug fixes.
|
|
79
|
+
* Pull requests should update the documentation for any added/changed functionality.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2023-2026 Noodle-Bytes. All Rights Reserved
|
|
3
|
+
|
|
4
|
+
from importlib.metadata import PackageNotFoundError
|
|
5
|
+
from importlib.metadata import version as _version
|
|
6
|
+
|
|
7
|
+
from . import rw
|
|
8
|
+
from .axisutils import AxisUtils
|
|
9
|
+
from .context import CoverageContext
|
|
10
|
+
from .covergroup import Covergroup
|
|
11
|
+
from .coverpoint import Coverpoint
|
|
12
|
+
from .covertop import Covertop
|
|
13
|
+
|
|
14
|
+
# PyPI distribution name. The import package remains `bucket`; `bucket` itself
|
|
15
|
+
# is already taken on PyPI (an unrelated Bitbucket CLI).
|
|
16
|
+
DIST_NAME = "noodle-bucket"
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
__version__ = _version(DIST_NAME)
|
|
20
|
+
except PackageNotFoundError:
|
|
21
|
+
__version__ = "unknown"
|
|
22
|
+
|
|
23
|
+
version = __version__
|
|
24
|
+
|
|
25
|
+
assert all((CoverageContext, Covergroup, Coverpoint, Covertop, AxisUtils, rw))
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2023-2026 Noodle-Bytes. All Rights Reserved
|
|
3
|
+
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Iterable
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from . import DIST_NAME
|
|
10
|
+
from .rw import (
|
|
11
|
+
ArchiveAccessor,
|
|
12
|
+
ConsoleWriter,
|
|
13
|
+
HTMLWriter,
|
|
14
|
+
JSONAccessor,
|
|
15
|
+
ReportWriter,
|
|
16
|
+
SQLAccessor,
|
|
17
|
+
)
|
|
18
|
+
from .rw.common import MergeReadout, Readout
|
|
19
|
+
from .rw.html import DEFAULT_WEB_PATH
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@click.group()
|
|
23
|
+
@click.pass_context
|
|
24
|
+
@click.version_option(package_name=DIST_NAME, prog_name="bucket")
|
|
25
|
+
@click.option(
|
|
26
|
+
"--web-path",
|
|
27
|
+
help="Path to the web viewer (only used by html/report)",
|
|
28
|
+
default=DEFAULT_WEB_PATH,
|
|
29
|
+
show_default=True,
|
|
30
|
+
type=click.Path(path_type=Path),
|
|
31
|
+
)
|
|
32
|
+
def cli(ctx, web_path):
|
|
33
|
+
ctx.obj = {"web_path": web_path}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
_VALID_READERS = ["sql", "json", "archive"]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _split_spec(spec: str) -> tuple[str | None, str, str]:
|
|
40
|
+
"""
|
|
41
|
+
Split a readout spec into its components.
|
|
42
|
+
|
|
43
|
+
Valid Formats:
|
|
44
|
+
- `<record>@<type>:<URI>`
|
|
45
|
+
- `<record>@:<URI>`
|
|
46
|
+
- `<type>:<URI>`
|
|
47
|
+
- `<URI>`
|
|
48
|
+
|
|
49
|
+
Returns: (record: str | None, type: str, uri: str)
|
|
50
|
+
"""
|
|
51
|
+
record = None
|
|
52
|
+
|
|
53
|
+
# Get record if present
|
|
54
|
+
if "@" in spec:
|
|
55
|
+
record, spec = spec.split("@", 1)
|
|
56
|
+
record = int(record)
|
|
57
|
+
|
|
58
|
+
# Get type if present
|
|
59
|
+
if ":" in spec:
|
|
60
|
+
head, tail = spec.split(":", 1)
|
|
61
|
+
if head in _VALID_READERS:
|
|
62
|
+
typ, uri = head, tail
|
|
63
|
+
elif head == "":
|
|
64
|
+
# `<record>@:<URI>` form — type omitted, infer it from the URI
|
|
65
|
+
typ, uri = None, tail
|
|
66
|
+
else:
|
|
67
|
+
typ, uri = None, spec
|
|
68
|
+
else:
|
|
69
|
+
typ, uri = None, spec
|
|
70
|
+
|
|
71
|
+
# Infer type if missing
|
|
72
|
+
if typ is None:
|
|
73
|
+
if uri.endswith(".db"):
|
|
74
|
+
typ = "sql"
|
|
75
|
+
elif uri.endswith(".json"):
|
|
76
|
+
typ = "json"
|
|
77
|
+
elif uri.endswith(".bktgz"):
|
|
78
|
+
typ = "archive"
|
|
79
|
+
else:
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"Could not infer reader type from '{uri}'; please specify explicitly."
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
return record, typ, uri
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def get_readouts_from_spec(*specs: str) -> Iterable[Readout]:
|
|
88
|
+
"Parse readout specs into Readout objects."
|
|
89
|
+
for spec in specs:
|
|
90
|
+
record, typ, uri = _split_spec(spec)
|
|
91
|
+
|
|
92
|
+
if typ == "sql":
|
|
93
|
+
sql_path = Path(uri)
|
|
94
|
+
assert sql_path.exists(), f"SQL path does not exist: {sql_path}"
|
|
95
|
+
assert sql_path.is_file(), f"SQL path is not a file: {sql_path}"
|
|
96
|
+
|
|
97
|
+
reader = SQLAccessor.File(sql_path).reader()
|
|
98
|
+
|
|
99
|
+
elif typ == "json":
|
|
100
|
+
json_path = Path(uri)
|
|
101
|
+
assert json_path.exists(), f"JSON path does not exist: {uri}"
|
|
102
|
+
assert json_path.is_file(), f"JSON path is not a file: {uri}"
|
|
103
|
+
|
|
104
|
+
reader = JSONAccessor(json_path).reader()
|
|
105
|
+
|
|
106
|
+
elif typ == "archive":
|
|
107
|
+
archive_path = Path(uri)
|
|
108
|
+
assert archive_path.exists(), f"Archive path does not exist: {uri}"
|
|
109
|
+
assert archive_path.is_file(), f"Archive path is not a file: {uri}"
|
|
110
|
+
|
|
111
|
+
reader = ArchiveAccessor(archive_path).reader()
|
|
112
|
+
else:
|
|
113
|
+
raise ValueError(f"Unknown reader type: {typ}")
|
|
114
|
+
|
|
115
|
+
if record is None:
|
|
116
|
+
yield from reader.read_all()
|
|
117
|
+
else:
|
|
118
|
+
yield reader.read(record)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@cli.group()
|
|
122
|
+
@click.pass_context
|
|
123
|
+
@click.option(
|
|
124
|
+
"--read",
|
|
125
|
+
"-r",
|
|
126
|
+
"readout_specs",
|
|
127
|
+
multiple=True,
|
|
128
|
+
type=str,
|
|
129
|
+
help=f"""Can be specified multiple times.
|
|
130
|
+
Valid Formats:
|
|
131
|
+
- `<record>@<type>:<URI>`
|
|
132
|
+
- `<record>@:<URI>`
|
|
133
|
+
- `<type>:<URI>`
|
|
134
|
+
- `<URI>`
|
|
135
|
+
|
|
136
|
+
If the <record> is omitted, all records from the source are read.
|
|
137
|
+
If the <type> is omitted, it is inferred from the URI extension.
|
|
138
|
+
<URI> is interpreted according to the <type> as follows:
|
|
139
|
+
- `sql`: path to an SQL database file
|
|
140
|
+
- `json`: path to a JSON file
|
|
141
|
+
- `archive`: path to a .bktgz archive
|
|
142
|
+
|
|
143
|
+
Valid <type> values are: {', '.join(_VALID_READERS)}.
|
|
144
|
+
""",
|
|
145
|
+
)
|
|
146
|
+
@click.option("--merge", "-m", is_flag=True, default=False, help="Merge all readouts.")
|
|
147
|
+
def write(ctx, readout_specs: Iterable[str], merge: bool):
|
|
148
|
+
ctx.obj = ctx.obj or {}
|
|
149
|
+
readouts = get_readouts_from_spec(*readout_specs)
|
|
150
|
+
|
|
151
|
+
if merge:
|
|
152
|
+
readouts = [MergeReadout(*readouts)]
|
|
153
|
+
|
|
154
|
+
ctx.obj["readouts"] = readouts
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@write.command()
|
|
158
|
+
@click.pass_context
|
|
159
|
+
@click.option(
|
|
160
|
+
"--output",
|
|
161
|
+
"-o",
|
|
162
|
+
help="Path to output the JSON",
|
|
163
|
+
required=True,
|
|
164
|
+
type=click.Path(path_type=Path),
|
|
165
|
+
)
|
|
166
|
+
def json(ctx, output: Path):
|
|
167
|
+
readouts = ctx.obj["readouts"]
|
|
168
|
+
writer = JSONAccessor(output).writer()
|
|
169
|
+
|
|
170
|
+
for readout in readouts:
|
|
171
|
+
writer.write(readout)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@write.command()
|
|
175
|
+
@click.pass_context
|
|
176
|
+
@click.option(
|
|
177
|
+
"--output",
|
|
178
|
+
"-o",
|
|
179
|
+
help="Path to output the Archive",
|
|
180
|
+
required=True,
|
|
181
|
+
type=click.Path(path_type=Path),
|
|
182
|
+
)
|
|
183
|
+
def archive(ctx, output: Path):
|
|
184
|
+
readouts = ctx.obj["readouts"]
|
|
185
|
+
writer = ArchiveAccessor(output).writer()
|
|
186
|
+
|
|
187
|
+
for readout in readouts:
|
|
188
|
+
writer.write(readout)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@write.command()
|
|
192
|
+
@click.pass_context
|
|
193
|
+
@click.option(
|
|
194
|
+
"--output",
|
|
195
|
+
"-o",
|
|
196
|
+
help="Path to output the SQL",
|
|
197
|
+
required=True,
|
|
198
|
+
type=click.Path(path_type=Path),
|
|
199
|
+
)
|
|
200
|
+
def sql(ctx, output: Path):
|
|
201
|
+
readouts = ctx.obj["readouts"]
|
|
202
|
+
writer = SQLAccessor.File(output).writer()
|
|
203
|
+
|
|
204
|
+
for readout in readouts:
|
|
205
|
+
writer.write(readout)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@write.command()
|
|
209
|
+
@click.pass_context
|
|
210
|
+
@click.option(
|
|
211
|
+
"--output",
|
|
212
|
+
"-o",
|
|
213
|
+
help="Path to output the HTML report",
|
|
214
|
+
required=True,
|
|
215
|
+
type=click.Path(path_type=Path),
|
|
216
|
+
)
|
|
217
|
+
def html(ctx, output: Path):
|
|
218
|
+
readouts = ctx.obj["readouts"]
|
|
219
|
+
web_path = ctx.obj["web_path"]
|
|
220
|
+
try:
|
|
221
|
+
writer = HTMLWriter(web_path, output)
|
|
222
|
+
for readout in readouts:
|
|
223
|
+
writer.write(readout)
|
|
224
|
+
except RuntimeError as exc:
|
|
225
|
+
raise click.ClickException(str(exc)) from exc
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@write.command()
|
|
229
|
+
@click.pass_context
|
|
230
|
+
@click.option(
|
|
231
|
+
"--output",
|
|
232
|
+
"-o",
|
|
233
|
+
help="Path to output the HTML coverage report",
|
|
234
|
+
required=True,
|
|
235
|
+
type=click.Path(path_type=Path),
|
|
236
|
+
)
|
|
237
|
+
@click.option(
|
|
238
|
+
"--max-axis-values",
|
|
239
|
+
"max_axis_values",
|
|
240
|
+
default=64,
|
|
241
|
+
type=click.IntRange(min=0),
|
|
242
|
+
help="Cap on listed values per axis; 0 means unlimited",
|
|
243
|
+
)
|
|
244
|
+
@click.option(
|
|
245
|
+
"--max-tier",
|
|
246
|
+
"max_tier",
|
|
247
|
+
default=None,
|
|
248
|
+
type=click.IntRange(min=0),
|
|
249
|
+
help="Only include coverpoints with tier <= this value",
|
|
250
|
+
)
|
|
251
|
+
@click.option(
|
|
252
|
+
"--tags",
|
|
253
|
+
default=None,
|
|
254
|
+
type=str,
|
|
255
|
+
help="Only include coverpoints with at least one of these comma-separated tags",
|
|
256
|
+
)
|
|
257
|
+
@click.option(
|
|
258
|
+
"--point",
|
|
259
|
+
default=None,
|
|
260
|
+
type=str,
|
|
261
|
+
help="Only include coverpoints matching this dotted-path glob, e.g. 'Pets.dogs*'",
|
|
262
|
+
)
|
|
263
|
+
def report(
|
|
264
|
+
ctx,
|
|
265
|
+
output: Path,
|
|
266
|
+
max_axis_values: int,
|
|
267
|
+
max_tier: int | None,
|
|
268
|
+
tags: str | None,
|
|
269
|
+
point: str | None,
|
|
270
|
+
):
|
|
271
|
+
readouts = ctx.obj["readouts"]
|
|
272
|
+
web_path = ctx.obj["web_path"]
|
|
273
|
+
try:
|
|
274
|
+
writer = ReportWriter(
|
|
275
|
+
web_path,
|
|
276
|
+
output,
|
|
277
|
+
max_axis_values=max_axis_values,
|
|
278
|
+
max_tier=max_tier,
|
|
279
|
+
tags=[tag.strip() for tag in tags.split(",") if tag.strip()]
|
|
280
|
+
if tags
|
|
281
|
+
else None,
|
|
282
|
+
point=point,
|
|
283
|
+
)
|
|
284
|
+
# A single write: the writer is single-use and all readouts belong in
|
|
285
|
+
# one report document.
|
|
286
|
+
writer.write(list(readouts))
|
|
287
|
+
except RuntimeError as exc:
|
|
288
|
+
raise click.ClickException(str(exc)) from exc
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
@write.command()
|
|
292
|
+
@click.pass_context
|
|
293
|
+
@click.option("--axes/--no-axes", default=False)
|
|
294
|
+
@click.option("--goals/--no-goals", default=False)
|
|
295
|
+
@click.option("--points/--no-points", default=False)
|
|
296
|
+
@click.option("--summary/--no-summary", default=True)
|
|
297
|
+
def console(
|
|
298
|
+
ctx,
|
|
299
|
+
axes: bool,
|
|
300
|
+
goals: bool,
|
|
301
|
+
points: bool,
|
|
302
|
+
summary: bool,
|
|
303
|
+
):
|
|
304
|
+
readouts = ctx.obj["readouts"]
|
|
305
|
+
writer = ConsoleWriter(axes=axes, goals=goals, points=points, summary=summary)
|
|
306
|
+
for readout in readouts:
|
|
307
|
+
writer.write(readout)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
if __name__ == "__main__":
|
|
311
|
+
cli()
|