Skip to content

Commit fe05ead

Browse files
haruntuncaycdgriffith
authored andcommitted
Solves issue#59 - Treat None values as non-existing keys for default_box (cdgriffith#108)
* Solves issue#59 - Treat None values as non-existing keys for default_box
1 parent 1007c2e commit fe05ead

4 files changed

Lines changed: 26 additions & 5 deletions

File tree

AUTHORS.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Code contributions:
1818
- (jandelgado)
1919
- Jeremiah Lowin (jlowin)
2020
- (pwwang)
21+
- Harun Tuncay (haruntuncay)
2122

2223
Suggestions and bug reporting:
2324

box/box.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ class Box(dict):
147147
:param default_box: Similar to defaultdict, return a default value
148148
:param default_box_attr: Specify the default replacement.
149149
WARNING: If this is not the default 'Box', it will not be recursive
150+
:param default_box_none_transform: When using default_box, treat keys with none values as absent. True by default
150151
:param frozen_box: After creation, the box cannot be modified
151152
:param camel_killer_box: Convert CamelCase to snake_case
152153
:param conversion_box: Check for near matching keys as attributes
@@ -159,8 +160,8 @@ class Box(dict):
159160

160161
_protected_keys = dir({}) + ['to_dict', 'to_json', 'to_yaml', 'from_yaml', 'from_json', 'from_toml', 'to_toml']
161162

162-
def __new__(cls, *args: Any, box_it_up: bool = False, default_box: bool = False,
163-
default_box_attr: Any = None, frozen_box: bool = False, camel_killer_box: bool = False,
163+
def __new__(cls, *args: Any, box_it_up: bool = False, default_box: bool = False, default_box_attr: Any = None,
164+
default_box_none_transform: bool = True, frozen_box: bool = False, camel_killer_box: bool = False,
164165
conversion_box: bool = True, modify_tuples_box: bool = False, box_safe_prefix: str = 'x',
165166
box_duplicates: str = 'ignore', box_intact_types: Union[Tuple, List] = (), **kwargs: Any):
166167
"""
@@ -172,6 +173,7 @@ def __new__(cls, *args: Any, box_it_up: bool = False, default_box: bool = False,
172173
obj._box_config.update({
173174
'default_box': default_box,
174175
'default_box_attr': default_box_attr or cls.__class__,
176+
'default_box_none_transform': default_box_none_transform,
175177
'conversion_box': conversion_box,
176178
'box_safe_prefix': box_safe_prefix,
177179
'frozen_box': frozen_box,
@@ -182,15 +184,16 @@ def __new__(cls, *args: Any, box_it_up: bool = False, default_box: bool = False,
182184
})
183185
return obj
184186

185-
def __init__(self, *args: Any, box_it_up: bool = False, default_box: bool = False,
186-
default_box_attr: Any = None, frozen_box: bool = False, camel_killer_box: bool = False,
187+
def __init__(self, *args: Any, box_it_up: bool = False, default_box: bool = False, default_box_attr: Any = None,
188+
default_box_none_transform: bool = True, frozen_box: bool = False, camel_killer_box: bool = False,
187189
conversion_box: bool = True, modify_tuples_box: bool = False, box_safe_prefix: str = 'x',
188190
box_duplicates: str = 'ignore', box_intact_types: Union[Tuple, List] = (), **kwargs: Any):
189191
super(Box, self).__init__()
190192
self._box_config = _get_box_config(kwargs.pop('__box_heritage', None))
191193
self._box_config.update({
192194
'default_box': default_box,
193195
'default_box_attr': default_box_attr or self.__class__,
196+
'default_box_none_transform': default_box_none_transform,
194197
'conversion_box': conversion_box,
195198
'box_safe_prefix': box_safe_prefix,
196199
'frozen_box': frozen_box,
@@ -208,6 +211,8 @@ def __init__(self, *args: Any, box_it_up: bool = False, default_box: bool = Fals
208211
for k, v in args[0].items():
209212
if v is args[0]:
210213
v = self
214+
if v is None and self._box_config['default_box'] and self._box_config['default_box_none_transform']:
215+
continue
211216
self[k] = v
212217
elif isinstance(args[0], Iterable):
213218
for k, v in args[0]:

box/converters.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414

1515
BOX_PARAMETERS = ('default_box', 'default_box_attr', 'conversion_box',
1616
'frozen_box', 'camel_killer_box', 'box_it_up',
17-
'box_safe_prefix', 'box_duplicates', 'ordered_box')
17+
'box_safe_prefix', 'box_duplicates', 'ordered_box',
18+
'default_box_none_transform')
1819

1920

2021
def _exists(filename, create=False):

test/test_box.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,20 @@ def test_default_box(self):
384384
bx3 = Box(default_box=True, default_box_attr=3)
385385
assert bx3.hello == 3
386386

387+
# Issue#59 https://github.com/cdgriffith/Box/issues/59 "Treat None values as non existing keys for default_box"
388+
def test_default_box_none_transforms(self):
389+
bx4 = Box({"noneValue": None, "inner": {"noneInner": None}}, default_box=True, default_box_attr="issue#59")
390+
assert bx4.noneValue == "issue#59"
391+
assert bx4.inner.noneInner == "issue#59"
392+
393+
bx5 = Box({"noneValue": None, "inner": {"noneInner": None}},
394+
default_box=True,
395+
default_box_none_transform=False,
396+
default_box_attr="attr")
397+
assert bx5.noneValue is None
398+
assert bx5.absentKey == "attr"
399+
assert bx5.inner.noneInner is None
400+
387401
def test_camel_killer_box(self):
388402
td = extended_test_dict.copy()
389403
td['CamelCase'] = 'Item'

0 commit comments

Comments
 (0)