# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/09_base.ipynb.

# %% auto 0
__all__ = ['Duck', 'DuckModule', 'DuckSpec']

# %% ../nbs/09_base.ipynb 6
import sys

# %% ../nbs/09_base.ipynb 8
from types import ModuleType, FunctionType, BuiltinFunctionType
from typing import Any, Self, Union, Callable

# %% ../nbs/09_base.ipynb 10
#| export

# %% ../nbs/09_base.ipynb 12
#| export

# %% ../nbs/09_base.ipynb 14
from .cons import DUCK_SPEC_ATTRS
from .type import P
from .util import (prep_kwargs, asset_last, tryattr)
from .meta import DuckModuleMeta, ABCDuck

# %% ../nbs/09_base.ipynb 17
class Duck(metaclass=ABCDuck):
    '''A class representing a duck-type.'''

# %% ../nbs/09_base.ipynb 22
class DuckModule(ModuleType, metaclass=DuckModuleMeta):
    ...

# %% ../nbs/09_base.ipynb 30
class DuckSpec(object):
    asset: str
    is_module: bool = False
    ducked: Union['DuckModule',  None] = None
    default: Any = None
    bases: tuple | Callable[[], tuple] = ()
    attrs: dict | Callable[[], dict] = {}
    delayed: dict = {}
    
    def __prep__(self, *args: P.args, **kwargs: P.kwargs) -> dict:
        # prepare arguments by overwriting classvars with *args / **kwargs
        return prep_kwargs(self, *args, __attrs=DUCK_SPEC_ATTRS, **kwargs)
    
    def __load__(self, *args: P.args, **kwargs: P.kwargs) -> type:
        # prepare arguments by overwriting classvars with *args / **kwargs
        kwds = self.__prep__(*args, **kwargs)
        
        # try to load the asset as a module or class
        item = tryattr(**kwds)
        
        # try to preserve the duckspec by storing it in the item
        try: setattr(item, 'duckspec', self)
        except: ...
        
        # update the duckspec with the item
        setattr(self, '_duck', item)
        return item
    
    def __new__(cls, *args: P.args, **kwargs: P.kwargs) -> Self:
        inst = super().__new__(cls)
        kwds = inst.__prep__(*args, **kwargs)
        name = asset_last(kwds.get('asset', ''))
        # save instance variables
        for k, v in kwds.items(): setattr(inst, k, v)
        return inst
    
    def __init__(
        self: Self, 
        asset: str, 
        is_module: bool = False, 
        ducked: Union['DuckModule', None] = None,
        default: Any = None, 
        bases: tuple | Callable[[], tuple] = (),
        attrs: dict | Callable[[], dict] = {},
        **kwargs
    ):
        kwds = dict(asset=asset, is_module=is_module, ducked=ducked, default=default, bases=bases, attrs=attrs)
        for k, v in kwds.items(): setattr(self, k, v)
            
    def __repr__(self: Self) -> str:
        return f'''<duckspec {getattr(self, 'asset', '')}>'''
    
    @property
    def duck(self: Self) -> Union[type, ModuleType, FunctionType, BuiltinFunctionType]:
        try: 
            return self._duck
        except AttributeError: 
            try:
                self._duck = self.__load__()
            except Exception as e:
                print(f'Error loading duckspec {self}: {e}')
                return self.default
        
        return self._duck
    
    def load(self: Self) -> Union[type, ModuleType, FunctionType, BuiltinFunctionType]: 
        if hasattr(self, '_duck'): return self._duck
        return self.duck 
