Source code for esc.oops

"""
oops.py - custom exceptions for esc
"""

import curses.ascii

[docs] class EscError(Exception): "Base application exception for esc. Don't raise directly."
[docs] class ProgrammingError(EscError): r""" Indicates an error caused by incorrectly written or defined esc plugins. This includes modes, menus, operations, and so on. It does not include runtime errors within operation functions themselves; these are :class:`FunctionExecutionError`\ s. """
[docs] class FunctionProgrammingError(ProgrammingError): r""" A more specific type of :class:`ProgrammingError` that occurs when a user's :func:`@Operation <esc.commands.Operation>` decorator or function parameters are invalid or function tests fail. The distinction is mostly for convenience within esc's codebase rather than because client code needs to tell the difference from other :class:`ProgrammingError`\ s; this class wraps some handy logic for generating a standardized message. """ def __init__(self, operation, problem): super().__init__() self.operation = operation self.function_name = self.operation.function.__name__ self.key = self.operation.key self.description = self.operation.description self.problem = problem self.message = (f"The function '{self.function_name}' " f"(key '{self.key}', description '{self.description}') " f"{self.problem}. ") def __str__(self): return self.message
[docs] class NotInMenuError(EscError): """ Raised when a keypress is parsed as a menu option or a menu's children are programmatically accessed by key, but the key doesn't refer to any choice on the current menu. Describes the problem in a message that can be printed to the status bar. """ def __init__(self, access_key): super().__init__() specials = (' ', '\n', '\r', '\t') if ord(access_key) > 256 or access_key in specials: self.msg = "The key you pressed doesn't mean anything to esc here." else: self.msg = (f"There's no option '{curses.ascii.unctrl(access_key)}' " f"in this menu.") def __str__(self): return self.msg
[docs] class InvalidNameError(EscError): """ Raised when the user chooses an invalid name for a register or other label. """
[docs] class FunctionExecutionError(EscError): r""" A broad exception type that occurs when the code within an operation function couldn't be executed successfully for some reason. Examples include: - a number is in the middle of being entered and isn't a valid number - a function performed an undefined operation like dividing by zero - there are too few items on the stack - the units of the inputs are incompatible or the operation can't handle the units of the items on the stack - a function directly raises this error due to invalid input or inability to complete its task for some other reason :class:`FunctionExecutionError`\ s normally result in the ``__str__`` of the exception being printed to the status bar, so exception messages should be concise. A :class:`FunctionExecutionError` may be raised directly, or one of its subclasses may be used. """
[docs] class InsufficientItemsError(FunctionExecutionError): """ Raised directly by operations functions that request the entire stack to indicate not enough items are on the stack to finish their work, or by the menu logic when an operation requests more parameters than items on the stack. Functions may use the simplified form of the exception, providing an int describing the number of items that should have been on the stack for the ``number_required`` constructor parameter. esc will then reraise the exception with a more useful message; a fallback message is provided in case this doesn't happen for some reason. """ def __init__(self, number_required, msg=None): super().__init__() self.number_required = number_required self.msg = msg def __str__(self): if self.msg: return self.msg else: return f"Insufficient items: needs at least {self.number_required}"
[docs] class UnitError(FunctionExecutionError): """ General class of exception raised when an operation that uses units in a potentially incorrect way is performed. All unit errors are overridable by the user by pressing the same key again, but many overrides will result in stripping all units from the inputs and outputs. """
[docs] class IncommensurableUnitsError(UnitError): "Raised when adding/subtracting values with different units." def __init__(self, unit_a, unit_b): self.unit_a = unit_a self.unit_b = unit_b a_str = unit_a.display() or "unitless" b_str = unit_b.display() or "unitless" super().__init__( f"Incommensurable units: {a_str} vs {b_str}. " f"Press again to override.")
[docs] class UnitlessOperandError(UnitError): """ Raised by an operation's unit handler when unitless and unitful operands are mixed in a way invalid for that operation. In the built-in unit handlers, this is raised when multiplying or dividing and one of the operands is unitless and not the identity value (1). Sometimes this is a warning and not an unrecoverable error, e.g., in the multiplication case described above. If this is the case, the raiser of this exception can allow the user to override the error without stripping the units using the ``override`` parameter of the :class:`~esc.units.UnitHandler`. If the ``override`` parameter is not used, overriding this error will strip all units and call the operation again; this is usually what you want if you're writing a custom unit handler. """ def __init__(self): super().__init__( "Mixing unitful and unitless operands. " "Press again to override.")
class OperationWillRemoveUnitsError(UnitError): """ Raised when an operation that doesn't support units is applied to unitful values. The user can override this, if intentional, by pressing the operation key again; units will then be stripped from the inputs and outputs. """ def __init__(self): super().__init__( "This operation will remove units. " "Press again to override.")
[docs] class UnitRootError(UnitError): """ Raised when a root operation on a unit produces non-integer exponents. """ def __init__(self, msg=None): super().__init__( msg or "Cannot simplify units through root. " "Press again to override.")
[docs] class UnitExponentError(UnitError): """ Raised when an exponentiation operation on a unit attempts to raise the unit to a power that has a unit, or a non-integer power. """ def __init__(self, msg="Exponent cannot have units. Press again to override."): super().__init__(msg)
class RollbackTransaction(Exception): """ Raised during a StackState .transaction() to indicate that the transaction should be rolled back due to an error. If status_message is provided, the status bar will be set to display the message. """ def __init__(self, status_message=None): super().__init__() self.status_message = status_message def __str__(self): return f"<RollbackTransaction: message {self.status_message}>"