8000 Merge pull request #304 from plotly/clean-up-error-handling · plotly/plotly.py@3311acb · GitHub
[go: up one dir, main page]

Skip to content

Commit 3311acb

Browse files
committed
Merge pull request #304 from plotly/clean-up-error-handling
Clean up error handling
2 parents a3b4023 + 45ca64e commit 3311acb

File tree

4 files changed

+166
-213
lines changed

4 files changed

+166
-213
lines changed

plotly/exceptions.py

Lines changed: 61 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,6 @@
44
55
A module that contains plotly's exception hierarchy.
66
7-
message (required!) (should be root message + caller message)
8-
info: (required!)
9-
path_to_error (required!)
10-
minimal_message (required!)
11-
12-
- minimal_message is set inside this module, should not be set elsewhere
13-
14-
- message is set inside this module, should not be set elsewhere
15-
16-
177
"""
188
import json
199

@@ -56,7 +46,7 @@ def __str__(self):
5646
return self.message
5747

5848

59-
# Grid Errors #
49+
# Grid Errors
6050
COLUMN_NOT_YET_UPLOADED_MESSAGE = (
6151
"Hm... it looks like your column '{column_name}' hasn't "
6252
"been uploaded to Plotly yet. You need to upload your "
@@ -72,126 +62,86 @@ def __str__(self):
7262
"the column \"{0}\" and try again."
7363
)
7464

75-
# Would Cause Server Errors
7665

7766
class PlotlyEmptyDataError(PlotlyError):
7867
pass
7968

8069

8170
# Graph Objects Errors
8271
class PlotlyGraphObjectError(PlotlyError):
83-
def __init__(self, message='', path=None, notes=None, plain_message=''):
72+
def __init__(self, message='', path=(), notes=()):
73+
"""
74+
General graph object error for validation failures.
75+
76+
:param (str|unicode) message: The error message.
77+
:param (iterable) path: A path pointing to the error.
78+
:param notes: Add additional notes, but keep default exception message.
79+
80+
"""
8481
self.message = message
85-
self.plain_message=plain_message
86-
if isinstance(path, list):
87-
self.path = path
88-
elif path is None:
89-
self.path = []
90-
else:
91-
self.path = [path]
92-
if isinstance(notes, list):
93-
self.notes = notes
94-
elif notes is None:
95-
self.notes = []
96-
else:
97-
self.notes = [notes]
82+
self.plain_message = message # for backwards compat
83+
self.path = list(path)
84+
self.notes = notes
9885
super(PlotlyGraphObjectError, self).__init__(message)
99-
self.prepare()
100-
101-
def add_note(self, note):
102-
if isinstance(note, list):
103-
self.notes += note
104-
else:
105-
self.notes += [note]
10686

107-
def add_to_error_path(self, path):
108-
if isinstance(path, list):
109-
self.path = path + self.path
110-
else:
111-
self.path = [path] + self.path
112-
113-
def prepare(self):
114-
message = self.message
115-
message += "\n\nPath To Error:\n["
116-
for iii, key in enumerate(self.path):
117-
message += repr(key)
118-
if iii < len(self.path) - 1:
119-
message += "]["
120-
message += "]"
121-
if len(self.notes):
122-
message += "\n\nAdditional Notes:\n{0}".format(
123-
"\n".join(self.notes))
124-
if len(self.args) > 1:
125-
self.args = (message, self.args[1:][0])
126-
else:
127-
self.args = message,
87+
def __str__(self):
88+
"""This is called by Python to present the error message."""
89+
format_dict = {
90+
'message': self.message,
91+
'path': '[' + ']['.join(repr(k) for k in self.path) + ']',
92+
'notes': '\n'.join(self.notes)
93+
}
94+
return ('{message}\n\nPath To Error: {path}\n\n{notes}'
95+
.format(**format_dict))
12896

12997

13098
class PlotlyDictKeyError(PlotlyGraphObjectError):
131-
def __init__(self, obj='', key='', **kwargs):
132-
message = (
133-
"Invalid key, '{key}', for class, '{obj_name}'.\n\nRun "
134-
"'help(plotly.graph_objs.{obj_name})' for more information."
135-
"".format(key=key, obj_name=obj.__class__.__name__)
99+
def __init__(self, obj, path, notes=()):
100+
"""See PlotlyGraphObjectError.__init__ for param docs."""
101+
format_dict = {'attribute': path[-1], 'object_name': obj._name}
102+
message = ("'{attribute}' is not allowed in '{object_name}'"
103+
.format(**format_dict))
104+
notes = [obj.help(return_help=True)] + list(notes)
105+
super(PlotlyDictKeyError, self).__init__(
106+
message=message, path=path, notes=notes
136107
)
137-
plain_message = ("Invalid key, '{key}', found in '{obj}' object"
138-
"".format(key=key, obj=obj.__class__.__name__))
139-
super(PlotlyDictKeyError, self).__init__(message=message,
140-
path=[key],
141-
plain_message=plain_message,
142-
**kwargs)
143108

144109

145110
class PlotlyDictValueError(PlotlyGraphObjectError):
146-
def __init__(self, obj='', key='', value='', val_types='', **kwargs):
147-
message = (
148-
"Invalid value type, '{value_name}', associated with key, "
149-
"'{key}', for class, '{obj_name}'.\nValid types for this key "
150-
"are:\n '{val_types}'.\n\nRun 'help(plotly.graph_objs.{obj_name})' "
151-
"for more information.".format(key=key,
152-
value_name=value.__class__.__name__,
153-
val_types=val_types,
154-
obj_name=obj.__class__.__name__)
111+
def __init__(self, obj, path, notes=()):
112+
"""See PlotlyGraphObjectError.__init__ for param docs."""
113+
format_dict = {'attribute': path[-1], 'object_name': obj._name}
114+
message = ("'{attribute}' has invalid value inside '{object_name}'"
115+
.format(**format_dict))
116+
notes = [obj.help(path[-1], return_help=True)] + list(notes)
117+
super(PlotlyDictValueError, self).__init__(
118+
message=message, notes=notes, path=path
155119
)
156-
plain_message = ("Invalid value found in '{obj}' associated with key, "
157-
"'{key}'".format(key=key, obj=obj.__class__.__name__))
158-
super(PlotlyDictValueError, self).__init__(message=message,
159-
plain_message=plain_message,
160-
path=[key],
161-
**kwargs)
162120

163121

164122
class PlotlyListEntryError(PlotlyGraphObjectError):
165-
def __init__(self, obj='', index='', entry='', **kwargs):
166-
message = (
167-
"The entry at index, '{0}', is invalid in a '{1}' object"
168-
"".format(index, obj.__class__.__name__)
169-
)
170-
plain_message = (
171-
"Invalid entry found in '{obj}' object at index, '{index}'."
172-
"".format(obj=obj.__class__.__name__, index=index)
123+
def __init__(self, obj, path, notes=()):
124+
"""See PlotlyGraphObjectError.__init__ for param docs."""
125+
format_dict = {'index': path[-1], 'object_name': obj._name}
126+
message = ("Invalid entry found in '{object_name}' at index, '{index}'"
127+
.format(**format_dict))
128+
notes = [obj.help(return_help=True)] + list(notes)
129+
super(PlotlyListEntryError, self).__init__(
130+
message=message, path=path, notes=notes
173131
)
174-
super(PlotlyListEntryError, self).__init__(message=message,
175-
plain_message=plain_message,
176-
path=[index],
177-
**kwargs)
178132

179133

180134
class PlotlyDataTypeError(PlotlyGraphObjectError):
181-
def __init__(self, obj='', index='', **kwargs):
182-
message = (
183-
"The entry at index, '{0}', is invalid because it does not "
184-
"contain a valid 'type' key-value. This is required for valid "
185-
"'{1}' lists.".format(index, obj.__class__.__name__)
135+
def __init__(self, obj, path, notes=()):
136+
"""See PlotlyGraphObjectError.__init__ for param docs."""
137+
format_dict = {'index': path[-1], 'object_name': obj._name}
138+
message = ("Invalid entry found in '{object_name}' at index, '{index}'"
139+
.format(**format_dict))
140+
note = "It's invalid because it does't contain a valid 'type' value."
141+
notes = [note] + list(notes)
142+
super(PlotlyDataTypeError, self).__init__(
143+
message=message, path=path, notes=notes
186144
)
187-
plain_message = (
188-
"Invalid entry found in 'data' object at index, '{0}'. It "
189-
"does not contain a valid 'type' key, required for 'data' "
190-
"lists.".format(index))
191-
super(PlotlyDataTypeError, self).__init__(message=message,
192-
plain_message=plain_message,
193-
path=[index],
194-
**kwargs)
195145

196146

197147
# Local Config Errors
@@ -201,7 +151,8 @@ class PlotlyLocalError(PlotlyError):
201151

202152
class PlotlyLocalCredentialsError(PlotlyLocalError):
203153
def __init__(self):
204-
message = ("\n"
154+
message = (
155+
"\n"
205156
"Couldn't find a 'username', 'api-key' pair for you on your local "
206157
"machine. To sign in temporarily (until you stop running Python), "
207158
"run:\n"
@@ -210,8 +161,10 @@ def __init__(self):
210161
"Even better, save your credentials permanently using the 'tools' "
211162
"module:\n"
212163
">>> import plotly.tools as tls\n"
213-
">>> tls.set_credentials_file(username='username', api_key='api-key')\n\n"
214-
"For more help, see https://plot.ly/python.\n")
164+
">>> tls.set_credentials_file(username='username', "
165+
"api_key='api-key')\n\n"
166+
"For more help, see https://plot.ly/python.\n"
167+
)
215168
super(PlotlyLocalCredentialsError, self).__init__(message)
216169

217170

plotly/graph_objs/graph_objs.py

Lines changed: 54 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,21 @@ def _get_class_name(self):
8888
"""For convenience. See `graph_reference.object_name_to_class_name`."""
8989
return graph_reference.object_name_to_class_name(self._name)
9090

91-
def help(self):
92-
"""Print a help string for this object."""
91+
def help(self, return_help=False):
92+
"""
93+
Print a help string for this object.
94+
95+
:param (bool) return_help: Return help string instead of prining?
96+
:return: (None|str) Optionally can return help string.
97+
98+
"""
9399
object_name = self._name
94100
path = self._get_path()
95101
parent_object_names = self._get_parent_object_names()
96102
help_string = graph_objs_tools.get_help(object_name, path,
97103
parent_object_names)
104+
if return_help:
105+
return help_string
98106
print(help_string)
99107

100108
def to_graph_objs(self, **kwargs):
@@ -137,21 +145,20 @@ def __init__(self, *args, **kwargs):
137145
)
138146

139147
if args and isinstance(args[0], dict):
140-
raise exceptions.PlotlyListEntryError(
141-
obj=self,
142-
index=0,
143-
notes="Just like a `list`, `{name}` must be instantiated with "
144-
"a *single* collection.\n"
145-
"In other words these are OK:\n"
146-
">>> {name}()\n"
147-
">>> {name}([])\n"
148-
">>> {name}([dict()])\n"
149-
">>> {name}([dict(), dict()])\n"
150-
"However, these don't make sense:\n"
151-
">>> {name}(dict())\n"
152-
">>> {name}(dict(), dict())"
153-
"".format(name=self._get_class_name())
148+
note = (
149+
"Just like a `list`, `{name}` must be instantiated "
150+
"with a *single* collection.\n"
151+
"In other words these are OK:\n"
152+
">>> {name}()\n"
153+
">>> {name}([])\n"
154+
">>> {name}([dict()])\n"
155+
">>> {name}([dict(), dict()])\n"
156+
"However, these don't make sense:\n"
157+
">>> {name}(dict())\n"
158+
">>> {name}(dict(), dict())"
159+
.format(name=self._get_class_name())
154160
)
161+
raise exceptions.PlotlyListEntryError(self, [0], notes=[note])
155162

156163
super(PlotlyList, self).__init__()
157164

@@ -211,10 +218,8 @@ def _value_to_graph_object(self, index, value, _raise=True):
211218
"""
212219
if not isinstance(value, dict):
213220
if _raise:
214-
e = exceptions.PlotlyListEntryError(self, index, value)
215-
e.path = self._get_path() + (index, )
216-
e.prepare()
217-
raise e
221+
path = self._get_path() + (index, )
222+
raise exceptions.PlotlyListEntryError(self, path)
218223
else:
219224
return
220225

