Default Box fails to handle singleton instances of itself
It appears that trying to use Default Box in a singleton pattern breaks it. See below for code and traceback
from box import Box
_test_box = None
class TestBox(Box):
def __new__(cls, *args, **kwargs):
global _test_box
if _test_box is None:
_test_box = super().__new__(cls, *args, **kwargs)
return _test_box
def __init__(self, *args, **kwargs):
super().__init__(default_box=True, *args, **kwargs)
self.__test_method()
def __test_method(self):
_this_should_not_fail = self.env
TestBox()
Traceback is as follows
Traceback (most recent call last):
File "/home/miversen/git-local/transcoder/venv/lib/python3.8/site-packages/box/box.py", line 488, in __getitem__
return super().__getitem__(item)
KeyError: 'env'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/miversen/git-local/transcoder/venv/lib/python3.8/site-packages/box/box.py", line 514, in __getattr__
value = self.__getitem__(item, _ignore_default=True)
File "/home/miversen/git-local/transcoder/venv/lib/python3.8/site-packages/box/box.py", line 509, in __getitem__
raise BoxKeyError(str(err)) from _exception_cause(err)
box.exceptions.BoxKeyError: "'env'"
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/miversen/git-local/transcoder/venv/lib/python3.8/site-packages/box/box.py", line 516, in __getattr__
value = object.__getattribute__(self, item)
AttributeError: 'TestBox' object has no attribute 'env'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "test.py", line 19, in <module>
c1 = TestBox()
File "test.py", line 14, in __init__
self.__test_method()
File "test.py", line 17, in __test_method
_this_should_not_fail = self.env
File "/home/miversen/git-local/transcoder/venv/lib/python3.8/site-packages/box/box.py", line 529, in __getattr__
return self.__get_default(item, attr=True)
File "/home/miversen/git-local/transcoder/venv/lib/python3.8/site-packages/box/box.py", line 426, in __get_default
value = self._box_config["box_class"](**self.__box_config())
File "test.py", line 13, in __init__
super().__init__(default_box=True, *args, **kwargs)
KeyError: 'default_box'
Environment Details
- Python3: Python 3.8.10
- OS: Ubuntu 20.04.3 LTS
- Kernel: 5.11.0-27-generic
I spent some time to look into this more, and there are just some nuances that need addressed to get it working.
default_box will replace missing objects with new Box objects, specifically newly created classes of the current Box class (in this case TestBox) if not specified otherwise with default_box_attr.
Just changing that will still result in dict objects being added later being a recursion of itself. Because the missing objects will be replaced with a new instance of TestBox, which in the new __new__ returns itself.
(Not tested in depth but working for simple examples)
from box import Box
_test_box = None
class TestBox(Box):
def __new__(cls, *args, **kwargs):
global _test_box
if _test_box is None:
kwargs['default_box'] = True
kwargs['default_box_attr'] = Box
kwargs['box_class'] = Box
_test_box = super().__new__(cls, *args, **kwargs)
return _test_box
def __init__(self, *args, **kwargs):
kwargs['default_box'] = True
kwargs['default_box_attr'] = Box
kwargs['box_class'] = Box
super().__init__(*args, **kwargs)
self.test_method()
def test_method(self):
_this_should_not_fail = self.env
I think this is good to document on the wiki as it did take a bit to fully understand it. Will keep this open until that documenting is complete