fm-weck 1.4.7__py3-none-any.whl → 1.4.8__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/__init__.py CHANGED
@@ -8,4 +8,4 @@
8
8
  from .config import Config # noqa: F401
9
9
  from .image_mgr import ImageMgr # noqa: F401
10
10
 
11
- __version__ = "1.4.7"
11
+ __version__ = "1.4.8"
fm_weck/cli.py CHANGED
@@ -470,12 +470,7 @@ def main_install(args: argparse.Namespace):
470
470
  logger.error("Unknown tool %s. Skipping installation...", tool)
471
471
  continue
472
472
 
473
- install_fm_tool(
474
- fm_tool=fm_data,
475
- version=tool.version,
476
- configuration=Config(),
477
- install_path=args.destination
478
- )
473
+ install_fm_tool(fm_tool=fm_data, version=tool.version, configuration=Config(), install_path=args.destination)
479
474
 
480
475
  return 0
481
476
 
fm_weck/config.py CHANGED
@@ -8,7 +8,6 @@
8
8
  import importlib.resources as pkg_resources
9
9
  import logging
10
10
  import os
11
- import shutil
12
11
  import stat
13
12
  import sys
14
13
  from functools import cache
@@ -40,6 +39,8 @@ except ImportError:
40
39
 
41
40
  from fm_weck.resources import RUN_WITH_OVERLAY, RUNEXEC_SCRIPT
42
41
 
42
+ from .file_util import copy_ensuring_unix_line_endings
43
+
43
44
  try:
44
45
  import tomllib as toml
45
46
  except ImportError:
@@ -206,7 +207,7 @@ class Config(object):
206
207
  # Try to copy from package resources
207
208
  try:
208
209
  with pkg_resources.path("fm_weck.resources", target_name) as source_path:
209
- shutil.copy(source_path, target)
210
+ copy_ensuring_unix_line_endings(source_path, target)
210
211
  except FileNotFoundError:
211
212
  logging.error(f"Resource {target_name} not found in package.")
212
213
  return None
@@ -214,7 +215,7 @@ class Config(object):
214
215
  # Compare modification time if the file exists
215
216
  with pkg_resources.path("fm_weck.resources", target_name) as source_path:
216
217
  if source_path.stat().st_mtime > target.stat().st_mtime:
217
- shutil.copy(source_path, target)
218
+ copy_ensuring_unix_line_endings(source_path, target)
218
219
  else:
219
220
  logging.debug(f"Using existing {target_name} script")
220
221
  return target
fm_weck/engine.py CHANGED
@@ -7,6 +7,7 @@
7
7
 
8
8
  import io
9
9
  import logging
10
+ import platform
10
11
  import shutil
11
12
  import signal
12
13
  import subprocess
@@ -73,8 +74,8 @@ class Engine(ABC):
73
74
  if self._tmp_output_dir.exists():
74
75
  shutil.rmtree(self._tmp_output_dir)
75
76
 
76
- def get_workdir(self):
77
- return Path(CWD_MOUNT_LOCATION)
77
+ def get_workdir(self) -> str:
78
+ return Path(CWD_MOUNT_LOCATION).as_posix()
78
79
 
79
80
  def set_output_log(self, output_log: Path):
80
81
  self.log_file = output_log
@@ -126,7 +127,7 @@ class Engine(ABC):
126
127
  "--entrypoint",
127
128
  '[""]',
128
129
  "-v",
129
- f"{Path.cwd().absolute()}:{CWD_MOUNT_LOCATION}",
130
+ f"{Path.cwd().absolute()}:{self.get_workdir()}",
130
131
  "-v",
131
132
  f"{Config().cache_location}:{CACHE_MOUNT_LOCATION}",
132
133
  "-v",
@@ -203,9 +204,9 @@ class Engine(ABC):
203
204
  return p
204
205
  mapped = _map_path(Path(p))
205
206
  if Path(p) == mapped:
206
- return p
207
+ return Path(p).as_posix()
207
208
  else:
208
- return mapped
209
+ return Path(mapped).as_posix()
209
210
 
210
211
  return tuple(map(_map_path, command))
211
212
 
@@ -227,7 +228,7 @@ class Engine(ABC):
227
228
 
228
229
  @staticmethod
229
230
  def _base_engine_class(config: Config):
230
- engine = config.defaults().get("engine", "podman").lower()
231
+ engine = "docker" if platform.system() != "Linux" else config.defaults().get("engine", "podman").lower()
231
232
 
232
233
  if engine == "docker":
233
234
  return Docker
