-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Config cleanup #761
Config cleanup #761
Conversation
it's more confusing to describe with it present - and we always strip it off in practice
also a fix for debug logic re: env vars
except server, index_string, and plugins
… use lock as context manager
dash/CHANGELOG.md
Outdated
@@ -1,5 +1,13 @@ | |||
## Unreleased | |||
### Changed | |||
- [#761](https://github.com/plotly/dash/pull/761) Several breaking changes to the `dash.Dash` API: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a 💥 here?
@@ -86,106 +86,223 @@ class _NoUpdate(object): | |||
# pylint: disable=too-many-instance-attributes | |||
# pylint: disable=too-many-arguments, too-many-locals | |||
class Dash(object): | |||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😸 nice docstring.
tangential question, do we have an agreed reference to follow? I feel like it's not very helpful to use super-long-detailed-sphinx-style; chris is also building sth in the house.
but from our users' perspective, data scientists might feel this more familiar https://sphinxcontrib-napoleon.readthedocs.io/en/latest/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be up for changing to a cleaner style, I was just copying the style we already had for run_server
and enable_dev_tools
.
Feels to me like we should have a decent level of detail here, and then use this to generate the corresponding page in dash-docs. Let's keep this format until we decide how we want to do that, so we only need to alter it once. @chriddyp I'm not aware if you're working on something else to fill this gap.
dash/dash.py
Outdated
Dash is a framework for building analytical web applications. | ||
No JavaScript required. | ||
|
||
If a parameter can be set by an environment variable, that is listed too. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that is listed with env:
prefix?
dash/dash.py
Outdated
:type server: boolean or flask.Flask | ||
|
||
:param assets_folder: a path, relative to the current working directory, | ||
for extra files to be used in the browser. Default ``'assets'`` By |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the Default 'asset'
By default needs a better split.
dash/dash.py
Outdated
Default ``'assets'``. | ||
:type asset_url_path: string | ||
|
||
:param assets_ignore: A regexp, as a string to pass to ``re.compile``, for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
regexp
is js flavor, pythonic name should be regex
dash/dash.py
Outdated
be called after the Flask server is attached. | ||
:type plugins: list of objects | ||
|
||
:param **kwargs: Do not use. Gives error messages for obsolete arguments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would it be more intuitive if we add an example section e.g. man lsof
for less experienced users?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean rather than the errors on providing old kwargs? I was thinking of those just as helpers for experienced users of old dash versions. This documentation of **kwargs
is just intended for people who see **kwargs
in the function signature and wonder why it's there. Perhaps I can omit this documentation and just rename it to **obsolete
, and assume that's sufficiently self-documenting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the confusion, I meant for all the docstring. Just happened to leave the comment at this line. Nothing wrong with kwargs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the confusion, I meant for all the docstring. Just happened to leave the comment at this line. Nothing wrong with kwargs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. I feel like once people get here they've seen examples already elsewhere in the docs, so this docstring is just about enumerating all the options.
Anyway I will change **kwargs
to **obsolete
and remove it from the docstring.
@@ -1164,7 +1164,7 @@ def test_removing_component_while_its_getting_updated(self): | |||
), | |||
html.Div(id='body') | |||
]) | |||
app.config.supress_callback_exceptions = True | |||
app.config.suppress_callback_exceptions = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
predicted merge conflicts here
@@ -870,6 +871,40 @@ def failure2(children): | |||
context.exception.args[0] | |||
) | |||
|
|||
def test_callback_dep_types(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alexcjohnson do you want to migrate this with new style?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not too hard to migrate -> 4052507
dash/testing/browser.py
Outdated
return self._wait_for( | ||
EC.presence_of_element_located, | ||
((By.CSS_SELECTOR, selector),), | ||
timeout, | ||
"timeout {} => waiting for selector {}".format(timeout, selector), | ||
"timeout {}s => waiting for selector {}".format(_time, selector), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice fix on the timeout
🐄 minor, I would use more pythonic way format (timeout if timeout else self._wait_timeout, selector)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🐄 in 55274be
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💃 nice job! the two new comments are good to have, feel free to merge without the change
input_.send_keys('xyz') | ||
dash_duo.wait_for_text_to_equal('#input', 'initial inputxyz') | ||
|
||
# callback1 runs 4x (initial page load and 3x through send_keys) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would put the comment as an assert msg, so you can see that directly in the assertion exception. the comment does show up too with the code snippet.
with self.assertRaises(InvalidCallbackReturnValue): | ||
multi('aaa') | ||
btn_elements = [ | ||
dash_duo.find_element('#' + x) for x in btns |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor: prefer btn
than x
here
app = Dash(__name__) | ||
btn = dash_duo.find_element('#btn') | ||
for _ in range(10): | ||
btn.click() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can use this API dash_duo.multiple_click()
. yep the API reference is coming soon
for _ in range(clicks):
self.find_element(css_selector).click()
]) | ||
|
||
with pytest.raises(IncorrectTypeException): | ||
@app.callback([[Output('out', 'children')]], # extra nesting |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment can be set as message='' inside raises
# Remove those | ||
comment_regex = r'<!--[^\[](.*?)-->' # noqa: W605 | ||
|
||
# Somehow the html attributes are unordered. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this permutation was a bad idea, I did similar clean up with beautiful soup parser for the initial state test case
dash/tests/integration/renderer/test_due_diligence.py
Lines 58 to 70 in 3a9c3cf
# Note: this .html file shows there's no undo/redo button by default | |
with open( | |
os.path.join( | |
os.path.dirname(__file__), "initial_state_dash_app_content.html" | |
) | |
) as fp: | |
expected_dom = BeautifulSoup(fp.read().strip(), "lxml") | |
fetched_dom = dash_duo.dash_outerhtml_dom | |
assert ( | |
fetched_dom.decode() == expected_dom.decode() | |
), "the fetching rendered dom is expected" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I noticed that - BeautifulSoup
is a nice idea, I'll try it here.
closes #746
Some breaking changes to the
dash.Dash
APIConstructor / config breaking changes:
static_folder
(assets_folder
is much smarter) andcomponents_cache_max_age
(all files have cache-busting URLs)supress_callback_exceptions
fallbackresources.config.infer_from_layout
app.config
: ALL constructor args are now stored inconfig
(and none are stored in any other instance attributes anymore), with three exceptions:server
-app.init_app(server)
is the right way to set thisindex_string
- it has a special setter methodplugins
- You could add more plugins by callingmy_plugin.plug(app)
but not by modifying a config entryDev tools breaking changes:
hot_reload_interval
from msec to seconds, for consistency withhot_reload_watch_interval
enable_dev_tools
,debug=True
by default. It's stillFalse
by default fromrun_server
.Non-breaking changes:
app.config
prevents you from adding new (ie misspelled or obsolete) items and makes some items read-only - the ones that will not take effect if changed after the constructor (andinit_app
, since that can happen in the constructor) have finished.assets_url_path
is now just'assets'
, not'/assets'
. Since in actual usage we always strip the leading'/'
and prepend the requests prefix, it's misleading as well as harder to explain with the slash there.serve_locally
- defaultTrue
as per serve_locally by default #722 - sets both JS and CSS configdash.Dash
constructor and improved docstrings for therun_server
andenable_dev_tools
methodsNot implemented:
I looked into the
name
parameter and seems to me we should NOT make it mandatory, but would be interested to hear what others think about this. The only time this seems to matter is if the file that was run (python run.py
in our common usage, or thegunicorn
equivalent) is in a different directory from the one that definesapp
(let's call itapp.py
). All it seems to affect is where Flask looks for other files specified by relative paths, the key one beingassets_folder
.'__main__'
, says these are relative torun.py
.name=__name__
, says they're relative toapp.py
package.sub.path
as inimport package.sub.path
, and it will be resolved exactly as that import would be)run.py
, when you ran PythonMost of the time this doesn't matter, as
run.py
andapp.py
, if they're even separate files, are usually in the same dir.__name__
is nice in that you get the same assets folder no matter where you importapp.py
from, but on the other hand I suspect the more likely situation is that you set up your project and then at some point decide to moveapp.py
into a subdirectory, without movingassets
orrun.py
. In that case__name__
would cause problems, and the current default'__main__'
is probably correct. That to me is a sufficient reason to leave the default behavior as it is.Contributor Checklist
CHANGELOG.md