Skip to content

Commit 943323b

Browse files
committed
fixes #116
1 parent 591067c commit 943323b

File tree

3 files changed

+178
-5
lines changed

3 files changed

+178
-5
lines changed

fastcore/_nbdev.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
"CollBase": "01_foundation.ipynb",
4747
"L": "01_foundation.ipynb",
4848
"L.__signature__": "01_foundation.ipynb",
49+
"save_config_file": "01_foundation.ipynb",
50+
"read_config_file": "01_foundation.ipynb",
51+
"Config": "01_foundation.ipynb",
4952
"ifnone": "02_utils.ipynb",
5053
"maybe_attr": "02_utils.ipynb",
5154
"basic_repr": "02_utils.ipynb",

fastcore/foundation.py

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22

33
__all__ = ['defaults', 'copy_func', 'patch_to', 'patch', 'patch_property', 'add_docs', 'docs', 'custom_dir', 'arg0',
44
'arg1', 'arg2', 'arg3', 'arg4', 'coll_repr', 'is_bool', 'mask2idxs', 'cycle', 'zip_cycle', 'is_indexer',
5-
'negate_func', 'GetAttr', 'delegate_attr', 'bind', 'listable_types', 'first', 'nested_attr', 'CollBase', 'L']
5+
'negate_func', 'GetAttr', 'delegate_attr', 'bind', 'listable_types', 'first', 'nested_attr', 'CollBase', 'L',
6+
'save_config_file', 'read_config_file', 'Config']
67

78
# Cell
89
from .imports import *
10+
from functools import lru_cache
911
from contextlib import contextmanager
1012
from copy import copy
13+
from configparser import ConfigParser
1114
import random,pickle
1215

1316
# Cell
@@ -192,7 +195,9 @@ def first(x):
192195
# Cell
193196
def nested_attr(o, attr, default=None):
194197
"Same as `getattr`, but if `attr` includes a `.`, then looks inside nested objects"
195-
for a in attr.split("."): o = getattr(o, a, default)
198+
try:
199+
for a in attr.split("."): o = getattr(o, a)
200+
except AttributeError: return default
196201
return o
197202

198203
# Cell
@@ -379,4 +384,46 @@ def product(self): return self.reduce(operator.mul)
379384
L.__signature__ = pickle.loads(b'\x80\x03cinspect\nSignature\nq\x00(cinspect\nParameter\nq\x01X\x05\x00\x00\x00itemsq\x02cinspect\n_ParameterKind\nq\x03K\x01\x85q\x04Rq\x05\x86q\x06Rq\x07}q\x08(X\x08\x00\x00\x00_defaultq\tNX\x0b\x00\x00\x00_annotationq\ncinspect\n_empty\nq\x0bubh\x01X\x04\x00\x00\x00restq\x0ch\x03K\x02\x85q\rRq\x0e\x86q\x0fRq\x10}q\x11(h\th\x0bh\nh\x0bubh\x01X\x08\x00\x00\x00use_listq\x12h\x03K\x03\x85q\x13Rq\x14\x86q\x15Rq\x16}q\x17(h\t\x89h\nh\x0bubh\x01X\x05\x00\x00\x00matchq\x18h\x14\x86q\x19Rq\x1a}q\x1b(h\tNh\nh\x0bubtq\x1c\x85q\x1dRq\x1e}q\x1fX\x12\x00\x00\x00_return_annotationq h\x0bsb.')
380385

381386
# Cell
382-
Sequence.register(L);
387+
Sequence.register(L);
388+
389+
# Cell
390+
def save_config_file(file, d):
391+
"Write settings dict to a new config file, or overwrite the existing one."
392+
config = ConfigParser()
393+
config['DEFAULT'] = d
394+
config.write(open(file, 'w'))
395+
396+
# Cell
397+
def read_config_file(file):
398+
config = ConfigParser()
399+
config.read(file)
400+
return config
401+
402+
# Cell
403+
def _add_new_defaults(cfg, file, **kwargs):
404+
for k,v in kwargs.items():
405+
if cfg.get(k, None) is None:
406+
cfg[k] = v
407+
save_config_file(file, cfg)
408+
409+
# Cell
410+
@lru_cache(maxsize=None)
411+
class Config:
412+
"Reading and writing `settings.ini`"
413+
def __init__(self, cfg_name='settings.ini'):
414+
cfg_path = Path.cwd()
415+
while cfg_path != cfg_path.parent and not (cfg_path/cfg_name).exists(): cfg_path = cfg_path.parent
416+
self.config_file = cfg_path/cfg_name
417+
assert self.config_file.exists(), f"Could not find {cfg_name}"
418+
self.d = read_config_file(self.config_file)['DEFAULT']
419+
_add_new_defaults(self.d, self.config_file,
420+
host="github", doc_host="https://%(user)s.github.io", doc_baseurl="/%(lib_name)s/")
421+
422+
def __getattr__(self,k):
423+
if k=='d' or k not in self.d: raise AttributeError(k)
424+
return self.config_file.parent/self.d[k] if k.endswith('_path') else self.get(k)
425+
426+
def get(self,k,default=None): return self.d.get(k, default)
427+
def __setitem__(self,k,v): self.d[k] = str(v)
428+
def __contains__(self,k): return k in self.d
429+
def save(self): save_config_file(self.config_file,self.d)

nbs/01_foundation.ipynb

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
"source": [
1818
"#export\n",
1919
"from fastcore.imports import *\n",
20+
"from functools import lru_cache\n",
2021
"from contextlib import contextmanager\n",
2122
"from copy import copy\n",
23+
"from configparser import ConfigParser\n",
2224
"import random,pickle"
2325
]
2426
},
@@ -1434,7 +1436,9 @@
14341436
"#export\n",
14351437
"def nested_attr(o, attr, default=None):\n",
14361438
" \"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",
14381442
" return o"
14391443
]
14401444
},
@@ -1822,7 +1826,7 @@
18221826
{
18231827
"data": {
18241828
"text/plain": [
1825-
"[11, 10, 9]"
1829+
"[0, 7, 11]"
18261830
]
18271831
},
18281832
"execution_count": null,
@@ -2853,6 +2857,125 @@
28532857
"test_eq(t.map_first(lambda o:o*2 if o>2 else None), 6)"
28542858
]
28552859
},
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+
},
28562979
{
28572980
"cell_type": "markdown",
28582981
"metadata": {},

0 commit comments

Comments
 (0)