31
31
Undefined = object ()
32
32
33
33
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
-
44
34
class BaseFigure (object ):
45
35
"""
46
36
Base class for all figure types (both widget and non-widget)
47
37
"""
38
+ _bracket_re = re .compile ('^(.*)\[(\d+)\]$' )
48
39
49
40
# Constructor
50
41
# -----------
@@ -143,7 +134,7 @@ class is a subclass of both BaseFigure and widgets.DOMWidget.
143
134
self ._data_defaults = [{} for _ in data ]
144
135
145
136
# ### Reparent trace objects ###
146
- for trace in data :
137
+ for trace_ind , trace in enumerate ( data ) :
147
138
# By setting the trace's parent to be this figure, we tell the
148
139
# trace object to use the figure's _data and _data_defaults
149
140
# 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.
153
144
# We clear the orphan props since the trace no longer needs then
154
145
trace ._orphan_props .clear ()
155
146
147
+ # Set trace index
148
+ trace ._trace_ind = trace_ind
149
+
156
150
# Layout
157
151
# ------
158
152
# ### Construct layout validator ###
@@ -463,6 +457,7 @@ def data(self, new_data):
463
457
old_trace = self .data [i ]
464
458
old_trace ._orphan_props .update (deepcopy (old_trace ._props ))
465
459
old_trace ._parent = None
460
+ old_trace ._trace_ind = None
466
461
467
462
# ### Compute trace props / defaults after removal ###
468
463
traces_props_post_removal = [t for t in self ._data ]
@@ -527,6 +522,10 @@ def data(self, new_data):
527
522
# Update trace objects tuple
528
523
self ._data_objs = list (new_data )
529
524
525
+ # Update trace indexes
526
+ for trace_ind , trace in enumerate (self ._data_objs ):
527
+ trace ._trace_ind = trace_ind
528
+
530
529
# Restyle
531
530
# -------
532
531
def plotly_restyle (self , restyle_data , trace_indexes = None , ** kwargs ):
@@ -690,7 +689,7 @@ def _restyle_child(self, child, key_path_str, val):
690
689
691
690
# Compute trace index
692
691
# -------------------
693
- trace_index = BaseFigure . _index_is ( self . data , child )
692
+ trace_index = child . _trace_ind
694
693
695
694
# Not in batch mode
696
695
# -----------------
@@ -743,7 +742,12 @@ def _str_to_dict_path(key_path_str):
743
742
-------
744
743
tuple[str | int]
745
744
"""
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 ):
747
751
# Nothing to do
748
752
return key_path_str
749
753
else :
@@ -752,11 +756,9 @@ def _str_to_dict_path(key_path_str):
752
756
753
757
# Split out bracket indexes.
754
758
# e.g. ['foo', 'bar[1]'] -> ['foo', 'bar', '1']
755
- bracket_re = re .compile ('(.*)\[(\d+)\]' )
756
759
key_path2 = []
757
760
for key in key_path :
758
- match = fullmatch (bracket_re , key )
759
- #match = bracket_re.fullmatch(key)
761
+ match = BaseFigure ._bracket_re .match (key )
760
762
if match :
761
763
key_path2 .extend (match .groups ())
762
764
else :
@@ -1065,6 +1067,10 @@ def add_traces(self, data, rows=None, cols=None):
1065
1067
# Validate traces
1066
1068
data = self ._data_validator .validate_coerce (data )
1067
1069
1070
+ # Set trace indexes
1071
+ for ind , new_trace in enumerate (data ):
1072
+ new_trace ._trace_ind = ind + len (self .data )
1073
+
1068
1074
# Validate rows / cols
1069
1075
n = len (data )
1070
1076
BaseFigure ._validate_rows_cols ('rows' , n , rows )
@@ -1212,14 +1218,9 @@ def _get_child_props(self, child):
1212
1218
"""
1213
1219
# Try to find index of child as a trace
1214
1220
# -------------------------------------
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
1223
1224
return self ._data [trace_index ]
1224
1225
1225
1226
# Child is the layout
@@ -1247,16 +1248,10 @@ def _get_child_prop_defaults(self, child):
1247
1248
-------
1248
1249
dict
1249
1250
"""
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
-
1257
1251
# Child is a trace
1258
1252
# ----------------
1259
- if trace_index is not None :
1253
+ if isinstance (child , BaseTraceType ):
1254
+ trace_index = child ._trace_ind
1260
1255
return self ._data_defaults [trace_index ]
1261
1256
1262
1257
# Child is the layout
@@ -3365,7 +3360,7 @@ class BaseLayoutType(BaseLayoutHierarchyType):
3365
3360
'polar' ]
3366
3361
3367
3362
_subplotid_prop_re = re .compile (
3368
- '(' + '|' .join (_subplotid_prop_names ) + ')(\d+)' )
3363
+ '^ (' + '|' .join (_subplotid_prop_names ) + ')(\d+)$ ' )
3369
3364
3370
3365
@property
3371
3366
def _subplotid_validators (self ):
@@ -3429,16 +3424,14 @@ def _process_kwargs(self, **kwargs):
3429
3424
unknown_kwargs = {
3430
3425
k : v
3431
3426
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 )
3434
3428
}
3435
3429
super (BaseLayoutHierarchyType , self )._process_kwargs (** unknown_kwargs )
3436
3430
3437
3431
subplot_kwargs = {
3438
3432
k : v
3439
3433
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 )
3442
3435
}
3443
3436
3444
3437
for prop , value in subplot_kwargs .items ():
@@ -3458,8 +3451,7 @@ def _set_subplotid_prop(self, prop, value):
3458
3451
# Get regular expression match
3459
3452
# ----------------------------
3460
3453
# 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 )
3463
3455
subplot_prop = match .group (1 )
3464
3456
suffix_digit = int (match .group (2 ))
3465
3457
@@ -3520,7 +3512,7 @@ def _strip_subplot_suffix_of_1(self, prop):
3520
3512
# Handle subplot suffix digit of 1
3521
3513
# --------------------------------
3522
3514
# 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 )
3524
3516
3525
3517
if match :
3526
3518
subplot_prop = match .group (1 )
@@ -3580,7 +3572,7 @@ def __setitem__(self, prop, value):
3580
3572
3581
3573
# Check for subplot assignment
3582
3574
# ----------------------------
3583
- match = fullmatch ( self ._subplotid_prop_re , prop )
3575
+ match = self ._subplotid_prop_re . match ( prop )
3584
3576
if match is None :
3585
3577
# Set as ordinary property
3586
3578
super (BaseLayoutHierarchyType , self ).__setitem__ (prop , value )
@@ -3594,8 +3586,7 @@ def __setattr__(self, prop, value):
3594
3586
"""
3595
3587
# Check for subplot assignment
3596
3588
# ----------------------------
3597
- # match = self._subplotid_prop_re.fullmatch(prop)
3598
- match = fullmatch (self ._subplotid_prop_re , prop )
3589
+ match = self ._subplotid_prop_re .match (prop )
3599
3590
if match is None :
3600
3591
# Set as ordinary property
3601
3592
super (BaseLayoutHierarchyType , self ).__setattr__ (prop , value )
@@ -3649,6 +3640,7 @@ class BaseTraceHierarchyType(BasePlotlyType):
3649
3640
3650
3641
def __init__ (self , plotly_name , ** kwargs ):
3651
3642
super (BaseTraceHierarchyType , self ).__init__ (plotly_name , ** kwargs )
3643
+
3652
3644
def _send_prop_set (self , prop_path_str , val ):
3653
3645
if self .parent :
3654
3646
# ### Inform parent of restyle operation ###
@@ -3680,6 +3672,9 @@ def __init__(self, plotly_name, **kwargs):
3680
3672
# ### Callbacks to be called on selection ###
3681
3673
self ._select_callbacks = []
3682
3674
3675
+ # ### Trace index in figure ###
3676
+ self ._trace_ind = None
3677
+
3683
3678
# uid
3684
3679
# ---
3685
3680
# All trace types must have a top-level UID
@@ -3951,6 +3946,11 @@ def _send_prop_set(self, prop_path_str, val):
3951
3946
# propagated to parents
3952
3947
pass
3953
3948
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
+
3954
3954
def on_change (self , callback , * args ):
3955
3955
raise NotImplementedError (
3956
3956
'Change callbacks are not supported on Frames' )
0 commit comments