New Features (nested_update, nested_delete) (#12)

* Fix for issue #10

* Renamed the test module

* New Features (nested_get, nested_update, nested_delete)

* Update Version to 0.3.0

* Updated Readme for new features

* Removed method (nested_get)

* Addressed review comments

* Removed nested_get references from README

* Rearanged the order of the functions
This commit is contained in:
Ramesh RV 2018-11-24 05:40:31 +05:30 committed by Russell Ballestrini
parent d49c15f293
commit 4564f8ed80
6 changed files with 297 additions and 58 deletions

View file

@ -1,16 +1,21 @@
nested_lookup
#############
.. image:: https://img.shields.io/badge/pypi-0.2.01-green.svg
.. image:: https://img.shields.io/badge/pypi-0.2.11-green.svg
:target: https://pypi.python.org/pypi/nested-lookup
A small Python library which enables:
The `nested_lookup` package provides many Python functions for working with deeply nested documents. A document in this case is a a mixture of Python dictionary and list objects typically derived from YAML or JSON
#. key lookups on deeply nested documents.
#. fetching all keys from a nested dictionary.
#. get the number of occurrences of a key/value from a nested dictionary
Documents may be built out of dictionaries (dicts) and/or lists.
*nested_lookup:*
Perform a key lookup on a deeply nested document. Returns all matches in a `list`. (Please see tutorial for more info)
*nested_delete:*
Returns a document that includes everything but the given key
*nested_update:*
Returns a document that has updated key, value pair
*get_all_keys:*
Fetch all from a nested dictionary. Returns `list` of keys.
*get_occurrence_of_key/get_occurrence_of_value:*
Returns the number of occurrences of a key/value from a nested dictionary.
Make working with JSON, YAML, and XML document responses fun again!
@ -59,6 +64,15 @@ quick tutorial
>>> get_occurrence_of_value(document, value='42')
1
>>> from nested_lookup import nested_update, nested_delete
>>> nested_update(document, key='burrito', value='Test')
[{'taco': 42}, {'salsa': [{'burrito': 'Test'}]}]
>>> nested_delete(document, 'taco')
[{}, {'salsa': [{'burrito': {}}]}]
longer tutorial
===============
@ -88,34 +102,6 @@ For example, given the following document:
},
},
To get a list of every nested key in a document, run this:
.. code-block:: python
from nested_lookup import get_all_keys
keys = get_all_keys(my_document)
print(keys)
.. code-block:: python
['name', 'email_address', 'other', 'secondary_email', 'EMAIL_RECOVERY', 'email_address']
To get the number of occurrence of the given key/value
.. code-block:: python
from nested_lookup import get_occurrence_of_key, get_occurrence_of_value
no_of_key_occurrence = get_occurrence_of_key(my_document, key='email_address')
print(no_of_key_occurrence) # result => 2
no_of_value_occurrence = get_occurrence_of_value(my_document, value='test2@example.com')
print(no_of_value_occurrence) # result => 1
Next, we could act `wild` and find all the email addresses like this:
.. code-block:: python
@ -154,6 +140,50 @@ Additionally, if you also needed the matched key names, you could do this:
}
To get a list of every nested key in a document, run this:
.. code-block:: python
from nested_lookup import get_all_keys
keys = get_all_keys(my_document)
print(keys)
.. code-block:: python
['name', 'email_address', 'other', 'secondary_email', 'EMAIL_RECOVERY', 'email_address']
To get the number of occurrence of the given key/value
.. code-block:: python
from nested_lookup import get_occurrence_of_key, get_occurrence_of_value
no_of_key_occurrence = get_occurrence_of_key(my_document, key='email_address')
print(no_of_key_occurrence) # result => 2
no_of_value_occurrence = get_occurrence_of_value(my_document, value='test2@example.com')
print(no_of_value_occurrence) # result => 1
To Get / Delete / Update a key->value pair in nested document
.. code-block:: python
from nested_lookup import nested_update, nested_delete
result = nested_delete(my_document, 'EMAIL_RECOVERY')
print(result) # result => {'other': {'secondary_email': 'test2@example.com', 'email_address': 'test4@example.com'}, 'email_address': 'test1@example.com', 'name': 'Russell Ballestrini'}
result = nested_update(my_document, key='other', value='Test')
print(result) # result => {'other': 'Test', 'email_address': 'test1@example.com', 'name': 'Russell Ballestrini'}
misc
========

