예제 #1
0
파일: script.py 프로젝트: ceallen/dtale
def main(host=None,
         port=None,
         debug=False,
         no_reaper=False,
         open_browser=False,
         name=None,
         **kwargs):
    """
    Runs a local server for the D-Tale application.

    This local server is recommended when you have a pandas object stored in a CSV
    or retrievable from :class:`arctic.Arctic` data store.
    """
    log_opts = get_log_options(kwargs)
    setup_logging(log_opts.get('logfile'), log_opts.get('log_level'),
                  log_opts.get('verbose'))

    data_loader = check_loaders(kwargs)

    show(host=host,
         port=int(port or find_free_port()),
         debug=debug,
         subprocess=False,
         data_loader=data_loader,
         reaper_on=not no_reaper,
         open_browser=open_browser,
         name=name,
         **kwargs)
예제 #2
0
def test_setup_logging():

    with ExitStack() as stack:
        mock_fh = stack.enter_context(
            mock.patch('logging.FileHandler', mock.Mock()))
        mock_lbc = stack.enter_context(
            mock.patch('logging.basicConfig', mock.Mock()))
        stack.enter_context(mock.patch('sys.argv', ['test']))
        stack.enter_context(
            mock.patch('socket.gethostname', mock.Mock(return_value='test')))
        mock_linf = stack.enter_context(mock.patch('logging.info',
                                                   mock.Mock()))
        stack.enter_context(mock.patch('logging.getLogger', mock.Mock()))
        setup_logging(None, None)
        mock_fh.assert_not_called()
        mock_lbc.has_call(
            mock.call(format="%(asctime)s - %(levelname)-8s - %(message)s",
                      level=logging.INFO))
        mock_linf.has_call(mock.call('test'))
        mock_linf.has_call(mock.call('Hostname: test'))

    with ExitStack() as stack:
        mock_fh = stack.enter_context(
            mock.patch('logging.FileHandler', mock.Mock()))
        mock_lbc = stack.enter_context(
            mock.patch('logging.basicConfig', mock.Mock()))
        stack.enter_context(mock.patch('logging.getLogger', mock.Mock()))
        stack.enter_context(mock.patch('sys.argv', ['test']))
        stack.enter_context(
            mock.patch('socket.gethostname', mock.Mock(return_value='test')))
        setup_logging('test_log', None, verbose=True)
        mock_fh.assert_called_once()
        mock_lbc.has_call(
            mock.call(format="%(asctime)s - %(levelname)-8s - %(message)s",
                      level=logging.DEBUG))

        for level in ['debug', 'info', 'warning', 'error', 'critical'][::-1]:
            setup_logging(None, level)
            mock_lbc.has_call(
                mock.call(format="%(asctime)s - %(levelname)-8s - %(message)s",
                          level=getattr(logging, level.upper())))

    with ExitStack() as stack:
        mock_fh = stack.enter_context(
            mock.patch('logging.FileHandler', mock.Mock()))
        mock_lbc = stack.enter_context(
            mock.patch('logging.basicConfig', mock.Mock()))
        stack.enter_context(mock.patch('logging.getLogger', mock.Mock()))
        stack.enter_context(mock.patch('sys.argv', ['test']))
        stack.enter_context(
            mock.patch('socket.gethostname',
                       mock.Mock(side_effect=Exception('hostname exception'))))
        mock_linf = stack.enter_context(mock.patch('logging.info',
                                                   mock.Mock()))
        setup_logging(None, None, verbose=True)
        mock_fh.assert_not_called()
        mock_lbc.has_call(
            mock.call(format="%(asctime)s - %(levelname)-8s - %(message)s",
                      level=logging.DEBUG))
        mock_linf.has_call(mock.call('hostname exception'))
