Package: pydlib
:yarn: pydlibThepythondictionarylibrary to get into complex nested python dictionary structures (e.g. json) in a safe and clean way. We take inspiration from Greek myth of Minotaur, where Ariadne with the help of a thread escaped the labyrinth with his beloved Theseus.Why use pydlibSometimes you have to navigate deep json dicts from remote sources, like elastic logs: you can make a series of.get()and check forNoneevery time; or you can doobj["path"]["to"]["nested"]["field"]and wrap everything in atry/except...Or you can usepydliband write:pydlib.get(obj,"path.to.nested.field")to get the value offield, orNoneif anything is not adictalong the given path.InstallationTo installpydlib, simply usepip:$pipinstallpydlibor install from the repository:$gitclonehttps://github.com/aitechnologies-it/dlib.git
$cddlib
$pythonsetup.pyinstallBasic usageget()You cangetthe value from a nested field, just by indicating the path to the nested sub-structure as follows:>>>importpydlibasdl>>>dictionary={>>>'path':{>>>'to':{>>>'nested':{>>>'field':42>>>}>>>}>>>}>>>}>>>dl.get(dictionary,path='path.to.nested.field',default=0)42Instead, if the field we are looking for doesn't exists, or, if it exists but has a None value, then:>>>...>>>dl.get(dictionary,path='path.to.nested.nonexisting.field',default=0)0has()You can also test for a field simply calling:>>>importpydlibasdl>>>dictionary={...}>>>dl.has(dictionary,path='path.to.nested.field')Trueupdate()Furthermore, thepydlibcomes with built-in functions to alsoupdateanddeletefields. For example, toupdate:>>>importpydlibasdl>>>dictionary={...}>>>dl.update(dictionary,path='path.to.nested.field',value=1){'path':{'to':{'nested':{'field':1}}}}delete()Instead, todelete:>>>importpydlibasdl>>>dictionary={...}>>>dl.delete(dictionary,path='path.to.nested.field'){'path':{'to':{'nested':{}}}}Type-safetypydlib istype safe, in fact you don't have to manually check the type of inputs,pydlibdoes it for you:>>>importpydlibasdl>>>res=dl.get("not a dictionary",path="nowhere",default=None)>>>resisNoneTrueAdvanced featuresCustom separatorIt may happen that a dictionary has a string key with.in it. In this case you should use a different separator:>>>importpydlibasdl>>>d={"a":{"b.c":42}}# Separator conflict>>>dl.get(d,"a.b.c")None# This works!>>>dl.get(d,"a/b.c",sep="/")42Search inside listshas()andget()(but notupdateanddelete!) can handle lists. This means that, if a list is encountered, the search for the rest of the path continues for each element of the list. A few examples are needed:bis a list, get() will return a list with all dictionaries containing the rest of the pathc.d:>>>d={"a":{"b":[{"c":{"d":1}},# <-- this{"bad":{"d":2}},{"c":{"d":3}},# <-- this{"c":{"bad":4}}]}}>>>dl.get(d,"a.b.c.d")[1,3]this works also for nested lists. In this case a nested list of matching depth is returned:>>>d={"a":{"b":[{"c":{"d":[{"e":1},{"e":2},{"bad":3},]}},{"bad":{"d":[{"e":4},]}},{"c":{"d":[{"e":5},]}},]}}>>>dl.get(d,"a.b.c.d.e")[[1,2],[5]]In this case the elements of listbare of different types,(1)and(3)are dictionaries,(2)is a list:>>>d={"a":{"b":[{"c":{"d":1}},# (1)[{"c":{"d":3}}],# (2){"c":{"d":4}},# (3)]}}>>>dl.get(d,"a.b.c.d")[1,[3],4]Handling of lists can be disabled by settingsearch_lists=False. Here's different behaviours forsearch_lists:>>>d={"a":{"b":[{"c":{"d":1}},{"bad":{"d":2}},{"c":{"d":3}},{"c":{"bad":4}}]}}>>>dl.get(d,"a.b.c.d",search_lists=True)[1,3]>>>dl.get(d,"a.b.c.d",search_lists=False)None# But if instead we want to get `a.b`, no lists are traversed and both return the value of `b`>>>dl.get(d,"a.b",search_lists=True)[{'c':{'d':1}},[{'c':{'d':3}}],{'c':{'d':4}}]>>>dl.get(d,"a.b",search_lists=False)[{'c':{'d':1}},[{'c':{'d':3}}],{'c':{'d':4}}]