Skip to content
This repository was archived by the owner on May 3, 2022. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 0 additions & 35 deletions .travis.yml

This file was deleted.

24 changes: 24 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
// Copy this file to `<workspace>/.vscode/launch.json` to run the Python debugger in Visual Studio Code
// Remember to activate the debugger in `<workspace>/config.json`

// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: phovea_server",
"type": "python",
"request": "attach",
"port": 5678,
"host": "localhost",
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "/phovea"
}
]
}
]
}
2 changes: 2 additions & 0 deletions __main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from __future__ import print_function
from phovea_server import launch

# Test if the phovea_server runs as main program or is embedded (i.e., imported) in a different Python script
# See https://stackoverflow.com/a/419185 for further information.
if __name__ == '__main__':
launch.run()
else:
Expand Down
20 changes: 19 additions & 1 deletion deploy/Dockerfile_dev
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,27 @@ RUN (!(test -s docker_packages.txt) || (apt-get -y update && cat docker_packages
(pip install --no-cache-dir -r requirements_dev.txt)
RUN (!(test -s docker_script.sh) || bash ./docker_script.sh)

####
# Environment mode (dev or prod)
####
ENV PHOVEA_ENV=dev

####
# The name must match the registred command in /phovea_server/phovea_server/__init__.py
# Example: `registry.append('command', 'api', 'phovea_server.server', {'isDefault': True})`
####
ENV PHOVEA_SERVICE=api

####
# The path to the phovea config.json
# In a local workspace setup the <workspace>/config.json is used here.
####
ENV PHOVEA_CONFIG_PATH=config.json
#start ssh and service

####
# Use `phovea_server` as entry point and add some arguments and the service as command.
# In a local workspace setup it will call the /phovea_server/__main__.py, which runs the /phovea_server/launcher.py
####
CMD python phovea_server --use_reloader --env ${PHOVEA_ENV} ${PHOVEA_SERVICE}

EXPOSE 80
1 change: 1 addition & 0 deletions deploy/docker-compose-debug.partial.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ version: '2.0'
services:
api:
ports:
- '5678:5678'
- '2222:22'
command: /usr/sbin/sshd -D
2 changes: 2 additions & 0 deletions phovea_server/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"nocache": false,
"error_stack_trace": false,

"ptvsd_debugger": false,

"port": 80,
"address": "0.0.0.0",

Expand Down
87 changes: 81 additions & 6 deletions phovea_server/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
###############################################################################
from __future__ import print_function
import logging.config
import logging


# set configured registry
Expand All @@ -14,6 +15,8 @@ def _get_config():


cc = _get_config()
# configure logging
logging.config.dictConfig(cc.logging)
_log = logging.getLogger(__name__)


Expand All @@ -33,6 +36,23 @@ def enable_prod_mode():
cc.set('nocache', False)


def attach_ptvsd_debugger():
if cc.getboolean('ptvsd_debugger', default=False) is False:
_log.info('Tip: You can enable the remote debugger `ptvsd` for Visual Studio Code by adding configuration `"ptvsd_debugger": true` to `phovea_server` section in the config.json of your workspace.')
return

try:
import ptvsd
ptvsd.enable_attach()
_log.info('Debugger is started')
_log.info('You can now start the debugger in Visual Studio Code')
_log.info('Waiting for a debugger to attach ...')
ptvsd.wait_for_attach()
_log.info('Debugger successfully attached')
except OSError as exc:
_log.error(exc)


def _config_files():
"""
list all known config files
Expand Down Expand Up @@ -64,7 +84,7 @@ def set_default_subparser(parser, name, args=None):
"""default subparser selection. Call after setup, just before parse_args()
name: is the name of the subparser to call by default
args: if set is the argument list handed to parse_args()
see http://stackoverflow.com/questions/5176691/argparse-how-to-specify-a-default-subcommand
see https://stackoverflow.com/a/26378414

, tested with 2.7, 3.2, 3.3, 3.4
it works with 2.6 assuming argparse is installed
Expand Down Expand Up @@ -93,28 +113,77 @@ def set_default_subparser(parser, name, args=None):


def _resolve_commands(parser):
"""
Resolve commands from the phovea registy, loads the phovea extension, adds the instance to the command parser.
"""
from .plugin import list as list_plugins

# create a subparser
subparsers = parser.add_subparsers(dest='cmd')

default_command = None

for command in list_plugins('command'):
_log.info('add command ' + command.id)
_log.info('add command: ' + command.id)

if hasattr(command, 'isDefault') and command.isDefault:
_log.info('set default command: ' + command.id)
default_command = command.id

# create a argument parser for the command
cmdparser = subparsers.add_parser(command.id)

_log.info('loading and initializing the command: ' + command.id)
# use the phovea extension point loading mechanism.
# pass the parser as argument to the factory method so that the extension point (i.e., command)
# can add further arguments to the parser (e.g., the address or port of the server).
# the factory must return a launcher function, which gets the previously defined parser arguments as parameter.
instance = command.load().factory(cmdparser)

# register the instance as argument `launcher` and the command as `launcherid` to the command parser
_log.info('add command instance to parser')
cmdparser.set_defaults(launcher=instance, launcherid=command.id)

return default_command


def _set_runtime_infos(args):
"""
Set run time information, such as the executed command (registered as phovea extension point).
The information is, for instance, used in the plugin.py when initializing the phovea registry.
Additionally the configuration value `absoluteDir` is set.
"""
import os
runtime = cc.view('_runtime')
runtime.set('command', args.launcherid)
runtime.set('reloader', args.use_reloader)

cc.set('absoluteDir', os.path.abspath(cc.get('dir')) + '/')


def run():
"""
Run an application. The execution of the application can be configured using a command and arguments.

Example terminal command:
```
cd <workspace>
python phovea_server --use_reloader --env dev api
```

Supported arguments:
`--use_reloader`: whether to automatically reload the server
`--env`: environment mode (dev or prod)

The last argument (e.g., `api`) is the command that must be registered as extension in the __init__.py and points to an execution file.

Example:
```py
registry.append('command', 'api', 'phovea_server.server', {'isDefault': True})
```

The example registers the api command that runs the `create()` factory method from the server.py.
"""
import argparse

parser = argparse.ArgumentParser(description='Phovea Server')
Expand All @@ -125,27 +194,33 @@ def run():
args = parser.parse_known_args()[0]
if args.env.startswith('dev'):
enable_dev_mode()
attach_ptvsd_debugger()
else:
enable_prod_mode()

# resolve the default command to decide which application to launch
default_command = _resolve_commands(parser)
if default_command is not None:
# set a default subparse to extract the defined arguments from the instance to the main arguments (?)
set_default_subparser(parser, default_command)

args = parser.parse_args()

_set_runtime_infos(args)
main = args.launcher(args)

main = args.launcher(args) # execute the launcher function, which returns another function

if args.use_reloader:
_log.info('start using reloader...')
_log.info('start application using reloader...')
from werkzeug._reloader import run_with_reloader
run_with_reloader(main, extra_files=_config_files())
else:
_log.info('start...')
_log.info('start application...')
main()


def create_embedded():
"""
Imports the phovea_server and creates an application
"""
from .server import create_application
return create_application()
1 change: 1 addition & 0 deletions phovea_server/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ def loader(e):

cc = view('phovea_server._runtime')
current_command = cc.get('command', default='unknown')
_log.info('read currently executed command from config: %s', current_command)

def compare(a, b):
a_prio = getattr(a, 'priority', 100)
Expand Down
24 changes: 19 additions & 5 deletions phovea_server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def _get_config():
# append the plugin directories as primary lookup path
cc = _get_config()
# configure logging
logging.config.dictConfig(cc.logging)
# logging.config.dictConfig(cc.logging) # configuration already set in launch.py
_log = logging.getLogger(__name__)


Expand Down Expand Up @@ -139,7 +139,7 @@ def _init_app(app, namespace, is_default_app=False):

# helper to plugin in function scope
def _loader(p):
print('add application: ' + p.id + ' at namespace: ' + p.namespace)
_log.info('add application: ' + p.id + ' at namespace: ' + p.namespace)

def load_app():
app = p.load().factory()
Expand Down Expand Up @@ -187,17 +187,31 @@ def create_application():


def create(parser):
parser.add_argument('--port', '-p', type=int, default=cc.getint('port'),
"""
Add arguments to the parser and return a launcher function, which in-turn creates a server instance

parser: ArgumentParser that allows to add custom arguments. The arguments will be passed as parameter to the launcher function.
"""
parser.add_argument('--port', '-p', type=int, default=cc.getint('port'), # get default value from config.json
help='server port')
parser.add_argument('--address', '-a', default=cc.get('address'),
parser.add_argument('--address', '-a', default=cc.get('address'), # get default value from config.json
help='server address')

def _launcher(args):
"""
Prepare the launch of the server instance

args: contains the arguments that are parsed from the command line (or set in the config as default value)
"""
from geventwebsocket.handler import WebSocketHandler
from gevent.pywsgi import WSGIServer

# create phovea server application
application = create_application()

_log.info('prepare server that will listen on %s:%s', args.address, args.port)
http_server = WSGIServer((args.address, args.port), application, handler_class=WebSocketHandler)

return http_server.serve_forever
return http_server.serve_forever # return function name only; initialization will be done later

return _launcher
1 change: 1 addition & 0 deletions requirements_dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ pytest==3.0.3
pytest-runner==2.9
Sphinx==1.5.2
recommonmark==0.4.0
ptvsd==4.3.2