• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mmschlk / shapiq / 18499875864

14 Oct 2025 02:26PM UTC coverage: 92.799% (-0.7%) from 93.522%
18499875864

push

github

web-flow
Enhance type safety and fix bugs across the codebase (#430)

* First Pyright cleanup

* TypeChecked game

* fixed introduced bugs in game and interaction_values

* Pyright Save Sampling

* TypeSafe Approximator

* Typechecked Datasets

* Explainer folder typechecked

* GameTheory Typechecked

* Imputer Typechecked

* Plot Typechecked

* Added static typechecking to pre-commit

* Refactoring

* Add pyright change to CHANGELOG

* Activate code quality show diff

* changed uv sync in pre-commit hook

* made fixtures local import

* Introduced Generic TypeVar in Approximator, reducing ignores

* Introduced Generic Types for Explainer. Approximator, Imputer and ExactComputer can either exist or not, depending on dynamic Type

* Bug fix caused through refactoring

* updated overrides

* tightened CoalitionMatrix to accept only bool arrays

* Remove Python reinstallation step in CI workflow

Removed the step to reinstall Python on Windows due to issues with tkinter. The linked GitHub issue was solved. Doing this as a first try.

* Add Python reinstallation and Tkinter installation steps

Reinstall Python and install Tkinter for Windows tests. prior commit did not help

* Fix command for installing Tkinter in workflow

* Update Windows workflow to install Tkinter via Chocolatey

* Remove Tkinter installation step from Windows workflow and adjust matplotlib usage for headless environments

* adapted some pyright types

* removed generics from explainer again

* tightened index type check

* made n_players None at assignment again

* moved comments

---------

Co-authored-by: Maximilian <maximilian.muschalik@gmail.com>

304 of 360 new or added lines in 51 files covered. (84.44%)

12 existing lines in 9 files now uncovered.

4987 of 5374 relevant lines covered (92.8%)

0.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.84
/src/shapiq/utils/saving.py
1
"""Utils io module for saving and loading data from disk."""
2

3
from __future__ import annotations
1✔
4

5
import datetime
1✔
6
import json
1✔
7
from importlib.metadata import version
1✔
8
from typing import TYPE_CHECKING
1✔
9

10
import numpy as np
1✔
11

12
if TYPE_CHECKING:
1✔
13
    from collections.abc import Mapping, Sequence
×
14
    from pathlib import Path
×
15
    from typing import Literal
×
16

NEW
17
    from shapiq.typing import InteractionScores, JSONType, MetadataBlock
×
18

19

20
def safe_tuple_to_str(t: tuple[int, ...]) -> str:
1✔
21
    """Converts a tuple of integers into a string representation for saving purposes."""
22
    if len(t) == 0:
1✔
23
        return "Empty"
1✔
24
    # make tuple into a hash
25
    return ",".join(map(str, t))
1✔
26

27

28
def safe_str_to_tuple(s: str) -> tuple[int, ...]:
1✔
29
    """Converts a string representation of integers back into a tuple of integers."""
30
    if s == "Empty":
1✔
31
        return ()
1✔
32
    return tuple(map(int, s.split(",")))
1✔
33

34

35
def interactions_to_dict(
1✔
36
    interactions: Mapping[tuple[int, ...], float],
37
) -> dict[str, float]:
38
    """Converts a mapping of interactions to a dictionary for saving."""
39
    return {safe_tuple_to_str(tup): value for tup, value in interactions.items()}
1✔
40

41

42
def dict_to_interactions(
1✔
43
    interaction_dict: dict[str, float],
44
) -> InteractionScores:
45
    """Converts a dictionary of interaction values back to a mapping of tuples to float values."""
46
    return {safe_str_to_tuple(tup_str): value for tup_str, value in interaction_dict.items()}
×
47

48

49
def lookup_and_values_to_dict(
1✔
50
    interaction_lookup: Mapping[tuple[int, ...], int],
51
    interaction_values: Sequence[float] | np.ndarray,
52
) -> dict[str, float]:
53
    """Converts a pair of interaction lookup and values into a dictionary for saving.
54

55
    Args:
56
        interaction_lookup: A mapping from tuples of integers to indices.
57
        interaction_values: A sequence of float values corresponding to the indices in the lookup.
58

59
    Returns:
60
        A dictionary mapping string representations of tuples to their corresponding interaction
61
            values.
62
    """
63
    return {
1✔
64
        safe_tuple_to_str(tup): interaction_values[interaction_lookup[tup]]
65
        for tup in interaction_lookup
66
    }
67

68

69
def dict_to_lookup_and_values(
1✔
70
    interaction_dict: dict[str, float],
71
) -> tuple[dict[tuple[int, ...], int], np.ndarray]:
72
    """Converts a dictionary of interaction values back to a lookup and values.
73

74
    Args:
75
        interaction_dict: A dictionary mapping string representations of tuples to float values.
76

77
    Returns:
78
        A tuple containing a mapping from tuples of integers to indices and a sequence of float
79
            values.
80
    """
81
    interaction_lookup = {
1✔
82
        safe_str_to_tuple(tup_str): idx for idx, tup_str in enumerate(interaction_dict)
83
    }
84
    interaction_values = [interaction_dict[tup_str] for tup_str in interaction_dict]
1✔
85
    interaction_values = np.array(interaction_values, dtype=float)
1✔
86
    return interaction_lookup, interaction_values
1✔
87

88

89
def make_file_metadata(
1✔
90
    object_to_store: object,
91
    *,
92
    data_type: Literal["interaction_values", "game"] | None = None,
93
    desc: str | None = None,
94
    created_from: object | None = None,
95
    parameters: JSONType = None,
96
) -> MetadataBlock:
97
    """Creates a metadata block for saving interaction values or games."""
98
    return {
1✔
99
        "object_name": object_to_store.__class__.__name__,
100
        "data_type": data_type,
101
        "version": version("shapiq"),
102
        "created_from": repr(created_from) if created_from else None,
103
        "description": desc,
104
        "parameters": parameters or {},
105
        "timestamp": datetime.datetime.now(tz=datetime.timezone.utc).isoformat() + "Z",
106
    }
107

108

109
def save_json(data: JSONType, path: Path) -> None:
1✔
110
    """Saves data to a JSON file."""
111
    if not path.name.endswith(".json"):
1✔
112
        path = path.with_suffix(".json")
1✔
113

114
    json_str = json.dumps(data, indent=2, ensure_ascii=False)
1✔
115
    with path.open("w") as file:
1✔
116
        file.write(json_str)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc