Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default Box fails to handle singleton instances of itself #210

Open
miversen33 opened this issue Sep 8, 2021 · 1 comment
Open

Default Box fails to handle singleton instances of itself #210

miversen33 opened this issue Sep 8, 2021 · 1 comment

Comments

@miversen33
Copy link

miversen33 commented Sep 8, 2021

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
@cdgriffith cdgriffith changed the title Box fails to handle singleton instances of itself Default Box fails to handle singleton instances of itself Feb 11, 2022
@cdgriffith
Copy link
Owner

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants