Skip to content

Commit 4f49480

Browse files
authored
Merge branch 'master' into param-repr
2 parents 802a493 + 2a12d01 commit 4f49480

23 files changed

+786
-634
lines changed

.github/workflows/docs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ jobs:
2727
hostname: ps625762.dreamhostps.com
2828
dest_path: fastcore.fast.ai
2929
ssh_key: ${{ secrets.DH_KEY }}
30+
delete: 'yes'

.github/workflows/main.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,28 @@ jobs:
3030
run: |
3131
pip install -Uqq nbdev fastcore
3232
nbdev_test_nbs
33+
34+
nbdev-integration-test:
35+
needs: filter
36+
container: fastai/jekyll
37+
runs-on: ubuntu-latest
38+
steps:
39+
- name: clone this branch [fastcore]
40+
uses: actions/checkout@v2
41+
with:
42+
path: fastcore_lib
43+
- name: clone nbdev
44+
uses: actions/checkout@v2
45+
with:
46+
repository: 'fastai/nbdev'
47+
path: nbdev
48+
- name: Install libraries
49+
run: |
50+
cd fastcore_lib && pip install -Ue .[dev]
51+
cd ../nbdev && pip install -Ue .[dev]
52+
- name: test nbdev notebooks
53+
run: |
54+
cd nbdev && make test
3355
3456
fastai-integration-test:
3557
needs: filter

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@
22

33
<!-- do not remove -->
44

