cpython icon indicating copy to clipboard operation
cpython copied to clipboard

functools.singledispatchfunction has confusing error message if no positional arguments are passed in

Open 6752910b-8c0e-4e9b-bdd2-0bbadb707f5e opened this issue 5 years ago • 2 comments

BPO 41122
Nosy @rhettinger, @ambv, @mgrandi, @ammaraskar, @mental32, @AlexWaygood
PRs
  • python/cpython#21471
  • python/cpython#23212
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = None
    created_at = <Date 2020-06-26.03:29:31.194>
    labels = ['type-bug', 'library', '3.9', '3.10', '3.11']
    title = 'functools.singledispatchfunction has confusing error message if no positional arguments are passed in'
    updated_at = <Date 2021-11-05.16:40:42.304>
    user = 'https://github.com/mgrandi'
    

    bugs.python.org fields:

    activity = <Date 2021-11-05.16:40:42.304>
    actor = 'AlexWaygood'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2020-06-26.03:29:31.194>
    creator = 'markgrandi'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 41122
    keywords = ['patch']
    message_count = 1.0
    messages = ['372406']
    nosy_count = 6.0
    nosy_names = ['rhettinger', 'lukasz.langa', 'markgrandi', 'ammar2', 'mental', 'AlexWaygood']
    pr_nums = ['21471', '23212']
    priority = 'normal'
    resolution = None
    stage = 'patch review'
    status = 'open'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue41122'
    versions = ['Python 3.9', 'Python 3.10', 'Python 3.11']
    

    this is with python 3.8:

    PS C:\Users\mark> py -3 --version --version
    Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)]
    

    So when using functools.singledispatch or functools.singledispatchmethod, you need to provide at least 1 positional argument so it can dispatch based on the type of said argument

    however, with functools.singledispatchmethod, you get an error message that is confusing and not obvious what the problem is.

    Example with functools.singledispatchmethod:

    class NegatorTwo:
        @singledispatchmethod
        def neg(arg):
            raise NotImplementedError("Cannot negate a")
    
        @neg.register
        def _(self, arg: int):
            return -arg
    
        @neg.register
        def _test(self, arg: bool):
            return not arg
    
    
    if __name__ == "__main__":
    
        n = NegatorTwo()
        print(n.neg(0))
        print(n.neg(False))
        print(n.neg(arg=0))
    
    

    you end up getting:

    PS C:\Users\mark> py -3 C:\Users\mark\Temp\singledisp.py
    0
    True
    Traceback (most recent call last):
      File "C:\Users\mark\Temp\singledisp.py", line 58, in <module>
        print(n.neg(arg=0))
      File "C:\Python38\lib\functools.py", line 910, in _method
        method = self.dispatcher.dispatch(args[0].__class__)
    IndexError: tuple index out of range
    
    

    but with just regular functools.singledispatch:

    
    @functools.singledispatch
    def negate_func(arg):
        raise NotImplementedError("can't negate")
    
    @negate_func.register
    def negate_int_func(arg:int):
        return -arg
    
    @negate_func.register
    def negate_bool_func(arg:bool):
        return not arg
    
    
    if __name__ == "__main__":
    
    
        print(negate_func(0))
        print(negate_func(False))
        print(negate_func(arg=0))
    
    

    you get an error that tells you what actually is wrong:

    
    PS C:\Users\mark> py -3 C:\Users\mark\Temp\singledisp.py
    0
    True
    Traceback (most recent call last):
      File "C:\Users\mark\Temp\singledisp.py", line 63, in <module>
        print(negate_func(arg=0))
      File "C:\Python38\lib\functools.py", line 871, in wrapper
        raise TypeError(f'{funcname} requires at least '
    TypeError: negate_func requires at least 1 positional argument
    
    

    it seems that the code in functools.singledispatchmethod needs to check to see if args is empty, and throw a similar (if not the same) exception as functools.singledispatch

    Any progress with this, I apparently found this by chance after having spent a good hour to understand the missing positional argument was the cause of my troubles.

    I'm on Python 3.10.7

    andreamoro avatar Nov 09 '22 15:11 andreamoro