|
17 | 17 | "source": [
|
18 | 18 | "#export\n",
|
19 | 19 | "from fastcore.imports import *\n",
|
| 20 | + "from functools import lru_cache\n", |
20 | 21 | "from contextlib import contextmanager\n",
|
21 | 22 | "from copy import copy\n",
|
| 23 | + "from configparser import ConfigParser\n", |
22 | 24 | "import random,pickle"
|
23 | 25 | ]
|
24 | 26 | },
|
|
1434 | 1436 | "#export\n",
|
1435 | 1437 | "def nested_attr(o, attr, default=None):\n",
|
1436 | 1438 | " \"Same as `getattr`, but if `attr` includes a `.`, then looks inside nested objects\"\n",
|
1437 |
| - " for a in attr.split(\".\"): o = getattr(o, a, default)\n", |
| 1439 | + " try:\n", |
| 1440 | + " for a in attr.split(\".\"): o = getattr(o, a)\n", |
| 1441 | + " except AttributeError: return default\n", |
1438 | 1442 | " return o"
|
1439 | 1443 | ]
|
1440 | 1444 | },
|
|
1822 | 1826 | {
|
1823 | 1827 | "data": {
|
1824 | 1828 | "text/plain": [
|
1825 |
| - "[11, 10, 9]" |
| 1829 | + "[0, 7, 11]" |
1826 | 1830 | ]
|
1827 | 1831 | },
|
1828 | 1832 | "execution_count": null,
|
|
2853 | 2857 | "test_eq(t.map_first(lambda o:o*2 if o>2 else None), 6)"
|
2854 | 2858 | ]
|
2855 | 2859 | },
|
| 2860 | + { |
| 2861 | + "cell_type": "markdown", |
| 2862 | + "metadata": {}, |
| 2863 | + "source": [ |
| 2864 | + "## Config -" |
| 2865 | + ] |
| 2866 | + }, |
| 2867 | + { |
| 2868 | + "cell_type": "code", |
| 2869 | + "execution_count": null, |
| 2870 | + "metadata": {}, |
| 2871 | + "outputs": [], |
| 2872 | + "source": [ |
| 2873 | + "#export\n", |
| 2874 | + "def save_config_file(file, d):\n", |
| 2875 | + " \"Write settings dict to a new config file, or overwrite the existing one.\"\n", |
| 2876 | + " config = ConfigParser()\n", |
| 2877 | + " config['DEFAULT'] = d\n", |
| 2878 | + " config.write(open(file, 'w'))" |
| 2879 | + ] |
| 2880 | + }, |
| 2881 | + { |
| 2882 | + "cell_type": "code", |
| 2883 | + "execution_count": null, |
| 2884 | + "metadata": {}, |
| 2885 | + "outputs": [], |
| 2886 | + "source": [ |
| 2887 | + "#export\n", |
| 2888 | + "def read_config_file(file):\n", |
| 2889 | + " config = ConfigParser()\n", |
| 2890 | + " config.read(file)\n", |
| 2891 | + " return config" |
| 2892 | + ] |
| 2893 | + }, |
| 2894 | + { |
| 2895 | + "cell_type": "markdown", |
| 2896 | + "metadata": {}, |
| 2897 | + "source": [ |
| 2898 | + "Config files are saved and read using Python's `configparser.ConfigParser`, inside the `DEFAULT` section." |
| 2899 | + ] |
| 2900 | + }, |
| 2901 | + { |
| 2902 | + "cell_type": "code", |
| 2903 | + "execution_count": null, |
| 2904 | + "metadata": {}, |
| 2905 | + "outputs": [], |
| 2906 | + "source": [ |
| 2907 | + "_d = dict(user='fastai', lib_name='fastcore')\n", |
| 2908 | + "try:\n", |
| 2909 | + " save_config_file('tmp.ini', _d)\n", |
| 2910 | + " res = read_config_file('tmp.ini')\n", |
| 2911 | + "finally: os.unlink('tmp.ini')\n", |
| 2912 | + "test_eq(res['DEFAULT'], _d)" |
| 2913 | + ] |
| 2914 | + }, |
| 2915 | + { |
| 2916 | + "cell_type": "code", |
| 2917 | + "execution_count": null, |
| 2918 | + "metadata": {}, |
| 2919 | + "outputs": [], |
| 2920 | + "source": [ |
| 2921 | + "#export\n", |
| 2922 | + "def _add_new_defaults(cfg, file, **kwargs):\n", |
| 2923 | + " for k,v in kwargs.items():\n", |
| 2924 | + " if cfg.get(k, None) is None:\n", |
| 2925 | + " cfg[k] = v\n", |
| 2926 | + " save_config_file(file, cfg)" |
| 2927 | + ] |
| 2928 | + }, |
| 2929 | + { |
| 2930 | + "cell_type": "code", |
| 2931 | + "execution_count": null, |
| 2932 | + "metadata": {}, |
| 2933 | + "outputs": [], |
| 2934 | + "source": [ |
| 2935 | + "#export\n", |
| 2936 | + "@lru_cache(maxsize=None)\n", |
| 2937 | + "class Config:\n", |
| 2938 | + " \"Reading and writing `settings.ini`\"\n", |
| 2939 | + " def __init__(self, cfg_name='settings.ini'):\n", |
| 2940 | + " cfg_path = Path.cwd()\n", |
| 2941 | + " while cfg_path != cfg_path.parent and not (cfg_path/cfg_name).exists(): cfg_path = cfg_path.parent\n", |
| 2942 | + " self.config_file = cfg_path/cfg_name\n", |
| 2943 | + " assert self.config_file.exists(), f\"Could not find {cfg_name}\"\n", |
| 2944 | + " self.d = read_config_file(self.config_file)['DEFAULT']\n", |
| 2945 | + " _add_new_defaults(self.d, self.config_file,\n", |
| 2946 | + " host=\"github\", doc_host=\"https://%(user)s.github.io\", doc_baseurl=\"/%(lib_name)s/\")\n", |
| 2947 | + "\n", |
| 2948 | + " def __getattr__(self,k):\n", |
| 2949 | + " if k=='d' or k not in self.d: raise AttributeError(k)\n", |
| 2950 | + " return self.config_file.parent/self.d[k] if k.endswith('_path') else self.get(k)\n", |
| 2951 | + "\n", |
| 2952 | + " def get(self,k,default=None): return self.d.get(k, default)\n", |
| 2953 | + " def __setitem__(self,k,v): self.d[k] = str(v)\n", |
| 2954 | + " def __contains__(self,k): return k in self.d\n", |
| 2955 | + " def save(self): save_config_file(self.config_file,self.d)" |
| 2956 | + ] |
| 2957 | + }, |
| 2958 | + { |
| 2959 | + "cell_type": "markdown", |
| 2960 | + "metadata": {}, |
| 2961 | + "source": [ |
| 2962 | + "`Config` searches parent directories for a config file, and provides direct access to the 'DEFAULT' section. " |
| 2963 | + ] |
| 2964 | + }, |
| 2965 | + { |
| 2966 | + "cell_type": "code", |
| 2967 | + "execution_count": null, |
| 2968 | + "metadata": {}, |
| 2969 | + "outputs": [], |
| 2970 | + "source": [ |
| 2971 | + "try:\n", |
| 2972 | + " save_config_file('../tmp.ini', _d)\n", |
| 2973 | + " cfg = Config('tmp.ini')\n", |
| 2974 | + " test_eq(cfg.user,'fastai')\n", |
| 2975 | + " test_eq(cfg.doc_baseurl,'/fastcore/')\n", |
| 2976 | + "finally: os.unlink('../tmp.ini')" |
| 2977 | + ] |
| 2978 | + }, |
2856 | 2979 | {
|
2857 | 2980 | "cell_type": "markdown",
|
2858 | 2981 | "metadata": {},
|
|
0 commit comments