diff --git a/requirements.txt b/requirements.txt index 071069d..fc500e0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ Babel==1.3 beautifulsoup4==4.4.1 blinker==1.3 +chromedriver_installer==0.0.6 click==6.6 extras==0.0.3 fixtures==2.0.0 @@ -32,10 +33,11 @@ pyrsistent==0.11.13 python-dateutil==2.5.0 python-mimeparse==1.5.1 pytz==2014.10 +selenium==3.3.1 simplejson==3.6.5 six>=1.9.0 speaklater==1.3 SQLAlchemy==1.0.14 sqlparse==0.1.19 Werkzeug==0.9.6 -WTForms==2.0.2 \ No newline at end of file +WTForms==2.0.2 diff --git a/web/pgAdmin4.py b/web/pgAdmin4.py index 6025514..23b7441 100644 --- a/web/pgAdmin4.py +++ b/web/pgAdmin4.py @@ -13,6 +13,13 @@ to start a web server.""" import os import sys +import socket +import threading +import time +import uuid + +import requests +from selenium import webdriver # We need to include the root directory in sys.path to ensure that we can # find everything we need when running in the standalone runtime. @@ -28,6 +35,19 @@ from pgadmin import create_app from pgadmin.model import SCHEMA_VERSION config.SETTINGS_SCHEMA_VERSION = SCHEMA_VERSION +def get_free_port(): + """ + Gets a free port number + Returns: The port number + """ + import socket + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.bind(("", 0)) + s.listen(1) + port = s.getsockname()[1] + s.close() + return port + ########################################################################## # Sanity checks ########################################################################## @@ -47,68 +67,107 @@ if not os.path.isfile(config.SQLITE_PATH): # Create the app! app = create_app() +@app.before_first_request +def start_browser(): + if config.SERVER_MODE: + return + + def go(): + # Figure out the correct URL + if config.SERVER_MODE: + url = server_url + else: + url = '%s?key=%s' % (server_url, app.PGADMIN_KEY) + + try: + driver = webdriver.Firefox() + driver.get(url) + app.logger.debug('Launched Firefox') + except: + try: + driver = webdriver.Chrome() + driver.get(url) + app.logger.debug('Launched Chrome') + except: + try: + driver = webdriver.Ie() + driver.get(url) + app.logger.debug('Launched IE') + except: + app.logger.error('Could not find a suitable browser. Tried Firefox, Chrome and IE. Exiting.') + os._exit(1) + + exited = False + while not exited: + app.logger.debug('Checking browser status...') + try: + driver.get_window_size() + except: + app.logger.debug('Browser quit. Exiting...') + driver.quit() + os._exit(0) + + app.logger.debug('Browser running.') + time.sleep(5) + + thread = threading.Thread(target=go) + thread.start() + +def startup_monitor(): + def start_loop(): + not_started = True + while not_started: + app.logger.debug('Waiting for server startup before launching browser...') + try: + if config.SERVER_MODE: + r = requests.get('%s/misc/ping' % server_url) + else: + r = requests.get('%s/misc/ping?key=%s' % (server_url, app.PGADMIN_KEY)) + + if r.status_code == 200: + app.logger.debug('Server startup appears to be complete.') + not_started = False + except: + app.logger.debug('Server not responding yet.') + time.sleep(2) + + app.logger.debug('Started browser launcher') + thread = threading.Thread(target=start_loop) + thread.start() + if config.DEBUG: app.debug = True else: app.debug = False -# Start the web server. The port number should have already been set by the -# runtime if we're running in desktop mode, otherwise we'll just use the -# Flask default. -PGADMIN_RUNTIME = False -if 'PGADMIN_PORT' in globals(): - app.logger.debug('Running under the desktop runtime, port: %s', - globals()['PGADMIN_PORT']) - server_port = int(globals()['PGADMIN_PORT']) - PGADMIN_RUNTIME = True -elif 'PGADMIN_PORT' in os.environ: - port = os.environ['PGADMIN_PORT'] - app.logger.debug( - 'Not running under the desktop runtime, port: %s', - port) - server_port = int(port) -else: - app.logger.debug( - 'Not running under the desktop runtime, port: %s', - config.DEFAULT_SERVER_PORT) +# Start the web server. If we're in desktop mode, we'll need to set a key +# and get a random port number +if config.SERVER_MODE: + app.PGADMIN_KEY = '' server_port = config.DEFAULT_SERVER_PORT - -# Let the application save the status about the runtime for using it later. -app.PGADMIN_RUNTIME = PGADMIN_RUNTIME - -# Set the key if appropriate -if 'PGADMIN_KEY' in globals(): - app.PGADMIN_KEY = globals()['PGADMIN_KEY'] - app.logger.debug("Desktop security key: %s" % app.PGADMIN_KEY) + server_url = "http://%s:%d" % (config.DEFAULT_SERVER, server_port) + app.logger.debug("Running in server mode. Please navigate to %s in your browser." % + server_url) else: - app.PGADMIN_KEY = '' + app.PGADMIN_KEY = str(uuid.uuid4()) + server_port = get_free_port() + server_url = "http://%s:%d" % (config.DEFAULT_SERVER, server_port) + app.logger.debug("Running in desktop mode. Please navigate to %s?key=%s in your browser." % + (server_url, app.PGADMIN_KEY)) # Output a startup message if we're not under the runtime and startup. # If we're under WSGI, we don't need to worry about this if __name__ == '__main__': - if not PGADMIN_RUNTIME: - print("Starting %s. Please navigate to http://%s:%d in your browser." % - (config.APP_NAME, config.DEFAULT_SERVER, server_port)) - sys.stdout.flush() - else: - # For unknown reason the Qt runtime does not pass the environment - # variables (i.e. PYTHONHOME, and PYTHONPATH), to the Python - # sub-processes, leading to failures executing background processes. - # - # This has been observed only on windows. On *nix systems, it is likely - # picking the system python environment, which is good enough to run - # the process-executor. - # - # Setting PYTHONHOME launch them properly. - from pgadmin.utils import IS_WIN - if IS_WIN: - os.environ['PYTHONHOME'] = sys.prefix - try: + # Start the browser + if not config.SERVER_MODE: + startup_monitor() + + # Start the app app.run( host=config.DEFAULT_SERVER, port=server_port, - use_reloader=((not PGADMIN_RUNTIME) and app.debug), + use_reloader=app.debug, threaded=config.THREADED_MODE ) except IOError: diff --git a/web/pgadmin/misc/bgprocess/processes.py b/web/pgadmin/misc/bgprocess/processes.py index ad6d0ff..41975d7 100644 --- a/web/pgadmin/misc/bgprocess/processes.py +++ b/web/pgadmin/misc/bgprocess/processes.py @@ -228,44 +228,7 @@ class BatchProcess(object): if interpreter is None: interpreter = which(u'python.exe', paths) - if interpreter is None and current_app.PGADMIN_RUNTIME: - # We've faced an issue with Windows 2008 R2 (x86) regarding, - # not honouring the environment variables set under the Qt - # (e.g. runtime), and also setting PYTHONHOME same as - # sys.executable (i.e. pgAdmin4.exe). - # - # As we know, we're running it under the runtime, we can assume - # that 'venv' directory will be available outside of 'bin' - # directory. - # - # We would try out luck to find python executable based on that - # assumptions. - bin_path = os.path.dirname(sys.executable) - - venv = os.path.realpath( - os.path.join(bin_path, u'..\\venv') - ) - - interpreter = which(u'pythonw.exe', [venv]) - if interpreter is None: - interpreter = which(u'pythonw.exe', [venv]) - - if interpreter is not None: - # Our assumptions are proven right. - # Let's append the 'bin' directory to the PATH environment - # variable. And, also set PYTHONHOME environment variable - # to 'venv' directory. - os.environ['PATH'] = bin_path + ';' + os.environ['PATH'] - os.environ['PYTHONHOME'] = venv - else: - # Let's not use sys.prefix in runtime. - # 'sys.prefix' is not identified on *nix systems for some unknown - # reason, while running under the runtime. - # We're already adding '/pgAdmin 4/venv/bin' - # directory in the PATH environment variable. Hence - it will - # anyway be the redundant value in paths. - if not current_app.PGADMIN_RUNTIME: - paths.insert(0, os.path.join(u(sys.prefix), u'bin')) + paths.insert(0, os.path.join(u(sys.prefix), u'bin')) interpreter = which(u'python', paths) p = None diff --git a/web/pgadmin/tools/datagrid/__init__.py b/web/pgadmin/tools/datagrid/__init__.py index fe2d3e5..d154440 100644 --- a/web/pgadmin/tools/datagrid/__init__.py +++ b/web/pgadmin/tools/datagrid/__init__.py @@ -185,7 +185,6 @@ def panel(trans_id, is_query_tool, editor_title): return render_template("datagrid/index.html", _=gettext, uniqueId=trans_id, is_query_tool=is_query_tool, editor_title=editor_title, script_type_url=sURL, - is_desktop_mode=app.PGADMIN_RUNTIME, is_linux=is_linux_platform, is_new_browser_tab=new_browser_tab) diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/index.html b/web/pgadmin/tools/datagrid/templates/datagrid/index.html index 1d71b21..524bd6d 100644 --- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html +++ b/web/pgadmin/tools/datagrid/templates/datagrid/index.html @@ -6,13 +6,6 @@ {% block body %}
diff --git a/web/pgadmin/tools/debugger/__init__.py b/web/pgadmin/tools/debugger/__init__.py index fa414ce..9b65937 100644 --- a/web/pgadmin/tools/debugger/__init__.py +++ b/web/pgadmin/tools/debugger/__init__.py @@ -329,7 +329,6 @@ def direct_new(trans_id): function_name=obj['function_name'], uniqueId=trans_id, debug_type=debug_type, - is_desktop_mode=current_app.PGADMIN_RUNTIME, is_linux=is_linux_platform, stylesheets=[url_for('debugger.static', filename='css/debugger.css')] ) diff --git a/web/pgadmin/tools/debugger/templates/debugger/direct.html b/web/pgadmin/tools/debugger/templates/debugger/direct.html index 297c362..2af9a0a 100644 --- a/web/pgadmin/tools/debugger/templates/debugger/direct.html +++ b/web/pgadmin/tools/debugger/templates/debugger/direct.html @@ -17,16 +17,6 @@ console.log(err); } {% endblock %} {% block body %} -{% if is_desktop_mode and is_linux %} - -{% endif %}