Skip to content

utils

OptimadeClientProgress

Bases: Progress

A wrapper around Rich.Progress that defines the OPTIMADE client progressbars.

Source code in optimade/client/utils.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
class OptimadeClientProgress(Progress):
    """A wrapper around `Rich.Progress` that defines the OPTIMADE client progressbars."""

    def __init__(self):
        super().__init__(
            SpinnerColumn(finished_text="[green]✓"),
            TextColumn("[progress.description]{task.description}"),
            BarColumn(),
            TaskProgressColumn(
                text_format="[progress.completed]{task.completed}/[progress.total]{task.total}",
                text_format_no_percentage="[progress.completed]{task.completed}",
            ),
            TimeElapsedColumn(),
            console=Console(stderr=True),
            auto_refresh=True,
            refresh_per_second=10,
        )

    def print(self, *args, **kwargs):
        if not self.disable:
            super().print(*args, **kwargs)

QueryResults dataclass

A container dataclass for the results from a given query.

Source code in optimade/client/utils.py
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
@dataclass
class QueryResults:
    """A container dataclass for the results from a given query."""

    data: dict | list[dict] = field(default_factory=list, init=False)  # type: ignore[assignment]
    errors: list[str] = field(default_factory=list, init=False)
    links: dict = field(default_factory=dict, init=False)
    included: list[dict] = field(default_factory=list, init=False)
    meta: dict = field(default_factory=dict, init=False)

    @property
    def included_index(self) -> set[str]:
        if not getattr(self, "_included_index", None):
            self._included_index: set[str] = set()
        return self._included_index

    def asdict(self):
        return asdict(self)

    def update(self, page_results: dict) -> None:
        """Combine the results from one page with the existing results for a given query.

        Parameters:
            page_results: The results for the current page.

        """

        if "data" in page_results:
            # If the `data` field is a list, add it to our existing results.
            # Otherwise, as is the case for `info` endpoints, `data` is a dictionary (or null)
            # and should be added as the only `data` field for these results.
            if isinstance(page_results["data"], list):
                self.data.extend(page_results["data"])  # type: ignore[union-attr]
            elif not self.data:
                self.data = page_results["data"]
            else:
                raise RuntimeError(
                    "Not overwriting old `data` field in `QueryResults`."
                )

        if "errors" in page_results:
            self.errors.extend(page_results["errors"])

        # Combine meta/links fields across all pages in a sensible way, i.e.,
        # if we really reached the last page of results, then make sure `links->next`
        # is null in the final response, and make sure `meta->more_data_available` is None or False.
        keys_to_filter = {
            "links": ("next", "prev"),
            "meta": ("query", "more_data_available"),
        }
        for top_level_key in keys_to_filter:
            if top_level_key not in page_results:
                page_results[top_level_key] = {}
            for k in keys_to_filter[top_level_key]:
                if k not in page_results[top_level_key]:
                    page_results[top_level_key][k] = None
            getattr(self, top_level_key).update(
                {k: page_results[top_level_key][k] for k in page_results[top_level_key]}
            )

        # Only add new unique entries to the included list
        for d in page_results.get("included", []):
            typed_id = f"{d['type']}/{d['id']}"
            if typed_id not in self.included_index:
                self.included_index.add(typed_id)
                self.included.append(d)

update(page_results)

Combine the results from one page with the existing results for a given query.

Parameters:

Name Type Description Default
page_results dict

The results for the current page.

required
Source code in optimade/client/utils.py
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
def update(self, page_results: dict) -> None:
    """Combine the results from one page with the existing results for a given query.

    Parameters:
        page_results: The results for the current page.

    """

    if "data" in page_results:
        # If the `data` field is a list, add it to our existing results.
        # Otherwise, as is the case for `info` endpoints, `data` is a dictionary (or null)
        # and should be added as the only `data` field for these results.
        if isinstance(page_results["data"], list):
            self.data.extend(page_results["data"])  # type: ignore[union-attr]
        elif not self.data:
            self.data = page_results["data"]
        else:
            raise RuntimeError(
                "Not overwriting old `data` field in `QueryResults`."
            )

    if "errors" in page_results:
        self.errors.extend(page_results["errors"])

    # Combine meta/links fields across all pages in a sensible way, i.e.,
    # if we really reached the last page of results, then make sure `links->next`
    # is null in the final response, and make sure `meta->more_data_available` is None or False.
    keys_to_filter = {
        "links": ("next", "prev"),
        "meta": ("query", "more_data_available"),
    }
    for top_level_key in keys_to_filter:
        if top_level_key not in page_results:
            page_results[top_level_key] = {}
        for k in keys_to_filter[top_level_key]:
            if k not in page_results[top_level_key]:
                page_results[top_level_key][k] = None
        getattr(self, top_level_key).update(
            {k: page_results[top_level_key][k] for k in page_results[top_level_key]}
        )

    # Only add new unique entries to the included list
    for d in page_results.get("included", []):
        typed_id = f"{d['type']}/{d['id']}"
        if typed_id not in self.included_index:
            self.included_index.add(typed_id)
            self.included.append(d)

RecoverableHTTPError

Bases: Exception

Base class for any HTTP issues that may be recoverable by just repeating the query.

Source code in optimade/client/utils.py
23
24
25
class RecoverableHTTPError(Exception):
    """Base class for any HTTP issues that may be recoverable by just
    repeating the query."""

TooManyRequestsException

Bases: RecoverableHTTPError

For when the underlying HTTP request returns 429: Too Many Requests.

Source code in optimade/client/utils.py
28
29
class TooManyRequestsException(RecoverableHTTPError):
    """For when the underlying HTTP request returns 429: Too Many Requests."""

silent_raise()

Raise an exception without printing a traceback, or the exception message itself.

Source code in optimade/client/utils.py
123
124
125
126
127
128
129
130
131
132
133
134
@contextmanager
def silent_raise():
    """Raise an exception without printing a traceback, or the exception message itself."""
    default_value = getattr(
        sys, "tracebacklimit", 1000
    )  # `1000` is a Python's default value
    default_excepthook = getattr(sys, "excepthook")
    sys.tracebacklimit = 0
    sys.excepthook = lambda type, value, traceback: None
    yield
    sys.tracebacklimit = default_value  # revert changes
    sys.excepthook = default_excepthook