cdgriffith / Box

Python dictionaries with advanced dot notation access

Home Page:https://github.com/cdgriffith/Box/wiki

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Default Box fails to handle singleton instances of itself

miversen33 opened this issue · comments

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