Skip to content

Commit 767e34d

Browse files
committed
fixes #114
1 parent 6e970a4 commit 767e34d

File tree

3 files changed

+1077
-1003
lines changed

3 files changed

+1077
-1003
lines changed

fastcore/_nbdev.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,16 @@
5353
"wrap_class": "02_utils.ipynb",
5454
"ignore_exceptions": "02_utils.ipynb",
5555
"exec_local": "02_utils.ipynb",
56+
"Inf": "02_utils.ipynb",
57+
"in_": "02_utils.ipynb",
58+
"operator.in_": "02_utils.ipynb",
59+
"true": "02_utils.ipynb",
5660
"stop": "02_utils.ipynb",
61+
"gen": "02_utils.ipynb",
62+
"chunked": "02_utils.ipynb",
5763
"AttrDict": "02_utils.ipynb",
5864
"dict2obj": "02_utils.ipynb",
65+
"with_cast": "02_utils.ipynb",
5966
"store_attr": "02_utils.ipynb",
6067
"attrdict": "02_utils.ipynb",
6168
"properties": "02_utils.ipynb",
@@ -85,12 +92,6 @@
8592
"rnum_methods": "02_utils.ipynb",
8693
"inum_methods": "02_utils.ipynb",
8794
"fastuple": "02_utils.ipynb",
88-
"Inf": "02_utils.ipynb",
89-
"in_": "02_utils.ipynb",
90-
"operator.in_": "02_utils.ipynb",
91-
"true": "02_utils.ipynb",
92-
"gen": "02_utils.ipynb",
93-
"chunked": "02_utils.ipynb",
9495
"trace": "02_utils.ipynb",
9596
"compose": "02_utils.ipynb",
9697
"maps": "02_utils.ipynb",

fastcore/utils.py

Lines changed: 94 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/02_utils.ipynb (unless otherwise specified).
22

33
__all__ = ['ifnone', 'maybe_attr', 'basic_repr', 'get_class', 'mk_class', 'wrap_class', 'ignore_exceptions',
4-
'exec_local', 'stop', 'AttrDict', 'dict2obj', 'store_attr', 'attrdict', 'properties', 'camel2snake',
5-
'snake2camel', 'class2attr', 'hasattrs', 'setattrs', 'ShowPrint', 'Int', 'Str', 'Float', 'tuplify',
6-
'detuplify', 'replicate', 'uniqueify', 'setify', 'merge', 'is_listy', 'range_of', 'groupby', 'last_index',
7-
'shufflish', 'IterLen', 'ReindexCollection', 'num_methods', 'rnum_methods', 'inum_methods', 'fastuple',
8-
'Inf', 'in_', 'lt', 'gt', 'le', 'ge', 'eq', 'ne', 'add', 'sub', 'mul', 'truediv', 'is_', 'is_not', 'in_',
9-
'true', 'gen', 'chunked', 'trace', 'compose', 'maps', 'partialler', 'mapped', 'instantiate', 'using_attr',
10-
'Self', 'Self', 'remove_patches_path', 'bunzip', 'join_path_file', 'urlread', 'urljson', 'run', 'do_request',
11-
'sort_by_run', 'PrettyString', 'round_multiple', 'even_mults', 'num_cpus', 'add_props', 'ContextManagers',
12-
'set_num_threads', 'ProcessPoolExecutor', 'ThreadPoolExecutor', 'parallel', 'run_procs', 'parallel_gen']
4+
'exec_local', 'Inf', 'in_', 'lt', 'gt', 'le', 'ge', 'eq', 'ne', 'add', 'sub', 'mul', 'truediv', 'is_',
5+
'is_not', 'in_', 'true', 'stop', 'gen', 'chunked', 'AttrDict', 'dict2obj', 'with_cast', 'store_attr',
6+
'attrdict', 'properties', 'camel2snake', 'snake2camel', 'class2attr', 'hasattrs', 'setattrs', 'ShowPrint',
7+
'Int', 'Str', 'Float', 'tuplify', 'detuplify', 'replicate', 'uniqueify', 'setify', 'merge', 'is_listy',
8+
'range_of', 'groupby', 'last_index', 'shufflish', 'IterLen', 'ReindexCollection', 'num_methods',
9+
'rnum_methods', 'inum_methods', 'fastuple', 'trace', 'compose', 'maps', 'partialler', 'mapped',
10+
'instantiate', 'using_attr', 'Self', 'Self', 'remove_patches_path', 'bunzip', 'join_path_file', 'urlread',
11+
'urljson', 'run', 'do_request', 'sort_by_run', 'PrettyString', 'round_multiple', 'even_mults', 'num_cpus',
12+
'add_props', 'ContextManagers', 'set_num_threads', 'ProcessPoolExecutor', 'ThreadPoolExecutor', 'parallel',
13+
'run_procs', 'parallel_gen']
1314

