Source code for sisl.io._exceptions

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
from __future__ import annotations

import warnings
from functools import wraps
from typing import Callable, List, Tuple, Union

from sisl._internal import set_module
from sisl.messages import SislError, SislException, SislInfo, SislWarning, info, warn

__all__ = [
    "SileError",
    "SileWarning",
    "SileInfo",
    "MissingInputSileException",
    "MissingInputSileError",
    "MissingInputSileInfo",
    "MissingInputSileWarning",
    "missing_input",
]


@set_module("sisl.io")
class SileError(SislError, IOError):
    """Define an error object related to the Sile objects"""

[docs] def __init__(self, value, obj=None): self.value = value self.obj = obj
def __str__(self): if self.obj: return f"{self.value!s} in {self.obj!s}" return self.value @set_module("sisl.io") class SileWarning(SislWarning): """Warnings that informs users of things to be carefull about when using their retrieved data These warnings should be issued whenever a read/write routine is unable to retrieve all information but are non-influential in the sense that sisl is still able to perform the action. """ @set_module("sisl.io") class SileInfo(SislInfo): """Information for the user, this is hidden in a warning, but is not as severe so as to issue a warning.""" InputsType = Union[List[Tuple[str, str]], List[str]] class MissingInputSileException(SislException): """Container for constructing error/warnings when a fdf flag is missing from the input file. This error message should preferably be raised through: >>> raise InheritedClass(method, ["Diag.ParallelOverk"]) from exc to retain a proper context. """ def __init__( self, executable: str, inputs: InputsType, method: Callable, msg: str = "" ): # Formulate the error message try: name = f"{method.__self__.__class__.__name__}.{method.__name__}" except AttributeError: name = f"{method.__name__}" def parse(v): if isinstance(v, str): return f" * {v}" return f" * {' '.join(v)}" str_except = "\n".join(map(parse, inputs)) super().__init__( f"{msg}\nData from method '{name}' failed due to missing output values.\n\n" f"This is because of missing options in the input file for executable {executable}.\n" f"Please read up on the following flags in the manual of '{executable}' to figure out " f"how to retrieve the expected quantities:\n{str_except}" ) class MissingInputSileError(MissingInputSileException, SileError): """Issued when specific flags in the input file can be used to extract data""" class MissingInputSileWarning(MissingInputSileException, SileWarning): """Issued when specific flags in the input file can be used to extract data""" class MissingInputSileInfo(MissingInputSileException, SileInfo): """Issued when specific flags in the input file can be used to extract data""" def missing_input( executable: str, inputs: InputsType, what: MissingInputSileException, when_exception: Exception = Exception, ): """Issues warnings, or errors based on `when` and `what`""" def decorator(func): what_inst = what(executable, inputs, func) if issubclass(when_exception, Warning): # we should do a warning catch thing @wraps(func) def deco(*args, **kwargs): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") ret = func(*args, **kwargs) if len(w) > 0: if isinstance(what_inst, MissingInputSileWarning): warn(what_inst) elif isinstance(what_inst, MissingInputSileInfo): info(what_inst) elif isinstance(what_inst, MissingInputSileError): raise what_inst return ret else: # it must be ane error to be raised @wraps(func) def deco(*args, **kwargs): try: return func(*args, **kwargs) except what as ke: raise what_inst from ke.__cause__ except when_exception as ke: raise what_inst from ke return deco return decorator