View file

@ -1,2 +1,3 @@
from .nested_lookup import nested_lookup, get_all_keys, get_occurrence_of_key,\
get_occurrence_of_value
from .lookup_api import nested_update, nested_delete

View file

@ -0,0 +1,54 @@
import copy
from six import iteritems
def nested_delete(document, key):
duplicate = copy.deepcopy(document)
return _nested_delete(document=duplicate, key=key)
def _nested_delete(document, key):
"""
Method to delete a key->value pair from a nested document
Args:
document: Might be List of Dicts (or) Dict of Lists (or)
Dict of List of Dicts etc...
key: Key to delete
Return:
Returns a document that includes everything but the given key
"""
if isinstance(document, list):
for list_items in document:
_nested_delete(document=list_items, key=key)
elif isinstance(document, dict):
if document.get(key):
del document[key]
for dict_key, dict_value in iteritems(document):
_nested_delete(document=dict_value, key=key)
return document
def nested_update(document, key, value):
duplicate = copy.deepcopy(document)
return _nested_update(document=duplicate, key=key, value=value)
def _nested_update(document, key, value):
"""
Method to update a key->value pair in a nested document
Args:
document: Might be List of Dicts (or) Dict of Lists (or)
Dict of List of Dicts etc...
key: Key to update the value
Return:
Returns a document that has updated key, value pair.
"""
if isinstance(document, list):
for list_items in document:
_nested_update(document=list_items, key=key, value=value)
elif isinstance(document, dict):
if document.get(key):
document[key] = value
for dict_key, dict_value in iteritems(document):
_nested_update(document=dict_value, key=key, value=value)
return document

View file

@ -7,7 +7,9 @@ def nested_lookup(key, document, wild=False, with_keys=False):
"""Lookup a key in a nested document, return a list of values"""
if with_keys:
d = defaultdict(list)
for k, v in _nested_lookup(key, document, wild=wild, with_keys=with_keys):
for k, v in _nested_lookup(
key, document, wild=wild, with_keys=with_keys
):
d[k].append(v)
return d
return list(_nested_lookup(key, document, wild=wild, with_keys=with_keys))
@ -17,7 +19,9 @@ def _nested_lookup(key, document, wild=False, with_keys=False):
"""Lookup a key in a nested document, yield a value"""
if isinstance(document, list):
for d in document:
for result in _nested_lookup(key, d, wild=wild, with_keys=with_keys):
for result in _nested_lookup(
key, d, wild=wild, with_keys=with_keys
):
yield result
if isinstance(document, dict):
@ -28,7 +32,9 @@ def _nested_lookup(key, document, wild=False, with_keys=False):
else:
yield v
if isinstance(v, dict):
for result in _nested_lookup(key, v, wild=wild, with_keys=with_keys):
for result in _nested_lookup(
key, v, wild=wild, with_keys=with_keys
):
yield result
elif isinstance(v, list):
for d in v:

View file

