Is there a way to create a typedispatched "show_batch" so that it doesn't that it doesn't call the "show/decodes" methods of the individual items in a batch?

(This is using the DataBlock API with a couple custom TransformBlocks)

I even tried just overwriting the default “catch all” show_batch as such:

@typedispatch
def show_batch(x, y, samples, ctxs=None, max_n=10, trunc_at=100, **kwargs):
    pdb.set_trace()
    pass

Never gets called. Instead, when I do a dls.show_batch I get an error like this:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-77-90634fcc3c9e> in <module>
----> 1 dls.show_batch()

~/development/_training/ml/nlp-playground/_libs/fastai2/fastai2/data/core.py in show_batch(self, b, max_n, ctxs, show, **kwargs)
     90         if b is None: b = self.one_batch()
     91         if not show: return self._pre_show_batch(b, max_n=max_n)
---> 92         show_batch(*self._pre_show_batch(b, max_n=max_n), ctxs=ctxs, max_n=max_n, **kwargs)
     93 
     94     def show_results(self, b, out, max_n=9, ctxs=None, show=True, **kwargs):

~/development/_training/ml/nlp-playground/_libs/fastai2/fastai2/data/core.py in _pre_show_batch(self, b, max_n)
     83         b = self.decode(b)
     84         if hasattr(b, 'show'): return b,None,None
---> 85         its = self._decode_batch(b, max_n, full=False)
     86         if not is_listy(b): b,its = [b],L((o,) for o in its)
     87         return detuplify(b[:self.n_inp]),detuplify(b[self.n_inp:]),its

~/development/_training/ml/nlp-playground/_libs/fastai2/fastai2/data/core.py in _decode_batch(self, b, max_n, full)
     77         f = self.after_item.decode
     78         f = compose(f, partial(getattr(self.dataset,'decode',noop), full = full))
---> 79         return L(batch_to_samples(b, max_n=max_n)).map(f)
     80 
     81     def _pre_show_batch(self, b, max_n=9):

~/development/_training/ml/nlp-playground/_libs/fastcore/fastcore/foundation.py in map(self, f, *args, **kwargs)
    360              else f.format if isinstance(f,str)
    361              else f.__getitem__)
--> 362         return self._new(map(g, self))
    363 
    364     def filter(self, f, negate=False, **kwargs):

~/development/_training/ml/nlp-playground/_libs/fastcore/fastcore/foundation.py in _new(self, items, *args, **kwargs)
    313     @property
    314     def _xtra(self): return None
--> 315     def _new(self, items, *args, **kwargs): return type(self)(items, *args, use_list=None, **kwargs)
    316     def __getitem__(self, idx): return self._get(idx) if is_indexer(idx) else L(self._get(idx), use_list=None)
    317     def copy(self): return self._new(self.items.copy())

~/development/_training/ml/nlp-playground/_libs/fastcore/fastcore/foundation.py in __call__(cls, x, *args, **kwargs)
     39             return x
     40 
---> 41         res = super().__call__(*((x,) + args), **kwargs)
     42         res._newchk = 0
     43         return res

~/development/_training/ml/nlp-playground/_libs/fastcore/fastcore/foundation.py in __init__(self, items, use_list, match, *rest)
    304         if items is None: items = []
    305         if (use_list is not None) or not _is_array(items):
--> 306             items = list(items) if use_list else _listify(items)
    307         if match is not None:
    308             if is_coll(match): match = len(match)

~/development/_training/ml/nlp-playground/_libs/fastcore/fastcore/foundation.py in _listify(o)
    240     if isinstance(o, list): return o
    241     if isinstance(o, str) or _is_array(o): return [o]
--> 242     if is_iter(o): return list(o)
    243     return [o]
    244 

~/development/_training/ml/nlp-playground/_libs/fastcore/fastcore/foundation.py in __call__(self, *args, **kwargs)
    206             if isinstance(v,_Arg): kwargs[k] = args.pop(v.i)
    207         fargs = [args[x.i] if isinstance(x, _Arg) else x for x in self.pargs] + args[self.maxi+1:]
--> 208         return self.fn(*fargs, **kwargs)
    209 
    210 # Cell

~/development/_training/ml/nlp-playground/_libs/fastcore/fastcore/utils.py in _inner(x, *args, **kwargs)
    339     if order is not None: funcs = funcs.sorted(order)
    340     def _inner(x, *args, **kwargs):
--> 341         for f in L(funcs): x = f(x, *args, **kwargs)
    342         return x
    343     return _inner

~/development/_training/ml/nlp-playground/_libs/fastai2/fastai2/data/core.py in decode(self, o, full)
    283     def __iter__(self): return (self[i] for i in range(len(self)))
    284     def __repr__(self): return coll_repr(self)
