Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom response support from callbacks redux #401

Closed
wants to merge 8 commits into from

Conversation

jkseppan
Copy link

Based on @oryba's PR #183, this adds an integration test and moves the json encoding and error handling into one place, as requested in the comments to that PR.

@jkseppan jkseppan changed the title Callback response support Custom response support from callbacks redux Sep 23, 2018
@jkseppan
Copy link
Author

Tagging @Plotly/dash as suggested in the CONTRIBUTING.md pull request (but perhaps that team doesn't exist yet?)

@chriddyp
Copy link
Member

thanks! cc @plotly/dash

Copy link
Contributor

@T4rk1n T4rk1n left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding a test.

I often use @flask.after_this_request to set response headers or cookies, this could be a good alternative.

Please rename dash_response to output_value and make it a property that auto-jsonify.

dash/response.py Outdated
other properties of the response like headers or cookies.
"""
def __init__(self, response, **kwargs):
self.dash_response = response
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call that output_value instead since it is the return value of a callback and not a response.

dash/response.py Outdated
def jsonify_response(self, output, validator):
"""
convert response to valid Dash json-encoded
:param output: output element for the callback
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring need a space before the :param: to be valid, also capitalize.

dash/response.py Outdated
'', # filled in by set_data later
mimetype='application/json', **kwargs)

def jsonify_response(self, output, validator):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dash._validate_callback_output can be made static, we should move it in DashResponse and remove the output_value to be the instance value.

dash/response.py Outdated
other properties of the response like headers or cookies.
"""
def __init__(self, response, **kwargs):
self.dash_response = response
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like the output value to be property that does the jsonify_response automatically when set so we don't have to call that from dash. A benefit would be that the stacktrace would be closer to where the output_value was set and easier to debug for the user.

dash/response.py Outdated
id=output.component_id))

self.set_data(json_value)
return self
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚡ There is no need to make this method tail, there is only one call and no other methods will be called after.

dash/response.py Show resolved Hide resolved
@T4rk1n
Copy link
Contributor

T4rk1n commented Sep 24, 2018

Sorry, look like I missed the output param of _validate_callback_output, making output_value a property would require that we have the Output object in the response __init__. Feel free to dismiss the change about the property.

@jkseppan
Copy link
Author

Thanks for the review!

Copy link
Contributor

@T4rk1n T4rk1n left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, only a minor naming issue. 💃

dash/response.py Outdated
Return a `DashResponse` object from a Dash callback in order to set
other properties of the response like headers or cookies.
"""
def __init__(self, response, **kwargs):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be output_value instead of response for uniformity between the arg and the instance attribute.

@T4rk1n
Copy link
Contributor

T4rk1n commented Nov 7, 2018

Sorry for the delay, this looks ready. Can you rebase/merge master to fix the conflicts ? I'll merge/release as soon as it's done.

@jkseppan jkseppan force-pushed the callback-response-support branch 5 times, most recently from ad03afd to 67f3611 Compare November 10, 2018 17:14
@jkseppan
Copy link
Author

Rebased. Somehow, the test no longer passed on CircleCI although it worked fine for me locally. It seems that the input1.clear() call didn't clear the input, but using an action chain to click on the field, select all and delete the selection using keyboard commands works both locally and on CircleCI.

@jkseppan
Copy link
Author

In the rebase, I had to replicate the changes made to the code that I moved into a different file. I don't know if Github has any way to see what happened there, but here's how I checked for myself on the command line:

git blame -M -C -w -L '/def _validate_callback_output/,/TODO/' master dash/dash.py > old
git blame -M -C -w -L '/def _validate_callback_output/,' callback-response-support dash/response.py > new
paste old new | expand -t 150 | less

The lines don't match exactly because I removed a redundant argument to _raise_invalid (pylint complained about too many arguments in the new function but not in the original method). Here's the output, best viewed in a really wide terminal:

git blame outputs side-by-side

