2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -4,7 +4,7 @@ travis:

clean:
find . -name "*.pyc" -exec rm -vf {} \;
find -name __pycache__ -deete
find -name __pycache__ -delete

tox:
tox
Expand Down
File renamed without changes.
7 changes: 2 additions & 5 deletions silpa/api/__init__.py
@@ -1,7 +1,6 @@
from flask import jsonify
from functools import wraps
from .. import factory
from .. import moduleloader


def route(bp, *args, **kwargs):
Expand All @@ -16,13 +15,11 @@ def wrapper(*args, **kwargs):
return decorator


def create_app(settings_override=None):
def create_app(conffile, settings_override=None):
app = factory.create_app(__name__, __path__,
settings_override)
settings_override, conffile)
app.errorhandler(404)(on_404)

# Load enabled modules from the config
moduleloader.load_modules()
return app


Expand Down
26 changes: 22 additions & 4 deletions silpa/api/jsonrpc.py
Expand Up @@ -20,7 +20,7 @@
INTERNAL_ERROR = -32603


bp = Blueprint('JSONRPC', __name__, url_prefix='/api')
bp = Blueprint('api_jsonrpc', __name__, url_prefix='/api')


@route(bp, '/JSONRPC', methods=['POST'])
Expand Down Expand Up @@ -85,8 +85,17 @@ def __call__(self):
# process request here
module, method = self.request.method.split('.')
if module not in sys.modules:
# Module is not yet loaded? handle this
pass
# Module is not yet loaded or the request module is not
# enabled pass an error here.
error = JsonRpcError(code=INTERNAL_ERROR,
message="Requested module is not loaded or not\
enabled by Admin",
data="{} is not loaded".format(module))
self.error_response = JsonRpcErrorResponse(jsonrpc="2.0",
error=dict(zip(
error._fields,
error)),
id=self.request.id)
else:
# module is present in sys
mod = sys.modules[module]
Expand All @@ -110,4 +119,13 @@ def __call__(self):
id=self.request.id)
else:
# module doesn't provide an interface to us?
pass
error = JsonRpcError(code=INTERNAL_ERROR,
message="Requested module doesn't provide \
getInstance interface",
data="{} is the module requested".format(
module))
self.error_response = JsonRpcErrorResponse(jsonrpc='2.0',
error=dict(zip(
error._fields,
error)),
id=self.request.id)
20 changes: 16 additions & 4 deletions silpa/factory.py
Expand Up @@ -3,10 +3,12 @@
import logging
import os


from flask import Flask, Blueprint
from .loadconfig import config
from .loadconfig import Config
from logging import Formatter
from logging.handlers import TimedRotatingFileHandler
from .helper import ModuleConfigHelper


def register_blueprints(app, package_name, package_path):
Expand All @@ -22,7 +24,7 @@ def register_blueprints(app, package_name, package_path):
return rv


def configure_logging(app):
def configure_logging(app, config):
log_level = config.get('logging', 'log_level')
log_folder = config.get('logging', 'log_folder')
log_name = config.get('logging', 'log_name')
Expand All @@ -49,13 +51,23 @@ def configure_logging(app):
app.logger.addHandler(handler)


def create_app(package_name, package_path, settings_override=None):
def create_app(package_name, package_path, settings_override=None,
conffile="silpa.conf"):
app = Flask(package_name, instance_relative_config=True)
app.config.from_object("silpa.settings")
app.config.from_pyfile('settings.cfg', silent=True)
app.config.from_object(settings_override)

config = Config(conffile)
configure_logging(app, config)

# Create ModuleConfigHelper class and pass it config this will
# instantiate class variables
ModuleConfigHelper(config=config)
ModuleConfigHelper.load_modules()

# Register blueprints at end so we have module,display and other
# stuff created
register_blueprints(app, package_name, package_path)
configure_logging(app)