fm_weck/file_util.py ADDED
@@ -0,0 +1,23 @@
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
+ from pathlib import Path
9
+
10
+ # replacement strings
11
+ WINDOWS_LINE_ENDING = b"\r\n"
12
+ UNIX_LINE_ENDING = b"\n"
13
+
14
+
15
+ def copy_ensuring_unix_line_endings(src: Path, dst: Path) -> None:
16
+ with open(src, "rb") as src_file:
17
+ content = src_file.read()
18
+
19
+ # Windows ➡ Unix
20
+ content = content.replace(WINDOWS_LINE_ENDING, UNIX_LINE_ENDING)
21
+
22
+ with open(dst, "wb") as dst_file:
23
+ dst_file.write(content)
@@ -6,7 +6,16 @@ description: |
6
6
  It features modular architecture, symbolic/distributed/multi-core reachability,
7
7
  and easy extensibility for new languages.
8
8
  input_languages:
9
+ - B
10
+ - DVE
11
+ - ETF
12
+ - Event-B
13
+ - MCRL
14
+ - MCRL2
9
15
  - PNML
16
+ - Promela
17
+ - TLA+
18
+ - Z
10
19
  project_url: https://ltsmin.utwente.nl/
11
20
  repository_url: https://github.com/utwente-fmt/ltsmin
12
21
  spdx_license_identifier: BSD-3-Clause
@@ -28,7 +37,7 @@ versions:
28
37
  url: "https://github.com/utwente-fmt/ltsmin/releases/download/v3.0.2/ltsmin-v3.0.2-linux.tgz"
29
38
  benchexec_toolinfo_options: ["pnml2lts-sym"]
30
39
  required_ubuntu_packages: []
31
- base_container_image: ["ubuntu:24.04"]
40
+ base_container_images: ["ubuntu:24.04"]
32
41
 
33
42
  competition_participations: []
34
43
 
@@ -36,5 +45,14 @@ techniques: []
36
45
 
37
46
  frameworks_solvers: []
38
47
 
39
- literature: []
48
+ literature:
49
+ - doi: 10.1007/978-3-662-46681-0_61
50
+ title: "LTSmin: High-Performance Language-Independent Model Checking"
51
+ year: 2015
52
+ - doi: 10.1007/978-3-642-20398-5_40
53
+ title: "Multi-Core LTSmin: Marrying Modularity and Scalability"
54
+ year: 2011
55
+ - doi: 10.1007/978-3-642-14295-6_31
56
+ title: "LTSmin: Distributed and Symbolic Reachability"
57
+ year: 2010
40
58
 
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
24
  from .engine import CACHE_MOUNT_LOCATION, 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")
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.4.8
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
@@ -36,7 +36,7 @@ We provide a tutorial on how to use fm-weck [here](doc/Tutorial.md).
36
36
 
37
37
  ## Install dependencies
38
38
 
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:
39
+ `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
40
 
41
41
  ```
42
42
  sudo apt install podman
@@ -46,11 +46,11 @@ sudo apt install podman
46
46
 
47
47
  There are three modes of operation: `run`, `shell` and `expert`.
48
48
 
49
+ - `run`: (**Recommended**) enables plug-and-play execution of formal methods tools: it
50
+ 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
51
  - `expert`: executes a tool in it's containerized environment specified through the
50
52
  corresponding fm-tools YAML file: All arguments are passed verbatim to the tool.
51
53
  - `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
54
 
55
55
  ## Development and Testing
56
56
 
@@ -63,4 +63,10 @@ In Linux this can be done by running the following command in the root directory
63
63
 
64
64
  ```
65
65
  ln -s $(pwd)/fm_tools/data $(pwd)/src/fm_weck/resources/fm_tools
