Skip to content

Commit 496242d

Browse files
committed
Make get_attributes return homogenous dict.
It’s a simpler implementation if the return is a dict where *all* the values are dicts. Basically, this splits `get_attributes` into: * `get_attributes_dicts` * `get_valid_attributes` * `get_deprecated_attributes` * `get_subplot_attributes`
1 parent 2131e98 commit 496242d

3 files changed

Lines changed: 92 additions & 45 deletions

File tree

plotly/graph_objs/graph_objs.py

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,9 @@ class PlotlyDict(dict, PlotlyBase):
322322
"""
323323
_name = None
324324
_parent_key = None
325-
_attributes = None
325+
_valid_attributes = None
326+
_deprecated_attributes = None
327+
_subplot_attributes = None
326328

327329
def __init__(self, *args, **kwargs):
328330
if self._name is None:
@@ -346,7 +348,7 @@ def __init__(self, *args, **kwargs):
346348

347349
def __dir__(self):
348350
"""Dynamically return the existing and possible attributes."""
349-
return sorted(list(self._get_attributes()['valid_names']))
351+
return sorted(list(self._get_valid_attributes()))
350352

351353
def __getitem__(self, key):
352354
"""Calls __missing__ when key is not found. May mutate object."""
@@ -366,7 +368,7 @@ def __setitem__(self, key, value, _raise=True):
366368
return
367369

368370
if key.endswith('src'):
369-
if key in self._get_attributes()['valid_names']:
371+
if key in self._get_valid_attributes():
370372
value = graph_objs_tools.assign_id_to_src(key, value)
371373
return super(PlotlyDict, self).__setitem__(key, value)
372374

@@ -377,9 +379,9 @@ def __setitem__(self, key, value, _raise=True):
377379
if isinstance(value, (PlotlyDict, PlotlyList)):
378380
return super(PlotlyDict, self).__setitem__(key, value)
379381

380-
if key not in self._get_attributes()['valid_names']:
382+
if key not in self._get_valid_attributes():
381383

382-
if key in self._get_attributes()['deprecated_names']:
384+
if key in self._get_deprecated_attributes():
383385
warnings.warn(
384386
"Oops! '{}' has been deprecated in '{}'\n"
385387
"This may still work, but you should update your code "
@@ -423,7 +425,7 @@ def __deepcopy__(self, memodict={}):
423425

424426
def __missing__(self, key):
425427
"""Mimics defaultdict. This is called from __getitem__ when key DNE."""
426-
if key in self._get_attributes()['valid_names']:
428+
if key in self._get_valid_attributes():
427429
if graph_objs_tools.get_role(self, key) == 'object':
428430
value = GraphObjectFactory.create(key, _parent=self,
429431
_parent_key=key)
@@ -435,23 +437,42 @@ def __missing__(self, key):
435437
_parent_key=key)
436438
super(PlotlyDict, self).__setitem__(key, value)
437439

438-
def _get_attributes(self):
439-
"""See `graph_reference.get_attributes`."""
440-
if self._attributes is None:
441-
parents = self.get_parents()
442-
parent_object_names = [parent._name for parent in parents]
443-
attributes = graph_reference.get_attributes(
440+
def _get_valid_attributes(self):
441+
"""See `graph_reference.get_valid_attributes`."""
442+
if self._valid_attributes is None:
443+
parent_object_names = [p._name for p in self.get_parents()]
444+
valid_attributes = graph_reference.get_valid_attributes(
444445
self._name, parent_object_names
445446
)
446-
self.__dict__['_attributes'] = attributes
447-
return self._attributes
447+
self.__dict__['_valid_attributes'] = valid_attributes
448+
return self._valid_attributes
449+
450+
def _get_deprecated_attributes(self):
451+
"""See `graph_reference.get_deprecated_attributes`."""
452+
if self._deprecated_attributes is None:
453+
parent_object_names = [p._name for p in self.get_parents()]
454+
deprecated_attributes = graph_reference.get_deprecated_attributes(
455+
self._name, parent_object_names
456+
)
457+
self.__dict__['_deprecated_attributes'] = deprecated_attributes
458+
return self._deprecated_attributes
459+
460+
def _get_subplot_attributes(self):
461+
"""See `graph_reference.get_subplot_attributes`."""
462+
if self._subplot_attributes is None:
463+
parent_object_names = [p._name for p in self.get_parents()]
464+
subplot_attributes = graph_reference.get_subplot_attributes(
465+
self._name, parent_object_names
466+
)
467+
self.__dict__['_subplot_attributes'] = subplot_attributes
468+
return self._subplot_attributes
448469

449470
def _get_subplot_key(self, key):
450471
"""Some keys can have appended integers, this handles that."""
451472
match = re.search(r'(?P<digits>\d+$)', key)
452473
if match:
453474
root_key = key[:match.start()]
454-
if (root_key in self._get_attributes()['subplot_names'] and
475+
if (root_key in self._get_subplot_attributes() and
455476
not match.group('digits').startswith('0')):
456477
return root_key
457478

plotly/graph_objs/graph_objs_tools.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def _make_list_doc(name):
4343
def _make_dict_doc(name):
4444

4545
# TODO: https://github.com/plotly/python-api/issues/289
46-
attributes = graph_reference.get_attributes(name)['valid_names']
46+
attributes = graph_reference.get_valid_attributes(name)
4747
attributes = sorted(attributes, key=sort_keys)
4848
doc = 'Documentation for {}'.format(name)
4949
doc = '\t' + '\n\t'.join(textwrap.wrap(doc, width=LINE_SIZE)) + '\n\n'
@@ -108,9 +108,11 @@ def get_role(parent, key, value=None):
108108
if parent._name in graph_reference.TRACE_NAMES and key == 'type':
109109
return 'info'
110110
matches = []
111-
for val in parent._get_attributes().values():
112-
if not isinstance(val, dict):
113-
continue
111+
parent_object_names = [p._name for p in parent.get_parents()]
112+
attributes_dicts = graph_reference.get_attributes_dicts(
113+
parent._name, parent_object_names=parent_object_names
114+
)
115+
for val in attributes_dicts.values():
114116

115117
for k, v in val.items():
116118
if k == key:

plotly/graph_reference.py

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def object_name_to_class_name(object_name):
103103
return string
104104

105105

106-
def get_attributes(object_name, parent_object_names=()):
106+
def get_attributes_dicts(object_name, parent_object_names=()):
107107
"""
108108
Returns *all* attribute information given the context of parents.
109109
@@ -112,16 +112,12 @@ def get_attributes(object_name, parent_object_names=()):
112112
('some', 'path'): {},
113113
('some', 'other', 'path'): {},
114114
...
115-
'additional_attributes': {},
116-
'valid_names': [],
117-
'deprecated_names': [],
118-
'subplot_names': []
115+
'additional_attributes': {}
119116
}
120117
121118
There may be any number of paths mapping to attribute dicts. There will be
122119
one attribute dict under 'additional_attributes' which will usually be
123-
empty. Finally, there is some meta information in the form of lists under
124-
the 'valid_names', 'deprecated_names', and 'subplot_names' keys.
120+
empty.
125121
126122
:param (str|unicode) object_name: The object name whose attributes we want.
127123
:param (list[str|unicode]) parent_object_names: Names of parent objects.
@@ -148,35 +144,63 @@ def get_attributes(object_name, parent_object_names=()):
148144

