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