def test_isinstance_invalidation(self): class A(metaclass=abc.ABCMeta): pass class B: pass b = B() self.assertFalse(isinstance(b, A)) self.assertFalse(isinstance(b, (A,))) token_old = abc.get_cache_token() A.register(B) token_new = abc.get_cache_token() self.assertNotEqual(token_old, token_new) self.assertTrue(isinstance(b, A)) self.assertTrue(isinstance(b, (A,)))
def dispatch(cls, role=None): """ Return the implementation for the given type and role. If role is given, return a function that receives a single positional argument and any number of keyword arguments. If role is not given, the return function should receive both an object and a role as positional arguments. """ # Invalidate cache when ABC cache is invalidated nonlocal cache_token if cache_token is not None and cache_token != get_cache_token(): dispatch_cache.clear() cache_token = get_cache_token() try: return dispatch_cache[cls, role] except KeyError: pass # Find implementation, if not in cache if role is None: impl = no_roles.dispatch(cls) elif role in roles: impl = roles[role].dispatch(cls) else: impl = partial(no_roles.dispatch(cls), role=role) # Cache and return dispatch_cache[cls, role] = impl return impl
def build_proxy_registry(cls): if cls.proxy_registry and abc.get_cache_token( ) == cls.last_cache_token: return cls.last_cache_token = abc.get_cache_token() registry = copy(cls.base_proxy_registry) MatrixProxy._register_subclasses(cls.__subclasses__(), registry) cls.proxy_registry = registry
def register(cls, func=None): """generic_func.register(cls, func) -> func Registers a new implementation for the given *cls* on a *generic_func*. """ nonlocal cache_token if func is None: if isinstance(cls, type): return lambda f: register(cls, f) ann = getattr(cls, '__annotations__', {}) if not ann: raise TypeError( f"Invalid first argument to `register()`: {cls!r}. " f"Use either `@register(some_class)` or plain `@register` " f"on an annotated function.") func = cls # only import typing if annotation parsing is necessary from typing import get_type_hints argname, cls = next(iter(get_type_hints(func).items())) assert isinstance(cls, type), ( f"Invalid annotation for {argname!r}. {cls!r} is not a group.") registry[cls] = func if cache_token is None and hasattr(cls, '__abstractmethods__'): cache_token = get_cache_token() dispatch_cache.clear() return func
def dispatch(cls): """generic_func.dispatch(cls) -> <function implementation> Runs the dispatch algorithm to return the best available implementation for the given *cls* registered on *generic_func*. """ nonlocal cache_token if cache_token is not None: current_token = get_cache_token() if cache_token != current_token: dispatch_cache.clear() cache_token = current_token try: impl = dispatch_cache[cls] except KeyError: try: impl = registry[cls] except KeyError: impl = find_implementation(cls, registry) if impl is func and lazy_registry: for qualname in possible_qualnames(cls): if qualname in lazy_registry: impl = lazy_registry.pop(qualname) register(cls)(impl) break dispatch_cache[cls] = impl return impl
def _dispatch(cls, vcls): global _cache_token if _cache_token is not None: current_token = get_cache_token() if _cache_token != current_token: _dispatch_cache.clear() _cache_token = current_token try: func = _dispatch_cache[(cls, vcls)] except KeyError: try: vreg = _registry[cls] except KeyError: vreg = _find_impl(cls, _registry) if not vreg: raise NotImplementedError( f"No implementation found for '{cls.__qualname__}'") try: return vreg[vcls] except KeyError: func = _find_impl(vcls, vreg) if not func: raise TypeError( f"No implementation found for '{cls.__qualname__}' from {vcls.__qualname__}") _dispatch_cache[(cls, vcls)] = func return func
def register(cls, impl=None): """generic_func.register(cls, impl) -> impl Registers a new implementation for the given *cls* on a *generic_func*. """ nonlocal cache_token # First argument is a list of classes: iterate over all classes # registering one at a time. if not isinstance(cls, (type, str)): cls_list = list(cls) if impl is not None: for cls in cls_list: register(cls, impl) return # Single class if impl is None: return lambda f: register(cls, f) if isinstance(cls, str): lazy_registry[cls] = impl else: registry[cls] = impl if cache_token is None and hasattr(cls, "__abstractmethods__"): cache_token = get_cache_token() dispatch_cache.clear() return impl
def register(cls, func=None): """generic_func.register(cls, func) -> func Registers a new implementation for the given *cls* on a *generic_func*. """ nonlocal cache_token if func is None: if isinstance(cls, type): return lambda f: register(cls, f) ann = getattr(cls, '__annotations__', {}) if not ann: raise TypeError( f"Invalid first argument to `register()`: {cls!r}. " f"Use either `@register(some_class)` or plain `@register` " f"on an annotated function." ) func = cls # only import typing if annotation parsing is necessary from typing import get_type_hints argname, cls = next(iter(get_type_hints(func).items())) assert isinstance(cls, type), ( f"Invalid annotation for {argname!r}. {cls!r} is not a class." ) registry[cls] = func if cache_token is None and hasattr(cls, '__abstractmethods__'): cache_token = get_cache_token() dispatch_cache.clear() return func
def __init__(self, default_callback: Callable[..., T]): self.default_callback = default_callback self.__name__ = default_callback.__name__ self.candidate_sets: Dict[int, Set[Candidate]] = defaultdict(set) self._cache_token = get_cache_token() self._layers_cache: Dict[int, List[Set[Candidate]]] = {} self._lookup_cache: Dict[ int, WeakTupleDict[List[LookupLayer]]] = defaultdict(WeakTupleDict)
def decorator(implementation): container = self._protocols if is_protocol else self._instances container[type_argument] = implementation # type: ignore if self._cache_token is None: # pragma: no cover if getattr(type_argument, '__abstractmethods__', None): self._cache_token = get_cache_token() self._dispatch_cache.clear() return implementation
def test_isinstance_invalidation(abc): # pylint: disable=unused-argument """Test after-the-fact registration behavior. Adapted from Python's test suite. """ class AClass(metaclass=abc.NamespaceableABCMeta): """A throwaway test class.""" class BClass: """A throwaway test class.""" b_instance = BClass() assert not isinstance(b_instance, AClass) assert not isinstance(b_instance, (AClass, )) token_old = abc_main.get_cache_token() AClass.register(BClass) token_new = abc_main.get_cache_token() assert token_old != token_new assert isinstance(b_instance, AClass) assert isinstance(b_instance, (AClass, ))
def _control_abc_cache(self) -> None: """ Required to drop cache if ``abc`` type got new subtypes in runtime. Copied from ``cpython``. """ if self._cache_token is not None: current_token = get_cache_token() if self._cache_token != current_token: self._dispatch_cache.clear() self._cache_token = current_token
def register(cls, func=None): """generic_func.register(cls, func) -> func Registers a new implementation for the given *cls* on a *generic_func*. """ nonlocal cache_token if func is None: return lambda f: register(cls, f) registry[cls] = func if cache_token is None and hasattr(cls, '__abstractmethods__'): cache_token = get_cache_token() dispatch_cache.clear() return func
def __call__(self, *args, **kwargs): new_cache_token = get_cache_token() if new_cache_token != self._cache_token: # our cache is out of date and must be rebuilt self.clear_cache() self._cache_token = new_cache_token t_args = tuple(type(a) for a in args) ll = self._get_lookup_layers(t_args) for layer in ll: if isinstance(layer, Exception): raise layer ret = layer.callback(*args, **kwargs) if ret is not NotImplemented: return ret return self.default_callback(*args, **kwargs)
def _register(func): global _cache_token sig = signature(func) if len(sig.parameters) < 3: raise TypeError( f"{func!r}() takes {len(sig.parameters)} arguments but 3 required") it = iter(sig.parameters.items()) argname, parameter = next(it) hints = get_type_hints(func) typ = hints.get(argname) if typ: type_args = _get_type_args(typ) if get_origin(typ) is not type or not type_args: raise TypeError( f"Invalid first argument to `cast.register()`: {typ!r}. " f"Use either typing.Type[] annotation or return type annotation." ) cls = type_args[0] else: cls = hints.get('return') if cls is None: raise TypeError( f"Invalid signature to `cast.register()`. " f"Use either typing.Type[] annotation or return type annotation." ) argname, parameter = next(it) vcls = hints.get(argname) if not vcls: vcls = Any if vcls == Any: vcls = object if cls in _registry: if vcls in _registry[cls]: raise RuntimeError(f"Ambiguous `cast.register()`") _registry[cls][vcls] = func else: _registry[cls] = {vcls: func} setattr(func, _TYPES, (cls, vcls)) if _cache_token is None: if hasattr(cls, '__abstractmethods__') or hasattr(vcls, '__abstractmethods__'): _cache_token = get_cache_token() _dispatch_cache.clear() return func
def register(cls, func=None): """generic_func.register(cls, func) -> func Registers a new implementation for the given *cls* on a *generic_func*. """ nonlocal cache_token if _is_valid_dispatch_type(cls): if func is None: return lambda f: register(cls, f) else: if func is not None: raise TypeError(f"Invalid first argument to `register()`. " f"{cls!r} is not a class or union type.") ann = getattr(cls, '__annotations__', {}) if not ann: raise TypeError( f"Invalid first argument to `register()`: {cls!r}. " f"Use either `@register(some_class)` or plain `@register` " f"on an annotated function.") func = cls # only import typing if annotation parsing is necessary from typing import get_type_hints argname, cls = next(iter(get_type_hints(func).items())) if not _is_valid_dispatch_type(cls): if _is_union_type(cls): raise TypeError(f"Invalid annotation for {argname!r}. " f"{cls!r} not all arguments are classes.") else: raise TypeError(f"Invalid annotation for {argname!r}. " f"{cls!r} is not a class.") if _is_union_type(cls): from typing import get_args for arg in get_args(cls): registry[arg] = func else: registry[cls] = func if cache_token is None and hasattr(cls, '__abstractmethods__'): cache_token = get_cache_token() dispatch_cache.clear() return func
def register(cls=None, func=None): """generic_func.register(cls, func) -> func Registers a new implementation for the given *cls* on a *generic_func*. """ nonlocal cache_token if func is None: return lambda f: register(cls, f) if cls is None: sign = inspect.signature(func) params = sign.parameters cls = list(params.values())[disp_param].annotation registry[cls] = func if cache_token is None and hasattr(cls, '__abstractmethods__'): cache_token = get_cache_token() dispatch_cache.clear() return func
def dispatch(cls): """generic_func.dispatch(cls) -> <function implementation> Runs the dispatch algorithm to return the best available implementation for the given *cls* registered on *generic_func*. """ nonlocal cache_token if cache_token is not None: current_token = get_cache_token() if cache_token != current_token: dispatch_cache.clear() cache_token = current_token try: impl = dispatch_cache[cls] except KeyError: try: impl = registry[cls] except KeyError: impl = _find_impl(cls, registry) dispatch_cache[cls] = impl return impl
def get_token(self): return abc.get_cache_token()
from typing import get_type_hints argname, cls = next(iter(get_type_hints(func).items())) <<<<<<< HEAD assert isinstance(cls, type), ( f"Invalid annotation for {argname!r}. {cls!r} is not a class." ) ======= if not isinstance(cls, type): raise TypeError( f"Invalid annotation for {argname!r}. " f"{cls!r} is not a class." ) >>>>>>> 716b15a33aed978ded8a6bde17855cb6c6aa7f78 registry[cls] = func if cache_token is None and hasattr(cls, '__abstractmethods__'): cache_token = get_cache_token() dispatch_cache.clear() return func def wrapper(*args, **kw): if not args: raise TypeError(f'{funcname} requires at least ' '1 positional argument') return dispatch(args[0].__class__)(*args, **kw) funcname = getattr(func, '__name__', 'singledispatch function') registry[object] = func wrapper.register = register wrapper.dispatch = dispatch wrapper.registry = types.MappingProxyType(registry)
def update_event(self, inp=-1): self.set_output_val(0, abc.get_cache_token())