433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 713)     def _validate_callback_output(self, output_value, output):                     56bbe545 dash/response.py (Jouni K Seppänen 2018-09-25 19:44:53 +0300  62) def _validate_callback_output(output_value, output):
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 714)         valid = [str, dict, int, float, type(None), Component]                     56bbe545 dash/response.py (Jouni K Seppänen 2018-09-25 19:44:53 +0300  63)     valid = [str, dict, int, float, type(None), Component]
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 715)                                                                                    56bbe545 dash/response.py (Jouni K Seppänen 2018-09-25 19:44:53 +0300  64) 
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 716)         def _raise_invalid(bad_val, outer_val, bad_type, path, index=None,         f6d8274b dash/response.py (Jouni K Seppänen 2018-09-25 19:59:37 +0300  65)     def _raise_invalid(bad_val, outer_val, path, index=None, toplevel=False):
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 717)                            toplevel=False):                                        ba33c6e2 dash/dash.py     (Ryan Marren      2018-06-22 08:31:35 -0400  66)         outer_id = "(id={:s})".format(outer_val.id) \
ba33c6e2 dash/dash.py  (Ryan Marren 2018-06-22 08:31:35 -0400 718)             outer_id = "(id={:s})".format(outer_val.id) \                          6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  67)                     if getattr(outer_val, 'id', False) else ''
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 719)                 if getattr(outer_val, 'id', False) else ''                         6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  68)         outer_type = type(outer_val).__name__
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 720)             outer_type = type(outer_val).__name__                                  f6d8274b dash/response.py (Jouni K Seppänen 2018-09-25 19:59:37 +0300  69)         bad_type = type(bad_val).__name__
718e2597 dash/dash.py  (Ryan Marren 2018-07-24 19:29:18 -0400 721)             raise exceptions.InvalidCallbackReturnValue('''                        718e2597 dash/dash.py     (Ryan Marren      2018-07-24 19:29:18 -0400  70)         raise exceptions.InvalidCallbackReturnValue('''
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 722)             The callback for property `{property:s}` of component `{id:s}`         6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  71)         The callback for property `{property:s}` of component `{id:s}`
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 723)             returned a {object:s} having type `{type:s}`                           6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  72)         returned a {object:s} having type `{type:s}`
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 724)             which is not JSON serializable.                                        f1743f67 dash/response.py (Jouni K Seppänen 2018-09-23 16:13:09 +0300  73)         which is not JSON serializable.
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 725)                                                                                    f1743f67 dash/response.py (Jouni K Seppänen 2018-09-23 16:13:09 +0300  74) 
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 726)             {location_header:s}{location:s}                                        6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  75)         {location_header:s}{location:s}
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 727)             and has string representation                                          433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400  76)         and has string representation
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 728)             `{bad_val}`                                                            6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  77)         `{bad_val}`
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 729)                                                                                    433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400  78) 
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 730)             In general, Dash properties can only be                                433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400  79)         In general, Dash properties can only be
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 731)             dash components, strings, dictionaries, numbers, None,                 433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400  80)         dash components, strings, dictionaries, numbers, None,
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 732)             or lists of those.                                                     433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400  81)         or lists of those.
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 733)             '''.format(                                                            433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400  82)         '''.format(
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 734)                 property=output.component_property,                                6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  83)             property=output.component_property,
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 735)                 id=output.component_id,                                            6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  84)             id=output.component_id,
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 736)                 object='tree with one value' if not toplevel else 'value',         6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  85)             object='tree with one value' if not toplevel else 'value',
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 737)                 type=bad_type,                                                     6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  86)             type=bad_type,
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 738)                 location_header=(                                                  6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  87)             location_header=(
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 739)                     'The value in question is located at'                          6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  88)                 'The value in question is located at'
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 740)                     if not toplevel else                                           6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  89)                 if not toplevel else
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 741)                     '''The value in question is either the only value returned,    6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  90)                 '''The value in question is either the only value returned,
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 742)                     or is in the top level of the returned list,'''                6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  91)                 or is in the top level of the returned list,'''
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 743)                 ),                                                                 6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  92)             ),
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 744)                 location=(                                                         6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  93)             location=(
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 745)                     "\n" +                                                         6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  94)                 "\n" +
ba33c6e2 dash/dash.py  (Ryan Marren 2018-06-22 08:31:35 -0400 746)                     ("[{:d}] {:s} {:s}".format(index, outer_type, outer_id)        ba33c6e2 dash/dash.py     (Ryan Marren      2018-06-22 08:31:35 -0400  95)                 ("[{:d}] {:s} {:s}".format(index, outer_type, outer_id)
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 747)                      if index is not None                                          433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400  96)                  if index is not None
7f609e85 dash/dash.py  (Ryan Marren 2018-07-04 10:07:58 -0400 748)                      else ('[*] ' + outer_type + ' ' + outer_id))                  7f609e85 dash/dash.py     (Ryan Marren      2018-07-04 10:07:58 -0400  97)                  else ('[*] ' + outer_type + ' ' + outer_id))
ba33c6e2 dash/dash.py  (Ryan Marren 2018-06-22 08:31:35 -0400 749)                     + "\n" + path + "\n"                                           ba33c6e2 dash/dash.py     (Ryan Marren      2018-06-22 08:31:35 -0400  98)                 + "\n" + path + "\n"
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 750)                 ) if not toplevel else '',                                         6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400  99)             ) if not toplevel else '',
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 751)                 bad_val=bad_val).replace('    ', ''))                              6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 100)             bad_val=bad_val).replace('    ', ''))
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 752)                                                                                    433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 101) 
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 753)         def _value_is_valid(val):                                                  433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 102)     def _value_is_valid(val):
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 754)             return (                                                               433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 103)         return (
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 755)                 # pylint: disable=unused-variable                                  433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 104)             # pylint: disable=unused-variable
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 756)                 any([isinstance(val, x) for x in valid]) or                        433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 105)             any([isinstance(val, x) for x in valid]) or
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 757)                 type(val).__name__ == 'unicode'                                    433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 106)             type(val).__name__ == 'unicode'
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 758)             )                                                                      433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 107)         )
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 759)                                                                                    433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 108) 
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 760)         def _validate_value(val, index=None):                                      433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 109)     def _validate_value(val, index=None):
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 761)             # val is a Component                                                   6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 110)         # val is a Component
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 762)             if isinstance(val, Component):                                         433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 111)         if isinstance(val, Component):
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 763)                 for p, j in val.traverse_with_paths():                             433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 112)             for p, j in val.traverse_with_paths():
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 764)                     # check each component value in the tree                       6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 113)                 # check each component value in the tree
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 765)                     if not _value_is_valid(j):                                     433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 114)                 if not _value_is_valid(j):
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 766)                         _raise_invalid(                                            6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 115)                     _raise_invalid(
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 767)                             bad_val=j,                                             6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 116)                         bad_val=j,
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 768)                             outer_val=val,                                         6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 117)                         outer_val=val,
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 769)                             bad_type=type(j).__name__,                             6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 118)                         path=p,
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 770)                             path=p,                                                6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 119)                         index=index
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 771)                             index=index                                            6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 120)                     )
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 772)                         )                                                          6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 121) 
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 773)                                                                                    6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 122)                 # Children that are not of type Component or
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 774)                     # Children that are not of type Component or                   81d764e0 dash/dash.py     (Ryan Marren      2018-10-18 15:17:08 -0400 123)                 # list/tuple not returned by traverse
81d764e0 dash/dash.py  (Ryan Marren 2018-10-18 15:17:08 -0400 775)                     # list/tuple not returned by traverse                          6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 124)                 child = getattr(j, 'children', None)
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 776)                     child = getattr(j, 'children', None)                           81d764e0 dash/dash.py     (Ryan Marren      2018-10-18 15:17:08 -0400 125)                 if not isinstance(child, (tuple,
81d764e0 dash/dash.py  (Ryan Marren 2018-10-18 15:17:08 -0400 777)                     if not isinstance(child, (tuple,                               81d764e0 dash/dash.py     (Ryan Marren      2018-10-18 15:17:08 -0400 126)                                           collections.MutableSequence)):
81d764e0 dash/dash.py  (Ryan Marren 2018-10-18 15:17:08 -0400 778)                                               collections.MutableSequence)):       6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 127)                     if child and not _value_is_valid(child):
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 779)                         if child and not _value_is_valid(child):                   6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 128)                         _raise_invalid(
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 780)                             _raise_invalid(                                        6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 129)                             bad_val=child,
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 781)                                 bad_val=child,                                     6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 130)                             outer_val=val,
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 782)                                 outer_val=val,                                     7f609e85 dash/dash.py     (Ryan Marren      2018-07-04 10:07:58 -0400 131)                             path=p + "\n" + "[*] " + type(child).__name__,
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 783)                                 bad_type=type(child).__name__,                     6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 132)                             index=index
7f609e85 dash/dash.py  (Ryan Marren 2018-07-04 10:07:58 -0400 784)                                 path=p + "\n" + "[*] " + type(child).__name__,     6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 133)                         )
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 785)                                 index=index                                        6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 134) 
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 786)                             )                                                      6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 135)             # Also check the child of val, as it will not be returned
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 787)                                                                                    6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 136)             child = getattr(val, 'children', None)
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 788)                 # Also check the child of val, as it will not be returned          50e0d7ef dash/dash.py     (Ryan Marren      2018-10-18 15:13:52 -0400 137)             if not isinstance(child, (tuple, collections.MutableSequence)):
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 789)                 child = getattr(val, 'children', None)                             6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 138)                 if child and not _value_is_valid(child):
50e0d7ef dash/dash.py  (Ryan Marren 2018-10-18 15:13:52 -0400 790)                 if not isinstance(child, (tuple, collections.MutableSequence)):    6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 139)                     _raise_invalid(
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 791)                     if child and not _value_is_valid(child):                       6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 140)                         bad_val=child,
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 792)                         _raise_invalid(                                            6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 141)                         outer_val=val,
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 793)                             bad_val=child,                                         6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 142)                         path=type(child).__name__,
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 794)                             outer_val=val,                                         6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 143)                         index=index
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 795)                             bad_type=type(child).__name__,                         6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 144)                     )
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 796)                             path=type(child).__name__,                             6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 145) 
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 797)                             index=index                                            6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 146)         # val is not a Component, but is at the top level of tree
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 798)                         )                                                          433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 147)         else:
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 799)                                                                                    433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 148)             if not _value_is_valid(val):
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 800)             # val is not a Component, but is at the top level of tree              6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 149)                 _raise_invalid(
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 801)             else:                                                                  6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 150)                     bad_val=val,
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 802)                 if not _value_is_valid(val):                                       6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 151)                     outer_val=type(val).__name__,
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 803)                     _raise_invalid(                                                6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 152)                     path='',
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 804)                         bad_val=val,                                               6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 153)                     index=index,
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 805)                         outer_val=type(val).__name__,                              6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 154)                     toplevel=True
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 806)                         bad_type=type(val).__name__,                               6bba6e91 dash/dash.py     (Ryan Marren      2018-06-22 08:05:17 -0400 155)                 )
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 807)                         path='',                                                   433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 156) 
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 808)                         index=index,                                               433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 157)     if isinstance(output_value, list):
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 809)                         toplevel=True                                              433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 158)         for i, val in enumerate(output_value):
6bba6e91 dash/dash.py  (Ryan Marren 2018-06-22 08:05:17 -0400 810)                     )                                                              433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 159)             _validate_value(val, index=i)
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 811)                                                                                    433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 160)     else:
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 812)         if isinstance(output_value, list):                                         433a65e5 dash/dash.py     (Ryan Marren      2018-06-18 20:53:59 -0400 161)         _validate_value(output_value)
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 813)             for i, val in enumerate(output_value):                                 
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 814)                 _validate_value(val, index=i)                                      
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 815)         else:                                                                      
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 816)             _validate_value(output_value)                                          
433a65e5 dash/dash.py  (Ryan Marren 2018-06-18 20:53:59 -0400 817)                                                                                    
48a11a59 dash/react.py (chriddyp    2017-02-15 14:21:48 -0500 818)     # TODO - Update nomenclature.                                                  