--> 285     def decode(self, o, full=True): return tuple(tl.decode(o_, full=full) for o_,tl in zip(o,tuplify(self.tls, match=o)))
    286     def subset(self, i): return type(self)(tls=L(tl.subset(i) for tl in self.tls), n_inp=self.n_inp)
    287     def _new(self, items, *args, **kwargs): return super()._new(items, tfms=self.tfms, do_setup=False, **kwargs)

~/development/_training/ml/nlp-playground/_libs/fastai2/fastai2/data/core.py in <genexpr>(.0)
    283     def __iter__(self): return (self[i] for i in range(len(self)))
    284     def __repr__(self): return coll_repr(self)
--> 285     def decode(self, o, full=True): return tuple(tl.decode(o_, full=full) for o_,tl in zip(o,tuplify(self.tls, match=o)))
    286     def subset(self, i): return type(self)(tls=L(tl.subset(i) for tl in self.tls), n_inp=self.n_inp)
    287     def _new(self, items, *args, **kwargs): return super()._new(items, tfms=self.tfms, do_setup=False, **kwargs)

~/development/_training/ml/nlp-playground/_libs/fastai2/fastai2/data/core.py in decode(self, o, **kwargs)
    219     def __iter__(self): return (self[i] for i in range(len(self)))
    220     def show(self, o, **kwargs): return self.tfms.show(o, **kwargs)
--> 221     def decode(self, o, **kwargs): return self.tfms.decode(o, **kwargs)
    222     def __call__(self, o, **kwargs): return self.tfms.__call__(o, **kwargs)
    223     def overlapping_splits(self): return L(Counter(self.splits.concat()).values()).filter(gt(1))

~/development/_training/ml/nlp-playground/_libs/fastcore/fastcore/transform.py in decode(self, o, full)
    195         for f in reversed(self.fs):
    196             if self._is_showable(o): return o
--> 197             o = f.decode(o, split_idx=self.split_idx)
    198         return o
    199 

~/development/_training/ml/nlp-playground/_libs/fastcore/fastcore/transform.py in decode(self, x, **kwargs)
     71     def name(self): return getattr(self, '_name', _get_name(self))
     72     def __call__(self, x, **kwargs): return self._call('encodes', x, **kwargs)
---> 73     def decode  (self, x, **kwargs): return self._call('decodes', x, **kwargs)
     74     def __repr__(self): return f'{self.name}: {self.encodes} {self.decodes}'
     75 

~/development/_training/ml/nlp-playground/_libs/fastcore/fastcore/transform.py in _call(self, fn, x, split_idx, **kwargs)
     80     def _call(self, fn, x, split_idx=None, **kwargs):
     81         if split_idx!=self.split_idx and self.split_idx is not None: return x
---> 82         return self._do_call(getattr(self, fn), x, **kwargs)
     83 
     84     def _do_call(self, f, x, **kwargs):

~/development/_training/ml/nlp-playground/_libs/fastcore/fastcore/transform.py in _do_call(self, f, x, **kwargs)
     85         if not _is_tuple(x):
     86             return x if f is None else retain_type(f(x, **kwargs), x, f.returns_none(x))
---> 87         res = tuple(self._do_call(f, x_, **kwargs) for x_ in x)
     88         return retain_type(res, x)
     89 

~/development/_training/ml/nlp-playground/_libs/fastcore/fastcore/transform.py in <genexpr>(.0)
     85         if not _is_tuple(x):
     86             return x if f is None else retain_type(f(x, **kwargs), x, f.returns_none(x))
---> 87         res = tuple(self._do_call(f, x_, **kwargs) for x_ in x)
     88         return retain_type(res, x)
     89 

~/development/_training/ml/nlp-playground/_libs/fastcore/fastcore/transform.py in _do_call(self, f, x, **kwargs)
     84     def _do_call(self, f, x, **kwargs):
     85         if not _is_tuple(x):
---> 86             return x if f is None else retain_type(f(x, **kwargs), x, f.returns_none(x))
     87         res = tuple(self._do_call(f, x_, **kwargs) for x_ in x)
     88         return retain_type(res, x)

~/development/_training/ml/nlp-playground/_libs/fastcore/fastcore/dispatch.py in __call__(self, *args, **kwargs)
     96         if not f: return args[0]
     97         if self.inst is not None: f = MethodType(f, self.inst)
---> 98         return f(*args, **kwargs)
     99 
    100     def __get__(self, inst, owner):

~/development/_training/ml/nlp-playground/_libs/fastai2/fastai2/text/core.py in decodes(self, o)
    300             except: return None
    301 
--> 302     def decodes(self, o): return TitledStr(self.sep.join(o))
    303 
    304 # Cell

TypeError: sequence item 0: expected str instance, Tensor found

This only happens with the DataBlock API. If I use the mid-level API, it works as expected. I can see what it’s doing … but again, I just want my custom show_batch method called where things are properly handled for showing.

No, you can only bypass the decode at the item level if your batch is of a type that knows how to show itself.