Skip to content

Commit cc1b13e

Browse files
authored
Merge pull request #1530 from plotly/dedent-errors-aj
More careful exception dedenting
2 parents 4ce87ee + cc04375 commit cc1b13e

File tree

4 files changed

+109
-94
lines changed

4 files changed

+109
-94
lines changed

Diff for: CHANGELOG.md

+4-6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ to close the error messages box.
1212
- [#1503](https://github.com/plotly/dash/pull/1506) Fix [#1466](https://github.com/plotly/dash/issues/1466): loosen `dash[testing]` requirements for easier integration in external projects. This PR also bumps many `dash[dev]` requirements.
1313

1414
### Fixed
15+
- [#1530](https://github.com/plotly/dash/pull/1530) Dedent error messages more carefully.
16+
- [#1527](https://github.com/plotly/dash/issues/1527)🐛 `get_asset_url` now pulls from an external source if `assets_external_path` is set.
17+
- updated `_add_assets_resource` to build asset urls the same way as `get_asset_url`.
18+
- updated doc string for `assets_external_path` Dash argument to be more clear that it will allways be joined with the `assets_url_path` argument when determining the url to an external asset.
1519
- [#1493](https://github.com/plotly/dash/pull/1493) Fix [#1143](https://github.com/plotly/dash/issues/1143), a bug where having a file with one of several common names (test.py, code.py, org.py, etc) that imports a dash component package would make `import dash` fail with a cryptic error message asking whether you have a file named "dash.py"
1620

1721
## [1.18.1] - 2020-12-09
@@ -695,9 +699,3 @@ app = dash.Dash(...)
695699

696700
## 0.17.3 - 2017-06-22
697701
✨ This is the initial open-source release of Dash.
698-
699-
### Fixed
700-
- [#1527](https://github.com/plotly/dash/issues/1527)🐛 `get_asset_url` now pulls from an external source if `assets_external_path` is set.
701-
- updated `_add_assets_resource` to build asset urls the same way as `get_asset_url`.
702-
- updated doc string for `assets_external_path` Dash argument to be more clear that it will allways be joined with
703-
the `assets_url_path` argument when determining the url to an external asset.

Diff for: dash/_utils.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,12 @@ def get_relative_path(requests_pathname, path):
6565
return requests_pathname
6666
elif not path.startswith("/"):
6767
raise exceptions.UnsupportedRelativePath(
68-
"Paths that aren't prefixed with a leading / are not supported.\n"
69-
+ "You supplied: {}".format(path)
68+
"""
69+
Paths that aren't prefixed with a leading / are not supported.
70+
You supplied: {}
71+
""".format(
72+
path
73+
)
7074
)
7175
return "/".join([requests_pathname.rstrip("/"), path.lstrip("/")])
7276

@@ -78,9 +82,10 @@ def strip_relative_path(requests_pathname, path):
7882
requests_pathname != "/" and not path.startswith(requests_pathname.rstrip("/"))
7983
) or (requests_pathname == "/" and not path.startswith("/")):
8084
raise exceptions.UnsupportedRelativePath(
81-
"Paths that aren't prefixed with a leading "
82-
+ "requests_pathname_prefix are not supported.\n"
83-
+ "You supplied: {} and requests_pathname_prefix was {}".format(
85+
"""
86+
Paths that aren't prefixed with requests_pathname_prefix are not supported.
87+
You supplied: {} and requests_pathname_prefix was {}
88+
""".format(
8489
path, requests_pathname
8590
)
8691
)

Diff for: dash/_validate.py

+83-78
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import collections
22
import re
3+
from textwrap import dedent
34

45
from .development.base_component import Component
56
from . import exceptions
@@ -15,26 +16,26 @@ def validate_callback(output, inputs, state, extra_args, types):
1516
if extra_args:
1617
if not isinstance(extra_args[0], (Output, Input, State)):
1718
raise exceptions.IncorrectTypeException(
18-
"""
19-
Callback arguments must be `Output`, `Input`, or `State` objects,
20-
optionally wrapped in a list or tuple. We found (possibly after
21-
unwrapping a list or tuple):
22-
{}
23-
""".format(
24-
repr(extra_args[0])
25-
)
19+
dedent(
20+
"""
21+
Callback arguments must be `Output`, `Input`, or `State` objects,
22+
optionally wrapped in a list or tuple. We found (possibly after
23+
unwrapping a list or tuple):
24+
{}
25+
"""
26+
).format(repr(extra_args[0]))
2627
)
2728

2829
raise exceptions.IncorrectTypeException(
29-
"""
30-
In a callback definition, you must provide all Outputs first,
31-
then all Inputs, then all States. After this item:
32-
{}
33-
we found this item next:
34-
{}
35-
""".format(
36-
repr((outputs + inputs + state)[-1]), repr(extra_args[0])
37-
)
30+
dedent(
31+
"""
32+
In a callback definition, you must provide all Outputs first,
33+
then all Inputs, then all States. After this item:
34+
{}
35+
we found this item next:
36+
{}
37+
"""
38+
).format(repr((outputs + inputs + state)[-1]), repr(extra_args[0]))
3839
)
3940

4041
for args in [outputs, inputs, state]:
@@ -45,11 +46,11 @@ def validate_callback(output, inputs, state, extra_args, types):
4546
def validate_callback_arg(arg):
4647
if not isinstance(getattr(arg, "component_property", None), _strings):
4748
raise exceptions.IncorrectTypeException(
48-
"""
49-
component_property must be a string, found {!r}
50-
""".format(
51-
arg.component_property
52-
)
49+
dedent(
50+
"""
51+
component_property must be a string, found {!r}
52+
"""
53+
).format(arg.component_property)
5354
)
5455

5556
if hasattr(arg, "component_event"):
@@ -68,11 +69,11 @@ def validate_callback_arg(arg):
6869

6970
else:
7071
raise exceptions.IncorrectTypeException(
71-
"""
72-
component_id must be a string or dict, found {!r}
73-
""".format(
74-
arg.component_id
75-
)
72+
dedent(
73+
"""
74+
component_id must be a string or dict, found {!r}
75+
"""
76+
).format(arg.component_id)
7677
)
7778

7879

@@ -85,12 +86,12 @@ def validate_id_dict(arg):
8586
# cause unwanted collisions
8687
if not isinstance(k, _strings):
8788
raise exceptions.IncorrectTypeException(
88-
"""
89-
Wildcard ID keys must be non-empty strings,
90-
found {!r} in id {!r}
91-
""".format(
92-
k, arg_id
93-
)
89+
dedent(
90+
"""
91+
Wildcard ID keys must be non-empty strings,
92+
found {!r} in id {!r}
93+
"""
94+
).format(k, arg_id)
9495
)
9596

9697

@@ -113,13 +114,13 @@ def validate_id_string(arg):
113114
def validate_multi_return(outputs_list, output_value, callback_id):
114115
if not isinstance(output_value, (list, tuple)):
115116
raise exceptions.InvalidCallbackReturnValue(
116-
"""
117-
The callback {} is a multi-output.
118-
Expected the output type to be a list or tuple but got:
119-
{}.
120-
""".format(
121-
callback_id, repr(output_value)
122-
)
117+
dedent(
118+
"""
119+
The callback {} is a multi-output.
120+
Expected the output type to be a list or tuple but got:
121+
{}.
122+
"""
123+
).format(callback_id, repr(output_value))
123124
)
124125

125126
if len(output_value) != len(outputs_list):
@@ -137,26 +138,26 @@ def validate_multi_return(outputs_list, output_value, callback_id):
137138
vi = output_value[i]
138139
if not isinstance(vi, (list, tuple)):
139140
raise exceptions.InvalidCallbackReturnValue(
140-
"""
141-
The callback {} output {} is a wildcard multi-output.
142-
Expected the output type to be a list or tuple but got:
143-
{}.
144-
output spec: {}
145-
""".format(
146-
callback_id, i, repr(vi), repr(outi)
147-
)
141+
dedent(
142+
"""
143+
The callback {} output {} is a wildcard multi-output.
144+
Expected the output type to be a list or tuple but got:
145+
{}.
146+
output spec: {}
147+
"""
148+
).format(callback_id, i, repr(vi), repr(outi))
148149
)
149150

150151
if len(vi) != len(outi):
151152
raise exceptions.InvalidCallbackReturnValue(
152-
"""
153-
Invalid number of output values for {} item {}.
154-
Expected {}, got {}
155-
output spec: {}
156-
output value: {}
157-
""".format(
158-
callback_id, i, len(vi), len(outi), repr(outi), repr(vi)
159-
)
153+
dedent(
154+
"""
155+
Invalid number of output values for {} item {}.
156+
Expected {}, got {}
157+
output spec: {}
158+
output value: {}
159+
"""
160+
).format(callback_id, i, len(vi), len(outi), repr(outi), repr(vi))
160161
)
161162

162163

@@ -170,34 +171,38 @@ def _raise_invalid(bad_val, outer_val, path, index=None, toplevel=False):
170171
)
171172
outer_type = type(outer_val).__name__
172173
if toplevel:
173-
location = """
174-
The value in question is either the only value returned,
175-
or is in the top level of the returned list,
176-
"""
174+
location = dedent(
175+
"""
176+
The value in question is either the only value returned,
177+
or is in the top level of the returned list,
178+
"""
179+
)
177180
else:
178181
index_string = "[*]" if index is None else "[{:d}]".format(index)
179-
location = """
180-
The value in question is located at
181-
{} {} {}
182-
{},
183-
""".format(
184-
index_string, outer_type, outer_id, path
185-
)
182+
location = dedent(
183+
"""
184+
The value in question is located at
185+
{} {} {}
186+
{},
187+
"""
188+
).format(index_string, outer_type, outer_id, path)
186189

187190
raise exceptions.InvalidCallbackReturnValue(
188-
"""
189-
The callback for `{output}`
190-
returned a {object:s} having type `{type}`
191-
which is not JSON serializable.
191+
dedent(
192+
"""
193+
The callback for `{output}`
194+
returned a {object:s} having type `{type}`
195+
which is not JSON serializable.
192196
193-
{location}
194-
and has string representation
195-
`{bad_val}`
197+
{location}
198+
and has string representation
199+
`{bad_val}`
196200
197-
In general, Dash properties can only be
198-
dash components, strings, dictionaries, numbers, None,
199-
or lists of those.
200-
""".format(
201+
In general, Dash properties can only be
202+
dash components, strings, dictionaries, numbers, None,
203+
or lists of those.
204+
"""
205+
).format(
201206
output=repr(output),
202207
object="tree with one value" if not toplevel else "value",
203208
type=bad_type,

Diff for: dash/resources.py

+12-5
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@ def _filter_resources(self, all_resources, dev_bundles=False):
2424
if "async" in s:
2525
if "dynamic" in s:
2626
raise exceptions.ResourceException(
27-
"Can't have both 'dynamic' and 'async'. "
28-
"{}".format(json.dumps(filtered_resource))
27+
"""
28+
Can't have both 'dynamic' and 'async'.
29+
{}
30+
""".format(
31+
json.dumps(filtered_resource)
32+
)
2933
)
3034

3135
# Async assigns a value dynamically to 'dynamic'
@@ -70,9 +74,12 @@ def _filter_resources(self, all_resources, dev_bundles=False):
7074
continue
7175
else:
7276
raise exceptions.ResourceException(
73-
"{} does not have a "
74-
"relative_package_path, absolute_path, or an "
75-
"external_url.".format(json.dumps(filtered_resource))
77+
"""
78+
{} does not have a relative_package_path, absolute_path,
79+
or an external_url.
80+
""".format(
81+
json.dumps(filtered_resource)
82+
)
7683
)
7784

7885
filtered_resources.append(filtered_resource)

0 commit comments

Comments
 (0)