fromcollections.abcimportHashablefromfunctoolsimportpartialfromitertoolsimportchainfromtextwrapimportshortenfromtypingimportAny,Mappingimportdask.coreimportxarrayasxrfrom.keyimportKey#: Default maximum length for outputs from :func:`describe_recursive`.MAX_ITEM_LENGTH=160
[docs]defdescribe_recursive(graph,comp,depth=0,seen=None):"""Recursive helper for :meth:`.describe`. Parameters ---------- graph : A dask graph. comp : A dask computation. depth : int Recursion depth. Used for indentation. seen : set Keys that have already been described. Used to avoid double-printing. """comp=compifisinstance(comp,tuple)else(comp,)seen=set()ifseenisNoneelseseenindent=(" "*2*(depth-1))+("- "ifdepth>0else"")# Strings for argumentsresult=[]forargincomp:# Don't fully reprint keys and their ancestors that have been seenifisinstance(arg,Hashable)andarginseen:ifdepth>0:# Don't print top-level items that have been seenresult.append(f"{indent}'{arg}' (above)")continueelifisinstance(arg,(str,Key))andargingraph:# key that exists in the graph → recurseitem="'{}':\n{}".format(arg,describe_recursive(graph,graph[arg],depth+1,seen))elifis_list_of_keys(arg,graph):# list → collection of itemsitem="list of:\n{}".format(describe_recursive(graph,tuple(arg),depth+1,seen))else:# Anything else: use a readable string representationitem=label(arg)try:seen.add(arg)exceptTypeError:pass# `arg` is unhashable, e.g. a listresult.append(indent+item)# Combine itemsreturn("\n"ifdepth>0else"\n\n").join(result)
[docs]defis_list_of_keys(arg:Any,graph:Mapping)->bool:"""Identify a task which is a list of other keys."""return(isinstance(arg,list)andlen(arg)>0andisinstance(arg[0],Hashable)andarg[0]ingraph)
[docs]deflabel(arg,max_length=MAX_ITEM_LENGTH)->str:"""Return a label for `arg`. The label depends on the type of `arg`: - :class:`.xarray.DataArray`: the first line of the string representation. - :func:`functools.partial` object: a less-verbose version that omits None arguments. - Item protected with :func:`.dask.core.quote`: its literal value. - A callable, e.g. a function: its name. - Anything else: its :class:`str` representation. In all cases, the string is no longer than `max_length`. """# Convert various types of arguments to stringifisinstance(arg,xr.DataArray):# DataArray → just the first line of the string representationreturnstr(arg).split("\n")[0]elifisinstance(arg,partial):# functools.partial → less verbose formatfn_args=", ".join(chain(map(repr,arg.args),filter(None,map(lambdakw:""ifkw[1]isNoneelsef"{kw[0]}={kw[1]}",arg.keywords.items(),),),["..."],))fn_repr=getattr(arg.func,"__name__",repr(arg.func))returnshorten(f"{fn_repr}({fn_args})",max_length)elifisinstance(arg,dask.core.literal):# Item protected with dask.core.quote()returnshorten(str(arg.data),max_length)elifcallable(arg):ifarg.__class__.__name__=="builtin_function_or_method":returnrepr(arg).replace("function ","")else:returngetattr(arg,"__name__",str(arg))else:returnshorten(str(arg),max_length)