149145
# We return a dict mapping paths to attributes. We also add in additional
150146
# attributes if defined.
151-
response = {path: utils.get_by_path(GRAPH_REFERENCE, path)
152-
for path in attribute_paths}
153-
response['additional_attributes'] = additional_attributes
147+
attributes_dicts = {path: utils.get_by_path(GRAPH_REFERENCE, path)
148+
for path in attribute_paths}
149+
attributes_dicts['additional_attributes'] = additional_attributes
154150

151+
return attributes_dicts
152+
153+
154+
def get_valid_attributes(object_name, parent_object_names=()):
155+
attributes = get_attributes_dicts(object_name, parent_object_names)
155156
# These are for documentation and quick lookups. They're just strings.
156-
valid_names = set()
157-
deprecated_names = set()
158-
subplot_names = set()
159-
for attributes in response.values():
157+
valid_attributes = set()
158+
for attributes_dict in attributes.values():
160159

161-
for key, val in attributes.items():
160+
for key, val in attributes_dict.items():
162161
if key not in GRAPH_REFERENCE['defs']['metaKeys']:
163-
valid_names.add(key)
164-
if isinstance(val, dict) and val.get('_isSubplotObj'):
165-
subplot_names.add(key)
162+
valid_attributes.add(key)
166163

167-
deprecated_attributes = attributes.get('_deprecated', {})
164+
deprecated_attributes = attributes_dict.get('_deprecated', {})
168165
for key, val in deprecated_attributes.items():
169166
if key not in GRAPH_REFERENCE['defs']['metaKeys']:
170-
valid_names.add(key)
171-
deprecated_names.add(key)
167+
valid_attributes.add(key)
168+
169+
return valid_attributes
170+
171+
172+
def get_deprecated_attributes(object_name, parent_object_names=()):
173+
attributes = get_attributes_dicts(object_name, parent_object_names)
174+
# These are for documentation and quick lookups. They're just strings.
175+
deprecated_attributes = set()
176+
for attributes_dict in attributes.values():
177+
178+
deprecated_attributes_dict = attributes_dict.get('_deprecated', {})
179+
for key, val in deprecated_attributes_dict.items():
180+
if key not in GRAPH_REFERENCE['defs']['metaKeys']:
181+
deprecated_attributes.add(key)
182+
183+
return deprecated_attributes
184+
185+
186+
def get_subplot_attributes(object_name, parent_object_names=()):
187+
attributes = get_attributes_dicts(object_name, parent_object_names)
188+
# These are for documentation and quick lookups. They're just strings.
189+
subplot_attributes = set()
190+
for attributes_dict in attributes.values():
191+
192+
for key, val in attributes_dict.items():
193+
if key not in GRAPH_REFERENCE['defs']['metaKeys']:
172194
if isinstance(val, dict) and val.get('_isSubplotObj'):
173-
subplot_names.add(key)
195+
subplot_attributes.add(key)
174196

175-
response['valid_names'] = valid_names
176-
response['deprecated_names'] = deprecated_names
177-
response['subplot_names'] = subplot_names
197+
deprecated_attributes = attributes_dict.get('_deprecated', {})
198+
for key, val in deprecated_attributes.items():
199+
if key not in GRAPH_REFERENCE['defs']['metaKeys']:
200+
if isinstance(val, dict) and val.get('_isSubplotObj'):
201+
subplot_attributes.add(key)
178202

179-
return response
203+
return subplot_attributes
180204

181205

182206
def _is_valid_sub_path(path, parent_paths):

0 commit comments

Comments
 (0)