1import os
2import pipes
3import signal
4
5
6def exit_code_str(exception_name, command, exit_code):
7    """String representation for an InvocationError, with exit code
8
9    NOTE: this might also be used by plugin tests (tox-venv at the time of writing),
10    so some coordination is needed if this is ever moved or a different solution for this hack
11    is found.
12
13    NOTE: this is a separate function because pytest-mock `spy` does not work on Exceptions
14    We can use neither a class method nor a static because of https://bugs.python.org/issue23078.
15    Even a normal method failed with "TypeError: descriptor '__getattribute__' requires a
16    'BaseException' object but received a 'type'".
17    """
18    str_ = "{} for command {}".format(exception_name, command)
19    if exit_code is not None:
20        str_ += " (exited with code {:d})".format(exit_code)
21        if (os.name == "posix") and (exit_code > 128):
22            signals = {
23                number: name for name, number in vars(signal).items() if name.startswith("SIG")
24            }
25            number = exit_code - 128
26            name = signals.get(number)
27            if name:
28                str_ += (
29                    "\nNote: this might indicate a fatal error signal "
30                    "({:d} - 128 = {:d}: {})".format(number + 128, number, name)
31                )
32    return str_
33
34
35class Error(Exception):
36    def __str__(self):
37        return "{}: {}".format(self.__class__.__name__, self.args[0])
38
39
40class MissingSubstitution(Error):
41    FLAG = "TOX_MISSING_SUBSTITUTION"
42    """placeholder for debugging configurations"""
43
44    def __init__(self, name):
45        self.name = name
46
47
48class ConfigError(Error):
49    """Error in tox configuration."""
50
51
52class UnsupportedInterpreter(Error):
53    """Signals an unsupported Interpreter."""
54
55
56class InterpreterNotFound(Error):
57    """Signals that an interpreter could not be found."""
58
59
60class InvocationError(Error):
61    """An error while invoking a script."""
62
63    def __init__(self, command, exit_code=None, out=None):
64        super(Error, self).__init__(command, exit_code)
65        self.command = command
66        self.exit_code = exit_code
67        self.out = out
68
69    def __str__(self):
70        return exit_code_str(self.__class__.__name__, self.command, self.exit_code)
71
72
73class MissingDirectory(Error):
74    """A directory did not exist."""
75
76
77class MissingDependency(Error):
78    """A dependency could not be found or determined."""
79
80
81class MissingRequirement(Error):
82    """A requirement defined in :config:`require` is not met."""
83
84    def __init__(self, config):
85        self.config = config
86
87    def __str__(self):
88        return " ".join(pipes.quote(i) for i in self.config.requires)
89
90
91class BadRequirement(Error):
92    """A requirement defined in :config:`require` cannot be parsed."""
93