3131Undefined = object ()
3232
3333
34- # back-port of fullmatch from Py3.4+
35- def fullmatch (regex , string , flags = 0 ):
36- """Emulate python-3.4 re.fullmatch()."""
37- if 'pattern' in dir (regex ):
38- regex_string = regex .pattern
39- else :
40- regex_string = regex
41- return re .match ("(?:" + regex_string + r")\Z" , string , flags = flags )
42-
43-
4434class BaseFigure (object ):
4535 """
4636 Base class for all figure types (both widget and non-widget)
4737 """
38+ _bracket_re = re .compile ('^(.*)\[(\d+)\]$' )
4839
4940 # Constructor
5041 # -----------
@@ -143,7 +134,7 @@ class is a subclass of both BaseFigure and widgets.DOMWidget.
143134 self ._data_defaults = [{} for _ in data ]
144135
145136 # ### Reparent trace objects ###
146- for trace in data :
137+ for trace_ind , trace in enumerate ( data ) :
147138 # By setting the trace's parent to be this figure, we tell the
148139 # trace object to use the figure's _data and _data_defaults
149140 # dicts to get/set it's properties, rather than using the trace
@@ -153,6 +144,9 @@ class is a subclass of both BaseFigure and widgets.DOMWidget.
153144 # We clear the orphan props since the trace no longer needs then
154145 trace ._orphan_props .clear ()
155146
147+ # Set trace index
148+ trace ._trace_ind = trace_ind
149+
156150 # Layout
157151 # ------
158152 # ### Construct layout validator ###
@@ -463,6 +457,7 @@ def data(self, new_data):
463457 old_trace = self .data [i ]
464458 old_trace ._orphan_props .update (deepcopy (old_trace ._props ))
465459 old_trace ._parent = None
460+ old_trace ._trace_ind = None
466461
467462 # ### Compute trace props / defaults after removal ###
468463 traces_props_post_removal = [t for t in self ._data ]
@@ -527,6 +522,10 @@ def data(self, new_data):
527522 # Update trace objects tuple
528523 self ._data_objs = list (new_data )
529524
525+ # Update trace indexes
526+ for trace_ind , trace in enumerate (self ._data_objs ):
527+ trace ._trace_ind = trace_ind
528+
530529 # Restyle
531530 # -------
532531 def plotly_restyle (self , restyle_data , trace_indexes = None , ** kwargs ):
@@ -690,7 +689,7 @@ def _restyle_child(self, child, key_path_str, val):
690689
691690 # Compute trace index
692691 # -------------------
693- trace_index = BaseFigure . _index_is ( self . data , child )
692+ trace_index = child . _trace_ind
694693
695694 # Not in batch mode
696695 # -----------------
@@ -743,7 +742,12 @@ def _str_to_dict_path(key_path_str):
743742 -------
744743 tuple[str | int]
745744 """
746- if isinstance (key_path_str , tuple ):
745+ if isinstance (key_path_str , string_types ) and \
746+ '.' not in key_path_str and \
747+ '[' not in key_path_str :
748+ # Fast path for common case that avoids regular expressions
749+ return (key_path_str ,)
750+ elif isinstance (key_path_str , tuple ):
747751 # Nothing to do
748752 return key_path_str
749753 else :
@@ -752,11 +756,9 @@ def _str_to_dict_path(key_path_str):
752756
753757 # Split out bracket indexes.
754758 # e.g. ['foo', 'bar[1]'] -> ['foo', 'bar', '1']
755- bracket_re = re .compile ('(.*)\[(\d+)\]' )
756759 key_path2 = []
757760 for key in key_path :
758- match = fullmatch (bracket_re , key )
759- #match = bracket_re.fullmatch(key)
761+ match = BaseFigure ._bracket_re .match (key )
760762 if match :
761763 key_path2 .extend (match .groups ())
762764 else :
@@ -1065,6 +1067,10 @@ def add_traces(self, data, rows=None, cols=None):
10651067 # Validate traces
10661068 data = self ._data_validator .validate_coerce (data )
10671069
1070+ # Set trace indexes
1071+ for ind , new_trace in enumerate (data ):
1072+ new_trace ._trace_ind = ind + len (self .data )
1073+
10681074 # Validate rows / cols
10691075 n = len (data )
10701076 BaseFigure ._validate_rows_cols ('rows' , n , rows )
@@ -1212,14 +1218,9 @@ def _get_child_props(self, child):
12121218 """
12131219 # Try to find index of child as a trace
12141220 # -------------------------------------
1215- try :
1216- trace_index = BaseFigure ._index_is (self .data , child )
1217- except ValueError as _ :
1218- trace_index = None
1219-
1220- # Child is a trace
1221- # ----------------
1222- if trace_index is not None :
1221+ if isinstance (child , BaseTraceType ):
1222+ # ### Child is a trace ###
1223+ trace_index = child ._trace_ind
12231224 return self ._data [trace_index ]
12241225
12251226 # Child is the layout
@@ -1247,16 +1248,10 @@ def _get_child_prop_defaults(self, child):
12471248 -------
12481249 dict
12491250 """
1250- # Try to find index of child as a trace
1251- # -------------------------------------
1252- try :
1253- trace_index = BaseFigure ._index_is (self .data , child )
1254- except ValueError as _ :
1255- trace_index = None
1256-
12571251 # Child is a trace
12581252 # ----------------
1259- if trace_index is not None :
1253+ if isinstance (child , BaseTraceType ):
1254+ trace_index = child ._trace_ind
12601255 return self ._data_defaults [trace_index ]
12611256
12621257 # Child is the layout
@@ -3365,7 +3360,7 @@ class BaseLayoutType(BaseLayoutHierarchyType):
33653360 'polar' ]
33663361
33673362 _subplotid_prop_re = re .compile (
3368- '(' + '|' .join (_subplotid_prop_names ) + ')(\d+)' )
3363+ '^ (' + '|' .join (_subplotid_prop_names ) + ')(\d+)$ ' )
33693364
33703365 @property
33713366 def _subplotid_validators (self ):
@@ -3429,16 +3424,14 @@ def _process_kwargs(self, **kwargs):
34293424 unknown_kwargs = {
34303425 k : v
34313426 for k , v in kwargs .items ()
3432- if not fullmatch (self ._subplotid_prop_re , k )
3433- # if not self._subplotid_prop_re.fullmatch(k)
3427+ if not self ._subplotid_prop_re .match (k )
34343428 }
34353429 super (BaseLayoutHierarchyType , self )._process_kwargs (** unknown_kwargs )
34363430
34373431 subplot_kwargs = {
34383432 k : v
34393433 for k , v in kwargs .items ()
3440- if fullmatch (self ._subplotid_prop_re , k )
3441- #if self._subplotid_prop_re.fullmatch(k)
3434+ if self ._subplotid_prop_re .match (k )
34423435 }
34433436
34443437 for prop , value in subplot_kwargs .items ():
@@ -3458,8 +3451,7 @@ def _set_subplotid_prop(self, prop, value):
34583451 # Get regular expression match
34593452 # ----------------------------
34603453 # Note: we already tested that match exists in the constructor
3461- # match = self._subplotid_prop_re.fullmatch(prop)
3462- match = fullmatch (self ._subplotid_prop_re , prop )
3454+ match = self ._subplotid_prop_re .match (prop )
34633455 subplot_prop = match .group (1 )
34643456 suffix_digit = int (match .group (2 ))
34653457
@@ -3520,7 +3512,7 @@ def _strip_subplot_suffix_of_1(self, prop):
35203512 # Handle subplot suffix digit of 1
35213513 # --------------------------------
35223514 # Remove digit of 1 from subplot id (e.g.. xaxis1 -> xaxis)
3523- match = fullmatch ( self ._subplotid_prop_re , prop )
3515+ match = self ._subplotid_prop_re . match ( prop )
35243516
35253517 if match :
35263518 subplot_prop = match .group (1 )
@@ -3580,7 +3572,7 @@ def __setitem__(self, prop, value):
35803572
35813573 # Check for subplot assignment
35823574 # ----------------------------
3583- match = fullmatch ( self ._subplotid_prop_re , prop )
3575+ match = self ._subplotid_prop_re . match ( prop )
35843576 if match is None :
35853577 # Set as ordinary property
35863578 super (BaseLayoutHierarchyType , self ).__setitem__ (prop , value )
@@ -3594,8 +3586,7 @@ def __setattr__(self, prop, value):
35943586 """
35953587 # Check for subplot assignment
35963588 # ----------------------------
3597- # match = self._subplotid_prop_re.fullmatch(prop)
3598- match = fullmatch (self ._subplotid_prop_re , prop )
3589+ match = self ._subplotid_prop_re .match (prop )
35993590 if match is None :
36003591 # Set as ordinary property
36013592 super (BaseLayoutHierarchyType , self ).__setattr__ (prop , value )
@@ -3649,6 +3640,7 @@ class BaseTraceHierarchyType(BasePlotlyType):
36493640
36503641 def __init__ (self , plotly_name , ** kwargs ):
36513642 super (BaseTraceHierarchyType , self ).__init__ (plotly_name , ** kwargs )
3643+
36523644 def _send_prop_set (self , prop_path_str , val ):
36533645 if self .parent :
36543646 # ### Inform parent of restyle operation ###
@@ -3680,6 +3672,9 @@ def __init__(self, plotly_name, **kwargs):
36803672 # ### Callbacks to be called on selection ###
36813673 self ._select_callbacks = []
36823674
3675+ # ### Trace index in figure ###
3676+ self ._trace_ind = None
3677+
36833678 # uid
36843679 # ---
36853680 # All trace types must have a top-level UID
@@ -3951,6 +3946,11 @@ def _send_prop_set(self, prop_path_str, val):
39513946 # propagated to parents
39523947 pass
39533948
3949+ def _restyle_child (self , child , key_path_str , val ):
3950+ # Note: Frames are not supported by FigureWidget, and updates are not
3951+ # propagated to parents
3952+ pass
3953+
39543954 def on_change (self , callback , * args ):
39553955 raise NotImplementedError (
39563956 'Change callbacks are not supported on Frames' )
0 commit comments