@T4rk1n
Copy link
Contributor

T4rk1n commented Nov 16, 2018

The input1 clear test failure is due to a bug in dcc. We locked the version on master, you can rebase again sorry. That bug is almost impossible to reproduce by hand, it's a question of timing with the callbacks I think.

@rmarren1 Can you have a look at the diff, this PR moved the callback validation method.

oryba and others added 7 commits November 16, 2018 07:56
This way there is only one place with the json logic and the related
error handling.
Rename dash_response to output_value, improve documentation,
don't make the jsonify call be in tail position.
@jkseppan jkseppan force-pushed the callback-response-support branch from 67f3611 to b119577 Compare November 16, 2018 05:58
@jkseppan
Copy link
Author

Still getting AssertionError: '"initial valueHello World"' != '"Hello World"' with the version of the tests on b119577

For whatever reason, input1.clear() didn't clear the input field
on CircleCI although it was fine when running tests locally.
@T4rk1n
Copy link
Contributor

T4rk1n commented Nov 16, 2018

Sorry, we locked the versions for the call count failures not the initial-value that appear in your new test. plotly/dash-core-components#384 adds a test for that bug, if the ActionChain clear works then it's good for the new test.

@alexcjohnson
Copy link
Collaborator

@T4rk1n is anything else needed or can we merge this?