66
- ```
66
+ ```
67
+
68
+ ## Publications
69
+
70
+ A paper about fm-weck has been accepted at the FM '24 conference.
71
+
72
+ - [<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,15 +1,17 @@
1
- fm_weck/__init__.py,sha256=qVPurJ_oj0SwP8asFRG0x8Qh7KDzNBLJf5jHKxcyDMc,351
1
+ fm_weck/__init__.py,sha256=hwPTOu20emETJm7zBfj7trYoLdZD-A_ZoL0mtmZcyLw,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
4
+ fm_weck/cli.py,sha256=gWiFsbkCOWS6oXTr6srWza-qFrfpU_XR1P-MxHrCQuM,17136
5
+ fm_weck/config.py,sha256=8XXlHbb9cW1N1jatNFY5AnaRdxsSz-ohCrqq5t90RAc,8099
6
+ fm_weck/engine.py,sha256=zway0nC4piPohK3hoBYr9Sv3xAa32dLeuPSuOw1Wers,17376
7
7
  fm_weck/exceptions.py,sha256=xXxbYK-FZW6YksBtaN79KaA4fGnFCBO-wc0QMqtq3Og,282
8
+ fm_weck/file_util.py,sha256=R9y6LUZCDNJR4j25_Q24lN_zhACnOjYP5BvcpYaQPiA,649
8
9
  fm_weck/image_mgr.py,sha256=mDqP-YD6AEOW2xldYJ4D-wxYKF0Ta5SgoJiX2CrhyQg,1906
9
10
  fm_weck/run_result.py,sha256=0d8G3py1VCPP4jLxPCVzL8Vljvrwt9IHlmvCkW0pwx8,1249
10
11
  fm_weck/runexec_mode.py,sha256=UamxVvYm0XErPjR2sRJaLMX8uHBzRcgCTWbQIZjdju0,2195
11
12
  fm_weck/runexec_util.py,sha256=YBvVIPpmEousZVxbZ5NS8jzpKPLyws31kIFE2z3Ki2E,1370
12
- fm_weck/serve.py,sha256=RL6LOnK0RMhFRWehwFdoydc0A619FqSaph7H0JFDYlo,9037
13
+ fm_weck/serve.py,sha256=mDaZ1BUKwUh_fPgGMR4LXrJ1c5GOmwwXbHbMllmJzi8,10608
14
+ fm_weck/tmp_file.py,sha256=oJiE8VGTPxhl-bXdtbM8eNqQ4e9ECPG1jDmiboVDo_k,1956
13
15
  fm_weck/version_listing.py,sha256=caaoC3n9R-Ao2sEQ_ngOVO3bnKr7cNVeH6EiA8jO5Sc,864
14
16
  fm_weck/resources/BenchExec-3.27-py3-none-any.whl,sha256=g-db8LM8HfqLhbnl7n5lvUbMnF2tZ4MHAVOxTGxqO8w,732849
15
17
  fm_weck/resources/BenchExec-3.27-py3-none-any.whl.license,sha256=Nq2Mwgn_pyr6ZZrTT095QPtFP3hr15ZeIRIaY0B7eC8,201
@@ -101,7 +103,7 @@ fm_weck/resources/fm_tools/legion.yml,sha256=ClD8_UEJq8wHIEp9_ifhTNKJuNmzvekaQx1
101
103
  fm_weck/resources/fm_tools/lf-checker.yml,sha256=lV4-GzzNvzWW505FFtYAUHlSG4gxOn-LL4xT8Tpyvq0,1386
102
104
  fm_weck/resources/fm_tools/liv.yml,sha256=t0-Rfs0Ot3VPn7DD_pt3VAbyfpUOP8kdYV3E7uN3qGI,3260
103
105
  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
106
+ fm_weck/resources/fm_tools/ltsmin.yml,sha256=WkQ5aP-p1yFP6XfRXJeb5ftcd7o_buvNjEKZRVr6mpM,1644
105
107
  fm_weck/resources/fm_tools/metaval++.yml,sha256=KAOcvftDNLvuHzIKaUoebekeOxBE6DVS2Acq2VbL1Dg,2006
106
108
  fm_weck/resources/fm_tools/metaval.yml,sha256=aclr5cfGDuCduSrlk9kLOcK8GaUQAzPtDAxIJHgHs44,4668
107
109
  fm_weck/resources/fm_tools/mlb.yml,sha256=S_ykc9ThLHBbMSXOgYIvk81r96cEwXbAXI5lPRDxJ4s,1907
@@ -148,7 +150,7 @@ fm_weck/resources/fm_tools/wit4java.yml,sha256=ylfze2XbV4zKkVUH57Veqn7G49gW0Byxd
148
150
  fm_weck/resources/fm_tools/witch.yml,sha256=wwe6lrI2sxGKVZbLeipa38rPhB2pcSUFi9uVngtXGUQ,1795
149
151
  fm_weck/resources/fm_tools/witnesslint.yml,sha256=EvMBcm5fx6lgSLRmHSKXSxXIJKZ-BrxLwTXI4GQ6FMs,6812
150
152
  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,,
153
+ fm_weck-1.4.8.dist-info/METADATA,sha256=gGPe1x8nywUCLB85Ldg2MTFg4j7gMU8LRPFarOjIQKo,3269
154
+ fm_weck-1.4.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
155
+ fm_weck-1.4.8.dist-info/entry_points.txt,sha256=toWpKCSY1u593MPnI_xW5gnwlnkerP4AvmPQ1s2nPgY,50
156
+ fm_weck-1.4.8.dist-info/RECORD,,