Skip to content

Commit

Permalink
* Adding #169 default functions with the box_instance and key paramet…
Browse files Browse the repository at this point in the history
…er (thanks to Коптев Роман Викторович)
  • Loading branch information
cdgriffith committed Jan 28, 2023
1 parent 39da630 commit 9a790f7
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 4 deletions.
2 changes: 2 additions & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,5 @@ Suggestions and bug reporting:
- aviveh21
- Nishikant Parmar (nishikantparmariam)
- Peter B (barmettl)
- Ash A. (dragonpaw)
- Коптев Роман Викторович (romikforest)
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Changelog
Version 7.0.0
-------------

* Adding #169 default functions with the box_instance and key parameter (thanks to Коптев Роман Викторович)
* Adding #170 Be able to initialize with a flattened dict - by using DDBox (thanks to Ash A.)
* Adding #192 box_dots treats all keys with periods in them as separate keys (thanks to Rexbard)
* Adding #211 support for properties and setters in subclasses (thanks to Serge Lu and David Aronchick)
* Adding #226 namespace to track changes to the box (thanks to Jacob Hayes)
Expand Down
26 changes: 23 additions & 3 deletions box/box.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from keyword import iskeyword
from os import PathLike
from typing import Any, Dict, Generator, List, Optional, Tuple, Type, Union
from inspect import signature

try:
from typing import Callable, Iterable, Mapping
Expand Down Expand Up @@ -482,6 +483,8 @@ def __setstate__(self, state):
self.__dict__.update(state)

def __get_default(self, item, attr=False):
if ipython and item in ("getdoc", "shape"):
return None
default_value = self._box_config["default_box_attr"]
if default_value in (self._box_config["box_class"], dict):
value = self._box_config["box_class"](**self.__box_config(extra_namespace=item))
Expand All @@ -490,15 +493,32 @@ def __get_default(self, item, attr=False):
elif isinstance(default_value, list):
value = box.BoxList(**self.__box_config(extra_namespace=item))
elif isinstance(default_value, Callable):
value = default_value()
args = []
kwargs = {}
p_sigs = [
p.name
for p in signature(default_value).parameters.values()
if p.kind in (p.POSITIONAL_ONLY, p.POSITIONAL_OR_KEYWORD)
]
k_sigs = [p.name for p in signature(default_value).parameters.values() if p.kind is p.KEYWORD_ONLY]
for name in p_sigs:
if name not in ("key", "box_instance"):
raise BoxError("default_box_attr can only have the arguments 'key' and 'box_instance'")
if "key" in p_sigs:
args.append(item)
if "box_instance" in p_sigs:
args.insert(p_sigs.index("box_instance"), self)
if "key" in k_sigs:
kwargs["key"] = item
if "box_instance" in k_sigs:
kwargs["box_instance"] = self
value = default_value(*args, **kwargs)
elif hasattr(default_value, "copy"):
value = default_value.copy()
else:
value = default_value
if self._box_config["default_box_create_on_get"]:
if not attr or not (item.startswith("_") and item.endswith("_")):
if ipython and item in ("getdoc", "shape"):
return value
if self._box_config["box_dots"] and isinstance(item, str) and ("." in item or "[" in item):
first_item, children = _parse_box_dots(self, item, setting=True)
if first_item in self.keys():
Expand Down
10 changes: 9 additions & 1 deletion test/test_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import pytest
from ruamel.yaml import YAML

from box import Box, BoxError, BoxKeyError, BoxList, ConfigBox, SBox
from box import Box, BoxError, BoxKeyError, BoxList, ConfigBox, SBox, DDBox
from box.box import _get_dot_paths, _camel_killer, _recursive_tuples # type: ignore
from box.converters import BOX_PARAMETERS

Expand Down Expand Up @@ -1434,3 +1434,11 @@ def test_union_frozen_box(self):

assert my_box | {"a": 1} == {"a": 1}
assert {"a": 1} | my_box == {"a": 5}

def test_default_box_callable(self):
def func(box_instance, key):
return DDBox(bi=str(box_instance), key=key)

my_box = DDBox(default_box_attr=func)

assert my_box.a == {"bi": "{}", "key": "a"}

0 comments on commit 9a790f7

Please sign in to comment.