Created
April 5, 2025 09:47
-
-
Save Kaligraphy247/14021559bd599417e34acc7d332da9b5 to your computer and use it in GitHub Desktop.
Safe Dictionary Value Retrieval with Dot Notation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Safe Dictionary Value Retrieval with Dot Notation | |
This module provides a function, `safe_get`, that safely retrieves values from | |
Python dictionaries, including nested dictionaries, using either a list of keys | |
or a dot-notation string. It avoids common `KeyError` exceptions and provides | |
a default value if the specified key path is not found. | |
This code was generated with the help Gemini 2.0 Flash. Test are completly AI generated. While Gemini 2.0 Flash has been trained | |
to generate correct and efficient code, it's essential to review and test | |
the code thoroughly before deploying it in a production environment. | |
Author: Written and formatted with Gemini 2.0 Flash | |
""" | |
from typing import Any, List, Dict, Optional, Union | |
def safe_get(data: Dict[str, Any], keys: Union[List[str], str], default: Optional[Any] = None) -> Any: | |
""" | |
Safely retrieves a value from a dictionary using either a list of keys | |
or a dot-notation string. | |
Args: | |
data (dict): The dictionary to traverse. | |
keys (Union[List[str], str]): A list of keys or a dot-notation string (e.g., "key1.key2.key3"). | |
default: The value to return if the nested value is not found (default: None). | |
Returns: | |
The value if found, the specified default value otherwise. | |
""" | |
if isinstance(keys, str): | |
keys = keys.split('.') # Split dot-notation string into a list of keys | |
if not isinstance(keys, list): | |
raise TypeError("keys must be a list of strings or a dot-notation string") | |
current = data | |
for key in keys: | |
if isinstance(current, dict) and key in current: | |
current = current[key] | |
else: | |
return default | |
return current | |
import unittest | |
class NestedDictAccessTest(unittest.TestCase): | |
def setUp(self): | |
self.empty_dict = {} | |
self.one_level_dict = {'key1': {}} | |
self.two_level_dict = {'key1': {'key2': 123}} | |
self.none_value_dict = {'key1': None} | |
self.false_value_dict = {'key1': False} | |
self.zero_value_dict = {'key1': 0} | |
self.empty_list_value_dict = {'key1': []} | |
self.empty_string_value_dict = {'key1': ''} | |
self.deep_dict = {'key1': {'key2': {'key3': {'key4': {'key5': 'deep_value'}}}}} | |
def test_list_access_existing(self): | |
self.assertEqual(safe_get(self.two_level_dict, ['key1', 'key2']), 123) | |
self.assertEqual(safe_get(self.deep_dict, ['key1', 'key2', 'key3', 'key4', 'key5']), 'deep_value') | |
def test_list_access_missing(self): | |
self.assertIsNone(safe_get(self.empty_dict, ['key1', 'key2'])) | |
self.assertIsNone(safe_get(self.one_level_dict, ['key1', 'key2'])) | |
self.assertIsNone(safe_get(self.two_level_dict, ['key1', 'key3'])) | |
self.assertIsNone(safe_get(self.deep_dict, ['key1', 'key2', 'key3', 'key4', 'key6'])) | |
def test_list_access_default(self): | |
self.assertEqual(safe_get(self.empty_dict, ['key1', 'key2'], 'default'), 'default') | |
self.assertEqual(safe_get(self.one_level_dict, ['key1', 'key2'], 'default'), 'default') | |
self.assertEqual(safe_get(self.two_level_dict, ['key1', 'key3'], 'default'), 'default') | |
self.assertEqual(safe_get(self.deep_dict, ['key1', 'key2', 'key3', 'key4', 'key6'], 'default'), 'default') | |
def test_list_access_none_values(self): | |
self.assertIsNone(safe_get(self.none_value_dict, ['key1'])) | |
self.assertIsNone(safe_get(self.false_value_dict, ['key1'])) | |
self.assertIsNone(safe_get(self.zero_value_dict, ['key1'])) | |
self.assertIsNone(safe_get(self.empty_list_value_dict, ['key1'])) | |
self.assertIsNone(safe_get(self.empty_string_value_dict, ['key1'])) | |
self.assertIsNone(safe_get(self.none_value_dict, ['key1', 'key2'])) | |
def test_dot_notation_access_existing(self): | |
self.assertEqual(safe_get(self.two_level_dict, "key1.key2"), 123) | |
self.assertEqual(safe_get(self.deep_dict, "key1.key2.key3.key4.key5"), 'deep_value') | |
def test_dot_notation_access_missing(self): | |
self.assertIsNone(safe_get(self.empty_dict, "key1.key2")) | |
self.assertIsNone(safe_get(self.one_level_dict, "key1.key2")) | |
self.assertIsNone(safe_get(self.two_level_dict, "key1.key3")) | |
self.assertIsNone(safe_get(self.deep_dict, "key1.key2.key3.key4.key6")) | |
def test_dot_notation_access_default(self): | |
self.assertEqual(safe_get(self.empty_dict, "key1.key2", 'default'), 'default') | |
self.assertEqual(safe_get(self.one_level_dict, "key1.key2", 'default'), 'default') | |
self.assertEqual(safe_get(self.two_level_dict, "key1.key3", 'default'), 'default') | |
self.assertEqual(safe_get(self.deep_dict, "key1.key2.key3.key4.key6", 'default'), 'default') | |
def test_dot_notation_access_none_values(self): | |
self.assertIsNone(safe_get(self.none_value_dict, "key1")) | |
self.assertIsNone(safe_get(self.false_value_dict, "key1")) | |
self.assertIsNone(safe_get(self.zero_value_dict, "key1")) | |
self.assertIsNone(safe_get(self.empty_list_value_dict, "key1")) | |
self.assertIsNone(safe_get(self.empty_string_value_dict, "key1")) | |
self.assertIsNone(safe_get(self.none_value_dict, "key1.key2")) | |
def test_invalid_keys_type(self): | |
with self.assertRaises(TypeError): | |
safe_get(self.two_level_dict, 123) # type: ignore | |
def test_mixed_access(self): | |
# Test accessing with a mix of list and dot notation is NOT supported and should act like a failed lookup | |
self.assertIsNone(safe_get(self.deep_dict, ["key1", "key2.key3", "key4", "key5"])) | |
if __name__ == '__main__': | |
unittest.main() |
More AI generated Test Data
realistic_dict = {
"user_id": "user12345",
"username": "john_doe",
"email": "[email protected]",
"profile": {
"first_name": "John",
"last_name": "Doe",
"age": 30,
"is_active": True,
"address": {
"street": "123 Main St",
"city": "Anytown",
"state": "CA",
"zip_code": "91234"
},
"interests": ["reading", "hiking", "coding"],
},
"settings": {
"notifications": {
"email_enabled": True,
"sms_enabled": False,
"push_enabled": True,
},
"privacy": {
"profile_visibility": "public",
"data_sharing_enabled": False
},
"theme": "dark"
},
"last_login": "2024-01-01T12:00:00Z",
"orders": [
{
"order_id": "order001",
"date": "2023-12-25",
"total": 49.99,
"items": ["Book A", "Book B"]
},
{
"order_id": "order002",
"date": "2023-12-30",
"total": 99.99,
"items": ["Gadget X", "Gadget Y", "Gadget Z"]
}
],
"social_connections": {
"friends": ["user0001", "user0002", "user0003"],
"followers": ["user0004", "user0005"],
"following": ["user0006", "user0007", "user0008"]
},
"preferences": {
"language": "en",
"currency": "USD",
"timezone": "America/Los_Angeles"
},
"billing_address": None,
"shipping_address": {
"street": "456 Oak Ave",
"city": "Springfield",
"state": "IL",
"zip_code": "62704"
},
"rewards": {
"points": 150,
"level": "Bronze"
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Feel free to copy only the
safe_get
function, the tests are not mandatory.