@ -5,32 +5,39 @@ from setuptools import (
find_packages,
)
# get list of requirement strings from requirements.txt
remove_whitespace = lambda x : ''.join(x.split())
sanitize = lambda x : not x.startswith('#') and x != ''
def remove_whitespace(x):
return ''.join(x.split())
def sanitize(x):
return not x.startswith('#') and x != ''
with open('requirements.txt', 'r') as f:
requires = filter(sanitize, map(remove_whitespace, f.readlines() ))
requires = filter(sanitize, map(remove_whitespace, f.readlines()))
setup(
name = 'nested-lookup',
version = '0.2.01',
description = 'lookup a key in a deeply nested document of dicts and lists',
keywords = 'nested document dictionary dict list lookup schema json xml yaml',
long_description = open('README.rst').read(),
name='nested-lookup',
version='0.2.11',
description='lookup a key in a deeply nested document of dicts and lists',
keywords='nested document dictionary dict list lookup schema json xml yaml',
long_description=open('README.rst').read(),
author = 'Russell Ballestrini',
author_email = 'russell@ballestrini.net',
url = 'https://github.com/russellballestrini/nested-lookup',
author='Russell Ballestrini',
author_email='russell@ballestrini.net',
url='https://github.com/russellballestrini/nested-lookup',
platforms = ['All'],
license = 'Public Domain',
platforms=['All'],
license='Public Domain',
packages = find_packages(),
include_package_data = True,
packages=find_packages(),
include_package_data=True,
install_requires = requires,
install_requires=requires,
classifiers = [
classifiers=[
# Specify the Python versions you support here. In particular, ensure
# that you indicate whether you support Python 2, Python 3 or both.
'Programming Language :: Python :: 2',

141
test_lookup_api.py Normal file
View file

@ -0,0 +1,141 @@
from unittest import TestCase
from nested_lookup import nested_update, nested_delete
class BaseLookUpApi(TestCase):
def setUp(self):
self.sample_data1 = {
"build_version": {
"model_name": 'MacBook Pro',
"build_version": {
"processor_name": 'Intel Core i7',
"processor_speed": '2.7 GHz',
"core_details": {
"build_version": '4',
"l2_cache(per_core)": '256 KB'
}
},
"number_of_cores": '4',
"memory": '256 KB'
},
"os_details": {
"product_version": '10.13.6',
"build_version": '17G65'
},
"name": 'Test',
"date": 'YYYY-MM-DD HH:MM:SS'
}
self.sample_data2 = {
"hardware_details": {
"model_name": 'MacBook Pro',
"processor_details": [
{
"processor_name": 'Intel Core i7',
"processor_speed": '2.7 GHz'
},
{
"total_number_of_cores": '4',
"l2_cache(per_core)": '256 KB'
}
],
"total_number_of_cores": '5',
"memory": '16 GB'
}
}
self.sample_data3 = {
"values": [{
"checks": [{
"monitoring_zones":
["mzdfw", "mzfra", "mzhkg", "mziad",
"mzlon", "mzord", "mzsyd"]
}]
}]
}
class TestNestedDelete(BaseLookUpApi):
def test_sample_data1(self):
result = {
"os_details": {
"product_version": '10.13.6'
},
"name": 'Test',
"date": 'YYYY-MM-DD HH:MM:SS'
}
self.assertEqual(
result, nested_delete(self.sample_data1, 'build_version')
)
def test_sample_data2(self):
result = {
"hardware_details": {
"model_name": 'MacBook Pro',
"total_number_of_cores": '5',
"memory": '16 GB'
}
}
self.assertEqual(
result, nested_delete(self.sample_data2, 'processor_details')
)
def test_sample_data3(self):
result = {"values": [{"checks": [{}]}]}
self.assertEqual(
result, nested_delete(self.sample_data3, 'monitoring_zones')
)
class TestNestedUpdate(BaseLookUpApi):
def test_sample_data1(self):
result = {
"build_version": "Test1",
"os_details": {
"product_version": '10.13.6',
"build_version": 'Test1'
},
"name": 'Test',
"date": 'YYYY-MM-DD HH:MM:SS'
}
self.assertEqual(
result, nested_update(self.sample_data1, 'build_version', 'Test1')
)
def test_sample_data2(self):
result = {
"hardware_details": {
"model_name": 'MacBook Pro',
"processor_details": {
'test_key1': 'test_value1'
},
"total_number_of_cores": '5',
"memory": '16 GB'
}
}
self.assertEqual(
result, nested_update(
self.sample_data2, 'processor_details',
{'test_key1': 'test_value1'}
)
)
def test_sample_data3(self):
result = {
"values": [{
"checks": {
'key1': ['value1'],
'key2': 'value2'
}
}]
}
self.assertEqual(
result, nested_update(
self.sample_data3, 'checks',
{
'key1': ['value1'],
'key2': 'value2'
}
)
)