diff --git a/pure_interface/__init__.py b/pure_interface/__init__.py index 9a21b77..7515c9a 100644 --- a/pure_interface/__init__.py +++ b/pure_interface/__init__.py @@ -7,4 +7,4 @@ from .adaption import adapts, register_adapter, AdapterTracker, adapt_args from .delegation import Delegate -__version__ = '8.0.1' +__version__ = '8.0.2' diff --git a/pure_interface/adaption.py b/pure_interface/adaption.py index 25845c3..78819b3 100644 --- a/pure_interface/adaption.py +++ b/pure_interface/adaption.py @@ -8,7 +8,7 @@ import warnings from .errors import InterfaceError, AdaptionError -from .interface import AnInterface, Interface, InterfaceType, AnInterfaceType, type_is_interface +from .interface import AnInterface, Interface, InterfaceType, type_is_interface from .interface import get_type_interfaces, get_pi_attribute diff --git a/pure_interface/interface.py b/pure_interface/interface.py index ef06323..1e46e73 100644 --- a/pure_interface/interface.py +++ b/pure_interface/interface.py @@ -7,6 +7,7 @@ from abc import abstractclassmethod, abstractmethod, abstractstaticmethod import collections import dis +import functools import inspect from inspect import Parameter, signature, Signature import sys @@ -382,16 +383,21 @@ def _ensure_everything_is_abstract(attributes): func = value.__func__ functions.append(func) interface_method_signatures[name] = signature(func) - value = abstractstaticmethod(func) + value = staticmethod(abstractmethod(func)) elif isinstance(value, classmethod): func = value.__func__ interface_method_signatures[name] = signature(func) functions.append(func) - value = abstractclassmethod(func) + value = classmethod(abstractmethod(func)) elif isinstance(value, types.FunctionType): functions.append(value) interface_method_signatures[name] = signature(value) value = abstractmethod(value) + elif isinstance(value, functools.singledispatchmethod): + func = value.func + functions.append(func) + interface_method_signatures[name] = signature(func) + value = func # ignore the singledispatchmethod decorator elif isinstance(value, property): interface_attribute_names.append(name) functions.extend([value.fget, value.fset, value.fdel]) # may contain Nones @@ -422,13 +428,15 @@ def _check_method_signatures(attributes, clsname, interface_method_signatures): if name not in attributes: continue value = attributes[name] - if not isinstance(value, (staticmethod, classmethod, types.FunctionType)): + if not isinstance(value, (staticmethod, classmethod, types.FunctionType, functools.singledispatchmethod)): if _is_descriptor(value): continue else: raise InterfaceError('Interface method over-ridden with non-method') if isinstance(value, (staticmethod, classmethod)): func = value.__func__ + elif isinstance(value, functools.singledispatchmethod): + func = value.func else: func = value func_sig = signature(func) diff --git a/tests/test_singledispatch.py b/tests/test_singledispatch.py new file mode 100644 index 0000000..01d3bc0 --- /dev/null +++ b/tests/test_singledispatch.py @@ -0,0 +1,28 @@ +from functools import singledispatchmethod +from typing import Any +import unittest + +import pure_interface + + +class TestSingleDispatch(unittest.TestCase): + def test_single_dispatch_allowed(self): + class IPerson(pure_interface.Interface): + @singledispatchmethod + def greet(self, other_person: Any) -> str: + pass + + self.assertSetEqual(pure_interface.get_interface_method_names(IPerson), {'greet'}) + + def test_single_dispatch_checked(self): + class IPerson(pure_interface.Interface): + def greet(self) -> str: + pass + + pure_interface.set_is_development(True) + with self.assertRaises(pure_interface.InterfaceError): + class Person(IPerson): + @singledispatchmethod + def greet(self, other_person: Any) -> str: + pass +