return app
9 changes: 4 additions & 5 deletions silpa/frontend/__init__.py
@@ -1,18 +1,17 @@
from .. import factory
from jinja2 import PackageLoader, ChoiceLoader
from ..loadconfig import config
from ..helper import ModuleConfigHelper


def create_app(settings_override=None):
def create_app(conffile, settings_override=None):
app = factory.create_app(__name__, __path__,
settings_override)
settings_override, conffile)
load_module_templates(app)
return app


def load_module_templates(app):
modules = [module for module, need in config.items('modules')
if need == 'yes']
modules = ModuleConfigHelper.get_modules()
templates = [app.jinja_loader]
for module in modules:
templates.append(PackageLoader(module))
Expand Down
32 changes: 14 additions & 18 deletions silpa/frontend/pages.py
@@ -1,16 +1,12 @@
from flask import Blueprint, render_template, abort
from ..loadconfig import config
from ..helper import ModuleConfigHelper

_BASE_URL = config.get('main', 'baseurl')
_modules = [module for module, need in config.items('modules')
if need == 'yes']
_modulename_to_display = sorted((display_name
for module, display_name in
config.items('module_display')
if module in _modules))
_display_module_map = {display_name: module for module, display_name in
config.items('module_display')
if module in _modules}
_BASE_URL = ModuleConfigHelper.get_baseurl()
_modules = ModuleConfigHelper.get_modules()
_modulename_to_display = ModuleConfigHelper.get_module_displaynames()

_display_module_map = sorted(zip(_modulename_to_display.keys(),
_modulename_to_display.values()))

bp = Blueprint('frontend', __name__)

Expand All @@ -21,25 +17,25 @@ def serve_pages(page):
if page == "index.html":
return render_template('index.html', title='SILPA',
main_page=_BASE_URL,
modules=_modulename_to_display)
modules=_display_module_map)
elif page == "License":
return render_template('license.html', title='SILPA License',
main_page=_BASE_URL,
modules=_modulename_to_display)
modules=_display_module_map)
elif page == "Credits":
return render_template('credits.html', title='Credits',
main_page=_BASE_URL,
modules=_modulename_to_display)
modules=_display_module_map)
elif page == "Contact":
return render_template('contact.html', title='Contact SILPA Team',
main_page=_BASE_URL,
modules=_modulename_to_display)
modules=_display_module_map)
else:
# modules requested!.
if page in _display_module_map:
return render_template(_display_module_map[page] + '.html',
if page in _modules:
return render_template(page + '.html',
title=page, main_page=_BASE_URL,
modules=_modulename_to_display)
modules=_display_module_map)
else:
# Did we encounter something which is not registered by us?
return abort(404)
4 changes: 2 additions & 2 deletions silpa/frontend/templates/silpa.html
Expand Up @@ -33,8 +33,8 @@
<!-- side bar goes here -->
<ul class="nav nav-tabs nav-stacked" role="navigation">
<li class="navbar-brand">Applications</li>
{% for module in modules %}
<li><a href="{{ url_for('.serve_pages', page=module) }}">{{ module }}</a></li>
{% for module,display in modules %}
<li><a href="{{ url_for('.serve_pages', page=module) }}">{{ display }}</a></li>
{% endfor %}
</ul>
</div>
Expand Down
43 changes: 43 additions & 0 deletions silpa/helper.py
@@ -0,0 +1,43 @@
from __future__ import print_function

import importlib
import sys


class ModuleConfigHelper(object):
module_names = []
module_display = {}
base_url = None

def __new__(cls, *args, **kwargs):
config = kwargs['config']
cls.module_names = [module for module, need in config.items('modules')
if need == 'yes']

cls.module_display = {module: display_name for module, display_name in
config.items('module_display')
if module in cls.module_names}
cls.base_url = config.get('main', 'baseurl')
return super(ModuleConfigHelper, cls).__new__(cls)

@classmethod
def get_modules(cls):
return cls.module_names