예제 #3
0
파일: script.py 프로젝트: kennethjhim/dtale
def main(host=None,
         port=None,
         debug=None,
         no_reaper=None,
         open_browser=False,
         name=None,
         no_cell_edits=None,
         hide_shutdown=None,
         github_fork=None,
         app_root=None,
         hide_drop_rows=None,
         **kwargs):
    """
    Runs a local server for the D-Tale application.

    This local server is recommended when you have a pandas object stored in a CSV
    or retrievable from :class:`arctic.Arctic` data store.
    """
    log_opts = get_log_options(kwargs)
    setup_logging(log_opts.get("logfile"), log_opts.get("log_level"),
                  log_opts.get("verbose"))

    data_loader = check_loaders(kwargs)

    # in order to handle the hierarchy of inputs if "--no-cell-edits" is not specified
    # then we'll update it to None
    allow_cell_edits = False if no_cell_edits is not None else None
    kwargs["show_columns"] = (kwargs["show_columns"].split(",")
                              if kwargs.get("show_columns") else None)
    kwargs["hide_columns"] = (kwargs["hide_columns"].split(",")
                              if kwargs.get("hide_columns") else None)
    kwargs["sort"] = ([
        tuple(sort.split("|")) for sort in kwargs["sort"].split(",")
    ] if kwargs.get("sort") else None)
    kwargs["locked"] = kwargs["locked"].split(",") if kwargs.get(
        "locked") else None
    show(host=host,
         port=int(port or find_free_port()),
         debug=debug,
         subprocess=False,
         data_loader=data_loader,
         reaper_on=not no_reaper,
         open_browser=open_browser,
         name=name,
         allow_cell_edits=allow_cell_edits,
         hide_shutdown=hide_shutdown,
         github_fork=github_fork,
         app_root=app_root,
         hide_drop_rows=hide_drop_rows,
         **kwargs)
예제 #4
0
파일: app.py 프로젝트: sigmakappa/dtale
def show(data=None,
         host=None,
         port=None,
         name=None,
         debug=False,
         subprocess=True,
         data_loader=None,
         reaper_on=True,
         open_browser=False,
         notebook=False,
         force=False,
         context_vars=None,
         ignore_duplicate=False,
         app_root=None,
         **kwargs):
    """
    Entry point for kicking off D-Tale :class:`flask:flask.Flask` process from python process

    :param data: data which D-Tale will display
    :type data: :class:`pandas:pandas.DataFrame` or :class:`pandas:pandas.Series`
                or :class:`pandas:pandas.DatetimeIndex` or :class:`pandas:pandas.MultiIndex`, optional
    :param host: hostname of D-Tale, defaults to 0.0.0.0
    :type host: str, optional
    :param port: port number of D-Tale process, defaults to any open port on server
    :type port: str, optional
    :param name: optional label to assign a D-Tale process
    :type name: str, optional
    :param debug: will turn on :class:`flask:flask.Flask` debug functionality, defaults to False
    :type debug: bool, optional
    :param subprocess: run D-Tale as a subprocess of your current process, defaults to True
    :type subprocess: bool, optional
    :param data_loader: function to load your data
    :type data_loader: func, optional
    :param reaper_on: turn on subprocess which will terminate D-Tale after 1 hour of inactivity
    :type reaper_on: bool, optional
    :param open_browser: if true, this will try using the :mod:`python:webbrowser` package to automatically open
                         your default browser to your D-Tale process
    :type open_browser: bool, optional
    :param notebook: if true, this will try displaying an :class:`ipython:IPython.display.IFrame`
    :type notebook: bool, optional
    :param force: if true, this will force the D-Tale instance to run on the specified host/port by killing any
                  other process running at that location
    :type force: bool, optional
    :param context_vars: a dictionary of the variables that will be available for use in user-defined expressions,
                         such as filters
    :type context_vars: dict, optional
    :param ignore_duplicate: if true, this will not check if this data matches any other data previously loaded to
                             D-Tale
    :type ignore_duplicate: bool, optional

    :Example:

        >>> import dtale
        >>> import pandas as pd
        >>> df = pandas.DataFrame([dict(a=1,b=2,c=3)])
        >>> dtale.show(df)
        D-Tale started at: http://hostname:port

        ..link displayed in logging can be copied and pasted into any browser
    """
    global ACTIVE_HOST, ACTIVE_PORT, USE_NGROK, USE_COLAB, JUPYTER_SERVER_PROXY

    try:
        logfile, log_level, verbose = map(kwargs.get,
                                          ["logfile", "log_level", "verbose"])
        setup_logging(logfile, log_level or "info", verbose)

        if USE_NGROK:
            if not PY3:
                raise Exception(
                    "In order to use ngrok you must be using Python 3 or higher!"
                )

            from flask_ngrok import _run_ngrok

            ACTIVE_HOST = _run_ngrok()
            ACTIVE_PORT = None
        else:
            initialize_process_props(host, port, force)

        app_url = build_url(ACTIVE_PORT, ACTIVE_HOST)
        startup_url, final_app_root = build_startup_url_and_app_root(app_root)
        instance = startup(
            startup_url,
            data=data,
            data_loader=data_loader,
            name=name,
            context_vars=context_vars,
            ignore_duplicate=ignore_duplicate,
        )
        is_active = not running_with_flask_debug() and is_up(app_url)
        if is_active:

            def _start():
                if open_browser:
                    instance.open_browser()

        else:
            if USE_NGROK:
                thread = Timer(1, _run_ngrok)
                thread.setDaemon(True)
                thread.start()

            def _start():
                app = build_app(
                    app_url,
                    reaper_on=reaper_on,
                    host=ACTIVE_HOST,
                    app_root=final_app_root,
                )
                if debug and not USE_NGROK:
                    app.jinja_env.auto_reload = True
                    app.config["TEMPLATES_AUTO_RELOAD"] = True
                else:
                    getLogger("werkzeug").setLevel(LOG_ERROR)

                if open_browser:
                    instance.open_browser()

                # hide banner message in production environments
                cli = sys.modules.get("flask.cli")
                if cli is not None:
                    cli.show_server_banner = lambda *x: None

                if USE_NGROK:
                    app.run(threaded=True)
                else:
                    app.run(host="0.0.0.0",
                            port=ACTIVE_PORT,
                            debug=debug,
                            threaded=True)

        if subprocess:
            if is_active:
                _start()
            else:
                _thread.start_new_thread(_start, ())

            if notebook:
                instance.notebook()
        else:
            logger.info("D-Tale started at: {}".format(app_url))
            _start()

        return instance
    except DuplicateDataError as ex:
        print(
            "It looks like this data may have already been loaded to D-Tale based on shape and column names. Here is "
            "URL of the data that seems to match it:\n\n{}\n\nIf you still want to load this data please use the "
            "following command:\n\ndtale.show(df, ignore_duplicate=True)".
            format(
                DtaleData(ex.data_id, build_url(ACTIVE_PORT,
                                                ACTIVE_HOST)).main_url()))
    return None
