cdgriffith / box Goto Github PK
View Code? Open in Web Editor NEWPython dictionaries with advanced dot notation access
Home Page: https://github.com/cdgriffith/Box/wiki
License: MIT License
Python dictionaries with advanced dot notation access
Home Page: https://github.com/cdgriffith/Box/wiki
License: MIT License
>>> b = box.Box()
>>> b.o.x = 42
AttributeError: 'Box' object has no attribute 'o'
IMHO this should work, possibly via a subtype (AutoBox
) or __init__
keyword arg (auto_attr=True
).
Another variant would be OpenBox
combined with a close()
method โ i.e. allow path construction initially, but then switch to default mode (AttributeError
) after construction.
from box import Box
bx = Box({'a': {'b': {'c': 1}}})
print(bx.a.b.c)
#out 1
but like this :
path_ = 'a.b.c'
print(bx.path_)
# error
thanks
are you aware of my "sanest" project which solves similar problems in what i consider a "sane" way? :)
https://sanest.readthedocs.io/
it is very complete and very thoroughly tested. you may find it inspiring. ;)
would be nice to use this in django templates. it doesnt resolve correctly when iterating over items.
Just like OrderedDict
, maybe we can have something like OrderedBox
.
Then we should be able to have something like this:
from box import OrderedBox
ob = OrderedBox()
ob.c = 1
ob.b = 2
ob.a = 3
assert ob.keys() != ['a', 'b', 'c']
assert ob.keys() == ['c', 'b', 'a']
The following code should print [['foo']]
but instead prints [[...]]
showing that a auto-referential list has been created.
from box import Box
def bl():
b = Box()
bl = b.setdefault("l", [])
bl.append(["foo"])
print(bl)
Issue lies on https://github.com/cdgriffith/Box/blob/master/box.py#L771 (or in the append function at https://github.com/cdgriffith/Box/blob/master/box.py#L802) that detects self referential entities.
The id(iterable)
is not robust enought as two object may have the same id if the first object has been garbage collected.
One can see this in the example below
def bl():
b = Box()
l1 = []
bl = b.setdefault("l", l1)
del l1 # comment this to see that now it works as l1 is not garbage collected
bl.append(["foo"])
print(bl)
A fix could be to replace https://github.com/cdgriffith/Box/blob/master/box.py#L771 by
self.box_org_ref = id(iterable) if iterable else 0
Some odd people like to put multiple JSON objects into a file, one per line. Possibly make a 'multiline' kwarg for to_json
under BoxList
to accommodate?
Enabling default_box make a Box undumpable
from pickle import dumps
from box import Box
dumps(Box()) # => OK
dumps(Box(default_box=True)) # => raise exception below
Traceback (most recent call last):
File "...", line 1, in <module>
dumps(Box(default_box=True))
TypeError: 'Box' object is not callable
I specifically ran into this issue when calling to_dict on a Box instance with an integer key. A TypeError is raised on this line:
Line 524 in 37ad926
TypeError: attribute name must be string, not 'int'
The following works for an integer key:
self[key] = value
so the change may be as simple as adding TypeError to line 525:
Line 525 in 37ad926
This was not an issue in 2.0 and most likely later versions, although it may not have been intentionally supported:
Line 71 in 8340399
Per Python docs, __hash__
should have this property:
The only required property is that objects which compare equal have the same hash value;
However:
def f():
return Box(data={'Python': 'Rocks', 'inferior': ['java', 'cobol']}, frozen_box=True)
b1 = f()
b2 = f()
print(b1 == b2)
print(hash(b1), hash(b2))
print(set([b1, b2]))
yields:
True
-7253217714821940563 1337995706200090896
{<Box: {'data': {'Python': 'Rocks', 'inferior': ('java', 'cobol')}}>, <Box: {'data': {'Python': 'Rocks', 'inferior': ('java', 'cobol')}}>}
This also makes is unusable for using in set
s, as seen in the above message.
The reason for this looks to be due to __hash__ having a random uuid4
included in the calculation:
hashing = hash(uuid4().hex)
Is there a reason for this line?
Hi @cdgriffith,
I am the one who proposed ordered_box
.
I was recently bothered by Box
turning some objects that are inherited from dict/list
into Box/BoxList
.
Consider the following codes:
from box import Box
class MyList(list):
def sq(self):
return [x*x for x in self]
b = Box(a = MyList([1,2,4]))
b.a.sq()
# AttributeError: 'BoxList' object has no attribute 'sq'
# Expect [1,4,16]
I extended list
to add some extra methods for convenience. However, when it was changed by Box
into a BoxList
, then I lost my method sq
.
Is it possible to keep the objects of extended dict/list
intact, but convert the raw dict/list
s?
One simple way is to check the __bases__
, if it is (object, )
then it should be a raw dict/list
:
def _israwinstance(obj, klass):
return isinstance(obj, klass) and obj.__class__.__bases__ \
and obj.__class__.__bases__[0] is object
Thanks,
Panwen
Thank you for your software!
Is there a way to receive a None
, or a default value, rather than a BoxKeyError
like we could in: get(key, default)
if the key is not found in the Box
?
When accessing a key which does not exist, an AttributeError will be raised. This sends the wrong message (in my opinion), implying that the user is attempting to access an attribute on the Box
object which does not exist. When the true behavior is just syntactic sugar over a dict object, a KeyError seems more appropriate. I get that this is a philosophical opinion, so I could certainly be convinced that AttributeError is appropriate.
Repro in python repl:
Python 2.7.10 (default, Aug 22 2015, 20:33:39)
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from box import Box
>>>
>>> b = Box({})
>>> b.attr
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/jiso/projects/jwplayer-python/venv/lib/python2.7/site-packages/box.py", line 385, in __getattr__
raise err
AttributeError: 'Box' object has no attribute 'attr'
Actual: in above example, AttributeError is raised
Expected: KeyError is raised
Idea generated from #15. Add a property to Box (and potentially BoxList if necessary) box_heritage.
Potential usage:
bx.a.b.box_heritage
# ['a', 'b']
Oversimplifying the whole library would imply JSON -> PyDict with dot notation
is what is being done here. As parsing JSON can be very slow, I am very much interested in some perf numbers as to how it scales. Can you please add these metrics in README ?
Box doesn't warn when data is shadowed
>>> d = {'to': '[email protected]', 'from': '[email protected]', 'subject': 'bugs'}
>>> b = Box(d)
>>> b.to
'[email protected]'
>>> b.from
SyntaxError: invalid syntax
>>> b.xfrom
'[email protected]'
>>> Box({'to': 'chris', 'from': 'wim', 'subject': 'bugs', 'xfrom': 'wat'}).xfrom
'wat'
OS:
Windows 7
Sublime 2.0
Works in terminal.
Low priority.
See gif. When running under default python build env runs without error but not of the links show up.
from box import Box
class Job(object):
"""Configure the job object via runner."""
def __init__(self, **kwargs):
self.job = Box(kwargs)
job = {
'uuid': None,
'started' : None,
'force_stop': None,
'created_by ': None,
'created_date': None,
'status': 'Building',
'last_saved': None,
'debug': True,
'human_name': "Refusa Record Extractor - Restaurant SIC Codes",
'record_tag': 'refusa-restaurant',
'error_count': 0,
'last_error': None,
'extraction_last_event': None,
'extraction_average_time': 0,
'extraction_completion_time': 0, #Seconds
'extraction_total_records': 0,
'extraction_duplicate_records': None,
'search_total_records_found': None,
'page_current' : None,
'page_last' : None,
'driver' : None,
'pending_tasks':[],
'task': {
'inputs': {
'load_page': { 'url': "https://google.com" },
'screenshot_as_base64' : { 'file_name' : None, }
},
'outputs': {
'generate_refind_key' : None,
'add_tag_record': None,
'save_record': None,
'refind_record': None,
}
},
'completed_tasks':[],
}
tjob = Job(**job)
tjob.job.status
tjob.job.error_count
tjob.job.task.inputs.load_page.url
tjob.job.human_name
tjob.job.task.inputs.screenshot_as_base64.file_name
odd anomaly seen once, needs more testing passing through queues.
Hi,
thanks a lot for your project, it's currently helping me a lot :)
one feature i missed is the ability to treat None values as if there was no key defined.
like in this example.
b=Box({'brol': None}, default_box=True)
b.brol.truc
gives
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'NoneType' object has no attribute 'truc'
but this works as exepected
b=Box({}, default_box=True)
b.brol.truc
many many thanks ๐
Test case:
>>> import box
>>> box.__version__
'3.1.0'
>>> b = box.Box()
>>> b.foo = {}
>>> b.values()
dict_values([{}])
>>> b.foo # Force conversion by accessing the key
<Box: {}>
>>> b.values()
dict_values([<Box: {}>])
Is this intentional?
d = dict.fromkeys([1,2,3,4],0)
d = box.Box(d)
print(type(d))
print(type(d.copy()))
---------------------------
>> <class 'box.Box'>
>> <class 'dict'>
On Python 2.7 there is no collections.abc module, instead the collection base types are available directly in the collections module
The current code says:
try:
from collections.abc import Mapping, Iterable
except ImportError:
Mapping = dict
Iterable = (tuple, list)
I recommend putting from collections import Iterable, Mapping as the except case, possibly still including the fallback for even earlier Pythons but possibly not as this totally changes how the library works rather unexpectedly. At the very least, a warning should happen when this fallback mode is used.
When I run this:
import box
import sys
print(sys.version)
print(box.__version__)
data = {
"fine": ['this', 'that', 'the other'],
"items": ['this', 'that', 'the other'],
"keys": ['this', 'that', 'the other'],
}
print("# working ########################################")
a = box.Box(data)
print(a.fine)
print(a.fine[1])
print("# broken ########################################")
print(a.items)
print(a.items[1])
print("# also broken but we don't get this far ########################################")
print(a.keys)
print(a.keys[1])
I get this:
3.7.1 (default, Nov 28 2018, 11:51:54)
[Clang 10.0.0 (clang-1000.11.45.5)]
3.2.3
# working ########################################
['this', 'that', 'the other']
that
# broken ########################################
<bound method Box.items of <Box: {'fine': ['this', 'that', 'the other'], 'items': ['this', 'that', 'the other'], 'keys': ['this', 'that', 'the other']}>>
Traceback (most recent call last):
File "parse_json_with_box.py", line 20, in <module>
print(a.items[1])
TypeError: 'method' object is not subscriptable
This seems related to #62 but is still a problem is version 3.2.3.
It would be beneficial to be able to access things that have spaces in them via dot notation, by assuming that spaces are underscores.
b = Box({"my dict": 1})
b.my_dict
This will require changes in both get and dir
.
Also consider adding auto snake_case to stuff that is camelCase, and potentially lower case for anything as well.
BoxList(args, frozen_box=True)
should return a BoxList
which will raise BoxError: BoxList is frozen
on item modification such as:
box_list[0] = "new_value"
box_list += [1,2]
box_list.append("new")
box_list.extend([1,2,3])
box_list.insert(..)
box_list.pop(..)
box_list.remove(..)
box_list.reverse(..)
box_list.sort(..)
I know that this can be sorted out using tuple(BoxList(args, frozen_box=True))
but would like BoxList
behavior to be consistent with Box
behavior when frozen_box
is True
When using Box
to represent a configuration hierarchy, it would be great to be able to have a LayeredBox
virtually merging multiple Box
es in layers. That way, Box
es containing content from say a system wide, user specific and directory specific configuration could be combined while retaining the ability to write back changes using a round-trip yaml parser to preserve formatting and comments in the config provided by the user.
The LayeredBox
would probably require some flags defining the way merging is done recursively. Should dictionaries at all levels be combined, or should they be overwritten? Should lists be overwritten or merged by appending? Typically, you'd want dictionaries to be merged and lists to be overwritten I think. It's a semantic decision though.
pickle.dump(Box({}), open('/tmp/test.p', 'wb'))
test = pickle.load(open('/tmp/test.p', 'rb'))
caused endless KeyError: '_box_config'
and then RecursionError: maximum recursion depth exceeded while calling a Python object
python version: 3.6
Line 450 in ae49ac2
DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
elif isinstance(default_value, collections.Callable):
Hey!
I've recently started using your awesome package and it has been a delight. Unfortunately, I encountered couple of issues with the camel_killer_box
option:
I've tested it on Python 2 and 3.
Copy-pasteable reproduction code:
from box import Box
b = Box({
'SomeKey': 999
}, camel_killer_box=True, conversion_box=True)
assert b.some_key == 999 # Cool, works as expected
assert b.SomeKey == 999 # Should not work because of conversion_box=True!
# When using default_box=True, things get even worse:
b = Box({
'SomeKey': 999
}, camel_killer_box=True, conversion_box=True, default_box=True)
assert b.SomeKey == 999 # Same issue
assert b.some_key == Box({}) # Oh man, the default_box is prioritized over camel_killer_box...
I'm working on a fix for this, but I'll be happy for input.
For default box, any key membership test returns True
even when they are not in box while has_key
correctly returns False.
>>> box = Box(default_box=True)
>>> print('a' in box)
True
>>> print(box.has_key('a'))
False
In [1]: a=box.Box()
In [2]: a.update({'items': 0})
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-7-ccd724442f09> in <module>()
----> 1 a.update({'items': 0})
~/.pyenv/versions/3.6.6/bin/box.py in update(self, item, **kwargs)
669 v = BoxList(v)
670 try:
--> 671 self.__setattr__(k, v)
672 except TypeError:
673 self.__setitem__(k, v)
~/.pyenv/versions/3.6.6/bin/box.py in __setattr__(self, key, value)
540 raise BoxError('Box is frozen')
541 if key in self._protected_keys:
--> 542 raise AttributeError("Key name '{0}' is protected".format(key))
543 if key == '_box_config':
544 return object.__setattr__(self, key, value)
AttributeError: Key name 'items' is protected
this breaks the original method of dict, and box can have such protected keys, just without dot accessing
because i use defaultbox only so i temporarily hotfix this line:
Line 671 in ae49ac2
to:
self[k] = v
I'd look at this as almost a way to subclass Box and specify fields to recast. For example:
class Example(Box):
_recast_params = {
'id': int,
'settings.group.id': int
}
Currently using x
as a prepend to conflicting keys to convert to attributes. May be confusing as pointed out by https://news.ycombinator.com/user?id=askvictor .
Thought up by @gaojiuli
Going to have to think if it should be list of lists format, aka would be a property of BoxList, or rather a dictionary of keys would be the column names and lists of the data in the column. (or heck, support both methods, depending on which it is invoked on).
Box 3.2.0, Python 3.6.2, Windows 7
For versions of ruamel.yaml since 0.15, the following files:
== 1.yaml ==
greeting: Hello!
date: 2018-01-01
== 1.py ==
import box
info = box.Box.from_yaml(filename='1.yaml')
print(info)
Generates the following output (paths edited):
<path>\box.py:112: UnsafeLoaderWarning
The default 'Loader' for 'load(stream)' without further arguments can be unsafe.
Use 'load(stream, Loader=ruamel.yaml.Loader)' explicitly if that is OK.
Alternatively include the following in your code:
import warnings
warnings.simplefilter('ignore', ruamel.yaml.error.UnsafeLoaderWarning)
In most other cases you should consider using 'safe_load(stream)'
data = yaml.load(f, **kwargs)
{'greeting': 'Hello!', 'date': datetime.date(2018, 1, 1)}
Can't believe I missed this. Need to override .get
from box import Box
class test(object):
def __init__(self, **kwargs):
d = Box({
'item':'value',
'item2': self.ok
})
self.d = d
def ok(self, **kwargs):
print "test"
print kwargs.keys()
for key in kwargs.keys():
print kwargs[key]
return
data={
'test':'value',
'test1':'value1'
}
t = test()
print t.d.item2(**data)
From t.d.item2 how do I get that path while in the 'ok' function.
Thanks!
Would you consider to support dotted string as a access key to sub boxes:
b=Box(dotted=True)
b.c.d.e = 10
b['c.d.e'] == b.c.d.e
That is, to have an equivalence between those two notations.
If positive, then also to support some corresponding functions for traversing (for example):
b = Box(dotted=True)
b.c = dict(e=1, f=2)
b.a = dict(b=3, c=4)
[*Box.keys(dotted=True)]
# ['c.d.e', 'c.d.f', 'a.b', 'a.c']
Thanks!
Hello,
Box is a nice little Dictionary Helper hich is working just fine most of the time. Currently i'm seeing a problem with the pop-method... When popping a non-exitent key with default_box=True i get a KeyError:
File "/mnt/d/projekte/python/envs/depmgmt/bin/box.py", line 592, in pop
del self[key]
File "/mnt/d/projekte/python/envs/depmgmt/bin/box.py", line 560, in delitem
super(Box, self).delitem(key)
Is this working as intended ? I would expect to get the given default value when the key is not existant ?
Thank you...
a = Box(ordered_box=True)
list(a)
[]
a.test = {}
a.test.aee = {}
list(a.test.aee)
['ordered_box_values']
Hi @cdgriffith,
Really appreciated that you acted so quick to add box_intact_types
.
Now I have another issue when I was trying to extend Box
class with some more features, personally:
from box import Box
class Box2(Box):
# My other methods go here ...
b = Box2(a=1)
b # <Box: {'a': 1}>
Everything worked fine, however, when I tried to copy the box:
c = b.copy()
I have got an exception:
---------------------------------------------------------------------------
RecursionError Traceback (most recent call last)
<ipython-input-4-f0d8dd24f807> in <module>
5
6 b = Box2(a=1)
----> 7 b.copy()
/.../lib/python3.7/site-packages/box.py in copy(self)
402
403 def copy(self):
--> 404 return self.__class__(super(self.__class__, self).copy())
405
406 def __copy__(self):
... last 1 frames repeated, from the frame below ...
/.../lib/python3.7/site-packages/box.py in copy(self)
402
403 def copy(self):
--> 404 return self.__class__(super(self.__class__, self).copy())
405
406 def __copy__(self):
RecursionError: maximum recursion depth exceeded while calling a Python object
Then I modified the source code of copy
:
def copy(self):
return Box(super(Box, self).copy())
every thing worked fine.
Maybe consider using a solid class name instead of self.__class__
?
Or I have to copy the copy
method to each subclass of Box
.
"box it up" doesn't handle recursion gracefully
>>> from box import Box
>>> d = {}
>>> d['d'] = d
>>> b = Box(d)
>>> b.box_it_up()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/wglenn/.virtualenvs/scratch/lib/python3.6/site-packages/box.py", line 251, in box_it_up
self[k].box_it_up()
File "/home/wglenn/.virtualenvs/scratch/lib/python3.6/site-packages/box.py", line 251, in box_it_up
self[k].box_it_up()
File "/home/wglenn/.virtualenvs/scratch/lib/python3.6/site-packages/box.py", line 251, in box_it_up
self[k].box_it_up()
[Previous line repeated 978 more times]
File "/home/wglenn/.virtualenvs/scratch/lib/python3.6/site-packages/box.py", line 250, in box_it_up
if hasattr(self[k], 'box_it_up'):
File "/home/wglenn/.virtualenvs/scratch/lib/python3.6/site-packages/box.py", line 317, in __getitem__
return self.__convert_and_store(item, value)
File "/home/wglenn/.virtualenvs/scratch/lib/python3.6/site-packages/box.py", line 332, in __convert_and_store
**self.__box_config())
File "/home/wglenn/.virtualenvs/scratch/lib/python3.6/site-packages/box.py", line 218, in __init__
if isinstance(args[0], Mapping):
File "/home/wglenn/.virtualenvs/scratch/lib64/python3.6/abc.py", line 182, in __instancecheck__
if subclass in cls._abc_cache:
File "/home/wglenn/.virtualenvs/scratch/lib64/python3.6/_weakrefset.py", line 75, in __contains__
return wr in self.data
RecursionError: maximum recursion depth exceeded in comparison
Now b.to_dict()
is broken.
It also mangles the string representation afterwards :
>>> print(b)
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d'
: {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d':
{'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'
d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {'d': {...}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
As discovered with #20
Issue can be duplicated with:
troll_dict = {}
troll_dict ['e'] = {'g': troll_dict}
bx = Box(troll_dict)
bx.e.g
<Box: {'e': {'g': {'e': {'g': {'e': {'g': {'e': {'g': {'e': {'g': {'e': ..................
Also would include way to detect and raise custom BoxError
instead of related recursive built in error for functions like box_it_up
.
Other libraries such as addict
and DotMap
cannot handle even first level circular references, so may not be an easy or worthwhile thing to address.
Only current thought on how to address it would be to keep list of references to original objects that were converted, and upon creation of sub-boxes check all other previously generated ones to see if any have that link already and re-reference the newly created sub-box. The issue is that this would be very memory and process heavy for a rare issue check.
Use
box.json
box.yaml
box.csv
@property
def json(self):
return {}
but not
box.to_json()
box.to_yaml()
box.to_csv()
which is more pythonic. And the arguments "filename" of to_yaml() could be instead by a method "save()". What do you think?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.