1415
# Cell
1516
from .imports import *
@@ -94,11 +95,73 @@ def exec_local(code, var_name):
9495
exec(code, globals(), loc)
9596
return loc[var_name]
9697

98+
# Cell
99+
#hide
100+
class _InfMeta(type):
101+
@property
102+
def count(self): return itertools.count()
103+
@property
104+
def zeros(self): return itertools.cycle([0])
105+
@property
106+
def ones(self): return itertools.cycle([1])
107+
@property
108+
def nones(self): return itertools.cycle([None])
109+
110+
# Cell
111+
class Inf(metaclass=_InfMeta):
112+
"Infinite lists"
113+
pass
114+
115+
# Cell
116+
def _oper(op,a,b=float('nan')): return (lambda o:op(o,a)) if b!=b else op(a,b)
117+
118+
def _mk_op(nm, mod):
119+
"Create an operator using `oper` and add to the caller's module"
120+
op = getattr(operator,nm)
121+
def _inner(a,b=float('nan')): return _oper(op, a,b)
122+
_inner.__name__ = _inner.__qualname__ = nm
123+
_inner.__doc__ = f'Same as `operator.{nm}`, or returns partial if 1 arg'
124+
mod[nm] = _inner
125+
126+
# Cell
127+
def in_(x, a):
128+
"`True` if `x in a`"
129+
return x in a
130+
131+
operator.in_ = in_
132+
133+
# Cell
134+
#nbdev_comment _all_ = ['lt','gt','le','ge','eq','ne','add','sub','mul','truediv','is_','is_not','in_']
135+
136+
# Cell
137+
for op in ['lt','gt','le','ge','eq','ne','add','sub','mul','truediv','is_','is_not','in_']: _mk_op(op, globals())
138+
139+
# Cell
140+
def true(*args, **kwargs):
141+
"Predicate: always `True`"
142+
return True
143+
97144
# Cell
98145
def stop(e=StopIteration):
99146
"Raises exception `e` (by default `StopException`)"
100147
raise e
101148

149+
# Cell
150+
def gen(func, seq, cond=true):
151+
"Like `(func(o) for o in seq if cond(func(o)))` but handles `StopIteration`"
152+
return itertools.takewhile(cond, map(func,seq))
153+
154+
# Cell
155+
def chunked(it, chunk_sz=None, drop_last=False, n_chunks=None):
156+
"Return batches from iterator `it` of size `chunk_sz` (or return `n_chunks` total)"
157+
assert bool(chunk_sz) ^ bool(n_chunks)
158+
if n_chunks: chunk_sz = math.ceil(len(it)/n_chunks)
159+
if not isinstance(it, Iterator): it = iter(it)
160+
while True:
161+
res = list(itertools.islice(it, chunk_sz))
162+
if res and (len(res)==chunk_sz or not drop_last): yield res
163+
if len(res)<chunk_sz: return
164+
102165
# Cell
103166
class AttrDict(dict):
104167
"`dict` subclass that also provides access to keys as attrs"
@@ -114,22 +177,39 @@ def dict2obj(d):
114177
return AttrDict(**{k:dict2obj(v) for k,v in d.items()})
115178

116179
# Cell
117-
def _store_attr(self, **attrs):
180+
def with_cast(f):
181+
"Decorator which uses any parameter annotations as preprocessing functions"
182+
anno = f.__annotations__
183+
params = f.__code__.co_varnames
184+
def _inner(*args, **kwargs):
185+
args = list(args)
186+
for i,v in enumerate(params):
187+
if v in anno:
188+
c = anno[v]
189+
if v in kwargs: kwargs[v] = c(kwargs[v])
190+
elif i<len(args): args[i] = c(args[i])
191+
return f(*args, **kwargs)
192+
return _inner
193+
194+
# Cell
195+
def _store_attr(self, anno, **attrs):
118196
for n,v in attrs.items():
197+
if n in anno: v = anno[n](v)
119198
setattr(self, n, v)
120199
self.__stored_args__[n] = v
121200

