fm-weck 1.4.7__py3-none-any.whl → 1.5.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.
fm_weck/serve.py CHANGED
@@ -9,18 +9,20 @@ import dbm
9
9
  import json
10
10
  import logging
11
11
  import os
12
- import shutil
13
12
  import sys
14
13
  from pathlib import Path
15
- from typing import Optional, Tuple, Union
14
+ from typing import Optional, Tuple, Union, cast
16
15
 
17
16
  from fm_tools.benchexec_helper import DataModel
17
+ from fm_tools.files import unzip
18
18
  from fm_tools.fmtoolversion import FmToolVersion
19
19
 
20
20
  from fm_weck.run_result import RunResult
21
+ from fm_weck.tmp_file import NTempFile
21
22
 
22
23
  from .config import Config, parse_fm_data
23
- from .engine import CACHE_MOUNT_LOCATION, Engine
24
+ from .engine import Engine
25
+ from .file_util import copy_ensuring_unix_line_endings
24
26
 
25
27
  logger = logging.getLogger(__name__)
26
28
 
@@ -69,6 +71,7 @@ def setup_fm_tool(
69
71
  # Don't explicitly disallow non-FmToolVersion here; Pythonic Users might want to exchange the FmToolVersion object
70
72
  # by a class with the same interface
71
73
  fm_data = parse_fm_data(fm_tool, version) if isinstance(fm_tool, (Path, str)) else fm_tool
74
+ fm_data = cast(FmToolVersion, fm_data)
72
75
 
73
76
  shelve_space = configuration.get_shelve_space_for(fm_data)
74
77
  logger.debug("Using shelve space %s", shelve_space)
@@ -84,7 +87,42 @@ def setup_fm_tool(
84
87
  skip_download = check_cache_entry(shelve_space, checksum, configuration)
85
88
 
86
89
  if not skip_download:
87
- fm_data.download_and_install_into(shelve_space)
90
+ if sys.platform != "win32":
91
+ fm_data.download_and_install_into(shelve_space)
92
+ checksum = fm_data.get_archive_location().resolve().checksum
93
+ if checksum is None:
94
+ logger.warning(
95
+ "No checksum available for %s, skipping checksum update", fm_data.get_tool_name_with_version()
96
+ )
97
+ else:
98
+ update_checksum(shelve_space, checksum, configuration)
99
+
100
+ # On Windows, we need to download the tool first and then unzip it
101
+ # This is because the unzip operation might fail due to a permission error
102
+ else:
103
+ dl_loc = NTempFile(f"{fm_data.get_tool_name_with_version()}-dl.zip")
104
+ fm_data.download_into(dl_loc.name)
105
+
106
+ success = False
107
+ last_error = None
108
+
109
+ logging.info("Unzipping downloaded archive...")
110
+
111
+ # On Windows, the unzip operation might fail due to a permission error.
112
+ # Retrying the operation a few times mitigates this issue.
113
+ for _ in range(100):
114
+ try:
115
+ unzip(dl_loc.name, shelve_space)
116
+ success = True
117
+ break
118
+ except PermissionError as e:
119
+ last_error = e
120
+ continue
121
+
122
+ if not success:
123
+ logger.error("Failed to unzip downloaded archive")
124
+ raise last_error
125
+
88
126
  checksum = fm_data.get_archive_location().resolve().checksum
89
127
  if checksum is None:
90
128
  logger.warning(
@@ -93,6 +131,7 @@ def setup_fm_tool(
93
131
  else:
94
132
  update_checksum(shelve_space, checksum, configuration)
95
133
  map_doi(fm_data, shelve_space)
134
+
96
135
  tool_info_module = fm_data.get_toolinfo_module()
97
136
 
98
137
  if (not offline_mode) or tool_info_module._trivially_resolved():
@@ -140,7 +179,7 @@ def run_guided(
140
179
  # copy the property to the weck_cache which should be mounted
141
180
  source_property_path = prop
142
181
  property_path = configuration.get_shelve_path_for_property(source_property_path)
143
- shutil.copyfile(source_property_path, property_path)
182
+ copy_ensuring_unix_line_endings(source_property_path, property_path)
144
183
  except KeyError:
145
184
  logger.error("Unknown property %s", prop)
146
185
  return RunResult(command=[], exit_code=1, raw_output="Unknown property")
@@ -151,10 +190,10 @@ def run_guided(
151
190
  engine = Engine.from_config(fm_data, configuration)
152
191
 
153
192
  if log_output_to is not None:
154
- engine.set_output_log(log_output_to)
193
+ engine.set_log_file(log_output_to)
155
194
 
156
195
  if output_files_to is not None:
157
- engine.set_output_files_dir(output_files_to)
196
+ engine.set_output_dir(output_files_to)
158
197
  engine.print_output_to_stdout = print_tool_output_to_console
159
198
 
160
199
  current_dir = Path.cwd().resolve()
@@ -181,11 +220,9 @@ def run_guided(
181
220
 
182
221
  logger.debug("Assembled command from fm-tools: %s", command)
183
222
 
184
- execution_result = engine.run(
185
- f"{CACHE_MOUNT_LOCATION}/.scripts/run_with_overlay.sh", shelve_space.name, *command, timeout_sec=timeout_sec
186
- )
223
+ engine.use_overlay(shelve_space.name)
187
224
 
188
- return execution_result
225
+ return engine.run(*command, timeout_sec=timeout_sec)
189
226
 
190
227
 
191
228
  def run_manual(
@@ -194,6 +231,7 @@ def run_manual(
194
231
  configuration: Config,
195
232
  command: list[str],
196
233
  offline_mode: bool = False,
234
+ use_overlay: bool = False,
197
235
  log_output_to: Optional[Path] = None,
198
236
  output_files_to: Optional[Path] = None,
199
237
  timeout_sec: Optional[float] = None,
@@ -203,20 +241,28 @@ def run_manual(
203
241
  engine = Engine.from_config(fm_data, configuration)
204
242
 
205
243
  if log_output_to is not None:
206
- engine.set_output_log(log_output_to)
244
+ engine.set_log_file(log_output_to)
207
245
 
208
246
  if output_files_to is not None:
209
- engine.set_output_files_dir(output_files_to)
247
+ engine.set_output_dir(output_files_to)
210
248
 
211
- executable = fm_data.get_executable_path(shelve_space)
212
- logger.debug("Using executable %s", executable)
213
- logger.debug("Assembled command %s", [executable, *command])
249
+ if use_overlay:
250
+ configuration.make_script_available()
251
+ current_dir = Path.cwd().resolve()
252
+ os.chdir(shelve_space)
253
+ executable = fm_data.get_executable_path(Path("."))
254
+ os.chdir(current_dir)
255
+
256
+ logger.debug("Running with overlay...")
257
+ engine.use_overlay(shelve_space.name)
258
+ else:
259
+ executable = fm_data.get_executable_path(shelve_space)
214
260
 
215
261
  if log_output_to is not None:
216
- engine.set_output_log(log_output_to)
262
+ engine.set_log_file(log_output_to)
217
263
 
218
264
  if output_files_to is not None:
219
- engine.set_output_files_dir(output_files_to)
265
+ engine.set_output_dir(output_files_to)
220
266
 
221
267
  engine.print_output_to_stdout = print_tool_output_to_console
222
268
 
@@ -0,0 +1,82 @@
1
+ # This file is part of fm-weck: executing fm-tools in containerized environments.
2
+ # https://gitlab.com/sosy-lab/software/fm-weck
3
+ #
4
+ # SPDX-FileCopyrightText: 2024 Dirk Beyer <https://www.sosy-lab.org>
5
+ #
6
+ # SPDX-License-Identifier: Apache-2.0
7
+
8
+ import logging
9
+ import subprocess
10
+ from pathlib import Path
11
+
12
+ from fm_tools.fmtoolversion import FmToolVersion
13
+
14
+ from .engine import CACHE_MOUNT_LOCATION, Engine
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ def run_smoke_test(fm_data, shelve_space, config):
20
+ if not shelve_space.exists() or not shelve_space.is_dir():
21
+ raise ValueError(f"Invalid shelve space path: {shelve_space}")
22
+
23
+ engine = Engine.from_config(fm_data, config)
24
+
25
+ tool_dir = shelve_space.relative_to(config.cache_location)
26
+ engine.work_dir = CACHE_MOUNT_LOCATION / tool_dir
27
+
28
+ # Check for smoketest.sh first, then smoke_test.sh
29
+ if (shelve_space / "smoketest.sh").exists():
30
+ command = "./smoketest.sh"
31
+ elif (shelve_space / "smoke_test.sh").exists():
32
+ command = "./smoke_test.sh"
33
+ else:
34
+ raise ValueError(f"Smoke test script not found in {shelve_space}. Expected ./smoketest.sh or ./smoke_test.sh")
35
+
36
+ engine.run(command)
37
+
38
+
39
+ def run_smoke_test_gitlab_ci(fm_data: FmToolVersion, tool_dir: Path):
40
+ """
41
+ Run smoke test in GitLab CI mode.
42
+ This mode directly installs required packages using apt instead of building/pulling images.
43
+
44
+ Args:
45
+ fm_data: The FmToolVersion object containing tool information
46
+ tool_dir: The directory containing the tool's smoke_test.sh script
47
+ """
48
+ # Get required packages from fm_data
49
+ required_packages = fm_data.get_images().required_packages
50
+
51
+ if required_packages:
52
+ logger.info("Installing required packages: %s", " ".join(required_packages))
53
+
54
+ # Install packages
55
+ try:
56
+ subprocess.run(["apt", "install", "-y", *required_packages], check=True)
57
+ logger.info("Successfully installed packages: %s", " ".join(required_packages))
58
+ except subprocess.CalledProcessError:
59
+ logger.error("Failed to install packages.")
60
+ raise
61
+ else:
62
+ logger.info("No required packages specified for this tool")
63
+
64
+ # Run the smoke test script
65
+ # Check for smoketest.sh first, then smoke_test.sh
66
+ smoke_test_script = tool_dir / "smoketest.sh"
67
+ if not smoke_test_script.exists():
68
+ smoke_test_script = tool_dir / "smoke_test.sh"
69
+
70
+ if not smoke_test_script.exists():
71
+ raise ValueError(
72
+ f"Smoke test script not found in downloaded tool directory: {tool_dir}. "
73
+ f"Expected ./smoketest.sh or ./smoke_test.sh"
74
+ )
75
+
76
+ logger.info("Running smoke test script: %s", smoke_test_script)
77
+ try:
78
+ subprocess.run([f"./{smoke_test_script.name}"], cwd=tool_dir, check=True)
79
+ logger.info("Smoke test completed successfully")
80
+ except subprocess.CalledProcessError as e:
81
+ logger.error("Smoke test failed with return code %d", e.returncode)
82
+ raise
fm_weck/tmp_file.py ADDED
@@ -0,0 +1,61 @@
1
+ # This file is part of fm-weck: executing fm-tools in containerized environments.
2
+ # https://gitlab.com/sosy-lab/software/fm-weck
3
+ #
4
+ # SPDX-FileCopyrightText: 2024 Dirk Beyer <https://www.sosy-lab.org>
5
+ #
6
+ # SPDX-License-Identifier: Apache-2.0
7
+
8
+ import contextlib
9
+ import os
10
+ from pathlib import Path
11
+ from tempfile import mkdtemp
12
+
13
+
14
+ class NTempFile:
15
+ """
16
+ Custom temporary file context manager.
17
+ It creates a temporary file and deletes it after the context manager is closed.
18
+ The file is not kept open in order to achieve compatibility with Windows.
19
+
20
+ Inspired by https://stackoverflow.com/a/63173312
21
+ """
22
+
23
+ def __init__(self, name, mode="wb"):
24
+ self.__tmp_dir = mkdtemp()
25
+ self.name = Path(self.__tmp_dir) / name
26
+ self._mode = mode
27
+ self._file = None
28
+
29
+ def __enter__(self):
30
+ """Enter the context manager and return the file object."""
31
+ self._file = open(self.name, self._mode)
32
+ return self
33
+
34
+ def __exit__(self, exc_type, exc_val, exc_tb):
35
+ """Exit the context manager and clean up."""
36
+ if self._file is not None:
37
+ self._file.close()
38
+ self._cleanup()
39
+
40
+ def write(self, data):
41
+ """Write data to the temporary file."""
42
+ if self._file is None:
43
+ # If not in context manager, open the file temporarily
44
+ with open(self.name, self._mode) as f:
45
+ f.write(data)
46
+ else:
47
+ self._file.write(data)
48
+ self._file.flush() # Ensure data is written immediately
49
+
50
+ def _cleanup(self):
51
+ """Clean up the temporary file and directory."""
52
+ with contextlib.suppress(OSError):
53
+ if self.name.exists():
54
+ os.unlink(self.name)
55
+ with contextlib.suppress(OSError):
56
+ if Path(self.__tmp_dir).exists():
57
+ os.rmdir(self.__tmp_dir)
58
+
59
+ def __del__(self):
60
+ """Cleanup when object is garbage collected."""
61
+ self._cleanup()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fm-weck
3
- Version: 1.4.7
3
+ Version: 1.5.0
4
4
  Author-email: Henrik Wachowitz <henrik.wachowitz@ifi.lmu.de>
5
5
  Maintainer-email: Henrik Wachowitz <henrik.wachowitz@ifi.lmu.de>
6
6
  Classifier: Development Status :: 4 - Beta
@@ -16,6 +16,8 @@ Requires-Dist: pyyaml>=6.0
16
16
  Requires-Dist: tabulate
17
17
  Requires-Dist: tomli>=2.0; python_version <= '3.10'
18
18
  Requires-Dist: yaspin>=3.0
19
+ Provides-Extra: remote
20
+ Requires-Dist: grpcio-tools; extra == 'remote'
19
21
  Description-Content-Type: text/markdown
20
22
 
21
23
  <!--
@@ -36,7 +38,7 @@ We provide a tutorial on how to use fm-weck [here](doc/Tutorial.md).
36
38
 
37
39
  ## Install dependencies
38
40
 
39
- `fm-weck` requires Python 3.10 or higher. `fm-weck` relies on an available Podman installation. To install Podman on Ubuntu, run the following command:
41
+ `fm-weck` requires Python 3.10 or higher. `fm-weck` relies on an available Podman or Docker installation. To install Podman on Ubuntu, run the following command:
40
42
 
41
43
  ```
42
44
  sudo apt install podman
@@ -46,11 +48,11 @@ sudo apt install podman
46
48
 
47
49
  There are three modes of operation: `run`, `shell` and `expert`.
48
50
 
51
+ - `run`: (**Recommended**) enables plug-and-play execution of formal methods tools: it
52
+ downloads and unpacks a tool from the fm-tools metadata file into a user-specified cache directory on the host system and then runs the tool in the containerized environment
49
53
  - `expert`: executes a tool in it's containerized environment specified through the
50
54
  corresponding fm-tools YAML file: All arguments are passed verbatim to the tool.
51
55
  - `shell`: enters an interactive shell inside of the container specified by the given tool
52
- - `run`: enables plug-and-play execution of formal methods tools: it
53
- downloads and unpacks a tool from the fm-tools metadata file into a user-specified cache directory on the host system and then runs the tool in the containerized environment
54
56
 
55
57
  ## Development and Testing
56
58
 
@@ -63,4 +65,10 @@ In Linux this can be done by running the following command in the root directory
63
65
 
64
66
  ```
65
67
  ln -s $(pwd)/fm_tools/data $(pwd)/src/fm_weck/resources/fm_tools
66
- ```
68
+ ```
69
+
70
+ ## Publications
71
+
72
+ A paper about fm-weck has been accepted at the FM '24 conference.
73
+
74
+ - [<img src="/doc/images/pdf.png" alt="PDF icon" width="32"/> FM-Weck: Containerized Execution of Formal-Methods Tools](https://link.springer.com/content/pdf/10.1007/978-3-031-71177-0_3.pdf), by Dirk Beyer and Henrik Wachowitz. Proc. FM. Springer (2024). [doi:10.1007/978-3-031-71177-0_3](https://doi.org/10.1007/978-3-031-71177-0_3)
@@ -1,20 +1,37 @@
1
- fm_weck/__init__.py,sha256=qVPurJ_oj0SwP8asFRG0x8Qh7KDzNBLJf5jHKxcyDMc,351
1
+ fm_weck/__init__.py,sha256=v0ltYQ0MeG-hs4LVU6TIPeboGSEmIMSDHqOGvQdQR_s,351
2
2
  fm_weck/__main__.py,sha256=IfNDAqM6MK6P7KsQoW3wOHPOscB8evdVlS9C7R4wd_0,391
3
3
  fm_weck/cache_mgr.py,sha256=3-OQFmCeswazXmX08ND4oEHFOR07ZDCwWzjmFTDkOSE,1373
4
- fm_weck/cli.py,sha256=k2-rHURw_w0yy5L97FofJ0jYVxqpNP79_XTtirYbyjY,17194
5
- fm_weck/config.py,sha256=72HfwKJlLnyrBUmLyjEebgPerGMq0CdnQnugz1SIWis,8017
6
- fm_weck/engine.py,sha256=1EhfL0tzgVyBNf4b40yt2w1netv2Elyop9aRpwzCvm0,17262
7
- fm_weck/exceptions.py,sha256=xXxbYK-FZW6YksBtaN79KaA4fGnFCBO-wc0QMqtq3Og,282
8
- fm_weck/image_mgr.py,sha256=mDqP-YD6AEOW2xldYJ4D-wxYKF0Ta5SgoJiX2CrhyQg,1906
9
- fm_weck/run_result.py,sha256=0d8G3py1VCPP4jLxPCVzL8Vljvrwt9IHlmvCkW0pwx8,1249
4
+ fm_weck/capture.py,sha256=iogn3JvCZEjD0SQL8Xa3TzmZAWifd9ZIP81c3JIsdUQ,982
5
+ fm_weck/cli.py,sha256=dPpJqyF0iXqZuRiPATypDbc6TyaUWzUFpb1qc2uo-mg,23657
6
+ fm_weck/config.py,sha256=8XXlHbb9cW1N1jatNFY5AnaRdxsSz-ohCrqq5t90RAc,8099
7
+ fm_weck/engine.py,sha256=HfRj5Okewteco7A-CccCIbk2aEGDh4K896YyQo7h1gE,21733
8
+ fm_weck/exceptions.py,sha256=AfqTt6gxZPUQ0rKqwgdGTyfIjWmU3xBFIJxQnMLbLGo,2465
9
+ fm_weck/file_util.py,sha256=FG_uBuNWGWbSivBv0dYzwugMkGfdS_iFY-hG6GLDD54,799
10
+ fm_weck/image_mgr.py,sha256=lkn1nWuwKtMhtVf_PFZOOJftY5uyE0EydY2f-sefHGE,1943
11
+ fm_weck/run_result.py,sha256=srg3w2MvC-2YqgpRtqrat2DoxhErtlc5FQO3uaFaGTI,1253
10
12
  fm_weck/runexec_mode.py,sha256=UamxVvYm0XErPjR2sRJaLMX8uHBzRcgCTWbQIZjdju0,2195
11
13
  fm_weck/runexec_util.py,sha256=YBvVIPpmEousZVxbZ5NS8jzpKPLyws31kIFE2z3Ki2E,1370
12
- fm_weck/serve.py,sha256=RL6LOnK0RMhFRWehwFdoydc0A619FqSaph7H0JFDYlo,9037
14
+ fm_weck/serve.py,sha256=RUz_3v15By_CvcBJlNBw-mKuDubAiIha5EfayFBnqps,10728
15
+ fm_weck/smoke_test_mode.py,sha256=SYXLf1mhdGEOS9YtJ7XuR8j_zfN9zUWhu1KSm4zgAR4,2990
16
+ fm_weck/tmp_file.py,sha256=oJiE8VGTPxhl-bXdtbM8eNqQ4e9ECPG1jDmiboVDo_k,1956
13
17
  fm_weck/version_listing.py,sha256=caaoC3n9R-Ao2sEQ_ngOVO3bnKr7cNVeH6EiA8jO5Sc,864
18
+ fm_weck/grpc_service/__init__.py,sha256=TvQSR0pVeh4MMMT40VfzJFyZTHpAOI7C808vjJpWiOs,390
19
+ fm_weck/grpc_service/fm_weck_client.py,sha256=xd3ltrHcJ1fdl9V3PUFNukJahoMP_VavXTwEbPcjMyk,4862
20
+ fm_weck/grpc_service/fm_weck_server.py,sha256=kYrhi2Egp09yMP_4-AaW5k9lGd63HTD3KekBFSsG2dk,6142
21
+ fm_weck/grpc_service/request_handling.py,sha256=w_ZNZ1PK22D6SoBMc-hqXhy6S48kmksMXu-wSniYLSU,10912
22
+ fm_weck/grpc_service/run_store.py,sha256=6pm_b2PCPys6qmVQvxq8qUpREdBsdOPpvOhTOfh-Rjc,864
23
+ fm_weck/grpc_service/server_utils.py,sha256=1A3_RdjsvkeCV2w1oZemd4v9YcpcWWwgjy62jF0wUmM,721
24
+ fm_weck/grpc_service/proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ fm_weck/grpc_service/proto/fm_weck_service.proto,sha256=HcRpwWnNTxHEGxitZZb_RcXTQX97erySKItGCg0AhBw,3316
26
+ fm_weck/grpc_service/proto/fm_weck_service_pb2.py,sha256=O8qtEYv77kGKaBbo-AXGTxYJTSTo6rBHN03PIZJB3cA,5647
27
+ fm_weck/grpc_service/proto/fm_weck_service_pb2.pyi,sha256=JH44EhTejp9Nk1dlnPuen2ZVp0WeAnbSvrT6FL9ihJg,6391
28
+ fm_weck/grpc_service/proto/fm_weck_service_pb2_grpc.py,sha256=YCnAPNEGQUNqgVzGB_JeV01fxsiiC7VlP0Aos-n5gWE,12076
29
+ fm_weck/grpc_service/proto/generate_protocol_files.sh,sha256=F9dd95qKejtB7B3P-x9d2oAr9VrW9AZRMfm8KKRKLpk,801
14
30
  fm_weck/resources/BenchExec-3.27-py3-none-any.whl,sha256=g-db8LM8HfqLhbnl7n5lvUbMnF2tZ4MHAVOxTGxqO8w,732849
15
31
  fm_weck/resources/BenchExec-3.27-py3-none-any.whl.license,sha256=Nq2Mwgn_pyr6ZZrTT095QPtFP3hr15ZeIRIaY0B7eC8,201
16
- fm_weck/resources/Containerfile,sha256=MltxP1of9klsQFNR8WyngRTJrPwxQTF4C9ennRxVqSo,391
17
- fm_weck/resources/__init__.py,sha256=-YLqFTF2Gu4RA9ye3SdvBk1rjhlYxk_f1TgPsix3SAk,1213
32
+ fm_weck/resources/Containerfile,sha256=HpPkrzJe1IKsmLOfbe5LbT-suaV6eJn1fT4csSvEkOk,390
33
+ fm_weck/resources/__init__.py,sha256=Gd3SNYBUUgDCJFMVwaaPynmugTUKoWamBXRhbasRKJE,1522
34
+ fm_weck/resources/c_program_example.c,sha256=WOTAn4XyQL8TlplI6M8FbKiUxiwnvHUy2o6n0hLIe6o,21862
18
35
  fm_weck/resources/run_with_overlay.sh,sha256=v1gV_6kMQ0v9BQ3chgDqI1MAOLHbPWeeTC52aCqVpEM,1162
19
36
  fm_weck/resources/runexec,sha256=ogIBO38HLu9C9kDTTANBgAqVnH-UIF1bSJ9d3DSjyF4,462
20
37
  fm_weck/resources/properties/coverage-branches.prp,sha256=Gl2r1cgBFoh4M2laa8dVGhteHkL04oiBRLzxz_hbkEU,56
@@ -101,7 +118,7 @@ fm_weck/resources/fm_tools/legion.yml,sha256=ClD8_UEJq8wHIEp9_ifhTNKJuNmzvekaQx1
101
118
  fm_weck/resources/fm_tools/lf-checker.yml,sha256=lV4-GzzNvzWW505FFtYAUHlSG4gxOn-LL4xT8Tpyvq0,1386
102
119
  fm_weck/resources/fm_tools/liv.yml,sha256=t0-Rfs0Ot3VPn7DD_pt3VAbyfpUOP8kdYV3E7uN3qGI,3260
103
120
  fm_weck/resources/fm_tools/locksmith.yml,sha256=c3wNvIzQ_PZaYv6Mm15QH-Rm4oTuNzILNJs1fssESYQ,1397
104
- fm_weck/resources/fm_tools/ltsmin.yml,sha256=vpfL4mBGlnxPtqoqaqUnR-Ph0kibaOd-h6FYuWHn91M,1206
121
+ fm_weck/resources/fm_tools/ltsmin.yml,sha256=WkQ5aP-p1yFP6XfRXJeb5ftcd7o_buvNjEKZRVr6mpM,1644
105
122
  fm_weck/resources/fm_tools/metaval++.yml,sha256=KAOcvftDNLvuHzIKaUoebekeOxBE6DVS2Acq2VbL1Dg,2006
106
123
  fm_weck/resources/fm_tools/metaval.yml,sha256=aclr5cfGDuCduSrlk9kLOcK8GaUQAzPtDAxIJHgHs44,4668
107
124
  fm_weck/resources/fm_tools/mlb.yml,sha256=S_ykc9ThLHBbMSXOgYIvk81r96cEwXbAXI5lPRDxJ4s,1907
@@ -148,7 +165,7 @@ fm_weck/resources/fm_tools/wit4java.yml,sha256=ylfze2XbV4zKkVUH57Veqn7G49gW0Byxd
148
165
  fm_weck/resources/fm_tools/witch.yml,sha256=wwe6lrI2sxGKVZbLeipa38rPhB2pcSUFi9uVngtXGUQ,1795
149
166
  fm_weck/resources/fm_tools/witnesslint.yml,sha256=EvMBcm5fx6lgSLRmHSKXSxXIJKZ-BrxLwTXI4GQ6FMs,6812
150
167
  fm_weck/resources/fm_tools/witnessmap.yml,sha256=FyZtEloxpWBBjLn9kyqoen2kPjOkH2r4fxAj5gfV8Bg,1692
151
- fm_weck-1.4.7.dist-info/METADATA,sha256=WnQnKmjvJ6HQouE2-s1Mil4cw9NJW0Sp3d9E43upkhk,2822
152
- fm_weck-1.4.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
153
- fm_weck-1.4.7.dist-info/entry_points.txt,sha256=toWpKCSY1u593MPnI_xW5gnwlnkerP4AvmPQ1s2nPgY,50
154
- fm_weck-1.4.7.dist-info/RECORD,,
168
+ fm_weck-1.5.0.dist-info/METADATA,sha256=UqR47gDzSfO-MS7U0lEmpMoYJ5Xe6Vx_4cxMXg7qq-0,3339
169
+ fm_weck-1.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
170
+ fm_weck-1.5.0.dist-info/entry_points.txt,sha256=toWpKCSY1u593MPnI_xW5gnwlnkerP4AvmPQ1s2nPgY,50
171
+ fm_weck-1.5.0.dist-info/RECORD,,