Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Very simple deep_set and deep_get functions to access nested dicts (or any object) using 'dotted strings' as key.
Simple functions to set or get values from a nested dict structure or in fact a deep structure of any object, because since version 2 we no longer assume we are dealing with dicts.
You may use a custom accessor or pass your own getter, setter, deleter callables so that you can traverse a nested structure of any kind of object.
This module DOES NOT implement dotted notation as an alternative access method for dicts. I generally do not like changing python dicts to enable dot notation, hence no available package fitted my needs for a simple deep accessor.
NEW IN VERSION 4: Since version 3 we make no assumption that we are dealing with dicts, so you can have your nested structure of any type. However, in version 4 we reintroduce better defaults so that for those that are indeed working with nested dicts the default values shall be enough without having to define an accessor or a getter.
Notes: With deep_get, you could use 'lambda o, k: o[k]' or 'lambda o, k: o.get(k)' as either the getter or the accessor. The only 'special' thing about the 'getter' function is that when it is invoked with 'o' being a list, it will instead iterate over the list and call the accessor for each item in the list.
In a simplified way, this is how deep_get works:
The key is broken down into a list of keys: "customer.address.city" -> ['customer', 'address', 'city']
The list of keys is iterated over, calling the getter for each key and the last value retrieved is returned.
for k in keys[:-1]:
if o is None:
return o
o = getter(o, k)
o = getter_last_step(o, keys[-1])
return o
You see that getter could be as simple as 'lambda o, k: o.get(k)'. However, by default the code uses a smarter getter as defined below, which tries to deal properly with nested lists.
def __default_getter(o, k):
if isinstance(o, list):
return [accessor(i, k) for i in o]
else:
return accessor(o, k)
If you do not want this checks for nested lists, just pass your own getter which could just as well be 'lambda o, k: o.get(k)'.
The default setter also knows how to deal with nested lists:
def __default_setter(o, k, v):
n_set = 0
if isinstance(o, list):
for i in o:
i[k] = v
n_set += 1
return n_set
else:
o[k] = v
return 1
You could just as well replace if with your own 'setter=lambda o, k, v: o[k]=v' if you know that you have no nested lists in your structures and want to avoid the overhead, but in that case you should also change the getter 'getter=lambda o, k: o.get(k)'.
However, if you like the list handling skills of the code but just needs to change the way the value is retrieved, in this case you pass an accessor only to deep_get or deep_set which could be, say, 'lambda o, k: o.getValueById(k)'
deep_get accepts:
Returns o[k]. If o[k] does not exist, should return None (but depends on the callables used).
deep_set accepts:
No return value
deep_del accepts:
Returns an integer with the number of entries that were deleted.
from dict_deep import deep_get, deep_set, deep_del
i = 0
# 1
i += 1
o = {'a': {'b': {}}}
deep_set(o, "a.b.c", "Hello World")
print("{}: {}".format(i, deep_get(o, "a.b.c")))
# 2
i += 1
o = {}
deep_set(o, ['a', 'b', 'c'], "Hello World")
print("{}: {}".format(i, deep_get(o, "a.b.c")))
# 3
i += 1
o = {}
deep_set(o, "a->b->c", "Hello World", sep="->")
print("{}: {}".format(i, deep_get(o, "a->b->c", sep="->")))
# 4
i += 1
o = {}
deep_set(o, "a->b->c", "Hello World", getter=lambda o, k: o.setdefault(k, dict()), sep="->")
print("{}: {}".format(i, deep_get(o, "a->b->c", sep="->")))
# 5
i += 1
o = {}
keys = 'a.b.c'
keys = keys.split('.')
_ = deep_get(o=o, k=keys[0:-1], accessor=lambda o, k: o.setdefault(k, dict()), sep=".")
_[keys[-1]] = "Hello World"
print("{}: {}".format(i, deep_get(o, keys)))
# 6
i += 1
o = {}
deep_set(o, "1.1.1", 'a', accessor=lambda o, k: o.setdefault(k, dict()))
deep_set(o, "1.1.2", 'Hello World')
deep_set(o, "1.1.3", 'c')
deep_del(o, "1.1.2")
print("{}: {}".format(i, o))
# 7
i += 1
o = {'students': [{'name': 'Joe', 'age': 10, 'gender': 'male'}, {'name': 'Maria', 'age': 12, 'gender': 'female'}]}
keys = ['students', 'name']
print("{}: {}".format(i, deep_get(o, keys)))
# 8
i += 1
keys = ['students', ['name', 'age']]
print("{}: {}".format(i, deep_get(o, keys)))
# 9
i += 1
keys = ['students', 'gender']
deep_set(o, keys, 'Nowadays better not ask')
print("{}: {}".format(i, o))
# 10
i += 1
keys = ['students', 'gender']
deep_del(o, keys)
print("{}: {}".format(i, o))
# 11
i += 1
keys = ['director', 'name']
print("{}: {}".format(i, deep_get(o, keys)))
FAQs
Very simple deep_set and deep_get functions to access nested dicts (or any object) using 'dotted strings' as key.
We found that dict-deep demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.