예제 #5
0
def test_setup_logging():

    with ExitStack() as stack:
        mock_fh = stack.enter_context(mock.patch("logging.FileHandler", mock.Mock()))
        mock_lbc = stack.enter_context(mock.patch("logging.basicConfig", mock.Mock()))
        stack.enter_context(mock.patch("sys.argv", ["test"]))
        stack.enter_context(
            mock.patch("socket.gethostname", mock.Mock(return_value="test"))
        )
        mock_linf = stack.enter_context(mock.patch("logging.info", mock.Mock()))
        stack.enter_context(mock.patch("logging.getLogger", mock.Mock()))
        setup_logging(None, None)
        mock_fh.assert_not_called()
        mock_lbc.has_call(
            mock.call(
                format="%(asctime)s - %(levelname)-8s - %(message)s", level=logging.INFO
            )
        )
        mock_linf.has_call(mock.call("test"))
        mock_linf.has_call(mock.call("Hostname: test"))

    with ExitStack() as stack:
        mock_fh = stack.enter_context(mock.patch("logging.FileHandler", mock.Mock()))
        mock_lbc = stack.enter_context(mock.patch("logging.basicConfig", mock.Mock()))
        stack.enter_context(mock.patch("logging.getLogger", mock.Mock()))
        stack.enter_context(mock.patch("sys.argv", ["test"]))
        stack.enter_context(
            mock.patch("socket.gethostname", mock.Mock(return_value="test"))
        )
        setup_logging("test_log", None, verbose=True)
        mock_fh.assert_called_once()
        mock_lbc.has_call(
            mock.call(
                format="%(asctime)s - %(levelname)-8s - %(message)s",
                level=logging.DEBUG,
            )
        )

        for level in ["debug", "info", "warning", "error", "critical"][::-1]:
            setup_logging(None, level)
            mock_lbc.has_call(
                mock.call(
                    format="%(asctime)s - %(levelname)-8s - %(message)s",
                    level=getattr(logging, level.upper()),
                )
            )

    with ExitStack() as stack:
        mock_fh = stack.enter_context(mock.patch("logging.FileHandler", mock.Mock()))
        mock_lbc = stack.enter_context(mock.patch("logging.basicConfig", mock.Mock()))
        stack.enter_context(mock.patch("logging.getLogger", mock.Mock()))
        stack.enter_context(mock.patch("sys.argv", ["test"]))
        stack.enter_context(
            mock.patch(
                "socket.gethostname",
                mock.Mock(side_effect=Exception("hostname exception")),
            )
        )
        mock_linf = stack.enter_context(mock.patch("logging.info", mock.Mock()))
        setup_logging(None, None, verbose=True)
        mock_fh.assert_not_called()
        mock_lbc.has_call(
            mock.call(
                format="%(asctime)s - %(levelname)-8s - %(message)s",
                level=logging.DEBUG,
            )
        )
        mock_linf.has_call(mock.call("hostname exception"))
