diff --git a/fastcore/_nbdev.py b/fastcore/_nbdev.py index 1a1d8776..b4e6dfe3 100644 --- a/fastcore/_nbdev.py +++ b/fastcore/_nbdev.py @@ -53,6 +53,7 @@ "store_attr": "01_basics.ipynb", "attrdict": "01_basics.ipynb", "properties": "01_basics.ipynb", + "camel2words": "01_basics.ipynb", "camel2snake": "01_basics.ipynb", "snake2camel": "01_basics.ipynb", "class2attr": "01_basics.ipynb", @@ -164,6 +165,7 @@ "sparkline": "03_xtras.ipynb", "autostart": "03_xtras.ipynb", "time_events": "03_xtras.ipynb", + "EventTimer": "03_xtras.ipynb", "stringfmt_names": "03_xtras.ipynb", "PartialFormatter": "03_xtras.ipynb", "partial_format": "03_xtras.ipynb", diff --git a/fastcore/basics.py b/fastcore/basics.py index 6830a5a6..20f72c9c 100644 --- a/fastcore/basics.py +++ b/fastcore/basics.py @@ -4,14 +4,14 @@ 'null', 'tonull', 'get_class', 'mk_class', 'wrap_class', 'ignore_exceptions', 'exec_local', 'risinstance', 'Inf', 'in_', 'lt', 'gt', 'le', 'ge', 'eq', 'ne', 'add', 'sub', 'mul', 'truediv', 'is_', 'is_not', 'in_', 'true', 'stop', 'gen', 'chunked', 'otherwise', 'custom_dir', 'AttrDict', 'type_hints', 'annotations', - 'anno_ret', 'argnames', 'with_cast', 'store_attr', 'attrdict', 'properties', 'camel2snake', 'snake2camel', - 'class2attr', 'getattrs', 'hasattrs', 'setattrs', 'try_attrs', 'GetAttrBase', 'GetAttr', 'delegate_attr', - 'ShowPrint', 'Int', 'Str', 'Float', 'concat', 'detuplify', 'replicate', 'setify', 'merge', 'range_of', - 'groupby', 'last_index', 'filter_dict', 'filter_keys', 'filter_values', 'cycle', 'zip_cycle', 'sorted_ex', - 'not_', 'argwhere', 'filter_ex', 'range_of', 'renumerate', 'first', 'nested_attr', 'nested_idx', 'val2idx', - 'uniqueify', 'num_methods', 'rnum_methods', 'inum_methods', 'fastuple', 'arg0', 'arg1', 'arg2', 'arg3', - 'arg4', 'bind', 'map_ex', 'compose', 'maps', 'partialler', 'instantiate', 'using_attr', 'Self', 'Self', - 'copy_func', 'patch_to', 'patch', 'patch_property', 'ImportEnum', 'StrEnum', 'str_enum', 'Stateful', + 'anno_ret', 'argnames', 'with_cast', 'store_attr', 'attrdict', 'properties', 'camel2words', 'camel2snake', + 'snake2camel', 'class2attr', 'getattrs', 'hasattrs', 'setattrs', 'try_attrs', 'GetAttrBase', 'GetAttr', + 'delegate_attr', 'ShowPrint', 'Int', 'Str', 'Float', 'concat', 'detuplify', 'replicate', 'setify', 'merge', + 'range_of', 'groupby', 'last_index', 'filter_dict', 'filter_keys', 'filter_values', 'cycle', 'zip_cycle', + 'sorted_ex', 'not_', 'argwhere', 'filter_ex', 'range_of', 'renumerate', 'first', 'nested_attr', 'nested_idx', + 'val2idx', 'uniqueify', 'num_methods', 'rnum_methods', 'inum_methods', 'fastuple', 'arg0', 'arg1', 'arg2', + 'arg3', 'arg4', 'bind', 'map_ex', 'compose', 'maps', 'partialler', 'instantiate', 'using_attr', 'Self', + 'Self', 'copy_func', 'patch_to', 'patch', 'patch_property', 'ImportEnum', 'StrEnum', 'str_enum', 'Stateful', 'PrettyString', 'even_mults', 'num_cpus', 'add_props', 'typed'] # Cell @@ -311,9 +311,15 @@ def properties(cls, *ps): for p in ps: setattr(cls,p,property(getattr(cls,p))) # Cell +_c2w_re = re.compile(r'((?<=[a-z])[A-Z]|(?str: +def truncstr(s:str, maxlen:int, suf:str='…', space='')->str: "Truncate `s` to length `maxlen`, adding suffix `suf` if truncated" - return s[:maxlen-len(suf)]+suf if len(s)>maxlen else s + return s[:maxlen-len(suf)]+suf if len(s)+len(space)>maxlen else s+space # Cell spark_chars = '▁▂▃▅▆▇' @@ -236,10 +236,10 @@ def _sparkchar(x, mn, incr, empty_zero): return spark_chars[res] # Cell -def sparkline(data, empty_zero=False): +def sparkline(data, mn=None, mx=None, empty_zero=False): "Sparkline for `data`, with `None`s (and zero, if `empty_zero`) shown as empty column" valid = [o for o in data if o is not None] - mn,mx,n = min(valid),max(valid),len(spark_chars) + mn,mx,n = ifnone(mn,min(valid)),ifnone(mx,max(valid)),len(spark_chars) res = [_sparkchar(o,mn,(mx-mn)/n,empty_zero) for o in data] return ''.join(res) @@ -260,6 +260,28 @@ def time_events(): start,events = default_timer(),0 while True: events += (yield events,events/(default_timer()-start)) or 0 +# Cell +class EventTimer: + "An event timer with history of `store` items of time `span`" + def __init__(self, store=5, span=60): + self.hist,self.span,self.last = collections.deque(maxlen=store),span,default_timer() + self._reset() + + def _reset(self): self.start,self.events = self.last,0 + + def add(self, n=1): + "Record `n` events" + if self.duration>self.span: + self.hist.append(self.freq) + self._reset() + self.events +=n + self.last = default_timer() + + @property + def duration(self): return default_timer()-self.start + @property + def freq(self): return self.events/self.duration + # Cell _fmt = string.Formatter() diff --git a/nbs/01_basics.ipynb b/nbs/01_basics.ipynb index 4622f22d..a4da60b6 100644 --- a/nbs/01_basics.ipynb +++ b/nbs/01_basics.ipynb @@ -1925,10 +1925,32 @@ "outputs": [], "source": [ "#export\n", + "_c2w_re = re.compile(r'((?<=[a-z])[A-Z]|(?str:\n", + "def truncstr(s:str, maxlen:int, suf:str='…', space='')->str:\n", " \"Truncate `s` to length `maxlen`, adding suffix `suf` if truncated\"\n", - " return s[:maxlen-len(suf)]+suf if len(s)>maxlen else s" + " return s[:maxlen-len(suf)]+suf if len(s)+len(space)>maxlen else s+space" ] }, { @@ -1338,9 +1338,11 @@ "outputs": [], "source": [ "w = 'abacadabra'\n", - "test_eq(truncstr(w, 15), w)\n", + "test_eq(truncstr(w, 10), w)\n", "test_eq(truncstr(w, 5), 'abac…')\n", "test_eq(truncstr(w, 5, suf=''), 'abaca')\n", + "test_eq(truncstr(w, 11, space='_'), w+\"_\")\n", + "test_eq(truncstr(w, 10, space='_'), w[:-1]+'…')\n", "test_eq(truncstr(w, 5, suf='!!'), 'aba!!')" ] }, @@ -1374,10 +1376,10 @@ "outputs": [], "source": [ "#export\n", - "def sparkline(data, empty_zero=False):\n", + "def sparkline(data, mn=None, mx=None, empty_zero=False):\n", " \"Sparkline for `data`, with `None`s (and zero, if `empty_zero`) shown as empty column\"\n", " valid = [o for o in data if o is not None]\n", - " mn,mx,n = min(valid),max(valid),len(spark_chars)\n", + " mn,mx,n = ifnone(mn,min(valid)),ifnone(mx,max(valid)),len(spark_chars)\n", " res = [_sparkchar(o,mn,(mx-mn)/n,empty_zero) for o in data]\n", " return ''.join(res)" ] @@ -1440,6 +1442,16 @@ "This is convenient for tracking the frequency of events. Call `send(n)` any time you want to add `n` events to the counter. Pass the object to `next()` to get a tuple of the number of events and the frequency/second." ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Random wait function for testing `time_events`\n", + "def _randwait(): yield from (sleep(random.random()/200) for _ in range(100))" + ] + }, { "cell_type": "code", "execution_count": null, @@ -1449,14 +1461,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "# Events: 10, Freq/sec: 80.30\n" + "# Events: 100, Freq/sec: 396.51\n" ] } ], "source": [ - "# Random wait function for testing `time_events`\n", - "def _randwait(): yield from (sleep(random.random()/30) for _ in range(10))\n", - "\n", "c = time_events() # Start timer\n", "for o in _randwait(): c.send(1) # Send an event\n", "events,freq = next(c) # Return counter values\n", @@ -1464,6 +1473,89 @@ "print(f'# Events: {events}, Freq/sec: {freq:.02f}')" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#export\n", + "class EventTimer:\n", + " \"An event timer with history of `store` items of time `span`\"\n", + " def __init__(self, store=5, span=60):\n", + " self.hist,self.span,self.last = collections.deque(maxlen=store),span,default_timer()\n", + " self._reset()\n", + "\n", + " def _reset(self): self.start,self.events = self.last,0\n", + "\n", + " def add(self, n=1):\n", + " \"Record `n` events\"\n", + " if self.duration>self.span:\n", + " self.hist.append(self.freq)\n", + " self._reset()\n", + " self.events +=n\n", + " self.last = default_timer()\n", + " \n", + " @property\n", + " def duration(self): return default_timer()-self.start\n", + " @property\n", + " def freq(self): return self.events/self.duration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "
class
EventTimer
[source]EventTimer
(**`store`**=*`5`*, **`span`**=*`60`*)\n",
+ "\n",
+ "An event timer with history of `store` items of time `span`"
+ ],
+ "text/plain": [
+ "