Skip to content

Commit 13ba045

Browse files
author
Jon M. Mease
committed
Big refactor of codegen and project output structure
- plotly/datatypes package is gone - codegen object hierarchy directly into plotly/graph_objs. - Refactor codegen output hierarchy to be compatible with legacy Figure/Layout/Trace types. e.g. go.Figure, go.Layout, go.Scatter. - Codegen deprecated classes as dict/list subclasses with deprecation warnings that point users to which classes to use instead. - The entire legacy graph_objs directory is gone (PlotlyDict/PlotlyList/etc.) - _plotly_utils package introduced as a package to hold code that should be available during both code generation and runtime. Moved base validators to this package so we can reuse their descriptions in docstrings. All of the order-dependency of code generation is now gone, and the plotly/ package is not imported. This makes code generation much less fragile to future extension.
1 parent f55d043 commit 13ba045

File tree

18 files changed

+3077
-1794
lines changed

18 files changed

+3077
-1794
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ plotly/api/v2/spectacle_presentations.py
2222

2323
plotly/presentation_objs/
2424

25-
plotly/datatypes
25+
plotly/graph_objs
2626

2727
plotly/validators
2828

_plotly_utils/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
This package is for utilities that are used during code generation
2+
and at runtime. The reason for not placing these under the main plotly/
3+
package is that this avoids the complications of importing the module
4+
we're generating code into during code generation.
5+
6+
This module must be independent of (it must not import from) both
7+
plotly/ and codegen/

_plotly_utils/__init__.py

Whitespace-only changes.

plotly/basevalidators.py renamed to _plotly_utils/basevalidators.py

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,8 @@ def numbers_allowed(self):
584584

585585
def description(self):
586586

587-
named_clrs_str = '\n'.join(textwrap.wrap(', '.join(self.named_colors), width=80, subsequent_indent=' ' * 12))
587+
named_clrs_str = '\n'.join(textwrap.wrap(', '.join(
588+
self.named_colors), width=79, subsequent_indent=' ' * 12))
588589