예제 #6
0
파일: app.py 프로젝트: imfht/flaskapps
def show(data=None, data_loader=None, name=None, context_vars=None, **options):
    """
    Entry point for kicking off D-Tale :class:`flask:flask.Flask` process from python process

    :param data: data which D-Tale will display
    :type data: :class:`pandas:pandas.DataFrame` or :class:`pandas:pandas.Series`
                or :class:`pandas:pandas.DatetimeIndex` or :class:`pandas:pandas.MultiIndex`, optional
    :param host: hostname of D-Tale, defaults to 0.0.0.0
    :type host: str, optional
    :param port: port number of D-Tale process, defaults to any open port on server
    :type port: str, optional
    :param name: optional label to assign a D-Tale process
    :type name: str, optional
    :param debug: will turn on :class:`flask:flask.Flask` debug functionality, defaults to False
    :type debug: bool, optional
    :param subprocess: run D-Tale as a subprocess of your current process, defaults to True
    :type subprocess: bool, optional
    :param data_loader: function to load your data
    :type data_loader: func, optional
    :param reaper_on: turn on subprocess which will terminate D-Tale after 1 hour of inactivity
    :type reaper_on: bool, optional
    :param open_browser: if true, this will try using the :mod:`python:webbrowser` package to automatically open
                         your default browser to your D-Tale process
    :type open_browser: bool, optional
    :param notebook: if true, this will try displaying an :class:`ipython:IPython.display.IFrame`
    :type notebook: bool, optional
    :param force: if true, this will force the D-Tale instance to run on the specified host/port by killing any
                  other process running at that location
    :type force: bool, optional
    :param context_vars: a dictionary of the variables that will be available for use in user-defined expressions,
                         such as filters
    :type context_vars: dict, optional
    :param ignore_duplicate: if true, this will not check if this data matches any other data previously loaded to
                             D-Tale
    :type ignore_duplicate: bool, optional
    :param app_root: Optional path to prepend to the routes of D-Tale. This is used when making use of
                     Jupyterhub server proxy
    :type app_root: str, optional
    :param allow_cell_edits: If false, this will not allow users to edit cells directly in their D-Tale grid
    :type allow_cell_edits: bool, optional
    :param inplace: If true, this will call `reset_index(inplace=True)` on the dataframe used as a way to save memory.
                    Otherwise this will create a brand new dataframe, thus doubling memory but leaving the dataframe
                    input unchanged.
    :type inplace: bool, optional
    :param drop_index: If true, this will drop any pre-existing index on the dataframe input.
    :type drop_index: bool, optional
    :param hide_shutdown: If true, this will hide the "Shutdown" buton from users
    :type hide_shutdown: bool, optional
    :param github_fork: If true, this will display a "Fork me on GitHub" ribbon in the upper right-hand corner of the
                        app
    :type github_fork: bool, optional

    :Example:

        >>> import dtale
        >>> import pandas as pd
        >>> df = pandas.DataFrame([dict(a=1,b=2,c=3)])
        >>> dtale.show(df)
        D-Tale started at: http://hostname:port

        ..link displayed in logging can be copied and pasted into any browser
    """
    global ACTIVE_HOST, ACTIVE_PORT, USE_NGROK

    try:
        final_options = dtale_config.build_show_options(options)
        logfile, log_level, verbose = map(
            final_options.get, ["logfile", "log_level", "verbose"]
        )
        setup_logging(logfile, log_level or "info", verbose)

        if USE_NGROK:
            if not PY3:
                raise Exception(
                    "In order to use ngrok you must be using Python 3 or higher!"
                )

            from flask_ngrok import _run_ngrok

            ACTIVE_HOST = _run_ngrok()
            ACTIVE_PORT = None
        else:
            initialize_process_props(
                final_options["host"], final_options["port"], final_options["force"]
            )

        app_url = build_url(ACTIVE_PORT, ACTIVE_HOST)
        startup_url, final_app_root = build_startup_url_and_app_root(
            final_options["app_root"]
        )
        instance = startup(
            startup_url,
            data=data,
            data_loader=data_loader,
            name=name,
            context_vars=context_vars,
            ignore_duplicate=final_options["ignore_duplicate"],
            allow_cell_edits=final_options["allow_cell_edits"],
            inplace=final_options["inplace"],
            drop_index=final_options["drop_index"],
            precision=final_options["precision"],
            show_columns=final_options["show_columns"],
            hide_columns=final_options["hide_columns"],
        )
        instance.started_with_open_browser = final_options["open_browser"]
        is_active = not running_with_flask_debug() and is_up(app_url)
        if is_active:

            def _start():
                if final_options["open_browser"]:
                    instance.open_browser()

        else:
            if USE_NGROK:
                thread = Timer(1, _run_ngrok)
                thread.setDaemon(True)
                thread.start()

            def _start():
                app = build_app(
                    app_url,
                    reaper_on=final_options["reaper_on"],
                    host=ACTIVE_HOST,
                    app_root=final_app_root,
                )
                if final_options["debug"] and not USE_NGROK:
                    app.jinja_env.auto_reload = True
                    app.config["TEMPLATES_AUTO_RELOAD"] = True
                else:
                    getLogger("werkzeug").setLevel(LOG_ERROR)

                if final_options["open_browser"]:
                    instance.open_browser()

                # hide banner message in production environments
                cli = sys.modules.get("flask.cli")
                if cli is not None:
                    cli.show_server_banner = lambda *x: None

                if USE_NGROK:
                    app.run(threaded=True)
                else:
                    app.run(
                        host="0.0.0.0",
                        port=ACTIVE_PORT,
                        debug=final_options["debug"],
                        threaded=True,
                    )

        if final_options["subprocess"]:
            if is_active:
                _start()
            else:
                _thread.start_new_thread(_start, ())

            if final_options["notebook"]:
                instance.notebook()
        else:
            logger.info("D-Tale started at: {}".format(app_url))
            _start()

        return instance
    except DuplicateDataError as ex:
        print(
            "It looks like this data may have already been loaded to D-Tale based on shape and column names. Here is "
            "URL of the data that seems to match it:\n\n{}\n\nIf you still want to load this data please use the "
            "following command:\n\ndtale.show(df, ignore_duplicate=True)".format(
                DtaleData(ex.data_id, build_url(ACTIVE_PORT, ACTIVE_HOST)).main_url()
            )
        )
    return None
