fm-weck 1.5.0__py3-none-any.whl → 1.5.2__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 +1 -1
- fm_weck/cli.py +157 -20
- fm_weck/config.py +1 -0
- fm_weck/engine.py +3 -3
- fm_weck/smoke_test_mode.py +68 -21
- {fm_weck-1.5.0.dist-info → fm_weck-1.5.2.dist-info}/METADATA +1 -1
- {fm_weck-1.5.0.dist-info → fm_weck-1.5.2.dist-info}/RECORD +9 -9
- {fm_weck-1.5.0.dist-info → fm_weck-1.5.2.dist-info}/WHEEL +0 -0
- {fm_weck-1.5.0.dist-info → fm_weck-1.5.2.dist-info}/entry_points.txt +0 -0
fm_weck/__init__.py
CHANGED
fm_weck/cli.py
CHANGED
|
@@ -419,8 +419,8 @@ def parse(raw_args: list[str]) -> Tuple[Callable[[], None], Namespace]:
|
|
|
419
419
|
"TOOL",
|
|
420
420
|
help="The tool for which to run the smoke test.",
|
|
421
421
|
type=ToolQualifier,
|
|
422
|
-
|
|
423
|
-
|
|
422
|
+
).completer = ShellCompletion.versions_completer # type: ignore[assignment]
|
|
423
|
+
|
|
424
424
|
smoke_test.add_argument(
|
|
425
425
|
"--gitlab-ci-mode",
|
|
426
426
|
action="store_true",
|
|
@@ -428,6 +428,15 @@ def parse(raw_args: list[str]) -> Tuple[Callable[[], None], Namespace]:
|
|
|
428
428
|
required=False,
|
|
429
429
|
default=False,
|
|
430
430
|
)
|
|
431
|
+
smoke_test.add_argument(
|
|
432
|
+
"--competition-year",
|
|
433
|
+
action="store",
|
|
434
|
+
type=int,
|
|
435
|
+
help="Automatically select the tool version used in the specified competition year (e.g., 2025). "
|
|
436
|
+
"Searches for SV-COMP or Test-Comp participation in that year.",
|
|
437
|
+
required=False,
|
|
438
|
+
default=None,
|
|
439
|
+
)
|
|
431
440
|
smoke_test.set_defaults(main=main_smoke_test)
|
|
432
441
|
|
|
433
442
|
with contextlib.suppress(ImportError):
|
|
@@ -483,6 +492,41 @@ def resolve_property_for_server(prop_name: str) -> Union[Path, str]:
|
|
|
483
492
|
return prop_name
|
|
484
493
|
|
|
485
494
|
|
|
495
|
+
def get_version_for_competition_year(tool_path: Path, year: int) -> Optional[str]:
|
|
496
|
+
"""
|
|
497
|
+
Find the tool version used in a competition for the given year.
|
|
498
|
+
Searches for SV-COMP or Test-Comp participation entries.
|
|
499
|
+
|
|
500
|
+
Args:
|
|
501
|
+
tool_path: Path to the tool's YAML file
|
|
502
|
+
year: Competition year (e.g., 2025)
|
|
503
|
+
|
|
504
|
+
Returns:
|
|
505
|
+
Version string if found, None otherwise
|
|
506
|
+
"""
|
|
507
|
+
import yaml
|
|
508
|
+
|
|
509
|
+
if not tool_path.exists() or not tool_path.is_file():
|
|
510
|
+
return None
|
|
511
|
+
|
|
512
|
+
with tool_path.open("r") as f:
|
|
513
|
+
data = yaml.safe_load(f)
|
|
514
|
+
|
|
515
|
+
competition_participations = data.get("competition_participations", [])
|
|
516
|
+
|
|
517
|
+
# Search for competition entries matching the year
|
|
518
|
+
for participation in competition_participations:
|
|
519
|
+
competition = participation.get("competition", "")
|
|
520
|
+
# Match "SV-COMP 2025", "Test-Comp 2025", etc.
|
|
521
|
+
if f"{year}" in competition and ("SV-COMP" in competition or "Test-Comp" in competition):
|
|
522
|
+
version = participation.get("tool_version")
|
|
523
|
+
if version:
|
|
524
|
+
logger.info("Found version '%s' for %s in %s", version, tool_path.stem, competition)
|
|
525
|
+
return version
|
|
526
|
+
|
|
527
|
+
return None
|
|
528
|
+
|
|
529
|
+
|
|
486
530
|
def set_log_options(loglevel: Optional[str], logfile: Optional[str], config: dict[str, Any]):
|
|
487
531
|
level = "WARNING"
|
|
488
532
|
level = loglevel.upper() if loglevel else config.get("logging", {}).get("level", level)
|
|
@@ -681,36 +725,129 @@ def check_client_options(timelimit_arg, output_path=None):
|
|
|
681
725
|
return timelimit, output_path
|
|
682
726
|
|
|
683
727
|
|
|
684
|
-
def
|
|
685
|
-
from .
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
try:
|
|
689
|
-
tool = resolve_tool(args.TOOL[0])
|
|
690
|
-
except KeyError:
|
|
691
|
-
logger.error("Unknown tool: %s", args.TOOL[0].tool)
|
|
692
|
-
return 1
|
|
693
|
-
|
|
694
|
-
fm_data, shelve_space = setup_fm_tool(
|
|
695
|
-
fm_tool=tool,
|
|
696
|
-
version=args.TOOL[0].version,
|
|
697
|
-
configuration=Config(),
|
|
728
|
+
def _do_smoke_test_mode(fm_data, shelve_space, tool, gitlab_ci_mode) -> int:
|
|
729
|
+
from .smoke_test_mode import (
|
|
730
|
+
run_smoke_test,
|
|
731
|
+
run_smoke_test_gitlab_ci,
|
|
698
732
|
)
|
|
699
733
|
|
|
700
|
-
|
|
734
|
+
# GitLab CI mode installs packages directly and runs the script on the host
|
|
735
|
+
if gitlab_ci_mode:
|
|
701
736
|
from subprocess import CalledProcessError
|
|
702
737
|
|
|
703
738
|
try:
|
|
704
739
|
run_smoke_test_gitlab_ci(fm_data, shelve_space)
|
|
740
|
+
return 0
|
|
705
741
|
except CalledProcessError as e:
|
|
742
|
+
logger.error(
|
|
743
|
+
"Smoke test script failed in GitLab CI mode (exit code %d).\n- Tool: %s\n- Script directory: %s\n",
|
|
744
|
+
e.returncode,
|
|
745
|
+
tool.stem,
|
|
746
|
+
shelve_space,
|
|
747
|
+
)
|
|
706
748
|
return e.returncode
|
|
707
749
|
|
|
708
|
-
|
|
750
|
+
# Containerized mode: run script inside the configured image
|
|
751
|
+
result = run_smoke_test(fm_data, shelve_space, Config())
|
|
752
|
+
if result.exit_code != 0:
|
|
753
|
+
# Print a concise but informative error for CI logs
|
|
754
|
+
output_lines = result.raw_output.splitlines()
|
|
755
|
+
tail = "\n".join(output_lines[-50:]) if output_lines else "<no output captured>"
|
|
756
|
+
logger.error(
|
|
757
|
+
"Smoke test failed (exit code %d).\n"
|
|
758
|
+
"- Tool: %s\n"
|
|
759
|
+
"- Tool cache dir (host): %s\n"
|
|
760
|
+
"- Script name: smoketest.sh or smoke_test.sh\n"
|
|
761
|
+
"Last 50 lines of output:\n%s",
|
|
762
|
+
result.exit_code,
|
|
763
|
+
tool.stem,
|
|
764
|
+
shelve_space,
|
|
765
|
+
tail,
|
|
766
|
+
)
|
|
767
|
+
return result.exit_code
|
|
768
|
+
|
|
769
|
+
return 0
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
def main_smoke_test(args: argparse.Namespace):
|
|
773
|
+
from fm_tools.exceptions import DownloadUnsuccessfulException, UnsupportedDOIException
|
|
774
|
+
|
|
775
|
+
from .serve import setup_fm_tool
|
|
776
|
+
from .smoke_test_mode import (
|
|
777
|
+
NoSmokeTestFileError,
|
|
778
|
+
SmokeTestError,
|
|
779
|
+
SmokeTestFileIsEmptyError,
|
|
780
|
+
SmokeTestFileIsNotExecutableError,
|
|
781
|
+
)
|
|
782
|
+
|
|
783
|
+
try:
|
|
784
|
+
tool = resolve_tool(args.TOOL)
|
|
785
|
+
except KeyError:
|
|
786
|
+
logger.error("Unknown tool: %s", args.TOOL.tool)
|
|
787
|
+
return 1
|
|
788
|
+
|
|
789
|
+
# Handle --competition-year flag
|
|
790
|
+
version = args.TOOL.version
|
|
791
|
+
if args.competition_year:
|
|
792
|
+
if version:
|
|
793
|
+
logger.warning(
|
|
794
|
+
"Both explicit version '%s' and --competition-year %d specified. "
|
|
795
|
+
"Using competition year to determine version.",
|
|
796
|
+
version,
|
|
797
|
+
args.competition_year,
|
|
798
|
+
)
|
|
799
|
+
competition_version = get_version_for_competition_year(tool, args.competition_year)
|
|
800
|
+
if competition_version:
|
|
801
|
+
logger.info("Using version '%s' for competition year %d", competition_version, args.competition_year)
|
|
802
|
+
version = competition_version
|
|
803
|
+
else:
|
|
804
|
+
logger.error("No competition participation found for year %d in tool %s", args.competition_year, tool.stem)
|
|
805
|
+
return 1
|
|
806
|
+
|
|
807
|
+
try:
|
|
808
|
+
fm_data, shelve_space = setup_fm_tool(
|
|
809
|
+
fm_tool=tool,
|
|
810
|
+
version=version,
|
|
811
|
+
configuration=Config(),
|
|
812
|
+
)
|
|
813
|
+
except (DownloadUnsuccessfulException, UnsupportedDOIException) as e:
|
|
814
|
+
if "code: 504" in str(e).lower():
|
|
815
|
+
print(
|
|
816
|
+
"Failed to download the tool due to a timeout (504 Gateway Timeout). "
|
|
817
|
+
"This issue is likely caused by Zenodo. Retry by rerunning the smoke test.",
|
|
818
|
+
)
|
|
819
|
+
else:
|
|
820
|
+
print(f"There was an error while downloading and unpacking the tool:\n{e}")
|
|
709
821
|
|
|
710
822
|
try:
|
|
711
|
-
|
|
823
|
+
return _do_smoke_test_mode(fm_data, shelve_space, tool, args.gitlab_ci_mode)
|
|
824
|
+
except NoSmokeTestFileError as e:
|
|
825
|
+
print(
|
|
826
|
+
f"{e}\n"
|
|
827
|
+
"Expected a smoke test script named 'smoketest.sh' or 'smoke_test.sh' in the tool directory.\n"
|
|
828
|
+
"Action: Add a minimal script 'smoke_test.sh' to the root of the tool directory that exercises the tool.\n"
|
|
829
|
+
"The top level contents of the tool directory were:\n"
|
|
830
|
+
f"{os.linesep.join([str(p.name) for p in shelve_space.iterdir()])}",
|
|
831
|
+
)
|
|
832
|
+
return 1
|
|
833
|
+
except SmokeTestFileIsEmptyError as e:
|
|
834
|
+
print(
|
|
835
|
+
f"{e}\nAction: Populate the smoke test script with at least one command that validates basic startup.",
|
|
836
|
+
)
|
|
837
|
+
return 1
|
|
838
|
+
except SmokeTestFileIsNotExecutableError as e:
|
|
839
|
+
print(
|
|
840
|
+
f"{e}\nAction: Make the smoke test script executable. On linux, you can do this by running:\n"
|
|
841
|
+
f" chmod +x {e.smoke_test_file}",
|
|
842
|
+
)
|
|
843
|
+
return 1
|
|
712
844
|
except ValueError as e:
|
|
713
|
-
|
|
845
|
+
# e.g., invalid shelve space path, or other validation errors
|
|
846
|
+
print(f"Smoke test setup failed: {e}")
|
|
847
|
+
return 1
|
|
848
|
+
except SmokeTestError as e:
|
|
849
|
+
# Fallback for any other smoke-test specific errors
|
|
850
|
+
print(f"Error starting the smoke test: {e}")
|
|
714
851
|
return 1
|
|
715
852
|
|
|
716
853
|
|
fm_weck/config.py
CHANGED
fm_weck/engine.py
CHANGED
|
@@ -66,7 +66,7 @@ class Engine(ABC):
|
|
|
66
66
|
overlay_tool_dir: Optional[str] = None
|
|
67
67
|
image: Optional[str] = None
|
|
68
68
|
dry_run: bool = False
|
|
69
|
-
work_dir:
|
|
69
|
+
work_dir: Path = Path(CWD_MOUNT_LOCATION)
|
|
70
70
|
|
|
71
71
|
def __init__(self, image: Union[str, FmImageConfig]):
|
|
72
72
|
self._tmp_output_dir = Path(mkdtemp("fm_weck_output")).resolve()
|
|
@@ -84,7 +84,7 @@ class Engine(ABC):
|
|
|
84
84
|
shutil.rmtree(self._tmp_output_dir)
|
|
85
85
|
|
|
86
86
|
def get_workdir(self) -> str:
|
|
87
|
-
return
|
|
87
|
+
return self.work_dir.as_posix()
|
|
88
88
|
|
|
89
89
|
def set_log_file(self, log_file: Path):
|
|
90
90
|
self.log_file = log_file
|
|
@@ -142,7 +142,7 @@ class Engine(ABC):
|
|
|
142
142
|
"--entrypoint",
|
|
143
143
|
'[""]',
|
|
144
144
|
"-v",
|
|
145
|
-
f"{Path.cwd().absolute()}:{
|
|
145
|
+
f"{Path.cwd().absolute()}:{Path(CWD_MOUNT_LOCATION).as_posix()}",
|
|
146
146
|
"-v",
|
|
147
147
|
f"{Config().cache_location}:{CACHE_MOUNT_LOCATION}",
|
|
148
148
|
"-v",
|
fm_weck/smoke_test_mode.py
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
# SPDX-License-Identifier: Apache-2.0
|
|
7
7
|
|
|
8
8
|
import logging
|
|
9
|
+
import os
|
|
9
10
|
import subprocess
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
|
|
@@ -16,6 +17,67 @@ from .engine import CACHE_MOUNT_LOCATION, Engine
|
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
class SmokeTestError(Exception):
|
|
21
|
+
"""Custom exception for smoke test errors."""
|
|
22
|
+
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class NoSmokeTestFileError(SmokeTestError):
|
|
27
|
+
"""Exception raised when no smoke test file is found."""
|
|
28
|
+
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SmokeTestFileIsEmptyError(SmokeTestError):
|
|
33
|
+
"""Exception raised when the smoke test file is empty."""
|
|
34
|
+
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class SmokeTestFileIsNotExecutableError(SmokeTestError):
|
|
39
|
+
"""Exception raised when the smoke test file is not executable."""
|
|
40
|
+
|
|
41
|
+
smoke_test_file: Path
|
|
42
|
+
|
|
43
|
+
def __init__(self, smoke_test_file: Path):
|
|
44
|
+
self.smoke_test_file = smoke_test_file
|
|
45
|
+
super().__init__(f"Smoke test file is not executable: {smoke_test_file}")
|
|
46
|
+
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def locate_and_check_smoke_test_file(shelve_space: Path) -> str:
|
|
51
|
+
"""Check if the smoke test file exists and is not empty.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
shelve_space: Path to the shelve space directory.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
The relative path to the smoke test file as a string.
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
NoSmokeTestFileError: If the smoke test file does not exist.
|
|
61
|
+
SmokeTestFileIsEmptyError: If the smoke test file is empty.
|
|
62
|
+
"""
|
|
63
|
+
if not (shelve_space / "smoketest.sh").exists() and not (shelve_space / "smoke_test.sh").exists():
|
|
64
|
+
raise NoSmokeTestFileError(f"Smoke test file not found in: {shelve_space}")
|
|
65
|
+
|
|
66
|
+
if (shelve_space / "smoketest.sh").exists():
|
|
67
|
+
file_path = shelve_space / "smoketest.sh"
|
|
68
|
+
else:
|
|
69
|
+
# Since we checked for existence above, this must exist
|
|
70
|
+
file_path = shelve_space / "smoke_test.sh"
|
|
71
|
+
|
|
72
|
+
if not file_path.stat().st_mode & os.X_OK:
|
|
73
|
+
raise SmokeTestFileIsNotExecutableError(file_path.relative_to(shelve_space))
|
|
74
|
+
|
|
75
|
+
if file_path.stat().st_size == 0:
|
|
76
|
+
raise SmokeTestFileIsEmptyError(f"Smoke test file is empty: {file_path}")
|
|
77
|
+
|
|
78
|
+
return f"./{file_path.name}"
|
|
79
|
+
|
|
80
|
+
|
|
19
81
|
def run_smoke_test(fm_data, shelve_space, config):
|
|
20
82
|
if not shelve_space.exists() or not shelve_space.is_dir():
|
|
21
83
|
raise ValueError(f"Invalid shelve space path: {shelve_space}")
|
|
@@ -25,15 +87,10 @@ def run_smoke_test(fm_data, shelve_space, config):
|
|
|
25
87
|
tool_dir = shelve_space.relative_to(config.cache_location)
|
|
26
88
|
engine.work_dir = CACHE_MOUNT_LOCATION / tool_dir
|
|
27
89
|
|
|
28
|
-
|
|
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")
|
|
90
|
+
command = locate_and_check_smoke_test_file(shelve_space)
|
|
35
91
|
|
|
36
|
-
|
|
92
|
+
# Return the result so callers can react to failures and print diagnostics
|
|
93
|
+
return engine.run(command)
|
|
37
94
|
|
|
38
95
|
|
|
39
96
|
def run_smoke_test_gitlab_ci(fm_data: FmToolVersion, tool_dir: Path):
|
|
@@ -61,21 +118,11 @@ def run_smoke_test_gitlab_ci(fm_data: FmToolVersion, tool_dir: Path):
|
|
|
61
118
|
else:
|
|
62
119
|
logger.info("No required packages specified for this tool")
|
|
63
120
|
|
|
64
|
-
|
|
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
|
-
)
|
|
121
|
+
script_command = locate_and_check_smoke_test_file(tool_dir)
|
|
75
122
|
|
|
76
|
-
logger.info("Running smoke test script: %s",
|
|
123
|
+
logger.info("Running smoke test script: %s", script_command)
|
|
77
124
|
try:
|
|
78
|
-
subprocess.run([
|
|
125
|
+
subprocess.run([script_command], cwd=tool_dir, check=True)
|
|
79
126
|
logger.info("Smoke test completed successfully")
|
|
80
127
|
except subprocess.CalledProcessError as e:
|
|
81
128
|
logger.error("Smoke test failed with return code %d", e.returncode)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
fm_weck/__init__.py,sha256=
|
|
1
|
+
fm_weck/__init__.py,sha256=P8xygPjF_sqOcyrYtuILR2BsTY8VZAPkvhl0WRrgKSc,351
|
|
2
2
|
fm_weck/__main__.py,sha256=IfNDAqM6MK6P7KsQoW3wOHPOscB8evdVlS9C7R4wd_0,391
|
|
3
3
|
fm_weck/cache_mgr.py,sha256=3-OQFmCeswazXmX08ND4oEHFOR07ZDCwWzjmFTDkOSE,1373
|
|
4
4
|
fm_weck/capture.py,sha256=iogn3JvCZEjD0SQL8Xa3TzmZAWifd9ZIP81c3JIsdUQ,982
|
|
5
|
-
fm_weck/cli.py,sha256=
|
|
6
|
-
fm_weck/config.py,sha256=
|
|
7
|
-
fm_weck/engine.py,sha256=
|
|
5
|
+
fm_weck/cli.py,sha256=wDRzNUvocQmBKicP7aeJPWLmiIlHd_nnH87vEowMNk4,29097
|
|
6
|
+
fm_weck/config.py,sha256=DHIdGA4OhVAHAPJyB--G19EwGshAirmb6PGwoJQtZaE,8136
|
|
7
|
+
fm_weck/engine.py,sha256=P48zCTeMHI3imE0ho5AxhDNTRY3mX0Nzd0gBzjqRCX4,21729
|
|
8
8
|
fm_weck/exceptions.py,sha256=AfqTt6gxZPUQ0rKqwgdGTyfIjWmU3xBFIJxQnMLbLGo,2465
|
|
9
9
|
fm_weck/file_util.py,sha256=FG_uBuNWGWbSivBv0dYzwugMkGfdS_iFY-hG6GLDD54,799
|
|
10
10
|
fm_weck/image_mgr.py,sha256=lkn1nWuwKtMhtVf_PFZOOJftY5uyE0EydY2f-sefHGE,1943
|
|
@@ -12,7 +12,7 @@ fm_weck/run_result.py,sha256=srg3w2MvC-2YqgpRtqrat2DoxhErtlc5FQO3uaFaGTI,1253
|
|
|
12
12
|
fm_weck/runexec_mode.py,sha256=UamxVvYm0XErPjR2sRJaLMX8uHBzRcgCTWbQIZjdju0,2195
|
|
13
13
|
fm_weck/runexec_util.py,sha256=YBvVIPpmEousZVxbZ5NS8jzpKPLyws31kIFE2z3Ki2E,1370
|
|
14
14
|
fm_weck/serve.py,sha256=RUz_3v15By_CvcBJlNBw-mKuDubAiIha5EfayFBnqps,10728
|
|
15
|
-
fm_weck/smoke_test_mode.py,sha256=
|
|
15
|
+
fm_weck/smoke_test_mode.py,sha256=5n2LdYyTxATFl-yaDVfkie-IiNACL3Hs2c_CARqMvek,4196
|
|
16
16
|
fm_weck/tmp_file.py,sha256=oJiE8VGTPxhl-bXdtbM8eNqQ4e9ECPG1jDmiboVDo_k,1956
|
|
17
17
|
fm_weck/version_listing.py,sha256=caaoC3n9R-Ao2sEQ_ngOVO3bnKr7cNVeH6EiA8jO5Sc,864
|
|
18
18
|
fm_weck/grpc_service/__init__.py,sha256=TvQSR0pVeh4MMMT40VfzJFyZTHpAOI7C808vjJpWiOs,390
|
|
@@ -165,7 +165,7 @@ fm_weck/resources/fm_tools/wit4java.yml,sha256=ylfze2XbV4zKkVUH57Veqn7G49gW0Byxd
|
|
|
165
165
|
fm_weck/resources/fm_tools/witch.yml,sha256=wwe6lrI2sxGKVZbLeipa38rPhB2pcSUFi9uVngtXGUQ,1795
|
|
166
166
|
fm_weck/resources/fm_tools/witnesslint.yml,sha256=EvMBcm5fx6lgSLRmHSKXSxXIJKZ-BrxLwTXI4GQ6FMs,6812
|
|
167
167
|
fm_weck/resources/fm_tools/witnessmap.yml,sha256=FyZtEloxpWBBjLn9kyqoen2kPjOkH2r4fxAj5gfV8Bg,1692
|
|
168
|
-
fm_weck-1.5.
|
|
169
|
-
fm_weck-1.5.
|
|
170
|
-
fm_weck-1.5.
|
|
171
|
-
fm_weck-1.5.
|
|
168
|
+
fm_weck-1.5.2.dist-info/METADATA,sha256=1a0oYolO5XlQSwC0tT2qWCdzzroGb-PdS6YQbFEZUME,3339
|
|
169
|
+
fm_weck-1.5.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
170
|
+
fm_weck-1.5.2.dist-info/entry_points.txt,sha256=toWpKCSY1u593MPnI_xW5gnwlnkerP4AvmPQ1s2nPgY,50
|
|
171
|
+
fm_weck-1.5.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|