{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Summary\n",
    "\n",
    "Originally was going to work on auto cli here and did some planning re interface. Ended up working mostly on fallback decorator, isstatic, etc."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-11-26T21:33:29.718937Z",
     "start_time": "2020-11-26T21:33:29.695456Z"
    }
   },
   "outputs": [],
   "source": [
    "%load_ext autoreload\n",
    "%autoreload 3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-11-26T21:33:47.615733Z",
     "start_time": "2020-11-26T21:33:47.610477Z"
    }
   },
   "outputs": [],
   "source": [
    "import gc\n",
    "import inspect\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import os\n",
    "from pathlib import Path\n",
    "\n",
    "from htools import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-11-26T21:33:36.898437Z",
     "start_time": "2020-11-26T21:33:36.885891Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Current directory: /Users/hmamin/incendio\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/hmamin/anaconda3/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n",
      "  and should_run_async(code)\n"
     ]
    }
   ],
   "source": [
    "cd_root()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-11-26T21:36:23.707217Z",
     "start_time": "2020-11-26T21:36:23.700078Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "# OPTION 1\n",
      "@auto_cli()\n",
      "def main():\n",
      "    x = f1(a, b, c)\n",
      "    x = f2(x=x, y=y, **f2_kwargs)\n",
      "    print(x)\n",
      "\n",
      "if __name__ == '__main__':\n",
      "    fire.Fire(main)\n",
      "    \n",
      "    \n",
      "# OPTION 2\n",
      "@auto_cli(f1, f2)\n",
      "def main():\n",
      "    x = f1(a, b, c)\n",
      "    x = f2(x=x, y=y, **f2_kwargs)\n",
      "    print(x)\n",
      "\n",
      "if __name__ == '__main__':\n",
      "    fire.Fire(main)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(\n",
    "\"\"\"\n",
    "# OPTION 1\n",
    "@auto_cli()\n",
    "def main():\n",
    "    x = f1(a, b, c)\n",
    "    x = f2(x=x, y=y, **f2_kwargs)\n",
    "    print(x)\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    fire.Fire(main)\n",
    "    \n",
    "    \n",
    "# OPTION 2\n",
    "@auto_cli(f1, f2)\n",
    "def main():\n",
    "    x = f1(a, b, c)\n",
    "    x = f2(x=x, y=y, **f2_kwargs)\n",
    "    print(x)\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    fire.Fire(main)\n",
    "\"\"\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## isstatic"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 609,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T18:51:05.140502Z",
     "start_time": "2020-12-05T18:51:05.134228Z"
    }
   },
   "outputs": [],
   "source": [
    "from inspect import ismethod, signature, getmembers, getmodule\n",
    "import inspect\n",
    "from types import FunctionType, MethodType"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-03T06:31:22.690092Z",
     "start_time": "2020-12-03T06:31:22.686599Z"
    }
   },
   "outputs": [],
   "source": [
    "def bar():\n",
    "    return 'in bar'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 114,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-03T06:46:59.094121Z",
     "start_time": "2020-12-03T06:46:59.088827Z"
    }
   },
   "outputs": [],
   "source": [
    "def baz(cls):\n",
    "    return 'in baz'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 135,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-03T06:52:30.770124Z",
     "start_time": "2020-12-03T06:52:30.766663Z"
    }
   },
   "outputs": [],
   "source": [
    "def bak(unk):\n",
    "    return 'in bak'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 495,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T06:24:11.005990Z",
     "start_time": "2020-12-05T06:24:10.999662Z"
    }
   },
   "outputs": [],
   "source": [
    "class FooBar:\n",
    "    \n",
    "    def walk(self, a):\n",
    "        return 'a'\n",
    "    \n",
    "    @staticmethod\n",
    "    def static_walk(x, y):\n",
    "        return x*y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 496,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T06:24:23.699869Z",
     "start_time": "2020-12-05T06:24:23.691971Z"
    }
   },
   "outputs": [],
   "source": [
    "class Foo:\n",
    "    \n",
    "    class_var = 'my_class_var'\n",
    "    \n",
    "    def __init__(self, inst_var='my_inst_var'):\n",
    "        self.inst_var = inst_var\n",
    "        self.inst_var_func = bar\n",
    "        self.inst_var_func_baz = baz\n",
    "        self.inst_var_func_bak = bak\n",
    "        self.inst_var_inspect = inspect.signature\n",
    "        self.inst_var_foobar = FooBar().walk\n",
    "        self.inst_var_foobar_static = FooBar.static_walk\n",
    "        \n",
    "    def my_method(self, x):\n",
    "        return 'in regular method'\n",
    "    \n",
    "    @staticmethod\n",
    "    def my_static(y):\n",
    "        return 'in staticmethod', y\n",
    "    \n",
    "    @classmethod\n",
    "    def my_classmethod(cls, *args):\n",
    "        return cls('-'.join(args))\n",
    "    \n",
    "    @abstractmethod\n",
    "    def my_abstract(self):\n",
    "        raise NotImplementedError()\n",
    "        \n",
    "    @property\n",
    "    def my_prop(self):\n",
    "        return 'my_prop_val'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 500,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T06:25:24.464857Z",
     "start_time": "2020-12-05T06:25:24.455064Z"
    }
   },
   "outputs": [],
   "source": [
    "f = Foo()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 501,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T06:25:24.768704Z",
     "start_time": "2020-12-05T06:25:24.764645Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function __main__.bar()>"
      ]
     },
     "execution_count": 501,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f.inst_var_func"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 502,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T06:25:24.984500Z",
     "start_time": "2020-12-05T06:25:24.979197Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function __main__.Foo.my_static(y)>"
      ]
     },
     "execution_count": 502,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f.my_static"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 503,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T06:25:25.130166Z",
     "start_time": "2020-12-05T06:25:25.125293Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'class_var': 'attribute',\n",
       " 'my_abstract': 'method',\n",
       " 'my_classmethod': 'method',\n",
       " 'my_method': 'method',\n",
       " 'my_prop': 'attribute',\n",
       " 'my_static': 'method'}"
      ]
     },
     "execution_count": 503,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "hdir(Foo)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 504,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T06:25:25.349034Z",
     "start_time": "2020-12-05T06:25:25.341238Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'class_var': 'attribute',\n",
       " 'inst_var': 'attribute',\n",
       " 'inst_var_foobar': 'method',\n",
       " 'inst_var_foobar_static': 'method',\n",
       " 'inst_var_func': 'method',\n",
       " 'inst_var_func_bak': 'method',\n",
       " 'inst_var_func_baz': 'method',\n",
       " 'inst_var_inspect': 'method',\n",
       " 'my_abstract': 'method',\n",
       " 'my_classmethod': 'method',\n",
       " 'my_method': 'method',\n",
       " 'my_prop': 'attribute',\n",
       " 'my_static': 'method'}"
      ]
     },
     "execution_count": 504,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "hdir(f)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 505,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T06:25:25.500261Z",
     "start_time": "2020-12-05T06:25:25.494991Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "function"
      ]
     },
     "execution_count": 505,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(f.my_static)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 521,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T06:33:16.474496Z",
     "start_time": "2020-12-05T06:33:16.470127Z"
    }
   },
   "outputs": [],
   "source": [
    "def _isstatic(meth):\n",
    "    # Logic relies on convention of reserving \"cls\" and \"self\" as names of the\n",
    "    # first argument of classmethods and standard methods, respectively, and\n",
    "    # not using them for other arguments.\n",
    "    if (ismethod(meth) or '.' not in getattr(meth, '__qualname__', '')\n",
    "        or not callable(meth)): \n",
    "        return False\n",
    "    params_ = list(params(meth))\n",
    "    if not params_ or params_[0] in {'cls', 'self'}:\n",
    "        return False\n",
    "    return True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 626,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T18:54:48.660389Z",
     "start_time": "2020-12-05T18:54:48.654992Z"
    }
   },
   "outputs": [],
   "source": [
    "def method_of(obj):\n",
    "    cls, name = obj.__qualname__.split('.')\n",
    "    return dict(getmembers(getmodule(obj)))[cls]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 655,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T19:04:33.293423Z",
     "start_time": "2020-12-05T19:04:33.286519Z"
    }
   },
   "outputs": [],
   "source": [
    "assert method_of(Foo.my_static) == method_of(f.my_static) == Foo"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 628,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T18:55:00.701093Z",
     "start_time": "2020-12-05T18:55:00.692436Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "__main__.Foo"
      ]
     },
     "execution_count": 628,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "method_of(f.my_static)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 653,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T18:57:52.612174Z",
     "start_time": "2020-12-05T18:57:52.602176Z"
    }
   },
   "outputs": [],
   "source": [
    "def _isstatic2(meth):\n",
    "    # First check isn't required but I want to avoid reaching the hackier bits\n",
    "    # of code if necessary. This catches regular methods and attributes.\n",
    "    if ismethod(meth) or not callable(meth): return False\n",
    "    parts = getattr(meth, '__qualname__', '').split('.')\n",
    "    if len(parts) != 2: return False\n",
    "    cls = method_of(meth)\n",
    "    return hasstatic(cls, parts[-1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 674,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T19:19:08.951722Z",
     "start_time": "2020-12-05T19:19:08.948393Z"
    }
   },
   "outputs": [],
   "source": [
    "def has_classmethod(cls, meth_name):\n",
    "    return isinstance(getattr_static(cls, meth_name), classmethod)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 675,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T19:19:09.126144Z",
     "start_time": "2020-12-05T19:19:09.119988Z"
    }
   },
   "outputs": [],
   "source": [
    "def is_classmethod(meth):\n",
    "    if not ismethod(meth): return False\n",
    "    parts = getattr(meth, '__qualname__', '').split('.')\n",
    "    if len(parts) != 2: return False\n",
    "    cls = method_of(meth)\n",
    "    return has_classmethod(cls, parts[-1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 676,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T19:19:09.730021Z",
     "start_time": "2020-12-05T19:19:09.724306Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "class_var attribute\n",
      "my_class_var\n",
      "<class 'str'>\n",
      "qualname dots 0\n",
      "\n",
      "inst_var attribute\n",
      "my_inst_var\n",
      "<class 'str'>\n",
      "qualname dots 0\n",
      "\n",
      "my_prop attribute\n",
      "my_prop_val\n",
      "<class 'str'>\n",
      "qualname dots 0\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# OBJ (attrs)\n",
    "for k, v in hdir(f).items():\n",
    "    if v == 'method': continue   \n",
    "    print(k, v)\n",
    "    obj = getattr(f, k)\n",
    "    print(obj)\n",
    "    print(type(obj))\n",
    "    print('qualname dots', getattr(obj, '__qualname__', '').count('.'))\n",
    "    print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 677,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T19:19:15.478396Z",
     "start_time": "2020-12-05T19:19:15.462736Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "inst_var_foobar <bound method FooBar.walk of <__main__.FooBar object at 0x11ce4da90>>\n",
      "has classmethod False\n",
      "is classmethod False\n",
      "\n",
      "inst_var_foobar_static <function FooBar.static_walk at 0x1a23e3b950>\n",
      "has classmethod False\n",
      "is classmethod False\n",
      "\n",
      "inst_var_func <function bar at 0x1a237e09d8>\n",
      "has classmethod False\n",
      "is classmethod False\n",
      "\n",
      "inst_var_func_bak <function bak at 0x1a22bc39d8>\n",
      "has classmethod False\n",
      "is classmethod False\n",
      "\n",
      "inst_var_func_baz <function baz at 0x1a23104ea0>\n",
      "has classmethod False\n",
      "is classmethod False\n",
      "\n",
      "inst_var_inspect <function signature at 0x10fc2dd08>\n",
      "has classmethod False\n",
      "is classmethod False\n",
      "\n",
      "my_abstract <bound method Foo.my_abstract of <__main__.Foo object at 0x11ce4dd30>>\n",
      "has classmethod False\n",
      "is classmethod False\n",
      "\n",
      "my_classmethod <bound method Foo.my_classmethod of <class '__main__.Foo'>>\n",
      "has classmethod True\n",
      "is classmethod True\n",
      "\n",
      "my_method <bound method Foo.my_method of <__main__.Foo object at 0x11ce4dd30>>\n",
      "has classmethod False\n",
      "is classmethod False\n",
      "\n",
      "my_static <function Foo.my_static at 0x1a23b5b8c8>\n",
      "has classmethod False\n",
      "is classmethod False\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# OBJ (methods)\n",
    "for k, v in hdir(f).items():\n",
    "    if v == 'attribute': continue  \n",
    "    obj = getattr(f, k)\n",
    "    print(k, obj)\n",
    "    \n",
    "#     print('type', type(obj))\n",
    "#     print('qualname', getattr(obj, '__qualname__', ''))\n",
    "#     print('is method:', ismethod(obj))\n",
    "#     print('is FunctionType:', isinstance(obj, FunctionType))\n",
    "#     print('is MethodType:', isinstance(obj, MethodType))\n",
    "#     print(list(params(obj)))  \n",
    "\n",
    "#     print('is static', _isstatic(obj))\n",
    "#     print('static 2', _isstatic2(obj))\n",
    "\n",
    "    print('has classmethod', has_classmethod(f, k))\n",
    "    print('is classmethod', is_classmethod(obj))\n",
    "    \n",
    "    print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 665,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T19:11:56.317482Z",
     "start_time": "2020-12-05T19:11:56.302413Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 665,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(Foo.__dict__['my_classmethod'], classmethod)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 666,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T19:12:03.747207Z",
     "start_time": "2020-12-05T19:12:03.740609Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 666,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(Foo.my_classmethod, classmethod)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 678,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T19:19:38.379793Z",
     "start_time": "2020-12-05T19:19:38.371952Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "class_var my_class_var\n",
      "has classmethod False\n",
      "is classmethod False\n",
      "\n",
      "my_prop <property object at 0x11cdfc8b8>\n",
      "has classmethod False\n",
      "is classmethod False\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# CLASS (attrs)\n",
    "for k, v in hdir(Foo).items():\n",
    "    if v == 'method': continue  \n",
    "    obj = getattr(Foo, k)\n",
    "    print(k, obj)\n",
    "    \n",
    "#     print('type', type(obj))\n",
    "#     print('qualname', getattr(obj, '__qualname__', ''))\n",
    "#     print('is method:', ismethod(obj))\n",
    "#print('is FunctionType:', isinstance(obj, FunctionType))\n",
    "#     print('is MethodType:', isinstance(obj, MethodType))\n",
    "#     print(list(params(obj)))  \n",
    "\n",
    "#     print('is static', _isstatic(obj))\n",
    "#     print('is static2', _isstatic2(obj))\n",
    "    \n",
    "    print('has classmethod', has_classmethod(Foo, k))\n",
    "    print('is classmethod', is_classmethod(obj))\n",
    "    print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 679,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T19:19:47.257242Z",
     "start_time": "2020-12-05T19:19:47.245047Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "my_abstract <function Foo.my_abstract at 0x1a23b5b840>\n",
      "has classmethod False\n",
      "is classmethod False\n",
      "\n",
      "my_classmethod <bound method Foo.my_classmethod of <class '__main__.Foo'>>\n",
      "has classmethod True\n",
      "is classmethod True\n",
      "\n",
      "my_method <function Foo.my_method at 0x1a23b5bd08>\n",
      "has classmethod False\n",
      "is classmethod False\n",
      "\n",
      "my_static <function Foo.my_static at 0x1a23b5b8c8>\n",
      "has classmethod False\n",
      "is classmethod False\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# CLASS (methods)\n",
    "for k, v in hdir(Foo).items():\n",
    "    if v == 'attribute': continue  \n",
    "    obj = getattr(Foo, k)\n",
    "    print(k, obj)\n",
    "    \n",
    "#     print('type', type(obj))\n",
    "#     print('qualname', getattr(obj, '__qualname__', ''))\n",
    "#     print('is method:', ismethod(obj))\n",
    "#     print('is FunctionType:', isinstance(obj, FunctionType))\n",
    "#     print('is MethodType:', isinstance(obj, MethodType))\n",
    "#     print(list(params(obj)))  \n",
    "\n",
    "#     print('is static', _isstatic(obj))\n",
    "#     print('is static 2', _isstatic2(obj))\n",
    "\n",
    "    print('has classmethod', has_classmethod(Foo, k))\n",
    "    print('is classmethod', is_classmethod(obj))\n",
    "    print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 642,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T18:56:40.858373Z",
     "start_time": "2020-12-05T18:56:40.850695Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['y']"
      ]
     },
     "execution_count": 642,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(params(Foo.my_static))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-03T06:22:44.117387Z",
     "start_time": "2020-12-03T06:22:44.046062Z"
    }
   },
   "outputs": [],
   "source": [
    "staticmethod??"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 563,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T18:31:00.511582Z",
     "start_time": "2020-12-05T18:31:00.504428Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 563,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "hasstatic(Foo, 'my_static')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 564,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T18:31:04.067102Z",
     "start_time": "2020-12-05T18:31:04.060870Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 564,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "hasstatic(f, 'my_static')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Override_self\n",
    "\n",
    "Thinking out loud: we can't use add_kwargs here to update the method's signature because we don't know what attributes self has until the method is called (we get them by examining args[0] in wrapper). Any way around this would probably be extremely hacky. I suppose if we mandated `keep` we could add those ahead of time but we wouldn't be able to show defaults."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 241,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-04T04:40:39.514166Z",
     "start_time": "2020-12-04T04:40:39.508657Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/hmamin/anaconda3/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n",
      "  and should_run_async(code)\n"
     ]
    }
   ],
   "source": [
    "from contextlib import contextmanager"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 153,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-03T06:56:39.609271Z",
     "start_time": "2020-12-03T06:56:39.572664Z"
    }
   },
   "outputs": [],
   "source": [
    "@auto_repr\n",
    "class Tree:\n",
    "    \n",
    "    def __init__(self, a, b, c=3):\n",
    "        self.a = a\n",
    "        self.b = b\n",
    "        self.c = c\n",
    "        \n",
    "    def call(self, **kwargs):\n",
    "        a = kwargs.pop('a', self.a)\n",
    "        b = kwargs.pop('b', self.b)\n",
    "        c = kwargs.pop('c', self.c)\n",
    "        return a, b, c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 154,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-03T06:56:39.984417Z",
     "start_time": "2020-12-03T06:56:39.976208Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Tree(a=1, b=2, c=3)"
      ]
     },
     "execution_count": 154,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "t = Tree(1, 2, 3)\n",
    "t"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 155,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-03T06:56:40.293327Z",
     "start_time": "2020-12-03T06:56:40.286878Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 2, 3)"
      ]
     },
     "execution_count": 155,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "t.call()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 156,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-03T06:56:51.467776Z",
     "start_time": "2020-12-03T06:56:51.461705Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 2, 44)"
      ]
     },
     "execution_count": 156,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "t.call(c=44)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "@auto_repr\n",
    "class Tree:\n",
    "    \n",
    "    def __init__(self, a, b, c=3):\n",
    "        self.a = a\n",
    "        self.b = b\n",
    "        self.c = c\n",
    "        \n",
    "    @kwargs_fallback\n",
    "    def call(self, **kwargs):\n",
    "        # Signature shows a, b, and c with class default values.\n",
    "        return a, b, c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 242,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-04T04:40:43.419597Z",
     "start_time": "2020-12-04T04:40:43.392578Z"
    }
   },
   "outputs": [],
   "source": [
    "@contextmanager\n",
    "def temporary_global_scope(kwargs):\n",
    "    # Make kwargs temporarily available as global vars.\n",
    "    old_globals = globals().copy()\n",
    "    globals().update(kwargs)\n",
    "    try:\n",
    "        yield\n",
    "    finally:\n",
    "        for k in kwargs:\n",
    "            if k in old_globals:\n",
    "                globals()[k] = old_globals[k]\n",
    "            else:\n",
    "                del globals()[k]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 435,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-04T05:15:17.173234Z",
     "start_time": "2020-12-04T05:15:17.160437Z"
    }
   },
   "outputs": [],
   "source": [
    "# diff name for now since htools has other kwargs_fallback func.\n",
    "def fallback(meth=None, *, keep=(), drop=(), save=False):\n",
    "    \"\"\"Make instance/class attributes available as default arguments for a \n",
    "    method. Kwargs can be passed in to override one or more of them. You can\n",
    "    also choose for kwargs to update the instance attributes if desired.\n",
    "    \n",
    "    When using default values for keep/drop/save, the decorator can be used\n",
    "    without parentheses. If you want to change one or more arguments, they\n",
    "    must be passed in as keyword args (meth is never explicitly passed in, of\n",
    "    course).\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    meth: method\n",
    "        The method to decorate. Unlike the other arguments, this is passed in\n",
    "        implicitly.\n",
    "    keep: Iterable[str] or str\n",
    "        Name(s) of instance attributes to include. If you specify a value \n",
    "        here, ONLY these instance attributes will be made available as \n",
    "        fallbacks. If you don't pass in any value, the default is for all\n",
    "        instance attributes to be made available. You can specify `keep`, \n",
    "        `drop`, or neither, but not both. This covers all possible options:\n",
    "        keep only a few, keep all BUT a few, or keep all (drop all is the\n",
    "        default case and doesn't require a decorator).\n",
    "    drop: Iterable[str] or str\n",
    "        Name(s) of instance attributes to ignore. I.e. if you want to make\n",
    "        all instance attributes available as fallbacks except for self.df,\n",
    "        you could specify drop=('df').\n",
    "    save: bool\n",
    "        If True, kwargs that share names with instance attributes will be\n",
    "        overwritten with their new values. E.g. if we previously had\n",
    "        self.lr = 3e-3 and you call your decorated method with \n",
    "        obj.mymethod(lr=1), self.lr will be set to 1.\n",
    "    \"\"\"\n",
    "    if meth is None: \n",
    "        # Want to avoid errors if user passes in string or leaves comma out of\n",
    "        # tuple when specifying keep/drop.\n",
    "        return partial(fallback, keep=tolist(keep), drop=tolist(drop), \n",
    "                       save=save)\n",
    "    \n",
    "    @wraps(meth)\n",
    "    def wrapper(*args, **kwargs):\n",
    "        self = args[0]\n",
    "        self_kwargs = vars(self)\n",
    "        if keep or drop: self_kwargs = select(self_kwargs, keep, drop)\n",
    "\n",
    "        # Update kwargs with instance attribute defaults. Also update self if\n",
    "        # user asked to save kwargs.\n",
    "        for k, v in self_kwargs.items():\n",
    "            if k not in kwargs:\n",
    "                kwargs[k] = v\n",
    "            elif save:\n",
    "                setattr(self, k, kwargs[k])\n",
    "                \n",
    "        # Execute and return.\n",
    "        with temporary_global_scope(kwargs):\n",
    "            return meth(*args, **kwargs)\n",
    "    return wrapper"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 436,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-04T05:15:17.740286Z",
     "start_time": "2020-12-04T05:15:17.736914Z"
    }
   },
   "outputs": [],
   "source": [
    "@auto_repr\n",
    "class Tree:\n",
    "    \n",
    "    def __init__(self, a, b, c=3):\n",
    "        self.a = a\n",
    "        self.b = b\n",
    "        self.c = c\n",
    "        \n",
    "    @fallback(save=True)\n",
    "    def call(self, **kwargs):\n",
    "        return a, b, c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 437,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-04T05:15:18.242127Z",
     "start_time": "2020-12-04T05:15:18.235890Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 'bbb', 3)"
      ]
     },
     "execution_count": 437,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "t = Tree(1, 2, 3)\n",
    "t.call(b='bbb')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 438,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-04T05:15:18.401351Z",
     "start_time": "2020-12-04T05:15:18.394114Z"
    },
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "assert t.b == 'bbb'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 439,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-04T05:15:18.786919Z",
     "start_time": "2020-12-04T05:15:18.772101Z"
    }
   },
   "outputs": [],
   "source": [
    "@auto_repr\n",
    "class Tree:\n",
    "    \n",
    "    def __init__(self, a, b, c=3):\n",
    "        self.a = a\n",
    "        self.b = b\n",
    "        self.c = c\n",
    "        \n",
    "    @fallback(save=False)\n",
    "    def call(self, **kwargs):\n",
    "        return a, b, c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 440,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-04T05:15:18.920081Z",
     "start_time": "2020-12-04T05:15:18.913828Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 'bbb', 3)"
      ]
     },
     "execution_count": 440,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "t = Tree(1, 2, 3)\n",
    "t.call(b='bbb')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 441,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-04T05:15:19.053327Z",
     "start_time": "2020-12-04T05:15:19.050757Z"
    }
   },
   "outputs": [],
   "source": [
    "assert t.b == 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 442,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-04T05:15:19.194153Z",
     "start_time": "2020-12-04T05:15:19.187642Z"
    }
   },
   "outputs": [],
   "source": [
    "@auto_repr\n",
    "class Tree:\n",
    "    \n",
    "    def __init__(self, a, b, c=3):\n",
    "        self.a = a\n",
    "        self.b = b\n",
    "        self.c = c\n",
    "        \n",
    "    @fallback\n",
    "    def call(self, **kwargs):\n",
    "        return a, b, c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 443,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-04T05:15:19.341503Z",
     "start_time": "2020-12-04T05:15:19.334254Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 'bbb', 3)"
      ]
     },
     "execution_count": 443,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "t = Tree(1, 2, 3)\n",
    "t.call(b='bbb')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 444,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-04T05:15:19.509232Z",
     "start_time": "2020-12-04T05:15:19.500956Z"
    }
   },
   "outputs": [],
   "source": [
    "assert t.b == 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 445,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-04T05:15:19.622428Z",
     "start_time": "2020-12-04T05:15:19.612941Z"
    }
   },
   "outputs": [],
   "source": [
    "@auto_repr\n",
    "class Tree:\n",
    "    \n",
    "    def __init__(self, a, b, c=3):\n",
    "        self.a = a\n",
    "        self.b = b\n",
    "        self.c = c\n",
    "        \n",
    "    @fallback(keep=('a'))\n",
    "    def call(self, **kwargs):\n",
    "        return a, b, c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 446,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-04T05:15:20.037891Z",
     "start_time": "2020-12-04T05:15:20.025354Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "As expected, got NameError(name 'c' is not defined).\n"
     ]
    }
   ],
   "source": [
    "t = Tree(1, 2, 3)\n",
    "with assert_raises(NameError):\n",
    "    t.call(a='aaa', b='bbb')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 460,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T05:45:54.981341Z",
     "start_time": "2020-12-05T05:45:54.975773Z"
    }
   },
   "outputs": [],
   "source": [
    "@auto_repr\n",
    "class Tree:\n",
    "    \n",
    "    def __init__(self, a, b, c=3):\n",
    "        \"\"\"testing\n",
    "        \n",
    "        Parameters\n",
    "        ----------\n",
    "        a: int\n",
    "        b: int\n",
    "        c: int\n",
    "        \"\"\"\n",
    "        self.a = a\n",
    "        self.b = b\n",
    "        self.c = c\n",
    "        \n",
    "    @add_docstring(Tree.__init__)\n",
    "    @fallback(drop=('c'))\n",
    "    def call(self, **kwargs):\n",
    "        return a, b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 461,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T05:45:57.885589Z",
     "start_time": "2020-12-05T05:45:57.864355Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 'bbb')"
      ]
     },
     "execution_count": 461,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "t = Tree(1, 2, 3)\n",
    "t.call(b='bbb')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 455,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-12-05T05:44:54.912332Z",
     "start_time": "2020-12-05T05:44:54.904004Z"
    }
   },
   "outputs": [],
   "source": [
    "assert t.b == 2"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
