@@ -171,6 +171,7 @@ class Box(dict):
171171 :param box_intact_types: tuple of types to ignore converting
172172 :param box_recast: cast certain keys to a specified type
173173 :param box_dots: access nested Boxes by period separated keys in string
174+ :param box_dots_exclude: optional regular expression for dotted keys to exclude
174175 :param box_class: change what type of class sub-boxes will be created as
175176 :param box_namespace: the namespace this (possibly nested) Box lives within
176177 """
@@ -204,6 +205,7 @@ def __new__(
204205 box_intact_types : Union [Tuple , List ] = (),
205206 box_recast : Optional [Dict ] = None ,
206207 box_dots : bool = False ,
208+ box_dots_exclude : str = None ,
207209 box_class : Optional [Union [Dict , Type ["Box" ]]] = None ,
208210 box_namespace : Union [Tuple [str , ...], Literal [False ]] = (),
209211 ** kwargs : Any ,
@@ -229,6 +231,7 @@ def __new__(
229231 "box_intact_types" : tuple (box_intact_types ),
230232 "box_recast" : box_recast ,
231233 "box_dots" : box_dots ,
234+ "box_dots_exclude" : re .compile (box_dots_exclude ) if box_dots_exclude else None ,
232235 "box_class" : box_class if box_class is not None else Box ,
233236 "box_namespace" : box_namespace ,
234237 }
@@ -251,6 +254,7 @@ def __init__(
251254 box_intact_types : Union [Tuple , List ] = (),
252255 box_recast : Optional [Dict ] = None ,
253256 box_dots : bool = False ,
257+ box_dots_exclude : str = None ,
254258 box_class : Optional [Union [Dict , Type ["Box" ]]] = None ,
255259 box_namespace : Union [Tuple [str , ...], Literal [False ]] = (),
256260 ** kwargs : Any ,
@@ -272,6 +276,7 @@ def __init__(
272276 "box_intact_types" : tuple (box_intact_types ),
273277 "box_recast" : box_recast ,
274278 "box_dots" : box_dots ,
279+ "box_dots_exclude" : re .compile (box_dots_exclude ) if box_dots_exclude else None ,
275280 "box_class" : box_class if box_class is not None else self .__class__ ,
276281 "box_namespace" : box_namespace ,
277282 }
@@ -489,6 +494,12 @@ def __setstate__(self, state):
489494 self ._box_config = state ["_box_config" ]
490495 self .__dict__ .update (state )
491496
497+ def __process_dotted_key (self ,item ):
498+ if self ._box_config ["box_dots" ] and isinstance (item , str ):
499+ return ("[" in item ) or ("." in item and not (self ._box_config ["box_dots_exclude" ]
500+ and self ._box_config ["box_dots_exclude" ].match (item )))
501+ return False
502+
492503 def __get_default (self , item , attr = False ):
493504 if item in ("getdoc" , "shape" ) and _is_ipython ():
494505 return None
@@ -526,7 +537,7 @@ def __get_default(self, item, attr=False):
526537 value = default_value
527538 if self ._box_config ["default_box_create_on_get" ]:
528539 if not attr or not (item .startswith ("_" ) and item .endswith ("_" )):
529- if self ._box_config [ "box_dots" ] and isinstance ( item , str ) and ( "." in item or "[" in item ):
540+ if self .__process_dotted_key ( item ):
530541 first_item , children = _parse_box_dots (self , item , setting = True )
531542 if first_item in self .keys ():
532543 if hasattr (self [first_item ], "__setitem__" ):
@@ -602,7 +613,7 @@ def __getitem__(self, item, _ignore_default=False):
602613 for x in list (super ().keys ())[item .start : item .stop : item .step ]:
603614 new_box [x ] = self [x ]
604615 return new_box
605- if self ._box_config [ "box_dots" ] and isinstance ( item , str ) and ( "." in item or "[" in item ):
616+ if self .__process_dotted_key ( item ):
606617 try :
607618 first_item , children = _parse_box_dots (self , item )
608619 except BoxError :
@@ -652,7 +663,7 @@ def __getattr__(self, item):
652663 def __setitem__ (self , key , value ):
653664 if key != "_box_config" and self ._box_config ["frozen_box" ] and self ._box_config ["__created" ]:
654665 raise BoxError ("Box is frozen" )
655- if self ._box_config [ "box_dots" ] and isinstance ( key , str ) and ( "." in key or "[" in key ):
666+ if self .__process_dotted_key ( key ):
656667 first_item , children = _parse_box_dots (self , key , setting = True )
657668 if first_item in self .keys ():
658669 if hasattr (self [first_item ], "__setitem__" ):
@@ -696,12 +707,7 @@ def __setattr__(self, key, value):
696707 def __delitem__ (self , key ):
697708 if self ._box_config ["frozen_box" ]:
698709 raise BoxError ("Box is frozen" )
699- if (
700- key not in self .keys ()
701- and self ._box_config ["box_dots" ]
702- and isinstance (key , str )
703- and ("." in key or "[" in key )
704- ):
710+ if key not in self .keys () and self .__process_dotted_key (key ):
705711 try :
706712 first_item , children = _parse_box_dots (self , key )
707713 except BoxError :
0 commit comments