Changeset - 69cd0c056aa1
[Not reviewed]
default
0 3 0
Thomas De Schampheleire - 9 years ago 2017-03-18 21:37:27
thomas.de.schampheleire@gmail.com
config: initialize routes directly from RootController

Let RootController directly initialize routes instead of app_cfg injecting
the mapper.

For test initialization, we also need a handle to the mapper. We could
either recreate it (but fragile if the real mapper initialization is changed
later) or obtain it from a fresh RootController. This commit opts for the
latter.
3 files changed with 13 insertions and 16 deletions:
0 comments (0 inline, 0 general)
kallithea/config/app_cfg.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
"""
 
Global configuration file for TurboGears2 specific settings in Kallithea.
 

	
 
This file complements the .ini file.
 
"""
 

	
 
import platform
 
import os, sys
 

	
 
import tg
 
from tg import hooks
 
from tg.configuration import AppConfig
 
from tg.support.converters import asbool
 

	
 
from kallithea.lib.middleware.https_fixup import HttpsFixup
 
from kallithea.lib.middleware.simplegit import SimpleGit
 
from kallithea.lib.middleware.simplehg import SimpleHg
 
from kallithea.config.routing import make_map
 
from kallithea.lib.auth import set_available_permissions
 
from kallithea.lib.db_manage import DbManage
 
from kallithea.lib.utils import load_rcextensions, make_ui, set_app_settings, set_vcs_config, \
 
    set_indexer_config, check_git_version, repo2db_mapper
 
from kallithea.lib.utils2 import str2bool
 
from kallithea.model.scm import ScmModel
 

	
 
import formencode
 
import kallithea
 

	
 

	
 
class KallitheaAppConfig(AppConfig):
 
    # Note: AppConfig has a misleading name, as it's not the application
 
    # configuration, but the application configurator. The AppConfig values are
 
    # used as a template to create the actual configuration, which might
 
    # overwrite or extend the one provided by the configurator template.
 

	
 
    # To make it clear, AppConfig creates the config and sets into it the same
 
    # values that AppConfig itself has. Then the values from the config file and
 
    # gearbox options are loaded and merged into the configuration. Then an
 
    # after_init_config(conf) method of AppConfig is called for any change that
 
    # might depend on options provided by configuration files.
 

	
 
    def __init__(self):
 
        super(KallitheaAppConfig, self).__init__()
 

	
 
        self['package'] = kallithea
 

	
 
        self['prefer_toscawidgets2'] = False
 
        self['use_toscawidgets'] = False
 

	
 
        self['renderers'] = []
 

	
 
        # Enable json in expose
 
        self['renderers'].append('json')
 

	
 
        # Configure template rendering
 
        self['renderers'].append('mako')
 
        self['default_renderer'] = 'mako'
 
        self['use_dotted_templatenames'] = False
 

	
 
        # Configure Sessions, store data as JSON to avoid pickle security issues
 
        self['session.enabled'] = True
 
        self['session.data_serializer'] = 'json'
 

	
 
        # Configure the base SQLALchemy Setup
 
        self['use_sqlalchemy'] = True
 
        self['model'] = kallithea.model.base
 
        self['DBSession'] = kallithea.model.meta.Session
 

	
 
        # Configure App without an authentication backend.
 
        self['auth_backend'] = None
 

	
 
        # Use custom error page for these errors. By default, Turbogears2 does not add
 
        # 400 in this list.
 
        # Explicitly listing all is considered more robust than appending to defaults,
 
        # in light of possible future framework changes.
 
        self['errorpage.status_codes'] = [400, 401, 403, 404]
 

	
 
        # Disable transaction manager -- currently Kallithea takes care of transactions itself
 
        self['tm.enabled'] = False
 

	
 
base_config = KallitheaAppConfig()
 

	
 
# TODO still needed as long as we use pylonslib
 
sys.modules['pylons'] = tg
 

	
 
# DebugBar, a debug toolbar for TurboGears2.
 
# (https://github.com/TurboGears/tgext.debugbar)
 
# To enable it, install 'tgext.debugbar' and 'kajiki', and run Kallithea with
 
# 'debug = true' (not in production!)
 
# See the Kallithea documentation for more information.
 
try:
 
    from tgext.debugbar import enable_debugbar
 
    import kajiki # only to check its existence
 
except ImportError:
 
    pass
 
else:
 
    base_config['renderers'].append('kajiki')
 
    enable_debugbar(base_config)
 

	
 

	
 
