certora-prover-cli 0.2.2__tar.gz → 0.2.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.
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/PKG-INFO +1 -1
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/_version.py +2 -2
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/api/aws_data_fetcher.py +7 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/api/data_fetcher.py +7 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/api/local_data_fetcher.py +14 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/api/main.py +32 -5
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/auth.py +40 -4
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/prover_cli.py +18 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/.gitignore +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/LICENSE +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/README.md +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/pyproject.toml +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/__init__.py +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/api/__init__.py +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/api/base_data_fetcher.py +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/api/cache.py +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/api/tree_parser.py +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/api/url_utils.py +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/aws_auth.py +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/breadcrumb.py +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/exceptions.py +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/group_summary.py +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/job_report.py +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/models.py +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/parsers.py +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/prover_api.py +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/py.typed +0 -0
- {certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/sighash.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: certora-prover-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: A Python API and CLI for parsing outputs from the Certora Prover
|
|
5
5
|
Project-URL: Homepage, https://github.com/Certora/ProverCLI
|
|
6
6
|
Project-URL: Repository, https://github.com/Certora/ProverCLI
|
|
@@ -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.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
21
|
+
__version__ = version = '0.2.3'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 3)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -206,6 +206,13 @@ class AWSDataFetcher(BaseDataFetcher):
|
|
|
206
206
|
except json.JSONDecodeError as e:
|
|
207
207
|
raise ProverAPIError(f"Failed to parse statsdata.json for {job_identifier}: {e}")
|
|
208
208
|
|
|
209
|
+
def fetch_prover_version(self, job_identifier: str) -> Dict[str, Any]:
|
|
210
|
+
"""Fetch a job's cvt_version.json (the prover version / commit it ran on)."""
|
|
211
|
+
try:
|
|
212
|
+
return cast(Dict[str, Any], json.loads(self.fetch_output_file(job_identifier, "cvt_version.json")))
|
|
213
|
+
except json.JSONDecodeError as e:
|
|
214
|
+
raise ProverAPIError(f"Failed to parse cvt_version.json for {job_identifier}: {e}")
|
|
215
|
+
|
|
209
216
|
def fetch_output_file(self, job_identifier: str, rel_path: str) -> str:
|
|
210
217
|
"""Fetch the raw text of a Reports/-relative output file (e.g. unsat_core_map.json).
|
|
211
218
|
|
{certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/api/data_fetcher.py
RENAMED
|
@@ -263,6 +263,13 @@ class DataFetcher(BaseDataFetcher):
|
|
|
263
263
|
except json.JSONDecodeError as e:
|
|
264
264
|
raise ProverAPIError(f"Failed to parse statsdata.json for {job_identifier}: {e}")
|
|
265
265
|
|
|
266
|
+
def fetch_prover_version(self, job_identifier: str) -> Dict[str, Any]:
|
|
267
|
+
"""Fetch a job's cvt_version.json (the prover version / commit it ran on)."""
|
|
268
|
+
try:
|
|
269
|
+
return cast(Dict[str, Any], json.loads(self.fetch_output_file(job_identifier, "cvt_version.json")))
|
|
270
|
+
except json.JSONDecodeError as e:
|
|
271
|
+
raise ProverAPIError(f"Failed to parse cvt_version.json for {job_identifier}: {e}")
|
|
272
|
+
|
|
266
273
|
@handle_token_expiration
|
|
267
274
|
def fetch_output_file(self, job_identifier: str, rel_path: str) -> str:
|
|
268
275
|
"""Fetch the raw text of a Reports/-relative output file (e.g. unsat_core_map.json).
|
|
@@ -206,6 +206,20 @@ class LocalDataFetcher(BaseDataFetcher):
|
|
|
206
206
|
except json.JSONDecodeError as e:
|
|
207
207
|
raise ProverAPIError(f"Failed to parse statsdata.json: {e}")
|
|
208
208
|
|
|
209
|
+
def fetch_prover_version(self, job_identifier: str) -> Dict[str, Any]:
|
|
210
|
+
"""Read cvt_version.json from a local emv-* folder's inputs/ directory."""
|
|
211
|
+
emv_path = os.path.abspath(job_identifier)
|
|
212
|
+
if not os.path.exists(emv_path):
|
|
213
|
+
raise JobNotFoundError(f"Local prover output path not found: {job_identifier}")
|
|
214
|
+
cvt_version_path = os.path.join(emv_path, "inputs", "cvt_version.json")
|
|
215
|
+
if not os.path.exists(cvt_version_path):
|
|
216
|
+
raise JobNotFoundError(f"cvt_version.json not found: {cvt_version_path}")
|
|
217
|
+
try:
|
|
218
|
+
with open(cvt_version_path, "r") as f:
|
|
219
|
+
return json.load(f)
|
|
220
|
+
except json.JSONDecodeError as e:
|
|
221
|
+
raise ProverAPIError(f"Failed to parse cvt_version.json: {e}")
|
|
222
|
+
|
|
209
223
|
def fetch_outputs(self, job_identifier: str) -> bytes:
|
|
210
224
|
"""
|
|
211
225
|
Fetch outputs archive for local job - not applicable for local files.
|
|
@@ -21,7 +21,7 @@ import requests
|
|
|
21
21
|
from requests.adapters import HTTPAdapter
|
|
22
22
|
from urllib3.util.retry import Retry
|
|
23
23
|
|
|
24
|
-
from ..auth import ProverAuth
|
|
24
|
+
from ..auth import ProverAuth, organization_api_key
|
|
25
25
|
from ..aws_auth import AWSAuth
|
|
26
26
|
from ..breadcrumb import BreadcrumbParser
|
|
27
27
|
from ..exceptions import JobNotFoundError, ProverAPIError
|
|
@@ -188,8 +188,11 @@ class ProverOutputAPI:
|
|
|
188
188
|
# Initialize appropriate data fetcher
|
|
189
189
|
if use_local:
|
|
190
190
|
self.data_fetcher = LocalDataFetcher()
|
|
191
|
-
elif os.getenv("CI") and self._is_aws_available():
|
|
192
|
-
# In CI with AWS credentials, use AWS Lambda authentication
|
|
191
|
+
elif organization_api_key() is None and os.getenv("CI") and self._is_aws_available():
|
|
192
|
+
# In CI with AWS credentials, use AWS Lambda authentication. The
|
|
193
|
+
# path exists for automation that has AWS credentials and no
|
|
194
|
+
# Certora login, so a key the caller supplied on purpose takes
|
|
195
|
+
# precedence over it.
|
|
193
196
|
self.logger.info("Using AWS Lambda authentication in CI")
|
|
194
197
|
aws_auth = AWSAuth()
|
|
195
198
|
aws_session = aws_auth.get_session()
|
|
@@ -197,15 +200,19 @@ class ProverOutputAPI:
|
|
|
197
200
|
# No session needed for AWS auth
|
|
198
201
|
self.session = None
|
|
199
202
|
else:
|
|
200
|
-
#
|
|
203
|
+
# Key or cookie authentication; ProverAuth resolves which applies
|
|
201
204
|
self.auth = ProverAuth()
|
|
202
205
|
self._setup_session()
|
|
203
206
|
|
|
204
207
|
def _setup_session(self, force_relogin: bool = False):
|
|
205
|
-
"""Setup session with
|
|
208
|
+
"""Setup session with whichever credential ProverAuth resolves: an
|
|
209
|
+
organization API key on the Authorization header, or login cookies."""
|
|
206
210
|
self.session = _TimedSession()
|
|
207
211
|
# Set up authentication
|
|
208
212
|
self.session.cookies = self.auth.get_auth_cookies(force_relogin=force_relogin)
|
|
213
|
+
# An organization API key travels on every request of the session;
|
|
214
|
+
# with cookie authentication this adds nothing.
|
|
215
|
+
self.session.headers.update(self.auth.get_auth_headers())
|
|
209
216
|
self.data_fetcher = DataFetcher(self.session, api_instance=self, api_base_url=self.api_base_url)
|
|
210
217
|
|
|
211
218
|
def _is_aws_available(self) -> bool:
|
|
@@ -798,6 +805,26 @@ class ProverOutputAPI:
|
|
|
798
805
|
except Exception as e:
|
|
799
806
|
raise ProverAPIError(f"Failed to get statsdata for job {job_identifier}: {e}")
|
|
800
807
|
|
|
808
|
+
def get_prover_version(self, job_input: str) -> Dict[str, Any]:
|
|
809
|
+
"""
|
|
810
|
+
Get a job's cvt_version.json — the prover version / commit it ran on.
|
|
811
|
+
|
|
812
|
+
Args:
|
|
813
|
+
job_input: Job URL, job ID, or local emv-* path
|
|
814
|
+
|
|
815
|
+
Returns:
|
|
816
|
+
Parsed cvt_version.json contents (e.g. a "version" field and commit hashes)
|
|
817
|
+
|
|
818
|
+
Raises:
|
|
819
|
+
ProverAPIError: If fetching fails
|
|
820
|
+
"""
|
|
821
|
+
job_identifier = self._extract_job_identifier(job_input)
|
|
822
|
+
|
|
823
|
+
try:
|
|
824
|
+
return self.data_fetcher.fetch_prover_version(job_identifier)
|
|
825
|
+
except Exception as e:
|
|
826
|
+
raise ProverAPIError(f"Failed to get prover version for job {job_identifier}: {e}")
|
|
827
|
+
|
|
801
828
|
def get_console_logs(self, job_input: str) -> str:
|
|
802
829
|
"""
|
|
803
830
|
Get console logs for a job. This contains the full console output
|
|
@@ -83,6 +83,30 @@ except ImportError:
|
|
|
83
83
|
SERVERS = None
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
# Environment variables holding an organization API key, in the order they are
|
|
87
|
+
# consulted. The env-scoped name comes first so a machine configured for one
|
|
88
|
+
# environment cannot be pointed at another by a stray generic value.
|
|
89
|
+
_API_KEY_ENV_VARS: Dict[LoginEnv, tuple[str, ...]] = {
|
|
90
|
+
"prod": ("CERTORA_API_KEY_PROD", "CERTORA_API_KEY"),
|
|
91
|
+
"stg": ("CERTORA_API_KEY_STG", "CERTORA_API_KEY"),
|
|
92
|
+
"dev": ("CERTORA_API_KEY_DEV", "CERTORA_API_KEY"),
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def organization_api_key() -> str | None:
|
|
97
|
+
"""The organization API key for the current login env, if one is set.
|
|
98
|
+
|
|
99
|
+
This is the key issued on the organization's API page, not ``CERTORAKEY``.
|
|
100
|
+
The API accepts it on the ``Authorization`` header and exchanges it for a
|
|
101
|
+
short-lived token itself, so a caller that has one needs no browser login.
|
|
102
|
+
"""
|
|
103
|
+
for name in _API_KEY_ENV_VARS[resolve_login_env()]:
|
|
104
|
+
value = (os.environ.get(name) or "").strip()
|
|
105
|
+
if value:
|
|
106
|
+
return value
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
|
|
86
110
|
class ProverAuth:
|
|
87
111
|
"""
|
|
88
112
|
Handles authentication with Certora Prover API using certora_login.
|
|
@@ -129,6 +153,11 @@ class ProverAuth:
|
|
|
129
153
|
"""
|
|
130
154
|
cookies = requests.cookies.RequestsCookieJar()
|
|
131
155
|
|
|
156
|
+
# An organization API key authenticates on the header instead, so
|
|
157
|
+
# there is nothing to log in for and no cookie to carry.
|
|
158
|
+
if organization_api_key() is not None:
|
|
159
|
+
return cookies
|
|
160
|
+
|
|
132
161
|
# Skip authentication in CI environment - return empty cookies
|
|
133
162
|
if os.getenv("CI"):
|
|
134
163
|
return cookies
|
|
@@ -163,10 +192,17 @@ class ProverAuth:
|
|
|
163
192
|
"""
|
|
164
193
|
Get authentication headers for API requests.
|
|
165
194
|
|
|
166
|
-
|
|
167
|
-
|
|
195
|
+
An organization API key authenticates on the ``Authorization``
|
|
196
|
+
header: the API classifies the credential by shape and exchanges an
|
|
197
|
+
opaque key for a short-lived token on the caller's behalf, so no
|
|
198
|
+
browser login and no cookie are involved. Without a key the cookies
|
|
199
|
+
from certora_login carry the identity and there is no header to add.
|
|
168
200
|
|
|
169
201
|
Returns:
|
|
170
|
-
|
|
202
|
+
The bearer header for the organization API key, or an empty
|
|
203
|
+
dictionary when authentication is via cookies.
|
|
171
204
|
"""
|
|
172
|
-
|
|
205
|
+
key = organization_api_key()
|
|
206
|
+
if key is None:
|
|
207
|
+
return {}
|
|
208
|
+
return {"Authorization": f"Bearer {key}"}
|
{certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/prover_cli.py
RENAMED
|
@@ -193,6 +193,11 @@ Environment Variables:
|
|
|
193
193
|
parser.add_argument("--calltrace", help="Get calltrace for specific output file path")
|
|
194
194
|
parser.add_argument("--breadcrumbs", help="Get breadcrumb trace from DAP file")
|
|
195
195
|
parser.add_argument("--statsdata", action="store_true", help="Get statsdata.json for the job")
|
|
196
|
+
parser.add_argument(
|
|
197
|
+
"--prover-version",
|
|
198
|
+
action="store_true",
|
|
199
|
+
help="Get cvt_version.json for the job (the prover version / commit it ran on)",
|
|
200
|
+
)
|
|
196
201
|
parser.add_argument(
|
|
197
202
|
"--all-checks",
|
|
198
203
|
action="store_true",
|
|
@@ -458,6 +463,11 @@ Environment Variables:
|
|
|
458
463
|
statsdata = api.get_statsdata(job_input)
|
|
459
464
|
result = {"job_id": job_input, "statsdata": statsdata}
|
|
460
465
|
|
|
466
|
+
elif args.prover_version:
|
|
467
|
+
# Get cvt_version.json (prover version / commit the job ran on)
|
|
468
|
+
prover_version = api.get_prover_version(job_input)
|
|
469
|
+
result = {"job_id": job_input, "prover_version": prover_version}
|
|
470
|
+
|
|
461
471
|
elif args.all_checks:
|
|
462
472
|
# Get all checks (both successful and failed)
|
|
463
473
|
all_checks = api.get_all_checks(job_input)
|
|
@@ -534,6 +544,8 @@ Environment Variables:
|
|
|
534
544
|
)
|
|
535
545
|
elif args.statsdata:
|
|
536
546
|
print(f"✅ Statsdata retrieved. Results written to {args.output}")
|
|
547
|
+
elif args.prover_version:
|
|
548
|
+
print(f"✅ prover_version retrieved. Results written to {args.output}")
|
|
537
549
|
elif args.all_checks:
|
|
538
550
|
count = result.get("checks_count", 0)
|
|
539
551
|
print(f"Found {count} checks (all tree nodes). Results written to {args.output}")
|
|
@@ -611,6 +623,12 @@ Environment Variables:
|
|
|
611
623
|
print(f" ... and {len(result['statsdata']) - 5} more keys")
|
|
612
624
|
print("Use --format json to see full data")
|
|
613
625
|
|
|
626
|
+
elif args.prover_version:
|
|
627
|
+
print(f"Job ID: {job_input}")
|
|
628
|
+
print("✅ Retrieved cvt_version.json")
|
|
629
|
+
for key, value in result["prover_version"].items():
|
|
630
|
+
print(f" {key}: {value}")
|
|
631
|
+
|
|
614
632
|
elif args.all_checks:
|
|
615
633
|
# Show all checks (both successful and failed)
|
|
616
634
|
print(f"Job ID: {job_input}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/api/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/api/cache.py
RENAMED
|
File without changes
|
{certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/api/tree_parser.py
RENAMED
|
File without changes
|
{certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/api/url_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
{certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/breadcrumb.py
RENAMED
|
File without changes
|
{certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/exceptions.py
RENAMED
|
File without changes
|
{certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/group_summary.py
RENAMED
|
File without changes
|
{certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/job_report.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{certora_prover_cli-0.2.2 → certora_prover_cli-0.2.3}/src/prover_output_utility/prover_api.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|