122201
# Cell
123-
def store_attr(names=None, self=None, but=None, **attrs):
202+
def store_attr(names=None, self=None, but=None, cast=False, **attrs):
124203
"Store params named in comma-separated `names` from calling context into attrs in `self`"
125204
fr = sys._getframe(1)
126205
args = fr.f_code.co_varnames[:fr.f_code.co_argcount]
127206
if self: args = ('self', *args)
128207
else: self = fr.f_locals[args[0]]
129208
if not hasattr(self, '__stored_args__'): self.__stored_args__ = {}
130-
if attrs: return _store_attr(self, **attrs)
209+
anno = self.__class__.__init__.__annotations__ if cast else {}
210+
if attrs: return _store_attr(self, anno, **attrs)
131211
ns = re.split(', *', names) if names else args[1:]
132-
_store_attr(self, **{n:fr.f_locals[n] for n in ns if n not in L(but)})
212+
_store_attr(self, anno, **{n:fr.f_locals[n] for n in ns if n not in L(but)})
133213

134214
# Cell
135215
def attrdict(o, *ks):
@@ -158,10 +238,9 @@ def snake2camel(s):
158238

159239
# Cell
160240
def class2attr(self, cls_name):
161-
"Return the snake-cased name of the class. Additionally, remove the substring `cls_name` only if it is a substring at the **end** of the string."
241+
"Return the snake-cased name of the class; strip ending `cls_name` if it exists."
162242
return camel2snake(re.sub(rf'{cls_name}$', '', self.__class__.__name__) or cls_name.lower())
163243

164-
165244
# Cell
166245
def hasattrs(o,attrs):
167246
"Test whether `o` contains all `attrs`"
@@ -334,68 +413,6 @@ def _f(self,*args): return self._op(op,*args)
334413
setattr(fastuple,'max',_get_op(max))
335414
setattr(fastuple,'min',_get_op(min))
336415

337-
# Cell
338-
#hide
339-
class _InfMeta(type):
340-
@property
341-
def count(self): return itertools.count()
342-
@property
343-
def zeros(self): return itertools.cycle([0])
344-
@property
345-
def ones(self): return itertools.cycle([1])
346-
@property
347-
def nones(self): return itertools.cycle([None])
348-
349-
# Cell
350-
class Inf(metaclass=_InfMeta):
351-
"Infinite lists"
352-
pass
353-
354-
# Cell
355-
def _oper(op,a,b=float('nan')): return (lambda o:op(o,a)) if b!=b else op(a,b)
356-
357-
def _mk_op(nm, mod):
358-
"Create an operator using `oper` and add to the caller's module"
359-
op = getattr(operator,nm)
360-
def _inner(a,b=float('nan')): return _oper(op, a,b)
361-
_inner.__name__ = _inner.__qualname__ = nm
362-
_inner.__doc__ = f'Same as `operator.{nm}`, or returns partial if 1 arg'
363-
mod[nm] = _inner
364-
365-
# Cell
366-
def in_(x, a):
367-
"`True` if `x in a`"
368-
return x in a
369-
370-
operator.in_ = in_
371-
372-
# Cell
373-
#nbdev_comment _all_ = ['lt','gt','le','ge','eq','ne','add','sub','mul','truediv','is_','is_not','in_']
374-
375-
# Cell
376-
for op in ['lt','gt','le','ge','eq','ne','add','sub','mul','truediv','is_','is_not','in_']: _mk_op(op, globals())
377-
378-
# Cell
379-
def true(*args, **kwargs):
380-
"Predicate: always `True`"
381-
return True
382-
383-
# Cell
384-
def gen(func, seq, cond=true):
385-
"Like `(func(o) for o in seq if cond(func(o)))` but handles `StopIteration`"
386-
return itertools.takewhile(cond, map(func,seq))
387-
388-
# Cell
389-
def chunked(it, chunk_sz=None, drop_last=False, n_chunks=None):
390-
"Return batches from iterator `it` of size `chunk_sz` (or return `n_chunks` total)"
391-
assert bool(chunk_sz) ^ bool(n_chunks)
392-
if n_chunks: chunk_sz = math.ceil(len(it)/n_chunks)
393-
if not isinstance(it, Iterator): it = iter(it)
394-
while True:
395-
res = list(itertools.islice(it, chunk_sz))
396-
if res and (len(res)==chunk_sz or not drop_last): yield res
397-
if len(res)<chunk_sz: return
398-
399416
# Cell
400417
def trace(f):
401418
"Add `set_trace` to an existing function `f`"

0 commit comments

Comments
 (0)