-
-
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
Pytest fixtures #744
Merged
Merged
Pytest fixtures #744
Changes from 50 commits
Commits
Show all changes
54 commits
Select commit
Hold shift + click to select a range
5440e41
:hocho: test.sh is already in circleci config
9f8d4c8
Merge branch 'master' into pytest-fixtures
byronz f33adad
Merge branch 'master' into pytest-fixtures
48c4826
:tada: basic set up for dash.testing
d67e3e5
:white_check_mark: add dash smoke test with fixtures
0832a18
:construction: fixing issues in python2
13f8b97
:construction: debug with circleci cli
fb72a18
:alembic: dbg
73b4dbc
:alembic: add missing test file
791f03d
:fire: :construction: add pythonpath
fc0d0ff
:art: fix lint
12b8c39
:art: lint
3bd185b
:bug: fix one logic error, skip unit to check intg
5361cc5
:bug: mock also html
6efa443
:construction: skip test_assets until fixture for selenium is ready
ae8fc18
:lipstick: polish the app_runner
8282fc5
:alembic: improvement
30dc943
:alembic: more delete
31a9f93
:fire: skip the process test for python2 now
bb755f5
Merge branch 'master' into pytest-fixtures
byronz 98249cf
:tada: browser class for wd fixtures
7b97105
Merge remote-tracking branch 'origin/pytest-fixtures' into pytest-fix…
e17a0c0
:art: fix lint
7ffd6ef
:art: fix lint
bd2cbb8
Merge branch 'pytest-fixtures' of github.com:plotly/dash into pytest-…
4d5e7f0
:ok_hand: based on feedbacks
b89219c
:art: fix lint
545a02c
:bug: fix logic error in wait
e6c545f
:sparkles: improve apis
0d6b1e2
:truck: make all fixtures with dash_ prefix, so it has less chance to…
4005368
:white_check_mark: refactoring on devtools with new fixtures
0c29ee8
:art: fix lint
e5f2e93
:wrench: remove --vv from pytest.ini
49a74ba
:twisted_rightwards_arrows: fix merge conflict
049f717
:pencil2: adding back the logs
25ace9a
:alembic:
12fa1b1
:alembic: dbg
688385e
:construction: improve for dbg experience
8bb8312
:alien: add and improve APIs
4849ace
:art: :recycle: adapt / translate tests
6690ccf
:recycle: API polish
43596f5
minor update tcid
bccbf29
:recycle: polish API
6782fe7
:hocho: move out
5e65b84
:white_check_mark: callbacks and other minor changes
4303e61
:boom: use a specific dcc commit for failed 4 cases, this needs to ge…
42a32dc
:wrench: html change also breaks one test
68e0dda
:wrench: all all integration test back
0b1adf2
:bug: oh forget to migrate this one
eee8429
:bug: fix multiple_clicks
b68b290
Merge branch 'master' into pytest-fixtures
byronz 3bf9a9e
:ok_hand: remove other clicks and add seconds
69371f4
:boom: shorten the api name from start_app_server to start_server
162bed7
Merge branch 'master' into pytest-fixtures
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,4 +14,5 @@ requests | |
beautifulsoup4 | ||
pytest | ||
pytest-sugar | ||
pytest-mock | ||
pytest-mock | ||
waitress |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,3 +14,4 @@ pytest-mock | |
lxml | ||
requests | ||
beautifulsoup4 | ||
waitress |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
__version__ = '0.23.0' | ||
__version__ = '0.24.0' |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
from __future__ import print_function | ||
|
||
import sys | ||
import uuid | ||
import shlex | ||
import threading | ||
import subprocess | ||
import logging | ||
|
||
import runpy | ||
import six | ||
import flask | ||
import requests | ||
|
||
from dash.testing.errors import ( | ||
NoAppFoundError, | ||
TestingTimeoutError, | ||
ServerCloseError, | ||
) | ||
import dash.testing.wait as wait | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def import_app(app_file, application_name="app"): | ||
""" | ||
Import a dash application from a module. | ||
The import path is in dot notation to the module. | ||
The variable named app will be returned. | ||
|
||
:Example: | ||
|
||
>>> app = import_app('my_app.app') | ||
|
||
Will import the application in module `app` of the package `my_app`. | ||
|
||
:param app_file: Path to the app (dot-separated). | ||
:type app_file: str | ||
:param application_name: The name of the dash application instance. | ||
:raise: dash_tests.errors.NoAppFoundError | ||
:return: App from module. | ||
:rtype: dash.Dash | ||
""" | ||
try: | ||
app_module = runpy.run_module(app_file) | ||
app = app_module[application_name] | ||
except KeyError: | ||
logger.exception("the app name cannot be found") | ||
raise NoAppFoundError( | ||
"No dash `app` instance was found in {}".format(app_file) | ||
) | ||
return app | ||
|
||
|
||
class BaseDashRunner(object): | ||
"""Base context manager class for running applications.""" | ||
|
||
def __init__(self, keep_open, stop_timeout): | ||
self.port = 8050 | ||
self.started = None | ||
self.keep_open = keep_open | ||
self.stop_timeout = stop_timeout | ||
|
||
def start(self, *args, **kwargs): | ||
raise NotImplementedError # pragma: no cover | ||
|
||
def stop(self): | ||
raise NotImplementedError # pragma: no cover | ||
|
||
def __call__(self, *args, **kwargs): | ||
return self.start(*args, **kwargs) | ||
|
||
def __enter__(self): | ||
return self | ||
|
||
def __exit__(self, exc_type, exc_val, traceback): | ||
if self.started and not self.keep_open: | ||
try: | ||
logger.info("killing the app runner") | ||
self.stop() | ||
except TestingTimeoutError: | ||
raise ServerCloseError( | ||
"Cannot stop server within {} timeout".format( | ||
alexcjohnson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.stop_timeout | ||
) | ||
) | ||
|
||
@property | ||
def url(self): | ||
"""the default server url""" | ||
return "http://localhost:{}".format(self.port) | ||
|
||
|
||
class ThreadedRunner(BaseDashRunner): | ||
"""Runs a dash application in a thread | ||
|
||
this is the default flavor to use in dash integration tests | ||
""" | ||
|
||
def __init__(self, keep_open=False, stop_timeout=3): | ||
super(ThreadedRunner, self).__init__( | ||
keep_open=keep_open, stop_timeout=stop_timeout | ||
) | ||
self.stop_route = "/_stop-{}".format(uuid.uuid4().hex) | ||
self.thread = None | ||
|
||
@staticmethod | ||
def _stop_server(): | ||
# https://werkzeug.palletsprojects.com/en/0.15.x/serving/#shutting-down-the-server | ||
stopper = flask.request.environ.get("werkzeug.server.shutdown") | ||
if stopper is None: | ||
raise RuntimeError("Not running with the Werkzeug Server") | ||
stopper() | ||
return "Flask server is shutting down" | ||
|
||
# pylint: disable=arguments-differ,C0330 | ||
def start(self, app, **kwargs): | ||
"""Start the app server in threading flavor""" | ||
app.server.add_url_rule( | ||
self.stop_route, self.stop_route, self._stop_server | ||
) | ||
|
||
def _handle_error(): | ||
self._stop_server() | ||
|
||
app.server.errorhandler(500)(_handle_error) | ||
|
||
def run(): | ||
app.scripts.config.serve_locally = True | ||
app.css.config.serve_locally = True | ||
if "port" not in kwargs: | ||
kwargs["port"] = self.port | ||
else: | ||
self.port = kwargs["port"] | ||
app.run_server(threaded=True, **kwargs) | ||
|
||
self.thread = threading.Thread(target=run) | ||
self.thread.daemon = True | ||
try: | ||
self.thread.start() | ||
except RuntimeError: # multiple call on same thread | ||
logger.exception("threaded server failed to start") | ||
self.started = False | ||
|
||
self.started = self.thread.is_alive() | ||
|
||
def accessible(): | ||
try: | ||
requests.get(self.url) | ||
except requests.exceptions.RequestException: | ||
return False | ||
return True | ||
|
||
# wait until server is able to answer http request | ||
wait.until(accessible, timeout=1) | ||
|
||
def stop(self): | ||
requests.get("{}{}".format(self.url, self.stop_route)) | ||
wait.until_not(self.thread.is_alive, self.stop_timeout) | ||
|
||
|
||
class ProcessRunner(BaseDashRunner): | ||
"""Runs a dash application in a waitress-serve subprocess | ||
|
||
this flavor is closer to production environment but slower | ||
""" | ||
|
||
def __init__(self, keep_open=False, stop_timeout=3): | ||
super(ProcessRunner, self).__init__( | ||
keep_open=keep_open, stop_timeout=stop_timeout | ||
) | ||
self.proc = None | ||
|
||
# pylint: disable=arguments-differ | ||
def start(self, app_module, application_name="app", port=8050): | ||
"""Start the server with waitress-serve in process flavor """ | ||
entrypoint = "{}:{}.server".format(app_module, application_name) | ||
self.port = port | ||
|
||
args = shlex.split( | ||
"waitress-serve --listen=0.0.0.0:{} {}".format(port, entrypoint), | ||
posix=sys.platform != "win32", | ||
) | ||
logger.debug("start dash process with %s", args) | ||
|
||
try: | ||
self.proc = subprocess.Popen( | ||
args, stdout=subprocess.PIPE, stderr=subprocess.PIPE | ||
) | ||
except (OSError, ValueError): | ||
logger.exception("process server has encountered an error") | ||
self.started = False | ||
return | ||
|
||
self.started = True | ||
|
||
def stop(self): | ||
if self.proc: | ||
try: | ||
self.proc.terminate() | ||
if six.PY3: | ||
# pylint:disable=no-member | ||
_except = subprocess.TimeoutExpired | ||
# pylint: disable=unexpected-keyword-arg | ||
self.proc.communicate(timeout=self.stop_timeout) | ||
else: | ||
_except = OSError | ||
self.proc.communicate() | ||
except _except: | ||
logger.exception( | ||
"subprocess terminate not success, trying to kill " | ||
"the subprocess in a safe manner" | ||
) | ||
self.proc.kill() | ||
self.proc.communicate() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
after these two commits, dev bundle was removed in dcc and html, which was the root cause of the 4 devtools props-check related failure.
we need to address that with the formal release and this is a workaround for this PR to pass all tests