{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#default_exp xtras" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "from fastcore.imports import *\n", "from fastcore.foundation import *\n", "from fastcore.basics import *\n", "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,collections\n", "from contextlib import contextmanager,ExitStack\n", "from pdb import set_trace\n", "from datetime import datetime, timezone\n", "from timeit import default_timer" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from fastcore.test import *\n", "from nbdev.showdoc import *\n", "from fastcore.nb_imports import *\n", "from time import sleep" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Utility functions\n", "\n", "> Utility functions used in the fastai library" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Collections" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def dict2obj(d):\n", " \"Convert (possibly nested) dicts (or lists of dicts) to `AttrDict`\"\n", " if isinstance(d, (L,list)): return L(d).map(dict2obj)\n", " if not isinstance(d, dict): return d\n", " return AttrDict(**{k:dict2obj(v) for k,v in d.items()})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is a convenience to give you \"dotted\" access to (possibly nested) dictionaries, e.g:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d1 = dict(a=1, b=dict(c=2,d=3))\n", "d2 = dict2obj(d1)\n", "test_eq(d2.b.c, 2)\n", "test_eq(d2.b['c'], 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It can also be used on lists of dicts." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "_list_of_dicts = [d1, d1]\n", "ds = dict2obj(_list_of_dicts)\n", "test_eq(ds[0].b.c, 2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def obj2dict(d):\n", " \"Convert (possibly nested) AttrDicts (or lists of AttrDicts) to `dict`\"\n", " if isinstance(d, (L,list)): return list(L(d).map(obj2dict))\n", " if not isinstance(d, dict): return d\n", " return dict(**{k:obj2dict(v) for k,v in d.items()})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`obj2dict` can be used to reverse what is done by `dict2obj`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(obj2dict(d2), d1)\n", "test_eq(obj2dict(ds), _list_of_dicts) " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def _repr_dict(d, lvl):\n", " if isinstance(d,dict):\n", " its = [f\"{k}: {_repr_dict(v,lvl+1)}\" for k,v in d.items()]\n", " elif isinstance(d,(list,L)): its = [_repr_dict(o,lvl+1) for o in d]\n", " else: return str(d)\n", " return '\\n' + '\\n'.join([\" \"*(lvl*2) + \"- \" + o for o in its])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def repr_dict(d):\n", " \"Print nested dicts and lists, such as returned by `dict2obj`\"\n", " return _repr_dict(d,0).strip()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "- a: 1\n", "- b: \n", " - c: 2\n", " - d: 3\n" ] } ], "source": [ "print(repr_dict(d2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`repr_dict` is used to display `AttrDict` both with `repr` and in Jupyter Notebooks:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "@patch\n", "def __repr__(self:AttrDict): return repr_dict(self)\n", "\n", "AttrDict._repr_markdown_ = AttrDict.__repr__" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "- a: 1\n", "- b: \n", " - c: 2\n", " - d: 3\n" ] } ], "source": [ "print(repr(d2))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "- a: 1\n", "- b: \n", " - c: 2\n", " - d: 3" ], "text/plain": [ "- a: 1\n", "- b: \n", " - c: 2\n", " - d: 3" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d2" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def is_listy(x):\n", " \"`isinstance(x, (tuple,list,L,slice,Generator))`\"\n", " return isinstance(x, (tuple,list,L,slice,Generator))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "assert is_listy((1,))\n", "assert is_listy([1])\n", "assert is_listy(L([1]))\n", "assert is_listy(slice(2))\n", "assert not is_listy(array([1]))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def shufflish(x, pct=0.04):\n", " \"Randomly relocate items of `x` up to `pct` of `len(x)` from their starting location\"\n", " n = len(x)\n", " return L(x[i] for i in sorted(range_of(x), key=lambda o: o+n*(1+random.random()*pct)))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def mapped(f, it):\n", " \"map `f` over `it`, unless it's not listy, in which case return `f(it)`\"\n", " return L(it).map(f) if is_listy(it) else f(it)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def _f(x,a=1): return x-a\n", "\n", "test_eq(mapped(_f,1),0)\n", "test_eq(mapped(_f,[1,2]),[0,1])\n", "test_eq(mapped(_f,(1,)),(0,))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Reindexing Collections" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "#hide\n", "class IterLen:\n", " \"Base class to add iteration to anything supporting `__len__` and `__getitem__`\"\n", " def __iter__(self): return (self[i] for i in range_of(self))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "@docs\n", "class ReindexCollection(GetAttr, IterLen):\n", " \"Reindexes collection `coll` with indices `idxs` and optional LRU cache of size `cache`\"\n", " _default='coll'\n", " def __init__(self, coll, idxs=None, cache=None, tfm=noop):\n", " if idxs is None: idxs = L.range(coll)\n", " store_attr()\n", " if cache is not None: self._get = functools.lru_cache(maxsize=cache)(self._get)\n", "\n", " def _get(self, i): return self.tfm(self.coll[i])\n", " def __getitem__(self, i): return self._get(self.idxs[i])\n", " def __len__(self): return len(self.coll)\n", " def reindex(self, idxs): self.idxs = idxs\n", " def shuffle(self): random.shuffle(self.idxs)\n", " def cache_clear(self): self._get.cache_clear()\n", " def __getstate__(self): return {'coll': self.coll, 'idxs': self.idxs, 'cache': self.cache, 'tfm': self.tfm}\n", " def __setstate__(self, s): self.coll,self.idxs,self.cache,self.tfm = s['coll'],s['idxs'],s['cache'],s['tfm']\n", "\n", " _docs = dict(reindex=\"Replace `self.idxs` with idxs\",\n", " shuffle=\"Randomly shuffle indices\",\n", " cache_clear=\"Clear LRU cache\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "
class
ReindexCollection
[source]ReindexCollection
(**`coll`**, **`idxs`**=*`None`*, **`cache`**=*`None`*, **`tfm`**=*`noop`*) :: [`GetAttr`](/basics.html#GetAttr)\n",
"\n",
"Reindexes collection `coll` with indices `idxs` and optional LRU cache of size `cache`"
],
"text/plain": [
"ReindexCollection.reindex
[source]ReindexCollection.reindex
(**`idxs`**)\n",
"\n",
"Replace `self.idxs` with idxs"
],
"text/plain": [
"ReindexCollection.cache_clear
[source]ReindexCollection.cache_clear
()\n",
"\n",
"Clear LRU cache"
],
"text/plain": [
"ReindexCollection.shuffle
[source]ReindexCollection.shuffle
()\n",
"\n",
"Randomly shuffle indices"
],
"text/plain": [
"class
EventTimer
[source]EventTimer
(**`store`**=*`5`*, **`span`**=*`60`*)\n",
"\n",
"An event timer with history of `store` items of time `span`"
],
"text/plain": [
"class
PartialFormatter
[source]PartialFormatter
() :: `Formatter`\n",
"\n",
"A `string.Formatter` that doesn't error on missing fields, and tracks missing fields and unused args"
],
"text/plain": [
"class
ContextManagers
[source]ContextManagers
(**`mgrs`**) :: [`GetAttr`](/basics.html#GetAttr)\n",
"\n",
"Wrapper for `contextlib.ExitStack` which enters a collection of context managers"
],
"text/plain": [
"