# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/02_prot.ipynb.

# %% auto 0
__all__ = ['ProtocolMeta', 'GuardProtocol', 'NotProtocol', 'OptProtocol', 'Hashable', 'Origin', 'Special', 'NotSpecial',
           'NonStrBytesIterable', 'HasAnyAttributeProtocol', 'HasAllAttributeProtocol', 'DuckedProtocol',
           'DuckSpecProtocol', 'DuckModuleProtocol', 'DuckTypeProtocol', 'AnnDataProtocol', 'NDArrayProtocol',
           'DataFrameProtocol']

# %% ../nbs/02_prot.ipynb 6
#| export


# %% ../nbs/02_prot.ipynb 8
from types import NoneType
from typing import (Any, Self, TypeGuard, Protocol, Iterable, _SpecialForm, _ProtocolMeta, Generic, get_origin,)

# %% ../nbs/02_prot.ipynb 10
#| export

# %% ../nbs/02_prot.ipynb 12
#| export

# %% ../nbs/02_prot.ipynb 14
from .cons import U1, U2, DUCK_ATTRS, DUCKED_ATTRS, DUCK_SPEC_ATTRS, __HASH__, __ATTRS__
from .type import T

# %% ../nbs/02_prot.ipynb 16
class ProtocolMeta(_ProtocolMeta):
    def __instancecheck__(cls, __instance) -> TypeGuard[T]:
        return cls.__guard__(__instance)
    
class GuardProtocol(Protocol[T], metaclass=ProtocolMeta):
    ...

# %% ../nbs/02_prot.ipynb 18
class NotProtocol(Protocol[T]):
    '''A protocol representing a not being a type'''
    def __instancecheck__(cls: type[T], inst: Any) -> TypeGuard[type[T]]: 
        return not isinstance(inst, cls)
    
class OptProtocol(Protocol[T]):
    '''A protocol representing an optional type'''
    def __instancecheck__(cls: type[T], inst: Any) -> TypeGuard[type[T]]: 
        return isinstance(inst, (cls, NoneType))

# %% ../nbs/02_prot.ipynb 20
class Hashable(Protocol):
    '''A protocol representing a hashable type'''
    def __hash__(self: Self) -> int: ...
    def __instancecheck__(self: Self, __instance) -> TypeGuard['Hashable']:
        return hasattr(__instance, __HASH__)

# %% ../nbs/02_prot.ipynb 22
class Origin(Hashable, Protocol):
    '''A protocol representing origin typing types'''
    def __hash__(self: Self) -> int:...
    def __instancecheck__(self: Self, __instance) -> TypeGuard['Origin']:
        return hasattr(__instance, __HASH__) and get_origin(__instance)

# %% ../nbs/02_prot.ipynb 24
class Special(Origin, Protocol):
    '''A protocol representing special typing types'''
    def __hash__(self: Self) -> int:...
    def __instancecheck__(self: Self, __instance) -> TypeGuard['Special']:
        origin = super().__instancecheck__(__instance)
        special = isinstance(__instance, _SpecialForm) or __instance in (Generic, Protocol)
        return (origin and special)
    
class NotSpecial(Origin, Protocol):
    '''A protocol representing not special typing types'''
    def __hash__(self: Self) -> int: ...
    def __instancecheck__(self: Self, __instance) -> TypeGuard['NotSpecial']:
        origin = super().__instancecheck__(__instance)
        special = isinstance(__instance, _SpecialForm) or __instance in (Generic, Protocol)
        return (origin and not special)

# %% ../nbs/02_prot.ipynb 26
class NonStrBytesIterable(Protocol):
    '''A protocol representing non-string, non-bytes iterable objects.'''
    def __iter__(self: Self) -> Iterable: ...
    def __instancecheck__(self: Self, __instance) -> TypeGuard['NonStrBytesIterable']:
        return isinstance(__instance, Iterable) and not isinstance(__instance, (str, bytes))

# %% ../nbs/02_prot.ipynb 28
class HasAnyAttributeProtocol(GuardProtocol[T]):
    '''A protocol representing to check if an object has any attribute in `__attrs__`.'''
    __attrs__: tuple[str, ...] = ()
    @classmethod
    def __guard__(self: Self, ins) -> TypeGuard[T]:
        attrs = getattr(self, __ATTRS__, ())
        if not any(hasattr(ins, attr) for attr in attrs): return False
        return True
    
    
class HasAllAttributeProtocol(GuardProtocol[T]):
    '''A protocol representing to check if an object has all attributes in `__attrs__`.'''
    __attrs__: tuple[str, ...] = ()
    @classmethod
    def __guard__(self: Self, ins) -> TypeGuard[T]:
        attrs = getattr(self, __ATTRS__, ())
        if not all(hasattr(ins, attr) for attr in attrs): return False
        return True

# %% ../nbs/02_prot.ipynb 30
class DuckedProtocol(HasAnyAttributeProtocol[T]):
    '''A protocol representing a something that might be like a duck'''
    __attrs__: tuple[str, ...] = DUCKED_ATTRS
    
class DuckSpecProtocol(HasAllAttributeProtocol[T]):
    '''A protocol representing a duckspec'''
    __attrs__: tuple[str, ...] = DUCK_SPEC_ATTRS
    
class DuckModuleProtocol(HasAllAttributeProtocol[T]):
    '''A protocol representing a duck module'''
    __attrs__: tuple[str, ...]
    
class DuckTypeProtocol(HasAllAttributeProtocol[T]):
    '''A protocol representing a duck-type'''
    __attrs__: tuple[str, ...] = DUCK_ATTRS

# %% ../nbs/02_prot.ipynb 33
class AnnDataProtocol(HasAllAttributeProtocol[T]):
    '''A protocol representing a `anndata.AnnData`'''
    __attrs__: tuple[str, ...] = ('X', 'obs', 'var', 'obsm', 'varm', 'uns', 'layers',)

# %% ../nbs/02_prot.ipynb 35
class NDArrayProtocol(HasAllAttributeProtocol[T]):
    '''A protocol representing a `numpy.ndarray`'''
    __attrs__: tuple[str, ...] = ('T', 'ndim', 'shape', )

# %% ../nbs/02_prot.ipynb 37
class DataFrameProtocol(HasAllAttributeProtocol[T]):
    '''A protocol representing a `pandas.DataFrame`'''
    __attrs__: tuple[str, ...] = (
        'T', 'describe', 'groupby', 'head', 'info', 'mean', 'median', 'nunique', 'quantile', 'shape', 'tail', 'isin',
        'keys', 'where', 'values', 'columns',
    )