def setup_configuration(app):
 
    config = app.config
 

	
 
    # store some globals into kallithea
 
    kallithea.CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
 
    kallithea.CELERY_EAGER = str2bool(config['app_conf'].get('celery.always.eager'))
 
    kallithea.CONFIG = config
 

	
 
    # Provide routes mapper to the RoutedController
 
    root_controller = app.find_controller('root')
 
    root_controller.mapper = config['routes.map'] = make_map(config)
 

	
 
    load_rcextensions(root_path=config['here'])
 

	
 
    # FIXME move test setup code out of here
 
    test = os.path.split(config['__file__'])[-1] == 'test.ini'
 
    if test:
 
        test_env = not int(os.environ.get('KALLITHEA_NO_TMP_PATH', 0))
 
        test_index = not int(os.environ.get('KALLITHEA_WHOOSH_TEST_DISABLE', 0))
 
        if os.environ.get('TEST_DB'):
 
            # swap config if we pass environment variable
 
            config['sqlalchemy.url'] = os.environ.get('TEST_DB')
 

	
 
        from kallithea.tests.fixture import create_test_env, create_test_index
 
        from kallithea.tests.base import TESTS_TMP_PATH
 
        #set KALLITHEA_NO_TMP_PATH=1 to disable re-creating the database and
 
        #test repos
 
        if test_env:
 
            create_test_env(TESTS_TMP_PATH, config)
 
        #set KALLITHEA_WHOOSH_TEST_DISABLE=1 to disable whoosh index during tests
 
        if test_index:
 
            create_test_index(TESTS_TMP_PATH, config, True)
 

	
 
    set_available_permissions(config)
 
    repos_path = make_ui('db').configitems('paths')[0][1]
 
    config['base_path'] = repos_path
 
    set_app_settings(config)
 

	
 
    instance_id = kallithea.CONFIG.get('instance_id', '*')
 
    if instance_id == '*':
 
        instance_id = '%s-%s' % (platform.uname()[1], os.getpid())
 
        kallithea.CONFIG['instance_id'] = instance_id
 

	
 
    # update kallithea.CONFIG with the meanwhile changed 'config'
 
    kallithea.CONFIG.update(config)
 

	
 
    # configure vcs and indexer libraries (they are supposed to be independent
 
    # as much as possible and thus avoid importing tg.config or
 
    # kallithea.CONFIG).
 
    set_vcs_config(kallithea.CONFIG)
 
    set_indexer_config(kallithea.CONFIG)
 

	
 
    check_git_version()
 

	
 
    if str2bool(config.get('initial_repo_scan', True)):
 
        repo2db_mapper(ScmModel().repo_scan(repos_path),
 
                       remove_obsolete=False, install_git_hooks=False)
 

	
 
    formencode.api.set_stdtranslation(languages=[config.get('lang')])
 

	
 
hooks.register('configure_new_app', setup_configuration)
 

	
 

	
 
def setup_application(app):
 
    config = app.config
 

	
 
    # we want our low level middleware to get to the request ASAP. We don't
 
    # need any stack middleware in them - especially no StatusCodeRedirect buffering
 
    app = SimpleHg(app, config)
 
    app = SimpleGit(app, config)
 

	
 
    # Enable https redirects based on HTTP_X_URL_SCHEME set by proxy
 
    if any(asbool(config.get(x)) for x in ['https_fixup', 'force_https', 'use_htsts']):
 
        app = HttpsFixup(app, config)
 
    return app
 

	
 
hooks.register('before_config', setup_application)
kallithea/controllers/root.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
from tgext.routes import RoutedController
 
from kallithea.config.routing import make_map
 
from kallithea.lib.base import BaseController
 
from kallithea.controllers.error import ErrorController
 

	
 
from tg import config
 

	
 
# With TurboGears, the RootController is the controller from which all routing
 
# starts from. It is 'magically' found based on the fact that a controller
 
# 'foo' is expected to have a class name FooController, located in a file
 
# foo.py, inside config['paths']['controllers']. The name 'root' for the root
 
# controller is the default name. The dictionary config['paths'] determines the
 
# directories where templates, static files and controllers are found. It is
 
# set up in tg.AppConfig based on AppConfig['package'] ('kallithea') and the
 
# respective defaults 'templates', 'public' and 'controllers'.
 
# Inherit from RoutedController to allow Kallithea to use regex-based routing.
 
# This is the main Kallithea entry point; TurboGears will forward all requests
 
# to an instance of 'controller.root.RootController' in the configured
 
# 'application' module (set by app_cfg.py).  Requests are forwarded to
 
# controllers based on the routing mapper that lives in this root instance.
 
# The mapper is configured using routes defined in routing.py.  This use of the
 
# 'mapper' attribute is a feature of tgext.routes, which is activated by
 
# inheriting from its RoutedController class.
 
class RootController(RoutedController, BaseController):
 

	
 
    mapper = make_map(config)
 

	
 
    # the following assignment hooks in error handling
 
    error = ErrorController()
kallithea/tests/conftest.py
Show inline comments
 
import os
 
import sys
 
import logging
 