예제 #7
0
파일: app.py 프로젝트: ceallen/dtale
def show(data=None, host=None, port=None, name=None, debug=False, subprocess=True, data_loader=None,
         reaper_on=True, open_browser=False, notebook=False, force=False, **kwargs):
    """
    Entry point for kicking off D-Tale :class:`flask:flask.Flask` process from python process

    :param data: data which D-Tale will display
    :type data: :class:`pandas:pandas.DataFrame` or :class:`pandas:pandas.Series`
                or :class:`pandas:pandas.DatetimeIndex` or :class:`pandas:pandas.MultiIndex`, optional
    :param host: hostname of D-Tale, defaults to 0.0.0.0
    :type host: str, optional
    :param port: port number of D-Tale process, defaults to any open port on server
    :type port: str, optional
    :param name: optional label to assign a D-Tale process
    :type name: str, optional
    :param debug: will turn on :class:`flask:flask.Flask` debug functionality, defaults to False
    :type debug: bool, optional
    :param subprocess: run D-Tale as a subprocess of your current process, defaults to True
    :type subprocess: bool, optional
    :param data_loader: function to load your data
    :type data_loader: func, optional
    :param reaper_on: turn on subprocess which will terminate D-Tale after 1 hour of inactivity
    :type reaper_on: bool, optional
    :param open_browser: if true, this will try using the :mod:`python:webbrowser` package to automatically open
                         your default browser to your D-Tale process
    :type open_browser: bool, optional
    :param notebook: if true, this will try displaying an :class:`ipython:IPython.display.IFrame`
    :type notebook: bool, optional
    :param force: if true, this will force the D-Tale instance to run on the specified host/port by killing any
                  other process running at that location
    :type force: bool, optional

    :Example:

        >>> import dtale
        >>> import pandas as pd
        >>> df = pandas.DataFrame([dict(a=1,b=2,c=3)])
        >>> dtale.show(df)
        D-Tale started at: http://hostname:port

        ..link displayed in logging can be copied and pasted into any browser
    """

    logfile, log_level, verbose = map(kwargs.get, ['logfile', 'log_level', 'verbose'])
    setup_logging(logfile, log_level or 'info', verbose)

    initialize_process_props(host, port, force)
    url = build_url(ACTIVE_PORT, ACTIVE_HOST)
    instance = startup(url, data=data, data_loader=data_loader, name=name)
    is_active = not running_with_flask_debug() and is_up(url)
    if is_active:
        def _start():
            if open_browser:
                instance.open_browser()
    else:
        def _start():
            app = build_app(url, reaper_on=reaper_on, host=ACTIVE_HOST)
            if debug:
                app.jinja_env.auto_reload = True
                app.config['TEMPLATES_AUTO_RELOAD'] = True
            else:
                getLogger("werkzeug").setLevel(LOG_ERROR)

            if open_browser:
                instance.open_browser()

            # hide banner message in production environments
            cli = sys.modules.get('flask.cli')
            if cli is not None:
                cli.show_server_banner = lambda *x: None

            app.run(host='0.0.0.0', port=ACTIVE_PORT, debug=debug, threaded=True)

    if subprocess:
        if is_active:
            _start()
        else:
            _thread.start_new_thread(_start, ())

        if notebook:
            instance.notebook()
    else:
        logger.info('D-Tale started at: {}'.format(url))
        _start()

    return instance
