@@ -277,6 +277,18 @@ def sort_keys(key):
277277
278278
279279def _key_parts (key ):
280+ """
281+ Split a key containing undescores into all its parts.
282+
283+ This function is aware of attributes that have underscores in their
284+ name (e.g. ``scatter.error_x``) and does not split them incorrectly.
285+
286+ Also, the function always returns a list, even if there is only one item
287+ in that list (e.g. `_key_parts("marker")` would return `["marker"]`)
288+
289+ :param (str|unicode) key: the attribute name
290+ :return: (list[str|unicode]): a list with all the parts of the attribute
291+ """
280292 if "_" in key :
281293 match = _underscore_attr_regex .search (key )
282294 if match is not None :
@@ -313,6 +325,45 @@ def _key_parts(key):
313325
314326
315327def _underscore_magic (parts , val , obj = None , skip_dict_check = False ):
328+ """
329+ Set a potentially "deep" attribute of `obj` specified by a list of parent
330+ keys (`parts`) to `val`.
331+
332+ :param (list[(str|unicode)] or str|unicode) parts: The path to the
333+ attribute to be set on obj. If the argument is a string, then it will
334+ first be passed to `_key_parts(key)` to construct the path and then
335+ this function will be called again
336+ :param val: The value the attribute should have
337+ :param (dict_like) obj: A dict_like object that should have the attribute
338+ set. If nothing is given, then an empty dictionary is created. If
339+ a subtype of `plotly.graph_obsj.PlotlyDict` is passed, then the
340+ setting of the attribute (and creation of parent nodes) will be
341+ validated
342+ :param (bool) skip_dict_check: Optional, default is False. If True and val
343+ is a dict, then this funciton will ensure that all parent nodes are
344+ created in `obj`.
345+ :returns (dict_like) obj: an updated version of the `obj` argument (or
346+ a newly created dict if `obj` was not passed).
347+
348+
349+ Example:
350+
351+ ```
352+ import plotly.graph_objs as go
353+ from plotly.graph_objs.graph_objs_tools import _underscore_magic
354+ layout = go.Layout()
355+ _underscore_magic(["xaxis", "title"], "this is my xaxis", layout)
356+ _underscore_magic("yaxis_titlefont", {"size": 10, "color": "red"}, layout)
357+ print(layout)
358+ ```
359+
360+ Results in
361+
362+ ```
363+ {'xaxis': {'title': 'this is my xaxis'},
364+ 'yaxis': {'titlefont': {'color': 'red', 'size': 10}}}
365+ ```
366+ """
316367 if obj is None :
317368 obj = {}
318369
@@ -376,6 +427,61 @@ def _underscore_magic_dict(parts, val, obj=None):
376427
377428
378429def attr (obj = None , ** kwargs ):
430+ """
431+ Create a nested attribute using "magic underscore" behavior
432+
433+ :param (dict_like) obj: A dict like container on which to set the
434+ attribute. This will be modified in place. If nothing is passed an
435+ empty dict is constructed and then returned. If a plotly graph object
436+ is passed, all attributes will be validated.
437+ :kwargs: All attributes that should be set on obj
438+ :returns (dict_like): A modified version of the object passed to this
439+ function
440+
441+ Example 1:
442+
443+ ```
444+ from plotly.graph_objs import attr, Scatter
445+ my_trace = attr(Scatter(),
446+ marker=attr(size=4, symbol="diamond", line_color="red"),
447+ hoverlabel_bgcolor="grey"
448+ )
449+ ```
450+
451+ Returns the following:
452+
453+ ```
454+ {'hoverlabel': {'bgcolor': 'grey'},
455+ 'marker': {'line': {'color': 'red'}, 'size': 4, 'symbol': 'diamond'},
456+ 'type': 'scatter'}
457+ ```
458+
459+ Example 2: incorrect attribute leads to an error
460+ ```
461+ from plotly.graph_objs import attr, Scatter
462+ my_trace = attr(Scatter(),
463+ marker_mode="markers" # incorrect, should just be mode
464+ )
465+ ```
466+
467+ Returns an error:
468+
469+ ```
470+ PlotlyDictKeyError: 'mode' is not allowed in 'marker'
471+
472+ Path To Error: ['marker']['mode']
473+
474+ Valid attributes for 'marker' at path ['marker'] under parents ['scatter']:
475+
476+ ['autocolorscale', 'cauto', 'cmax', 'cmin', 'color', 'colorbar',
477+ 'colorscale', 'colorsrc', 'gradient', 'line', 'maxdisplayed',
478+ 'opacity', 'opacitysrc', 'reversescale', 'showscale', 'size',
479+ 'sizemin', 'sizemode', 'sizeref', 'sizesrc', 'symbol', 'symbolsrc']
480+
481+ Run `<marker-object>.help('attribute')` on any of the above.
482+ '<marker-object>' is the object at ['marker']
483+ ```
484+ """
379485 if obj is None :
380486 obj = dict ()
381487
0 commit comments