@classmethod
def get_module_displaynames(cls):
return cls.module_display

@classmethod
def get_baseurl(cls):
return cls.base_url

@classmethod
def load_modules(cls):
for module in cls.module_names:
try:
importlib.import_module(module)
except ImportError as e:
print("Failed to import {module}: {message}".
format(module=module, message=e.message),
file=sys.stderr)
7 changes: 2 additions & 5 deletions silpa/loadconfig.py
@@ -1,7 +1,6 @@
__all__ = ['IncompleteConfigError', 'config']
__all__ = ['IncompleteConfigError', 'Config']

import configparser
import os


class IncompleteConfigError(Exception):
Expand All @@ -19,7 +18,7 @@ def __str__(self):
section=self.section)


class _Config(configparser.ConfigParser):
class Config(configparser.ConfigParser):

def __init__(self, location="silpa.conf"):
configparser.ConfigParser.__init__(self)
Expand All @@ -42,5 +41,3 @@ def verify(self):
def _verify_item(self, section, option):
if not self.has_option(section, option):
raise IncompleteConfigError(section, option)

config = _Config(os.path.join(os.path.dirname(__file__), "silpa.conf"))
18 changes: 0 additions & 18 deletions silpa/moduleloader.py

This file was deleted.

5 changes: 4 additions & 1 deletion tests/api/__init__.py
@@ -1,11 +1,14 @@
from silpa.api import create_app
from .. import SILPAAppTestCase, settings
import os


class SILPAApiTestCase(SILPAAppTestCase):

def _create_app(self):
return create_app(settings)
self.conffile = os.path.join(os.path.dirname(__file__), '../resources',
'silpa.conf')
return create_app(self.conffile, settings)

def setUp(self):
super(SILPAApiTestCase, self).setUp()
22 changes: 22 additions & 0 deletions tests/api/jsonrpc_tests.py
Expand Up @@ -32,6 +32,12 @@ def assertJsonRpcParseErrors(self, response):
error_obj = jsonrpc.JsonRpcError(**response_dict['error'])
self.assertEquals(error_obj.code, jsonrpc.PARSE_ERRORS)

def assertJsonRpcInternalError(self, response):
response_dict = json.loads(self.assertBadJson(response).data)
self.assertIn('error', response_dict)
error_obj = jsonrpc.JsonRpcError(**response_dict['error'])
self.assertEquals(error_obj.code, jsonrpc.INTERNAL_ERROR)

def assertJsonRpcResult(self, response):
response_dict = json.loads(self.assertOkJson(response).data)
self.assertIn('result', response_dict)
Expand Down Expand Up @@ -65,3 +71,19 @@ def test_result_jsonrpc(self):
def test_notfound(self):
r = self.post('/api/JS')
self.assertStatusCode(r, 404)

def test_module_notloaded(self):
# Test assumes scriptrender module will never be enabled in
# test setup configuration
data = dict(jsonrpc='2.0',
method='scriptrender.render_text',
params=['some text', 'png', 'Black'],
id=random.randint(1, 1000))
self.assertJsonRpcInternalError(self.jpost('/api/JSONRPC', data=data))

def test_no_interface(self):
data = dict(jsonrpc='2.0',
method='flask.Flask',
params=[__name__],
id=random.randint(1, 1000))
self.assertJsonRpcInternalError(self.jpost('/api/JSONRPC', data=data))
5 changes: 4 additions & 1 deletion tests/frontend/__init__.py
Expand Up @@ -8,12 +8,15 @@

from silpa.frontend import create_app
from .. import SILPAAppTestCase, settings
import os


class SILPAFrontEndTestCase(SILPAAppTestCase):

def _create_app(self):
return create_app(settings)
self.conffile = os.path.join(os.path.dirname(__file__), '../resources',
'silpa.conf')
return create_app(self.conffile, settings)

def setUp(self):
super(SILPAFrontEndTestCase, self).setUp()