@@ -423,10 +428,8 @@ def __setitem__(self, key, value, _raise=True):
423428
return super(PlotlyDict, self).__setitem__(key, value)
424429
else:
425430
if _raise:
426-
e = exceptions.PlotlyDictKeyError(self, key)
427-
e.path = self._get_path() + (key, )
428-
e.prepare()
429-
raise e
431+
path = self._get_path() + (key, )
432+
raise exceptions.PlotlyDictKeyError(self, path)
430433
return
431434

432435
if self._get_attribute_role(key) == 'object':
@@ -534,30 +537,36 @@ def _value_to_graph_object(self, key, value, _raise=True):
534537

535538
if not isinstance(value, val_types):
536539
if _raise:
537-
e = exceptions.PlotlyDictValueError(self, key, value,
538-
val_types)
539-
e.path = self._get_path() + (key, )
540-
e.prepare()
541-
raise e
540+
path = self._get_path() + (key, )
541+
raise exceptions.PlotlyDictValueError(self, path)
542542
else:
543543
return
544544

545545
# this can be `None` when `_raise == False`
546546
return GraphObjectFactory.create(key, value, _raise=_raise,
547547
_parent=self, _parent_key=key)
548548

549-
def help(self, attribute=None):
550-
"""Print help string for this object or an attribute of this object."""
549+
def help(self, attribute=None, return_help=False):
550+
"""
551+
Print help string for this object or an attribute of this object.
552+
553+
:param (str) attribute: A valid attribute string for this object.
554+
:param (bool) return_help: Return help_string instead of printing it?
555+
:return: (None|str)
556+
557+
"""
551558
if not attribute:
552-
super(PlotlyDict, self).help()
553-
else:
554-
object_name = self._name
555-
path = self._get_path()
556-
parent_object_names = self._get_parent_object_names()
557-
help_string = graph_objs_tools.get_help(object_name, path,
558-
parent_object_names,
559-
attribute)
560-
print(help_string)
559+
return super(PlotlyDict, self).help(return_help=return_help)
560+
561+
object_name = self._name
562+
path = self._get_path()
563+
parent_object_names = self._get_parent_object_names()
564+
help_string = graph_objs_tools.get_help(object_name, path,
565+
parent_object_names, attribute)
566+
567+
if return_help:
568+
return help_string
569+
print(help_string)
561570

562571
def update(self, dict1=None, **dict2):
563572
"""
@@ -929,19 +938,17 @@ def _value_to_graph_object(self, index, value, _raise=True):
929938

930939
if not isinstance(value, dict):
931940
if _raise:
932-
e = exceptions.PlotlyListEntryError(self, index, value)
933-
e.add_note('Entry should subclass dict.')
934-
raise e
941+
notes = ['Entry should subclass dict.']
942+
path = self._get_path() + (index, )
943+
raise exceptions.PlotlyListEntryError(self, path, notes=notes)
935944
else:
936945
return
937946

938947
item = value.get('type', 'scatter')
939948
if item not in graph_reference.ARRAYS['data']['items']:
940949
if _raise:
941-
err = exceptions.PlotlyDataTypeError(self, index)
942-
err.path = self._get_path() + (0, )
943-
err.prepare()
944-
raise err
950+
path = self._get_path() + (0, )
951+
raise exceptions.PlotlyDataTypeError(self, path)
945952

946953
return GraphObjectFactory.create(item, _raise=_raise, _parent=self,
947954
_parent_key=index, **value)

0 commit comments

Comments
 (0)
0