Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dispatch: Better error when defining duplicate signature #62

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions runtype/dispatch.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from collections import defaultdict
from functools import wraps
from typing import Any, Dict, Callable, Sequence
from typing import Any, Dict, Callable, Sequence, List
from operator import itemgetter
import warnings

Expand All @@ -14,6 +14,22 @@ class DispatchError(Exception):
"Thrown whenever a dispatch fails. Contains text describing the conflict."


@dataclass
class DuplicateSignatureError(Exception):
signature: List[type]
first: Callable
second: Callable

def __str__(self):
code1 = self.first.__code__
code2 = self.second.__code__
return (f"Duplicate signature defined for '{self.first.__name__}'\n"
f" - Signature: {self.signature}.\n"
f" - Definition 1: {code1.co_filename}:{code1.co_firstlineno}\n"
f" - Definition 2: {code2.co_filename}:{code2.co_firstlineno}\n"
)


# TODO: Remove test_subtypes, replace with support for Type[], like isa(t, Type[t])
class MultiDispatch:
"""Creates a dispatch group for multiple dispatch
Expand Down Expand Up @@ -180,10 +196,8 @@ def define_function(self, f):
node = node.follow_type[t]

if node.func is not None:
code_obj = node.func[0].__code__
raise ValueError(
f"Function {f.__name__} at {code_obj.co_filename}:{code_obj.co_firstlineno} matches existing signature: {signature}!"
)
raise DuplicateSignatureError(signature, node.func[0], f)

node.func = f, signature

def choose_most_specific_function(self, args, *funcs):
Expand Down
12 changes: 6 additions & 6 deletions tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
logging.basicConfig(level=logging.INFO)

from runtype import Dispatch, DispatchError, dataclass, isa, is_subtype, issubclass, assert_isa, String, Int, validate_func, cv_type_checking, multidispatch
from runtype.dispatch import MultiDispatch
from runtype.dispatch import MultiDispatch, DuplicateSignatureError
from runtype.dataclass import Configuration

try:
Expand Down Expand Up @@ -291,7 +291,7 @@ def f(s:str):
@dy
def f(x: int):
return NotImplemented
except ValueError:
except DuplicateSignatureError:
pass
else:
assert False, f
Expand Down Expand Up @@ -512,7 +512,7 @@ def f(i:int, j:object, k:object):
@dy
def f(i:int, j:object, k:object):
return "Oops"
except ValueError:
except DuplicateSignatureError:
pass
else:
assert False
Expand All @@ -536,7 +536,7 @@ def f(x: types[0]):
def f(x):
pass
assert False
except ValueError:
except DuplicateSignatureError:
pass

for t in types[1:]:
Expand All @@ -545,7 +545,7 @@ def f(x):
def f(x: t):
pass
assert False, t
except ValueError:
except DuplicateSignatureError:
pass

_test_canon(object, Union[object], include_none=True)
Expand Down Expand Up @@ -688,7 +688,7 @@ def f(a: int):
def f(a: int):
return 'a'
f.__module__ = 'a'
self.assertRaises(ValueError, multidispatch, f)
self.assertRaises(DuplicateSignatureError, multidispatch, f)

def test_none(self):
dp = Dispatch()
Expand Down
Loading