Source code for aspis.internal.errors.arity_error

import re


[docs] class ArityError(TypeError): """ Custom exception for handling function arity mismatches. ArityError parses TypeError exceptions to determine if they're caused by incorrect function arity (wrong number of arguments). It categorizes errors as under-application (too few arguments) or over-application (too many arguments), and tracks whether the error involves keyword arguments. Attributes: is_arity_error (bool): True if the error is an arity-related TypeError. underapplied (bool): True if too few arguments were provided. overapplied (bool): True if too many arguments were provided. kwarg_error (bool): True if the error involves keyword arguments. unexpected_kwargs (list): List of unexpected keyword argument names. Example: >>> def foo(a, b): ... return a + b >>> try: ... foo(1) ... except TypeError as e: ... arity_err = ArityError(e) ... print(arity_err.underapplied) True """
[docs] def __init__(self, e: Exception): """ Initialize ArityError by parsing a TypeError exception. Args: e: The exception to parse. Should be a TypeError from a function call with incorrect arity. """ super().__init__(str(e)) self.is_arity_error = False self.underapplied = False self.overapplied = False self.kwarg_error = False self.unexpected_kwargs = [] self._parse_error(e)
def _parse_error(self, e: Exception): """ Parse the exception to determine arity error type. Uses regex patterns to match common TypeError messages related to function arity and categorize them appropriately. Args: e: The exception to parse. """ if not isinstance(e, TypeError): return message = str(e.args[0]).lower() arg_error_patterns = [ (r"expected (\d+) arguments?,? got (\d+)", self._match_expected_received), (r"takes (\d+) positional arguments? but (\d+) were given", self._match_expected_received), ( r"missing (\d+) required positional arguments?: ((?:'[\w_]+'(?:, )?)+)", self._handle_underapplication_args, ), (r"must have at least (\w+) arguments.", self._handle_underapplication_args), (r"got multiple values for argument '(.*?)'", self._handle_overapplication_args), ] kwarg_error_patterns = [ ( r"missing (\d+) required keyword-only arguments?: ((?:'[\w_]+'(?:, )?)+)", self._handle_underapplication_args, ), (r"got an unexpected keyword argument '(.*?)'", self._handle_overapplication_kwargs), ] for pattern, handler in arg_error_patterns + kwarg_error_patterns: match = re.search(pattern, message) if match: self.is_arity_error = True handler(match) break def _match_expected_received(self, match): """ Handle errors with explicit expected/received argument counts. Compares expected vs received argument counts and delegates to appropriate under/over-application handler. Args: match: Regex match object containing expected and received counts. """ if int(match.group(1)) > int(match.group(2)): self._handle_underapplication_args(match) else: self._handle_overapplication_args(match) def _handle_underapplication_args(self, _): """Mark error as underapplication (too few arguments).""" self.underapplied = True def _handle_overapplication_args(self, _): """Mark error as overapplication (too many arguments).""" self.overapplied = True def _handle_underapplication_kwargs(self, _): """Mark error as underapplication with missing keyword arguments.""" self.underapplied = True self.kwarg_error = True def _handle_overapplication_kwargs(self, match): """ Mark error as overapplication with unexpected keyword arguments. Args: match: Regex match object containing the unexpected kwarg name. """ self.overapplied = True self.kwarg_error = True self.unexpected_kwargs.append(match.group(1))
[docs] def __bool__(self): """ Check if the error is a valid arity error. Returns: bool: True if this is a recognized arity error, False otherwise. """ return self.is_arity_error