import pkg_resources
 

	
 
from paste.deploy import loadapp
 
from routes.util import URLGenerator
 
from tg import config
 

	
 
import pytest
 
from kallithea.controllers.root import RootController
 
from kallithea.model.user import UserModel
 
from kallithea.model.meta import Session
 
from kallithea.model.db import Setting, User, UserIpMap
 
from kallithea.tests.base import invalidate_all_caches, TEST_USER_REGULAR_LOGIN
 
import kallithea.tests.base # FIXME: needed for setting testapp instance!!!
 

	
 
from tg.util.webtest import test_context
 

	
 
def pytest_configure():
 
    path = os.getcwd()
 
    sys.path.insert(0, path)
 
    pkg_resources.working_set.add_entry(path)
 

	
 
    # Disable INFO logging of test database creation, restore with NOTSET
 
    logging.disable(logging.INFO)
 
    kallithea.tests.base.testapp = loadapp('config:kallithea/tests/test.ini', relative_to=path)
 
    logging.disable(logging.NOTSET)
 

	
 
    kallithea.tests.base.url = URLGenerator(config['routes.map'], kallithea.tests.base.environ)
 
    kallithea.tests.base.url = URLGenerator(RootController().mapper, kallithea.tests.base.environ)
 

	
 

	
 
@pytest.fixture
 
def create_test_user():
 
    """Provide users that automatically disappear after test is over."""
 
    test_user_ids = []
 
    def _create_test_user(user_form):
 
        user = UserModel().create(user_form)
 
        test_user_ids.append(user.user_id)
 
        return user
 
    yield _create_test_user
 
    for user_id in test_user_ids:
 
        UserModel().delete(user_id)
 
    Session().commit()
 

	
 

	
 
def _set_settings(*kvtseq):
 
    session = Session()
 
    for kvt in kvtseq:
 
        assert len(kvt) in (2, 3)
 
        k = kvt[0]
 
        v = kvt[1]
 
        t = kvt[2] if len(kvt) == 3 else 'unicode'
 
        Setting.create_or_update(k, v, t)
 
    session.commit()
 

	
 

	
 
@pytest.fixture
 
def set_test_settings():
 
    """Restore settings after test is over."""
 
    # Save settings.
 
    settings_snapshot = [
 
        (s.app_settings_name, s.app_settings_value, s.app_settings_type)
 
        for s in Setting.query().all()]
 
    yield _set_settings
 
    # Restore settings.
 
    session = Session()
 
    keys = frozenset(k for (k, v, t) in settings_snapshot)
 
    for s in Setting.query().all():
 
        if s.app_settings_name not in keys:
 
            session.delete(s)
 
    for k, v, t in settings_snapshot:
 
        if t == 'list' and hasattr(v, '__iter__'):
 
            v = ','.join(v) # Quirk: must format list value manually.
 
        Setting.create_or_update(k, v, t)
 
    session.commit()
 

	
 
@pytest.fixture
 
def auto_clear_ip_permissions():
 
    """Fixture that provides nothing but clearing IP permissions upon test
 
    exit. This clearing is needed to avoid other test failing to make fake http
 
    accesses."""
 
    yield
 
    # cleanup
 
    user_model = UserModel()
 

	
 
    user_ids = []
 
    user_ids.append(User.get_default_user().user_id)
 
    user_ids.append(User.get_by_username(TEST_USER_REGULAR_LOGIN).user_id)
 

	
 
    for user_id in user_ids:
 
        for ip in UserIpMap.query().filter(UserIpMap.user_id == user_id):
 
            user_model.delete_extra_ip(user_id, ip.ip_id)
 

	
 
    # IP permissions are cached, need to invalidate this cache explicitly
 
    invalidate_all_caches()
 

	
 
@pytest.fixture
 
def test_context_fixture(app_fixture):
 
    """
 
    Encompass the entire test using this fixture in a test_context,
 
    making sure that certain functionality still works even if no call to
 
    self.app.get/post has been made.
 
    The typical error message indicating you need a test_context is:
 
        TypeError: No object (name: context) has been registered for this thread
 

	
 
    The standard way to fix this is simply using the test_context context
 
    manager directly inside your test:
 
        with test_context(self.app):
 
            <actions>
 
    but if test setup code (xUnit-style or pytest fixtures) also needs to be
 
    executed inside the test context, that method is not possible.
 
    Even if there is no such setup code, the fixture may reduce code complexity
 
    if the entire test needs to run inside a test context.
 

	
 
    To apply this fixture (like any other fixture) to all test methods of a
 
    class, use the following class decorator:
 
        @pytest.mark.usefixtures("test_context_fixture")
 
        class TestFoo(TestController):
 
            ...
 
    """
 
    with test_context(app_fixture):
 
        yield
0 comments (0 inline, 0 general)