예제 #8
0
def show(data=None,
         host='0.0.0.0',
         port=None,
         name=None,
         debug=False,
         subprocess=True,
         data_loader=None,
         reaper_on=True,
         open_browser=False,
         notebook=False,
         **kwargs):
    """
    Entry point for kicking off D-Tale Flask process from python process

    :param data: data which D-Tale will display
    :type data: :class:`pandas:pandas.DataFrame` or :class:`pandas:pandas.Series`
                or :class:`pandas:pandas.DatetimeIndex` or :class:`pandas:pandas.MultiIndex`, optional
    :param host: hostname of D-Tale, defaults to 0.0.0.0
    :type host: str, optional
    :param port: port number of D-Tale process, defaults to any open port on server
    :type port: str, optional
    :param name: optional label to assign a D-Tale process
    :type name: str, optional
    :param debug: will turn on Flask debug functionality, defaults to False
    :type debug: bool, optional
    :param subprocess: run D-Tale as a subprocess of your current process, defaults to True
    :type subprocess: bool, optional
    :param data_loader: function to load your data
    :type data_loader: func, optional
    :param reaper_on: turn on subprocess which will terminate D-Tale after 1 hour of inactivity
    :type reaper_on: bool, optional
    :param open_browser: if true, this will try using the :mod:`python:webbrowser` package to automatically open
                         your default browser to your D-Tale process
    :type open_browser: bool, optional
    :param notebook: if true, this will try displaying an :class:`ipython:IPython.display.IFrame`
    :type notebook: bool, optional

    :Example:

        >>> import dtale
        >>> import pandas as pd
        >>> df = pandas.DataFrame([dict(a=1,b=2,c=3)])
        >>> dtale.show(df)
        D-Tale started at: http://hostname:port

        ..link displayed in logging can be copied and pasted into any browser
    """

    logfile, log_level, verbose = map(kwargs.get,
                                      ['logfile', 'log_level', 'verbose'])
    setup_logging(logfile, log_level or 'info', verbose)

    selected_port = int(port or find_free_port())
    instance = startup(data=data,
                       data_loader=data_loader,
                       port=selected_port,
                       name=name)

    def _show():
        app = build_app(reaper_on=reaper_on)
        if debug:
            app.jinja_env.auto_reload = True
            app.config['TEMPLATES_AUTO_RELOAD'] = True
        else:
            getLogger("werkzeug").setLevel(LOG_ERROR)
        url = build_url(selected_port)
        logger.info('D-Tale started at: {}'.format(url))
        if open_browser:
            webbrowser.get().open(url)

        app.run(host=host, port=selected_port, debug=debug)

    if subprocess:
        _thread.start_new_thread(_show, ())

        if notebook:
            instance.notebook()
    else:
        _show()

    return instance