@jkseppan
Copy link
Author

We could see if the test change in that last commit is still necessary. Perhaps #384 makes it unnecessary? I can't test right now.

@T4rk1n
Copy link
Contributor

T4rk1n commented Dec 18, 2018

is anything else needed or can we merge this?

We need to validate with @rmarren1 the move of the callback validation method, not sure if it conflicts with the validation PR.

We could see if the test change in that last commit is still necessary. Perhaps #384 makes it unnecessary? I can't test right now.

The test is good, selenium has a bug with the input not firing a onChange when cleared. The ActionChain simulate a manual clear by selecting all the text and deleting it so it fires a onChange to the input.

@alexcjohnson
Copy link
Collaborator

I was about to update this PR to the latest master - and we could certainly do that - but I wonder if it would make more sense to extend the pattern we started in #608 with dash.callback_context? ie we pre-create a flask.Response object before firing the callback, and set it as a new global like dash.callback_response. Then in your callback you can call:
dash.callback_response.set_cookie('dash cookie', value)
or whatever other flask.Response method you like. When the callback returns we attach its return value to that response object and away it goes.

Either way the user needs to know a new piece of syntax - either the dash.response.DashResponse class or the dash.callback_response object. But the global way seems to me a little simpler to use - the object is already there, you just modify it - and it would be a smaller change to the codebase.

Thoughts @jkseppan @plotly/dash-core ?

@jkseppan
Copy link
Author

jkseppan commented Mar 2, 2019

@alexcjohnson That does seem like a good design, especially given the symmetry with dash.callback_context.

@jkseppan
Copy link
Author

jkseppan commented Mar 4, 2019

Superseded by #623

@jkseppan jkseppan closed this Mar 4, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants