certora-cli-alpha-master 20250520.17.33.568414__py3-none-macosx_10_9_universal2.whl → 20250521.12.50.829403__py3-none-macosx_10_9_universal2.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.
- certora_cli/Mutate/mutateApp.py +7 -2
- certora_cli/Shared/proverCommon.py +300 -0
- certora_cli/Shared/rustProverCommon.py +62 -0
- certora_cli/certoraEVMProver.py +2 -1
- certora_cli/certoraRanger.py +4 -36
- certora_cli/certoraRun.py +62 -166
- certora_cli/certoraSolanaProver.py +39 -134
- certora_cli/certoraSorobanProver.py +31 -224
- {certora_cli_alpha_master-20250520.17.33.568414.dist-info → certora_cli_alpha_master-20250521.12.50.829403.dist-info}/METADATA +2 -2
- {certora_cli_alpha_master-20250520.17.33.568414.dist-info → certora_cli_alpha_master-20250521.12.50.829403.dist-info}/RECORD +16 -14
- certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -1
- certora_jars/Typechecker.jar +0 -0
- {certora_cli_alpha_master-20250520.17.33.568414.dist-info → certora_cli_alpha_master-20250521.12.50.829403.dist-info}/LICENSE +0 -0
- {certora_cli_alpha_master-20250520.17.33.568414.dist-info → certora_cli_alpha_master-20250521.12.50.829403.dist-info}/WHEEL +0 -0
- {certora_cli_alpha_master-20250520.17.33.568414.dist-info → certora_cli_alpha_master-20250521.12.50.829403.dist-info}/entry_points.txt +0 -0
- {certora_cli_alpha_master-20250520.17.33.568414.dist-info → certora_cli_alpha_master-20250521.12.50.829403.dist-info}/top_level.txt +0 -0
certora_cli/Mutate/mutateApp.py
CHANGED
@@ -44,7 +44,9 @@ from CertoraProver.certoraContextValidator import KEY_ENV_VAR
|
|
44
44
|
from Mutate import mutateConstants as MConstants
|
45
45
|
from Shared import certoraUtils as Util
|
46
46
|
from Shared.certoraLogging import LoggingManager
|
47
|
-
from certoraRun import run_certora
|
47
|
+
from certoraRun import run_certora
|
48
|
+
from Shared.proverCommon import CertoraRunResult, CertoraFoundViolations
|
49
|
+
from certoraSorobanProver import run_soroban_prover
|
48
50
|
from Shared import certoraValidateFuncs as Vf
|
49
51
|
from CertoraProver.Compiler.CompilerCollectorFactory import get_relevant_compiler
|
50
52
|
from Mutate import mutateUtil as MutUtil
|
@@ -1416,7 +1418,10 @@ class MutateApp:
|
|
1416
1418
|
certora_args.extend(["--disable_local_typechecking"])
|
1417
1419
|
mutation_logger.debug(f"Running the Prover: {certora_args}")
|
1418
1420
|
try:
|
1419
|
-
|
1421
|
+
if self.is_soroban_run():
|
1422
|
+
certora_run_result = run_soroban_prover(certora_args)
|
1423
|
+
else:
|
1424
|
+
certora_run_result = run_certora(certora_args)
|
1420
1425
|
except CertoraFoundViolations as e:
|
1421
1426
|
assert e.results, "expect e.results not to be None"
|
1422
1427
|
certora_run_result = e.results
|
@@ -0,0 +1,300 @@
|
|
1
|
+
# The Certora Prover
|
2
|
+
# Copyright (C) 2025 Certora Ltd.
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, version 3 of the License.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
"""
|
17
|
+
Shared helpers for certoraRun entry points (Solana & Soroban & EVM).
|
18
|
+
|
19
|
+
Placing the context_build environment preparation logic and verification helpers in one module
|
20
|
+
reduces duplication from the dedicated entry scripts.
|
21
|
+
"""
|
22
|
+
|
23
|
+
from __future__ import annotations
|
24
|
+
|
25
|
+
import sys
|
26
|
+
import logging
|
27
|
+
import functools
|
28
|
+
from pathlib import Path
|
29
|
+
from dataclasses import dataclass
|
30
|
+
from rich.console import Console
|
31
|
+
from typing import List, Tuple, Type, Optional, Dict, NoReturn, Callable
|
32
|
+
|
33
|
+
scripts_dir_path = Path(__file__).parent.parent.resolve() # containing directory
|
34
|
+
sys.path.insert(0, str(scripts_dir_path))
|
35
|
+
|
36
|
+
from CertoraProver.certoraCloudIO import validate_version_and_branch
|
37
|
+
from Shared.certoraLogging import LoggingManager
|
38
|
+
from Shared import certoraUtils as Util
|
39
|
+
from Shared.certoraAttrUtil import Attributes
|
40
|
+
import CertoraProver.certoraContext as Ctx
|
41
|
+
from CertoraProver.certoraContextClass import CertoraContext
|
42
|
+
import CertoraProver.certoraContextAttributes as Attrs
|
43
|
+
from CertoraProver.certoraCollectRunMetadata import collect_run_metadata
|
44
|
+
from CertoraProver.certoraCollectConfigurationLayout import collect_configuration_layout
|
45
|
+
from CertoraProver import certoraContextValidator as Cv
|
46
|
+
from CertoraProver.certoraCloudIO import CloudVerification
|
47
|
+
|
48
|
+
log = logging.getLogger(__name__)
|
49
|
+
|
50
|
+
|
51
|
+
VIOLATIONS_EXIT_CODE = 100
|
52
|
+
|
53
|
+
@dataclass
|
54
|
+
class CertoraRunResult:
|
55
|
+
link: Optional[str] # Path to emv_dir if running locally, or the link to the job status page
|
56
|
+
is_local_link: bool
|
57
|
+
src_dir: Path
|
58
|
+
rule_report_link: Optional[str]
|
59
|
+
|
60
|
+
class CertoraFoundViolations(Exception):
|
61
|
+
def __init__(self, message: str, results: Optional[CertoraRunResult] = None) -> None:
|
62
|
+
super().__init__(message)
|
63
|
+
self.results = results
|
64
|
+
|
65
|
+
# --------------------------------------------------------------------------- #
|
66
|
+
# Setup Environment
|
67
|
+
# --------------------------------------------------------------------------- #
|
68
|
+
def build_context(args: List[str], attributes: Type[Attributes]) -> Tuple[CertoraContext, LoggingManager]:
|
69
|
+
"""
|
70
|
+
Build the context for the Certora Prover.
|
71
|
+
This function is responsible for setting up the context and logging manager
|
72
|
+
for the Certora Prover. It handles the following tasks:
|
73
|
+
1. Setting up the logging manager
|
74
|
+
2. Parsing the command line arguments
|
75
|
+
3. Setting up the context
|
76
|
+
|
77
|
+
Args:
|
78
|
+
args: The command line arguments to parse.
|
79
|
+
attributes: The attributes class to use for the context. (e.g., SolanaProverAttributes or SorobanProverAttributes)
|
80
|
+
Returns:
|
81
|
+
A tuple containing the CertoraContext object and the LoggingManager object.
|
82
|
+
"""
|
83
|
+
Attrs.set_attribute_class(attributes)
|
84
|
+
non_str_els = [x for x in args if not isinstance(x, str)]
|
85
|
+
if non_str_els:
|
86
|
+
print(f"args for run_certora that are not strings: {non_str_els}")
|
87
|
+
exit(1)
|
88
|
+
|
89
|
+
# If we are not in debug mode, we do not want to print the traceback in case of exceptions.
|
90
|
+
if '--debug' not in args: # We check manually, because we want no traceback in argument parsing exceptions
|
91
|
+
sys.tracebacklimit = 0
|
92
|
+
|
93
|
+
# creating the default internal dir, files may be copied to user defined build directory after
|
94
|
+
# parsing the input
|
95
|
+
|
96
|
+
if not ('--help' in args or '--version' in args):
|
97
|
+
Util.reset_certora_internal_dir()
|
98
|
+
Util.safe_create_dir(Util.get_build_dir())
|
99
|
+
logging_manager = LoggingManager()
|
100
|
+
|
101
|
+
Ctx.handle_flags_in_args(args)
|
102
|
+
context = Ctx.get_args(args) # Parse arguments
|
103
|
+
|
104
|
+
assert logging_manager, "logging manager was not set"
|
105
|
+
logging_manager.set_log_level_and_format(is_quiet=Ctx.is_minimal_cli_output(context),
|
106
|
+
debug=context.debug,
|
107
|
+
debug_topics=context.debug_topics,
|
108
|
+
show_debug_topics=context.show_debug_topics)
|
109
|
+
|
110
|
+
return context, logging_manager
|
111
|
+
|
112
|
+
|
113
|
+
def collect_and_dump_metadata(context: CertoraContext) -> None:
|
114
|
+
"""
|
115
|
+
Collect and validate run metadata.
|
116
|
+
|
117
|
+
Args:
|
118
|
+
context: The Certora context containing verification settings
|
119
|
+
|
120
|
+
Raises:
|
121
|
+
Util.TestResultsReady: If this is a metadata test run
|
122
|
+
"""
|
123
|
+
metadata = collect_run_metadata(wd=Path.cwd(), raw_args=sys.argv, context=context)
|
124
|
+
|
125
|
+
if context.test == str(Util.TestValue.CHECK_METADATA):
|
126
|
+
raise Util.TestResultsReady(metadata)
|
127
|
+
|
128
|
+
metadata.dump()
|
129
|
+
|
130
|
+
|
131
|
+
def collect_and_dump_config_layout(context: CertoraContext) -> None:
|
132
|
+
"""
|
133
|
+
Collect and dump the configuration layout.
|
134
|
+
|
135
|
+
Args:
|
136
|
+
context: The Certora context containing verification settings
|
137
|
+
|
138
|
+
Raises:
|
139
|
+
Util.TestResultsReady: If this is a configuration layout test run
|
140
|
+
"""
|
141
|
+
configuration_layout = collect_configuration_layout()
|
142
|
+
|
143
|
+
if context.test == str(Util.TestValue.CHECK_CONFIG_LAYOUT):
|
144
|
+
raise Util.TestResultsReady(configuration_layout)
|
145
|
+
|
146
|
+
configuration_layout.dump()
|
147
|
+
|
148
|
+
|
149
|
+
def ensure_version_compatibility(context: CertoraContext) -> None:
|
150
|
+
if not (context.local or context.build_only or context.compilation_steps_only):
|
151
|
+
"""
|
152
|
+
Before running the local type checker, we see if the current package version is compatible with
|
153
|
+
the latest. We check it before running the local type checker, because local type checking
|
154
|
+
errors could be simply a result of syntax introduced in the newest version.
|
155
|
+
The line below will raise an exception if the local version is incompatible.
|
156
|
+
"""
|
157
|
+
validate_version_and_branch(context)
|
158
|
+
|
159
|
+
# --------------------------------------------------------------------------- #
|
160
|
+
# Verification helpers
|
161
|
+
# --------------------------------------------------------------------------- #
|
162
|
+
|
163
|
+
def run_local(context: CertoraContext, timings: Dict, additional_commands: Optional[List[str]] = None, compare_with_expected_file: bool = False) -> int:
|
164
|
+
"""
|
165
|
+
Run the verifier locally and return its exit code (0 = success).
|
166
|
+
Args:
|
167
|
+
context: The CertoraContext object containing the configuration.
|
168
|
+
timings: A dictionary to store timing information.
|
169
|
+
additional_commands: A list of additional commands to pass to the verifier.
|
170
|
+
Returns:
|
171
|
+
An integer representing the exit code of the verifier run.
|
172
|
+
"""
|
173
|
+
cmd: List[str] = Ctx.get_local_run_cmd(context)
|
174
|
+
|
175
|
+
if additional_commands:
|
176
|
+
cmd.extend(additional_commands)
|
177
|
+
|
178
|
+
print("Verifier run command:\n %s", " ".join(cmd))
|
179
|
+
rc = Util.run_jar_cmd(
|
180
|
+
cmd,
|
181
|
+
override_exit_code=compare_with_expected_file,
|
182
|
+
logger_topic="verification",
|
183
|
+
print_output=True,
|
184
|
+
)
|
185
|
+
|
186
|
+
if rc == 0:
|
187
|
+
Util.print_completion_message("Finished running verifier:")
|
188
|
+
print("\t%s", " ".join(cmd))
|
189
|
+
timings.setdefault("buildTime", 0.0) # ensure key exists
|
190
|
+
return 0
|
191
|
+
|
192
|
+
return 1
|
193
|
+
|
194
|
+
|
195
|
+
def run_remote(
|
196
|
+
context: CertoraContext,
|
197
|
+
args: List[str],
|
198
|
+
timings: Dict,
|
199
|
+
) -> Tuple[int, Optional[CertoraRunResult]]:
|
200
|
+
"""
|
201
|
+
Run verification in Certora Cloud.
|
202
|
+
|
203
|
+
Args:
|
204
|
+
context: The CertoraContext object containing the configuration.
|
205
|
+
args: The command line arguments to pass to the cloud verification.
|
206
|
+
timings: A dictionary to store timing information.
|
207
|
+
Returns:
|
208
|
+
A tuple containing the exit code (0 = success) and an optional CertoraRunResult object.
|
209
|
+
"""
|
210
|
+
if context.compilation_steps_only:
|
211
|
+
return 0, CertoraRunResult(None, False, Util.get_certora_sources_dir(), None)
|
212
|
+
|
213
|
+
context.key = Cv.validate_certora_key()
|
214
|
+
cloud = CloudVerification(context, timings)
|
215
|
+
|
216
|
+
pretty_args = [f"'{a}'" if " " in a else a for a in args]
|
217
|
+
|
218
|
+
ok = cloud.cli_verify_and_report(" ".join(pretty_args), context.wait_for_results)
|
219
|
+
|
220
|
+
exit_code = 0 if ok else VIOLATIONS_EXIT_CODE
|
221
|
+
result: Optional[CertoraRunResult] = None
|
222
|
+
if cloud.statusUrl:
|
223
|
+
result = CertoraRunResult(
|
224
|
+
cloud.statusUrl,
|
225
|
+
False,
|
226
|
+
Util.get_certora_sources_dir(),
|
227
|
+
cloud.reportUrl,
|
228
|
+
)
|
229
|
+
return exit_code, result
|
230
|
+
|
231
|
+
|
232
|
+
def handle_exit(exit_code: int, return_value: Optional[CertoraRunResult]) -> Optional[CertoraRunResult]:
|
233
|
+
"""
|
234
|
+
Handle the exit code of the verification run.
|
235
|
+
Args:
|
236
|
+
exit_code: The exit code of the verification run.
|
237
|
+
return_value: The CertoraRunResult object containing the results of the verification run.
|
238
|
+
Raises:
|
239
|
+
CertoraFoundViolations: If violations were found during the verification run.
|
240
|
+
Util.CertoraUserInputError: If there was an error with the user input.
|
241
|
+
Returns:
|
242
|
+
The CertoraRunResult object containing the results of the verification run.
|
243
|
+
"""
|
244
|
+
if exit_code == VIOLATIONS_EXIT_CODE:
|
245
|
+
raise CertoraFoundViolations("violations were found", return_value)
|
246
|
+
if exit_code != 0:
|
247
|
+
raise Util.CertoraUserInputError(f"run_certora failed (code {exit_code})")
|
248
|
+
return return_value
|
249
|
+
|
250
|
+
|
251
|
+
# --------------------------------------------------------------------------- #
|
252
|
+
# Entry point decorator
|
253
|
+
# --------------------------------------------------------------------------- #
|
254
|
+
|
255
|
+
console = Console()
|
256
|
+
|
257
|
+
def catch_exits(fn: Callable[..., None]) -> Callable[..., NoReturn]:
|
258
|
+
"""
|
259
|
+
Wrap any entry-point in a standard try/except + sys.exit logic.
|
260
|
+
The wrapped function should do its work and then return normally;
|
261
|
+
this decorator will exit(0) on success or exit(1/other) on failure.
|
262
|
+
"""
|
263
|
+
@functools.wraps(fn)
|
264
|
+
def wrapper(*args, **kwargs) -> NoReturn: # type: ignore
|
265
|
+
try:
|
266
|
+
fn(*args, **kwargs)
|
267
|
+
sys.exit(0)
|
268
|
+
|
269
|
+
except KeyboardInterrupt:
|
270
|
+
console.print("[bold red]\nInterrupted by user")
|
271
|
+
sys.exit(1)
|
272
|
+
|
273
|
+
except Util.TestResultsReady:
|
274
|
+
print("reached checkpoint")
|
275
|
+
sys.exit(0)
|
276
|
+
|
277
|
+
except CertoraFoundViolations as e:
|
278
|
+
link = getattr(e.results, "rule_report_link", None)
|
279
|
+
if link:
|
280
|
+
print(f"report url: {link}")
|
281
|
+
console.print("[bold red]\nViolations were found\n")
|
282
|
+
sys.exit(1)
|
283
|
+
|
284
|
+
except Util.CertoraUserInputError as e:
|
285
|
+
if e.orig:
|
286
|
+
print(f"\n{str(e.orig).strip()}")
|
287
|
+
if e.more_info:
|
288
|
+
print(f"\n{e.more_info.strip()}")
|
289
|
+
console.print(f"[bold red]\n{e}\n")
|
290
|
+
sys.exit(1)
|
291
|
+
|
292
|
+
except Util.ExitException as e:
|
293
|
+
console.print(f"[bold red]{e}")
|
294
|
+
sys.exit(e.exit_code)
|
295
|
+
|
296
|
+
except Exception as e:
|
297
|
+
console.print(f"[bold red]{e}")
|
298
|
+
sys.exit(1)
|
299
|
+
|
300
|
+
return wrapper
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# The Certora Prover
|
2
|
+
# Copyright (C) 2025 Certora Ltd.
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, version 3 of the License.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
"""
|
17
|
+
Shared helpers for Rust-based targets (Solana & Soroban).
|
18
|
+
|
19
|
+
Placing the build logic in one module removes
|
20
|
+
duplication from the dedicated entry scripts.
|
21
|
+
"""
|
22
|
+
|
23
|
+
from __future__ import annotations
|
24
|
+
|
25
|
+
import sys
|
26
|
+
from pathlib import Path
|
27
|
+
|
28
|
+
scripts_dir_path = Path(__file__).parent.parent.resolve() # containing directory
|
29
|
+
sys.path.insert(0, str(scripts_dir_path))
|
30
|
+
|
31
|
+
import time
|
32
|
+
import logging
|
33
|
+
from typing import Dict
|
34
|
+
|
35
|
+
from Shared import certoraUtils as Util
|
36
|
+
|
37
|
+
from CertoraProver.certoraContextClass import CertoraContext
|
38
|
+
|
39
|
+
from CertoraProver.certoraBuildRust import set_rust_build_directory
|
40
|
+
|
41
|
+
|
42
|
+
log = logging.getLogger(__name__)
|
43
|
+
|
44
|
+
|
45
|
+
# --------------------------------------------------------------------------- #
|
46
|
+
# Build
|
47
|
+
# --------------------------------------------------------------------------- #
|
48
|
+
|
49
|
+
def build_rust_project(context: CertoraContext, timings: Dict) -> None:
|
50
|
+
"""
|
51
|
+
Compile the Rust artefact and record elapsed time in *timings*.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
context: The CertoraContext object containing the configuration.
|
55
|
+
timings: A dictionary to store timing information.
|
56
|
+
"""
|
57
|
+
log.debug("Build Rust target")
|
58
|
+
start = time.perf_counter()
|
59
|
+
set_rust_build_directory(context)
|
60
|
+
timings["buildTime"] = round(time.perf_counter() - start, 4)
|
61
|
+
if context.test == str(Util.TestValue.AFTER_BUILD):
|
62
|
+
raise Util.TestResultsReady(context)
|
certora_cli/certoraEVMProver.py
CHANGED
@@ -22,7 +22,8 @@ scripts_dir_path = Path(__file__).parent.resolve() # containing directory
|
|
22
22
|
sys.path.insert(0, str(scripts_dir_path))
|
23
23
|
|
24
24
|
import CertoraProver.certoraContextAttributes as Attrs
|
25
|
-
from
|
25
|
+
from Shared.proverCommon import CertoraRunResult
|
26
|
+
from certoraRun import run_certora
|
26
27
|
from typing import List, Optional
|
27
28
|
|
28
29
|
|
certora_cli/certoraRanger.py
CHANGED
@@ -17,15 +17,13 @@
|
|
17
17
|
|
18
18
|
import sys
|
19
19
|
from pathlib import Path
|
20
|
-
from rich.console import Console
|
21
|
-
|
22
20
|
|
23
21
|
scripts_dir_path = Path(__file__).parent.resolve() # containing directory
|
24
22
|
sys.path.insert(0, str(scripts_dir_path))
|
25
23
|
|
26
24
|
import CertoraProver.certoraContextAttributes as Attrs
|
27
|
-
from
|
28
|
-
from
|
25
|
+
from certoraRun import run_certora
|
26
|
+
from Shared.proverCommon import CertoraRunResult, catch_exits
|
29
27
|
|
30
28
|
from typing import List, Optional
|
31
29
|
|
@@ -33,39 +31,9 @@ from typing import List, Optional
|
|
33
31
|
def run_ranger(args: List[str]) -> Optional[CertoraRunResult]:
|
34
32
|
return run_certora(args, Attrs.RangerAttributes, prover_cmd=sys.argv[0])
|
35
33
|
|
34
|
+
@catch_exits
|
36
35
|
def entry_point() -> None:
|
37
|
-
|
38
|
-
run_ranger(sys.argv[1:])
|
39
|
-
sys.exit(0)
|
40
|
-
except KeyboardInterrupt:
|
41
|
-
Console().print("[bold red]\nInterrupted by user")
|
42
|
-
sys.exit(1)
|
43
|
-
except Util.TestResultsReady:
|
44
|
-
print("reached checkpoint")
|
45
|
-
sys.exit(0)
|
46
|
-
except CertoraFoundViolations as e:
|
47
|
-
try:
|
48
|
-
assert e.results
|
49
|
-
print(f"report url: {e.results.rule_report_link}")
|
50
|
-
except Exception:
|
51
|
-
pass
|
52
|
-
Console().print("[bold red]\nViolations were found\n")
|
53
|
-
sys.exit(1)
|
54
|
-
except Util.CertoraUserInputError as e:
|
55
|
-
if e.orig:
|
56
|
-
print(f"\n{str(e.orig).strip()}")
|
57
|
-
if e.more_info:
|
58
|
-
print(f"\n{e.more_info.strip()}")
|
59
|
-
Console().print(f"[bold red]\n{e}\n")
|
60
|
-
sys.exit(1)
|
61
|
-
|
62
|
-
except Util.ExitException as e:
|
63
|
-
Console().print(f"[bold red]{e}")
|
64
|
-
sys.exit(e.exit_code)
|
65
|
-
|
66
|
-
except Exception as e:
|
67
|
-
Console().print(f"[bold red]{e}")
|
68
|
-
sys.exit(1)
|
36
|
+
run_ranger(sys.argv[1:])
|
69
37
|
|
70
38
|
if __name__ == '__main__':
|
71
39
|
entry_point()
|