5+
## 1.3.2
6+
7+
### New Features
8+
9+
- add `repr_dict` and use for display for `AttrDict` ([#172](https://github.com/fastai/fastcore/issues/172))
10+
- add `urlsave`, `urlclean`, and `repo_details` ([#171](https://github.com/fastai/fastcore/issues/171))
11+
- `remove_suffix` function ([#170](https://github.com/fastai/fastcore/issues/170))
12+
- add `urlcheck` and `urlwrap` ([#168](https://github.com/fastai/fastcore/issues/168))
13+
- new `AutoInit` mixin ([#165](https://github.com/fastai/fastcore/issues/165))
14+
15+
### Bugs Squashed
16+
17+
- `risinstance` fails if param is not truthy ([#166](https://github.com/fastai/fastcore/issues/166))
18+
519

620
## 1.3.1
721

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ To install fastcore run: `conda install fastcore` (if you use Anaconda, which we
1414

1515
- `test`: Simple testing functions
1616
- `foundation`: Mixins, delegation, composition, and more
17-
- `utils`: Utility functions to help with functional-style programming, parallel processing, and more
17+
- `xtras`: Utility functions to help with functional-style programming, parallel processing, and more
1818
- `dispatch`: Multiple dispatch methods
1919
- `transform`: Pipelines of composed partially reversible transformations
2020

examples/test_fastcore.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#!/usr/bin/env python
22

33
from fastcore.script import *
4+
45
@call_parse
56
def main(msg:Param("The message", str),
67
upper:Param("Convert to uppercase?", store_true)):
78
"Print `msg`, optionally converting to uppercase"
89
print(msg.upper() if upper else msg)
10+

fastcore/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "1.3.2"
1+
__version__ = "1.3.4"

fastcore/_nbdev.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,18 @@
4444
"otherwise": "01_basics.ipynb",
4545
"custom_dir": "01_basics.ipynb",
4646
"AttrDict": "01_basics.ipynb",
47+
"type_hints": "01_basics.ipynb",
48+
"annotations": "01_basics.ipynb",
49+
"anno_ret": "01_basics.ipynb",
50+
"argnames": "01_basics.ipynb",
4751
"with_cast": "01_basics.ipynb",
4852
"store_attr": "01_basics.ipynb",
4953
"attrdict": "01_basics.ipynb",
5054
"properties": "01_basics.ipynb",
5155
"camel2snake": "01_basics.ipynb",
5256
"snake2camel": "01_basics.ipynb",
5357
"class2attr": "01_basics.ipynb",
58+
"getattrs": "01_basics.ipynb",
5459
"hasattrs": "01_basics.ipynb",
5560
"setattrs": "01_basics.ipynb",
5661
"try_attrs": "01_basics.ipynb",
@@ -121,6 +126,8 @@
121126
"read_config_file": "02_foundation.ipynb",
122127
"Config": "02_foundation.ipynb",
123128
"dict2obj": "03_xtras.ipynb",
129+
"repr_dict": "03_xtras.ipynb",
130+
"AttrDict.__repr__": "03_xtras.ipynb",
124131
"tuplify": "03_xtras.ipynb",
125132
"uniqueify": "03_xtras.ipynb",
126133
"is_listy": "03_xtras.ipynb",
@@ -139,10 +146,16 @@
139146
"image_size": "03_xtras.ipynb",
140147
"bunzip": "03_xtras.ipynb",
141148
"join_path_file": "03_xtras.ipynb",
149+
"urlwrap": "03_xtras.ipynb",
150+
"urlopen": "03_xtras.ipynb",
142151
"urlread": "03_xtras.ipynb",
143152
"urljson": "03_xtras.ipynb",
144-
"urlwrap": "03_xtras.ipynb",
145153
"urlcheck": "03_xtras.ipynb",
154+
"urlclean": "03_xtras.ipynb",
155+
"urlsave": "03_xtras.ipynb",
156+
"urlvalid": "03_xtras.ipynb",
157+
"untar_dir": "03_xtras.ipynb",
158+
"repo_details": "03_xtras.ipynb",
146159
"run": "03_xtras.ipynb",
147160
"do_request": "03_xtras.ipynb",
148161
"sort_by_run": "03_xtras.ipynb",
@@ -158,8 +171,6 @@
158171
"run_procs": "03_xtras.ipynb",
159172
"parallel_gen": "03_xtras.ipynb",
160173
"threaded": "03_xtras.ipynb",
161-
"type_hints": "04_dispatch.ipynb",
162-
"anno_ret": "04_dispatch.ipynb",
163174
"lenient_issubclass": "04_dispatch.ipynb",
164175
"sorted_topologically": "04_dispatch.ipynb",
165176
"TypeDispatch": "04_dispatch.ipynb",
@@ -182,7 +193,6 @@
182193
"gather_attrs": "05_transform.ipynb",
183194
"gather_attr_names": "05_transform.ipynb",
184195
"Pipeline": "05_transform.ipynb",
185-
"log_args": "06_logargs.ipynb",
186196
"test_sig": "07_meta.ipynb",
187197
"FixSigMeta": "07_meta.ipynb",
188198
"PrePostInitMeta": "07_meta.ipynb",
@@ -203,6 +213,7 @@
203213
"Param": "08_script.ipynb",
204214
"anno_parser": "08_script.ipynb",
205215
"args_from_prog": "08_script.ipynb",
216+
"SCRIPT_INFO": "08_script.ipynb",
206217
"call_parse": "08_script.ipynb"}
207218

208219
modules = ["test.py",
@@ -211,7 +222,6 @@
211222
"xtras.py",
212223
"dispatch.py",
213224
"transform.py",
214-
"logargs.py",
215225
"meta.py",
216226
"script.py"]
217227

fastcore/all.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,4 @@
66
from .test import *
77
from .meta import *
88
from .imports import *
9-
from .logargs import *
109
from .script import *

fastcore/basics.py

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
__all__ = ['defaults', 'ifnone', 'maybe_attr', 'basic_repr', 'is_array', 'listify', 'true', 'NullType', 'null',
44
'tonull', 'get_class', 'mk_class', 'wrap_class', 'ignore_exceptions', 'exec_local', 'risinstance', 'Inf',
55
'in_', 'lt', 'gt', 'le', 'ge', 'eq', 'ne', 'add', 'sub', 'mul', 'truediv', 'is_', 'is_not', 'in_', 'true',
6-
'stop', 'gen', 'chunked', 'otherwise', 'custom_dir', 'AttrDict', 'with_cast', 'store_attr', 'attrdict',
7-
'properties', 'camel2snake', 'snake2camel', 'class2attr', 'hasattrs', 'setattrs', 'try_attrs', 'ShowPrint',
8-
'Int', 'Str', 'Float', 'detuplify', 'replicate', 'setify', 'merge', 'range_of', 'groupby', 'last_index',
9-
'filter_dict', 'filter_keys', 'filter_values', 'cycle', 'zip_cycle', 'sorted_ex', 'negate_func', 'argwhere',
10-
'filter_ex', 'range_of', 'renumerate', 'first', 'nested_attr', 'nested_idx', 'num_methods', 'rnum_methods',
11-
'inum_methods', 'fastuple', 'arg0', 'arg1', 'arg2', 'arg3', 'arg4', 'bind', 'map_ex', 'compose', 'maps',
12-
'partialler', 'instantiate', 'using_attr', 'Self', 'Self', 'PrettyString', 'even_mults', 'num_cpus',
13-
'add_props', 'typed']
6+
'stop', 'gen', 'chunked', 'otherwise', 'custom_dir', 'AttrDict', 'type_hints', 'annotations', 'anno_ret',
7+
'argnames', 'with_cast', 'store_attr', 'attrdict', 'properties', 'camel2snake', 'snake2camel', 'class2attr',
8+
'getattrs', 'hasattrs', 'setattrs', 'try_attrs', 'ShowPrint', 'Int', 'Str', 'Float', 'detuplify',
9+
'replicate', 'setify', 'merge', 'range_of', 'groupby', 'last_index', 'filter_dict', 'filter_keys',
10+
'filter_values', 'cycle', 'zip_cycle', 'sorted_ex', 'negate_func', 'argwhere', 'filter_ex', 'range_of',
11+
'renumerate', 'first', 'nested_attr', 'nested_idx', 'num_methods', 'rnum_methods', 'inum_methods',
12+
'fastuple', 'arg0', 'arg1', 'arg2', 'arg3', 'arg4', 'bind', 'map_ex', 'compose', 'maps', 'partialler',
13+
'instantiate', 'using_attr', 'Self', 'Self', 'PrettyString', 'even_mults', 'num_cpus', 'add_props', 'typed']
1414

1515
# Cell
1616
from .imports import *
@@ -216,12 +216,37 @@ def __getattr__(self,k): return self[k] if k in self else stop(AttributeError(k)
216216
def __setattr__(self, k, v): (self.__setitem__,super().__setattr__)[k[0]=='_'](k,v)
217217
def __dir__(self): return custom_dir(self, list(self.keys()))
218218

219+
# Cell
220+
def type_hints(f):
221+
"Same as `typing.get_type_hints` but returns `{}` if not allowed type"
222+
return typing.get_type_hints(f) if isinstance(f, typing._allowed_types) else {}
223+
224+
# Cell
225+
def annotations(o):
226+
"Annotations for `o`, or `type(o)`"
227+
res = {}
228+
if not o: return res
229+
res = type_hints(o)
230+
if not res: res = type_hints(getattr(o,'__init__',None))
231+
if not res: res = type_hints(type(o))
232+
return res
233+
234+
# Cell
235+
def anno_ret(func):
236+
"Get the return annotation of `func`"
237+
return annotations(func).get('return', None) if func else None
238+
239+
# Cell
240+
def argnames(f):
241+
"Names of arguments to function `f`"
242+
code = f.__code__
243+
return code.co_varnames[:code.co_argcount]
244+
219245
# Cell
220246
def with_cast(f):
221247
"Decorator which uses any parameter annotations as preprocessing functions"
222-
anno = f.__annotations__
223-
params = f.__code__.co_varnames[:f.__code__.co_argcount]
224-
defaults = dict(zip(reversed(params), reversed(f.__defaults__))) if f.__defaults__ else {}
248+
anno,params = annotations(f),argnames(f)
249+
defaults = dict(zip(reversed(params), reversed(f.__defaults__ or {})))
225250
@functools.wraps(f)
226251
def _inner(*args, **kwargs):
227252
args = list(args)
@@ -236,28 +261,32 @@ def _inner(*args, **kwargs):
236261

237262
# Cell
238263
def _store_attr(self, anno, **attrs):
264+
stored = self.__stored_args__
239265
for n,v in attrs.items():
240266
if n in anno: v = anno[n](v)
241267
setattr(self, n, v)
242-
self.__stored_args__[n] = v
268+
stored[n] = v
243269

244270
# Cell
245-
def store_attr(names=None, self=None, but=None, cast=False, **attrs):
271+
def store_attr(names=None, self=None, but='', cast=False, **attrs):
246272
"Store params named in comma-separated `names` from calling context into attrs in `self`"
247273
fr = sys._getframe(1)
248-
args = fr.f_code.co_varnames[:fr.f_code.co_argcount]
274+
args = fr.f_code.co_varnames[:fr.f_code.co_argcount+fr.f_code.co_kwonlyargcount]
249275
if self: args = ('self', *args)
250276
else: self = fr.f_locals[args[0]]
251277
if not hasattr(self, '__stored_args__'): self.__stored_args__ = {}
252-
anno = self.__class__.__init__.__annotations__ if cast else {}
253-
if attrs: return _store_attr(self, anno, **attrs)
254-
ns = re.split(', *', names) if names else args[1:]
255-
_store_attr(self, anno, **{n:fr.f_locals[n] for n in ns if n not in listify(but)})
278+
anno = annotations(self) if cast else {}
279+
if not attrs:
280+
ns = re.split(', *', names) if names else args[1:]
281+
attrs = {n:fr.f_locals[n] for n in ns}
282+
if isinstance(but,str): but = re.split(', *', but)
283+
attrs = {k:v for k,v in attrs.items() if k not in but}
284+
return _store_attr(self, anno, **attrs)
256285

257286
# Cell
258-
def attrdict(o, *ks):
287+
def attrdict(o, *ks, default=None):
259288
"Dict from each `k` in `ks` to `getattr(o,k)`"
260-
return {k:getattr(o,k) for k in ks}
289+
return {k:getattr(o, k, default) for k in ks}
261290

262291
# Cell
263292
def properties(cls, *ps):
@@ -284,6 +313,11 @@ def class2attr(self, cls_name):
284313
"Return the snake-cased name of the class; strip ending `cls_name` if it exists."
285314
return camel2snake(re.sub(rf'{cls_name}$', '', self.__class__.__name__) or cls_name.lower())
286315

316+
# Cell
317+
def getattrs(o, *attrs, default=None):
318+
"List of all `attrs` in `o`"
319+
return [getattr(o,attr,default) for attr in attrs]
320+
287321
# Cell
288322
def hasattrs(o,attrs):
289323
"Test whether `o` contains all `attrs`"
@@ -660,14 +694,14 @@ def _typeerr(arg, val, typ): return TypeError(f"{arg}=={val} not {typ}")
660694
def typed(f):
661695
"Decorator to check param and return types at runtime"
662696
names = f.__code__.co_varnames
663-
anno = f.__annotations__
697+
anno = annotations(f)
664698
ret = anno.pop('return',None)
665699
def _f(*args,**kwargs):
666700
kw = {**kwargs}
667701
if len(anno) > 0:
668702
for i,arg in enumerate(args): kw[names[i]] = arg
669703
for k,v in kw.items():
670-
if not isinstance(v,anno[k]): raise _typeerr(k, v, anno[k])
704+
if k in anno and not isinstance(v,anno[k]): raise _typeerr(k, v, anno[k])
671705
res = f(*args,**kwargs)
672706
if ret is not None and not isinstance(res,ret): raise _typeerr("return", res, ret)
673707
return res

fastcore/dispatch.py

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,14 @@
11
# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/04_dispatch.ipynb (unless otherwise specified).
22

3-
__all__ = ['type_hints', 'anno_ret', 'lenient_issubclass', 'sorted_topologically', 'TypeDispatch', 'DispatchReg',
4-
'typedispatch', 'cast', 'retain_meta', 'default_set_meta', 'retain_type', 'retain_types', 'explode_types']
3+
__all__ = ['lenient_issubclass', 'sorted_topologically', 'TypeDispatch', 'DispatchReg', 'typedispatch', 'cast',
4+
'retain_meta', 'default_set_meta', 'retain_type', 'retain_types', 'explode_types']
55

66
# Cell
77
from .imports import *
88
from .foundation import *
99
from .utils import *
10-
from collections import defaultdict
11-
12-
# Cell
13-
def type_hints(f):
14-
"Same as `typing.get_type_hints` but returns `{}` if not allowed type"
15-
return typing.get_type_hints(f) if isinstance(f, typing._allowed_types) else {}
1610

17-
# Cell
18-
def anno_ret(func):
19-
"Get the return annotation of `func`"
20-
if not func: return None
21-
ann = type_hints(func)
22-
if not ann: return None
23-
return ann.get('return')
11+
from collections import defaultdict
2412

2513
# Cell
2614
def lenient_issubclass(cls, types):
@@ -114,7 +102,7 @@ def returns_none(self, x):
114102

115103
def _attname(self,k): return getattr(k,'__name__',str(k))
116104
def __repr__(self):
117-
r = [f'({self._attname(k)},{self._attname(l)}) -> {getattr(v, "__name__", v.__class__.__name__)}'
105+
r = [f'({self._attname(k)},{self._attname(l)}) -> {getattr(v, "__name__", type(v).__name__)}'
118106
for k in self.funcs.d for l,v in self.funcs[k].d.items()]
119107
r = r + [o.__repr__() for o in self.bases]
120108
return '\n'.join(r)

0 commit comments

Comments
 (0)