589590
valid_color_description = """\
590591
The '{plotly_name}' property is a color and may be specified as:
@@ -1090,38 +1091,60 @@ def validate_coerce(self, v):
10901091
class CompoundValidator(BaseValidator):
10911092
def __init__(self, plotly_name, parent_name, data_class, data_docs):
10921093
super().__init__(plotly_name=plotly_name, parent_name=parent_name)
1093-
self.data_class = data_class
1094+
1095+
# Save element class string
1096+
self.data_class_str = data_class
1097+
self._data_class = None
10941098
self.data_docs = data_docs
1099+
self.module_str = CompoundValidator.compute_graph_obj_module_str(
1100+
self.data_class_str, parent_name)
10951101

10961102
@staticmethod
1097-
def get_constructor_params_str(data_class):
1098-
params_match = re.search("Parameters\n\W*-+\n\W*(.*?)(Returns|$)",
1099-
str(data_class.__init__.__doc__),
1100-
flags=re.DOTALL)
1101-
1102-
if params_match is not None:
1103-
param_descs = params_match.groups()[0]
1103+
def import_graph_objs_class(data_class_str, module_str):
1104+
# Import class module
1105+
module = import_module(module_str)
11041106

1105-
# Increase indent by 4 spaces
1106-
param_descs_indented = ('\n' + ' ' * 4).join(param_descs.split('\n'))
1107+
# Get class reference
1108+
return getattr(module, data_class_str)
11071109

1108-
return param_descs_indented
1110+
@staticmethod
1111+
def compute_graph_obj_module_str(data_class_str, parent_name):
1112+
if parent_name == 'frame' and data_class_str in ['Data', 'Layout']:
1113+
# Special case. There are no graph_objs.frame.Data or
1114+
# graph_objs.frame.Layout classes. These are remapped to
1115+
# graph_objs.Data and graph_objs.Layout
1116+
1117+
parent_parts = parent_name.split('.')
1118+
module_str = '.'.join(['plotly.graph_objs'] + parent_parts[1:])
1119+
elif parent_name:
1120+
module_str = 'plotly.graph_objs.' + parent_name
11091121
else:
1110-
return ''
1122+
module_str = 'plotly.graph_objs'
1123+
1124+
return module_str
1125+
1126+
@property
1127+
def data_class(self):
1128+
if self._data_class is None:
1129+
self._data_class = CompoundValidator.import_graph_objs_class(
1130+
self.data_class_str, self.module_str)
1131+
1132+
return self._data_class
11111133

11121134
def description(self):
11131135

11141136
desc = ("""\
1115-
The '{plotly_name}' property is an instance of {data_class}
1137+
The '{plotly_name}' property is an instance of {class_str}
11161138
that may be specified as:
1117-
- An instance of {data_class}
1139+
- An instance of {module_str}.{class_str}
11181140
- A dict of string/value properties that will be passed to the
1119-
{data_class} constructor
1141+
{class_str} constructor
11201142
11211143
Supported dict properties:
11221144
{constructor_params_str}"""
11231145
).format(plotly_name=self.plotly_name,
1124-
data_class=type_str(self.data_class),
1146+
class_str=self.data_class_str,
1147+
module_str=self.module_str,
11251148
constructor_params_str=self.data_docs)
11261149

11271150
return desc
@@ -1150,29 +1173,44 @@ def validate_coerce(self, v):
11501173
class CompoundArrayValidator(BaseValidator):
11511174
def __init__(self, plotly_name, parent_name, element_class, element_docs):
11521175
super().__init__(plotly_name=plotly_name, parent_name=parent_name)
1153-
self.data_class = element_class
1176+
1177+
# Save element class string
1178+
self.data_class_str = element_class
1179+
self._data_class = None
1180+
11541181
self.data_docs = element_docs
1182+
self.module_str = CompoundValidator.compute_graph_obj_module_str(
1183+
self.data_class_str, parent_name)
11551184

11561185
def description(self):
11571186

11581187
desc = ("""\
1159-
The '{plotly_name}' property is a tuple of instances of {data_class} that may be specified as:
1160-
- A list or tuple of instances of {data_class}
1161-
- A list or tuple of dicts of string/value properties that will be passed to the {data_class} constructor
1188+
The '{plotly_name}' property is a tuple of instances of {class_str} that may be specified as:
1189+
- A list or tuple of instances of {module_str}.{class_str}
1190+
- A list or tuple of dicts of string/value properties that will be passed to the {class_str} constructor
11621191
11631192
Supported dict properties:
11641193
{constructor_params_str}"""
11651194
).format(plotly_name=self.plotly_name,
1166-
data_class=type_str(self.data_class),
1195+
class_str=self.data_class_str,
1196+
module_str=self.module_str,
11671197
constructor_params_str=self.data_docs)
11681198

11691199
return desc
11701200

1201+
@property
1202+
def data_class(self):
1203+
if self._data_class is None:
1204+
self._data_class = CompoundValidator.import_graph_objs_class(
1205+
self.data_class_str, self.module_str)
1206+
1207+
return self._data_class
1208+
11711209
def validate_coerce(self, v):
11721210

11731211
if isinstance(self.data_class, str):
11741212
raise ValueError("Invalid data_class of type 'string': {data_class}"
1175-
.format(data_class = self.data_class))
1213+
.format(data_class=self.data_class))
11761214

11771215
if v is None:
11781216
v = ()
@@ -1203,15 +1241,16 @@ def validate_coerce(self, v):
12031241
class BaseDataValidator(BaseValidator):
12041242
def __init__(self, class_map, plotly_name, parent_name):
12051243
super().__init__(plotly_name=plotly_name, parent_name=parent_name)
1206-
self.class_map = class_map
1244+
self.class_strs_map = class_map
1245+
self._class_map = None
12071246

12081247
def description(self):
12091248

1210-
trace_types = str(list(self.class_map.keys()))
1249+
trace_types = str(list(self.class_strs_map.keys()))
12111250

12121251
trace_types_wrapped = '\n'.join(textwrap.wrap(trace_types,
12131252
subsequent_indent=' ' * 21,
1214-
width=80 - 8))
1253+
width=79 - 8))
12151254

12161255
desc = ("""\
12171256
The '{plotly_name}' property is a tuple of trace instances that may be specified as:
@@ -1228,6 +1267,20 @@ def description(self):
12281267

12291268
return desc
12301269

1270+
@property
1271+
def class_map(self):
1272+
if self._class_map is None:
1273+
1274+
# Initialize class map
1275+
self._class_map = {}
1276+
1277+
# Import trace classes
1278+
trace_module = import_module('plotly.graph_objs')
1279+
for k, class_str in self.class_strs_map.items():
1280+
self._class_map[k] = getattr(trace_module, class_str)
1281+
1282+
return self._class_map
1283+
12311284
def validate_coerce(self, v):
12321285

12331286
if v is None:

0 commit comments

Comments
 (0)