Skip to content

Commit 63e2fb5

Browse files
committed
Make get_role and get_valid_attributes memoizable.
The `value` argument currently allows for any value type. This isn’t very helpful for memoization. Instead, the top-level function now categorizes `value` as a string so that we *can* memoize the function. Also, we need hashable args, so we `tuple` the `parent_object_names`.
1 parent dd04d95 commit 63e2fb5

File tree

1 file changed

+41
-22
lines changed

1 file changed

+41
-22
lines changed

plotly/graph_reference.py

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ def get_attributes_dicts(object_name, parent_object_names=()):
229229
return attributes_dicts
230230

231231

232-
def get_valid_attributes(object_name, parent_object_names=()):
232+
def _get_valid_attributes(object_name, parent_object_names):
233233
attributes = get_attributes_dicts(object_name, parent_object_names)
234234
# These are for documentation and quick lookups. They're just strings.
235235
valid_attributes = set()
@@ -245,6 +245,11 @@ def get_valid_attributes(object_name, parent_object_names=()):
245245
return valid_attributes
246246

247247

248+
def get_valid_attributes(object_name, parent_object_names=()):
249+
# Enforce that parent_object_names is hashable (a tuple).
250+
return _get_valid_attributes(object_name, tuple(parent_object_names))
251+
252+
248253
def get_deprecated_attributes(object_name, parent_object_names=()):
249254
attributes = get_attributes_dicts(object_name, parent_object_names)
250255
# These are for documentation and quick lookups. They're just strings.
@@ -340,21 +345,9 @@ def attribute_path_to_object_names(attribute_container_path):
340345
return tuple(object_names)
341346

342347

343-
def get_role(object_name, attribute, value=None, parent_object_names=()):
344-
"""
345-
Values have types associated with them based on graph_reference.
346-
347-
'data' type values are always kept
348-
'style' values are kept if they're sequences (but not strings)
349-
350-
:param (str) object_name: The name of the object containing 'attribute'.
351-
:param (str) attribute: The attribute we want the `role` of.
352-
:param (*) value: If the value is an array, the return can be different.
353-
:param parent_object_names: An iterable of obj names from graph reference.
354-
:returns: (str) This will be 'data', 'style', or 'info'.
355-
356-
"""
357-
if object_name in TRACE_NAMES and attribute == 'type':
348+
def _get_role(object_name, attribute, value_type, parent_object_names=()):
349+
"""Private, more easily memoized version of get_role."""
350+
if attribute == 'type' and object_name in TRACE_NAMES:
358351
return 'info'
359352
attributes_dicts = get_attributes_dicts(object_name, parent_object_names)
360353
matches = []
@@ -372,12 +365,8 @@ def get_role(object_name, attribute, value=None, parent_object_names=()):
372365
for match in matches:
373366
role = match['role']
374367
array_ok = match.get('arrayOk')
375-
if value is not None and array_ok:
376-
iterable = hasattr(value, '__iter__')
377-
stringy = isinstance(value, six.string_types)
378-
dicty = isinstance(value, dict)
379-
if iterable and not stringy and not dicty:
380-
role = 'data'
368+
if array_ok and value_type == 'array':
369+
role = 'data'
381370
roles.append(role)
382371

383372
# TODO: this is ambiguous until the figure is in place...
@@ -388,6 +377,36 @@ def get_role(object_name, attribute, value=None, parent_object_names=()):
388377
return role
389378

390379

380+
def get_role(object_name, attribute, value=None, parent_object_names=()):
381+
"""
382+
Values have types associated with them based on graph_reference.
383+
384+
'data' type values are always kept
385+
'style' values are kept if they're sequences (but not strings)
386+
387+
:param (str) object_name: The name of the object containing 'attribute'.
388+
:param (str) attribute: The attribute we want the `role` of.
389+
:param (*) value: If the value is an array, the return can be different.
390+
:param parent_object_names: An iterable of obj names from graph reference.
391+
:returns: (str) This will be 'data', 'style', or 'info'.
392+
393+
"""
394+
if value is None:
395+
value_type = 'none'
396+
elif isinstance(value, dict):
397+
value_type = 'dict'
398+
elif isinstance(value, six.string_types):
399+
value_type = 'string'
400+
elif hasattr(value, '__iter__'):
401+
value_type = 'array'
402+
else:
403+
value_type = 'unknown'
404+
405+
# Enforce that parent_object_names is hashable (a tuple).
406+
return _get_role(object_name, attribute, value_type,
407+
tuple(parent_object_names))
408+
409+
391410
def _is_valid_sub_path(path, parent_paths):
392411
"""
393412
Check if a sub path is valid given an iterable of parent paths.

0 commit comments

Comments
 (0)