PK$N interfaces/__init__.py"""A new approach to interfaces in Python """ import interfaces.base import interfaces.compat import interfaces.exceptions import interfaces.spec import interfaces.util __version__ = '0.1.0' __all__ = ['Interface', 'Object', 'isimplementation'] interface = Interface = interfaces.base.Interface object = Object = interfaces.compat.Object InterfaceNoInstanceAllowedError = interfaces.exceptions.InterfaceNoInstanceAllowedError InterfaceNotImplementedError = interfaces.exceptions.InterfaceNotImplementedError InterfaceOverloadingError = interfaces.exceptions.InterfaceOverloadingError interface_spec = interfaces.spec.interface_spec isimplementation = interfaces.util.isimplementation PK N'{interfaces/base.pyfrom __future__ import annotations import typing import interfaces.exceptions import interfaces.spec import interfaces.util __all__ = ['Interface'] class _InterfaceMeta(type): def __interface_spec__(self) -> interfaces.spec.InterfaceSpec: return interfaces.spec.InterfaceSpec(iface=self) def __subclasscheck__(self, subclass: typing.Type) -> bool: return interfaces.util.isimplementation(subclass, self) class Interface(metaclass=_InterfaceMeta): def __new__(cls, *args: typing.Any, **kwargs: typing.Any) -> None: raise interfaces.exceptions.InterfaceNoInstanceAllowedError(iface=cls) def __init_subclass__(cls) -> None: cls_method_names = interfaces.spec._PartialInterfaceSpec(cls).keys() for cls_base in cls.__bases__: if not isinstance(cls_base, _InterfaceMeta): raise TypeError( "Cannot create a consistent method resolution order (MRO) for bases" f" {', '.join(k.__name__ for k in cls.__bases__)}" ) base_spec = interfaces.spec.interface_spec(cls_base) if cls_method_names.isdisjoint(base_spec.keys()): continue raise interfaces.exceptions.InterfaceOverloadingError( method_names=set(base_spec).intersection(cls_method_names), ancestor_iface=cls_base, descendant_iface=cls, ) super().__init_subclass__() PKz N,interfaces/compat.pyimport collections.abc import typing import interfaces.base import interfaces.util __all__ = ['Object'] class Object: def __init_subclass__( cls, implements: typing.Optional[ typing.Union[ typing.Iterable[typing.Type[interfaces.base.Interface]], typing.Type[interfaces.base.Interface], ] ] = None, ) -> None: if implements is None: implements = () if not isinstance(implements, collections.abc.Iterable): implements = (implements,) for iface in implements: if not isinstance(iface, interfaces.base._InterfaceMeta): raise TypeError( "Arguments to `implements` must be subclasses of `interface`," " not `%r`", iface, ) for iface in implements: interfaces.util._isimplementation(cls, iface, raise_errors=True) super().__init_subclass__() PK9Ndinterfaces/exceptions.pyfrom __future__ import annotations import typing import interfaces.typing __all__ = [ 'InterfaceNoInstanceAllowedError', 'InterfaceNotImplementedError', 'InterfaceOverloadingError', ] class InterfaceError(Exception): pass class InterfaceNoInstanceAllowedError(InterfaceError): def __init__(self, *, iface: interfaces.typing.InterfaceType) -> None: self._iface = iface def __str__(self) -> str: return ( f"Attempted to create an instance of interface `{self._iface!r}` which is" " not allowed" ) class InterfaceNotImplementedError(InterfaceError): def __init__( self, *, klass: typing.Type, method_name: str, iface: interfaces.typing.InterfaceType, ) -> None: self._klass = klass self._method_name = method_name self._iface = iface def __str__(self) -> str: return ( f"`{self._klass!r}` must fully implement `{self._method_name!s}` method of" f" `{self._iface}`" ) class InterfaceOverloadingError(InterfaceError): def __init__( self, *, method_names: typing.Container[str], ancestor_iface: interfaces.typing.InterfaceType, descendant_iface: interfaces.typing.InterfaceType, ) -> None: self._method_names = method_names self._ancestor_iface = ancestor_iface self._descendant_iface = descendant_iface def __str__(self) -> str: return ( f"Attempted to overload method(s) `{self._method_names!s}` of" f" `{self._ancestor_iface!r}` in `{self._descendant_iface!r}`" ) PKNlinterfaces/spec.pyfrom __future__ import annotations import collections.abc import functools import types import typing import interfaces.base import interfaces.typing class InterfaceSpec(collections.abc.Mapping): slots = ('_iface', '_iface_spec') def __init__(self, iface: interfaces.typing.InterfaceType) -> None: self._iface = iface self._iface_spec = { attr_name: getattr(iface, attr_name) for attr_name in self._get_iface_attrs(iface) if not (attr_name[:2] == attr_name[-2:] == '__') } def __getitem__(self, key: str) -> types.MethodType: return self._iface_spec[key] def __iter__(self) -> typing.Iterator[str]: return iter(self._iface_spec) def __len__(self) -> int: return len(self._iface_spec) def __repr__(self) -> str: return f"{self.__class__.__name__!s}({self._iface!r})" @staticmethod def _get_iface_attrs( iface: interfaces.typing.InterfaceType ) -> collections.abc.Iterable[str]: return dir(iface) class _PartialInterfaceSpec(InterfaceSpec): @staticmethod def _get_iface_attrs( iface: interfaces.typing.InterfaceType ) -> collections.abc.Iterable[str]: return vars(iface).keys() @functools.lru_cache(maxsize=None) def interface_spec(iface: interfaces.typing.InterfaceType) -> InterfaceSpec: return iface.__interface_spec__() PKN/xxinterfaces/typing.pyimport typing if typing.TYPE_CHECKING: import interfaces.base InterfaceType = interfaces.base._InterfaceMeta PKINc  interfaces/util.pyfrom __future__ import annotations import collections.abc import inspect import typing import interfaces.exceptions import interfaces.spec import interfaces.typing __all__ = ['isimplementation'] def isimplementation( cls: typing.Type, interface_or_iterable: typing.Union[ typing.Iterable[interfaces.typing.InterfaceType], interfaces.typing.InterfaceType, ], ) -> bool: return all( _isimplementation(cls, iface) for iface in ( interface_or_iterable # type: ignore if isinstance(interface_or_iterable, collections.abc.Iterable) else (interface_or_iterable,) ) ) def _isimplementation( cls: type, iface: interfaces.typing.InterfaceType, *, raise_errors: bool = False ) -> bool: for attr_name, iface_attr in interfaces.spec.interface_spec(iface).items(): if attr_name[:2] == attr_name[-2:] == '__': continue if not hasattr(cls, attr_name): return _isimplementation_fail(cls, attr_name, iface, raise_errors) cls_attr = inspect.getattr_static(cls, attr_name) if ( inspect.isdatadescriptor(iface_attr) and inspect.isdatadescriptor(cls_attr) and ( inspect.signature(cls_attr.__get__) == inspect.signature(iface_attr.__get__) ) and ( inspect.signature(cls_attr.__set__) == inspect.signature(iface_attr.__set__) ) and ( inspect.signature(cls_attr.__delete__) == inspect.signature(iface_attr.__delete__) ) ): continue if ( inspect.isfunction(iface_attr) and inspect.isfunction(cls_attr) and inspect.signature(cls_attr) == inspect.signature(iface_attr) ): continue return _isimplementation_fail(cls, attr_name, iface, raise_errors) return True def _isimplementation_fail( cls: type, attr_name: str, iface: interfaces.typing.InterfaceType, raise_errors: bool, ) -> bool: if raise_errors: raise interfaces.exceptions.InterfaceNotImplementedError( klass=cls, method_name=attr_name, iface=iface ) else: return False PKU"Ne,::)strict_interfaces-0.1.0.dist-info/LICENSEThe MIT License (MIT) Copyright (c) 2019 Serge Matveenko Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!HPO'strict_interfaces-0.1.0.dist-info/WHEEL HM K-*ϳR03rOK-J,/RH,szd&Y)r$[)T&UrPK!HB *strict_interfaces-0.1.0.dist-info/METADATAWo6_qkfd6h17i7jw{(NH*Hɖ[uO@3tc˺,F0Ue4O p:%ZZuv\el-\Q'T3)ֳw"EeI_[nlU07pUVKJQPa5hg]#+~`Ʌ\Z?h ?jaN#xmjDON4UnV7#02QDrkE.<5J]$ KE&. 3י@l l X.`&Uz?[v 2BȨ5,a +Wk+j]SJ_yHC5BJnz+],u6Ónڞ30R(0c`'lصIn208mRJTDİuGߐXMk?i.Sp#2*M%i 1(6(S J;(ZP@1u1pʹQ1RH2R9 v7O|ON&z=ѵ+} ɤo*FFjڰ2H&7Hd7\V .aNU>Io>e sD@) zY 2VK5g 'AlZ!U ɍlLb;76HO(}k{Ԡ6~+mv]ALл x[ B>X=:7;H4Պu|DY(i7k&/q#g=/eR|WCz(sail *{{Wd_ 0LkIL&^Cn%݉їSݻxuOIPh2v!ڇ1Bo cwOh?._^L)C,5!bJ/vNL\@~ ͿCh{qTh#3)b+CpW'pxm(oA|O-^#0lb6ôFwf+oam(#vuHZi;ǯxaGBF몛`Ord;.#=ҳwœf(/)95$QcmZ޴Q@zI޺I U%ニƆV c|zmJ (a|/3Z̤QA^qPX 塴j2)M0D^DAyXKj/W˔vlA~:z?mk!Pxvh:,S:Ep}0AuVϋQd_.vdCUSwzhN#Ԁ0x^t͉{วPK$N interfaces/__init__.pyPK N'{interfaces/base.pyPKz N,interfaces/compat.pyPK9Nd interfaces/exceptions.pyPKNlinterfaces/spec.pyPKN/xxinterfaces/typing.pyPKINc  /interfaces/util.pyPKU"Ne,::)w#strict_interfaces-0.1.0.dist-info/LICENSEPK!HPO''strict_interfaces-0.1.0.dist-info/WHEELPK!HB *(strict_interfaces-0.1.0.dist-info/METADATAPK!Hـ#g(-strict_interfaces-0.1.0.dist-info/RECORDPK (l/