Skip to content

Commit

Permalink
fixes #263
Browse files Browse the repository at this point in the history
  • Loading branch information
jph00 committed Dec 20, 2020
1 parent 37a649c commit 9f13ba7
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 26 deletions.
2 changes: 2 additions & 0 deletions fastcore/_nbdev.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
22 changes: 14 additions & 8 deletions fastcore/basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]|(?<!\A)[A-Z](?=[a-z]))')
_camel_re1 = re.compile('(.)([A-Z][a-z]+)')
_camel_re2 = re.compile('([a-z0-9])([A-Z])')

# Cell
def camel2words(s, space=' '):
"Convert CamelCase to 'spaced words'"
return re.sub(_c2w_re, rf'{space}\1', s)

# Cell
def camel2snake(name):
"Convert CamelCase to snake_case"
Expand Down
36 changes: 29 additions & 7 deletions fastcore/xtras.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
__all__ = ['dict2obj', 'obj2dict', 'repr_dict', 'is_listy', 'shufflish', 'mapped', 'IterLen', 'ReindexCollection',
'maybe_open', 'image_size', 'bunzip', 'join_path_file', 'loads', 'untar_dir', 'repo_details', 'run',
'open_file', 'save_pickle', 'load_pickle', 'truncstr', 'spark_chars', 'sparkline', 'autostart',
'time_events', 'stringfmt_names', 'PartialFormatter', 'partial_format', 'utc2local', 'local2utc', 'trace',
'round_multiple', 'modified_env', 'ContextManagers', 'str2bool', 'sort_by_run']
'time_events', 'EventTimer', 'stringfmt_names', 'PartialFormatter', 'partial_format', 'utc2local',
'local2utc', 'trace', 'round_multiple', 'modified_env', 'ContextManagers', 'str2bool', 'sort_by_run']

# Cell
from .imports import *
Expand All @@ -13,7 +13,7 @@
from functools import wraps

import mimetypes,pickle,random,json,subprocess,shlex,bz2,gzip,zipfile,tarfile
import imghdr,struct,distutils.util,tempfile,time,string
import imghdr,struct,distutils.util,tempfile,time,string,collections
from contextlib import contextmanager,ExitStack
from pdb import set_trace
from datetime import datetime, timezone
Expand Down Expand Up @@ -222,9 +222,9 @@ def __repr__(self:Path):
return f"Path({self.as_posix()!r})"

# Cell
def truncstr(s:str, maxlen:int, suf:str='…')->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 = '▁▂▃▅▆▇'
Expand All @@ -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)

Expand All @@ -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()

Expand Down
22 changes: 22 additions & 0 deletions nbs/01_basics.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1925,10 +1925,32 @@
"outputs": [],
"source": [
"#export\n",
"_c2w_re = re.compile(r'((?<=[a-z])[A-Z]|(?<!\\A)[A-Z](?=[a-z]))')\n",
"_camel_re1 = re.compile('(.)([A-Z][a-z]+)')\n",
"_camel_re2 = re.compile('([a-z0-9])([A-Z])')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#export\n",
"def camel2words(s, space=' '):\n",
" \"Convert CamelCase to 'spaced words'\"\n",
" return re.sub(_c2w_re, rf'{space}\\1', s)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"test_eq(camel2words('ClassAreCamel'), 'Class Are Camel')"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down
114 changes: 103 additions & 11 deletions nbs/03_xtras.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"from functools import wraps\n",
"\n",
"import mimetypes,pickle,random,json,subprocess,shlex,bz2,gzip,zipfile,tarfile\n",
"import imghdr,struct,distutils.util,tempfile,time,string\n",
"import imghdr,struct,distutils.util,tempfile,time,string,collections\n",
"from contextlib import contextmanager,ExitStack\n",
"from pdb import set_trace\n",
"from datetime import datetime, timezone\n",
Expand Down Expand Up @@ -618,7 +618,7 @@
{
"data": {
"text/plain": [
"['d', 'c', 'h', 'a', 'b', 'g', 'e', 'f']"
"['a', 'e', 'h', 'b', 'g', 'd', 'c', 'f']"
]
},
"execution_count": null,
Expand Down Expand Up @@ -1326,9 +1326,9 @@
"outputs": [],
"source": [
"#export\n",
"def truncstr(s:str, maxlen:int, suf:str='…')->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"
]
},
{
Expand All @@ -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!!')"
]
},
Expand Down Expand Up @@ -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)"
]
Expand Down Expand Up @@ -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,
Expand All @@ -1449,21 +1461,101 @@
"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",
"c.close() # Close when done\n",
"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": [
"<h4 id=\"EventTimer\" class=\"doc_header\"><code>class</code> <code>EventTimer</code><a href=\"\" class=\"source_link\" style=\"float:right\">[source]</a></h4>\n",
"\n",
"> <code>EventTimer</code>(**`store`**=*`5`*, **`span`**=*`60`*)\n",
"\n",
"An event timer with history of `store` items of time `span`"
],
"text/plain": [
"<IPython.core.display.Markdown object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"show_doc(EventTimer, title_level=4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Add events with `add`, and get number of `events` and their frequency (`freq`)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Num Events: 5, Freq/sec: 646.7\n",
"Most recent: ▁▇▃▇▆ 259.5 450.4 362.1 441.0 427.9\n"
]
}
],
"source": [
"c = EventTimer(store=5, span=0.03)\n",
"for o in _randwait(): c.add(1)\n",
"print(f'Num Events: {c.events}, Freq/sec: {c.freq:.01f}')\n",
"print('Most recent: ', sparkline(c.hist), *L(c.hist).map('{:.01f}'))"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down

0 comments on commit 9f13ba7

Please sign in to comment.