##// END OF EJS Templates
backend: replace Pylons with TurboGears2...
Alessandro Molina -
r6522:e1ab8261 default
parent child Browse files
Show More
@@ -0,0 +1,31 b''
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 from tgext.routes import RoutedController
15 from kallithea.lib.base import BaseController
16 from kallithea.controllers.error import ErrorController
17
18
19 # With TurboGears, the RootController is the controller from which all routing
20 # starts from. It is 'magically' found based on the fact that a controller
21 # 'foo' is expected to have a class name FooController, located in a file
22 # foo.py, inside config['paths']['controllers']. The name 'root' for the root
23 # controller is the default name. The dictionary config['paths'] determines the
24 # directories where templates, static files and controllers are found. It is
25 # set up in tg.AppConfig based on AppConfig['package'] ('kallithea') and the
26 # respective defaults 'templates', 'public' and 'controllers'.
27 # Inherit from RoutedController to allow Kallithea to use regex-based routing.
28 class RootController(RoutedController, BaseController):
29
30 # the following assignment hooks in error handling
31 error = ErrorController()
@@ -6,3 +6,4 b' pytest-sugar>=0.7.0'
6 6 pytest-catchlog
7 7 mock
8 8 sphinx
9 webtest < 3
@@ -512,7 +512,7 b' script_location = kallithea:alembic'
512 512 ################################
513 513
514 514 [loggers]
515 keys = root, routes, kallithea, sqlalchemy, gearbox, beaker, templates, whoosh_indexer
515 keys = root, routes, kallithea, sqlalchemy, tg, gearbox, beaker, templates, whoosh_indexer
516 516
517 517 [handlers]
518 518 keys = console, console_sql
@@ -553,6 +553,12 b' handlers ='
553 553 qualname = kallithea
554 554 propagate = 1
555 555
556 [logger_tg]
557 level = DEBUG
558 handlers =
559 qualname = tg
560 propagate = 1
561
556 562 [logger_gearbox]
557 563 level = DEBUG
558 564 handlers =
@@ -30,10 +30,6 b' Original author and date, and relevant c'
30 30 import sys
31 31 import platform
32 32
33 # temporary aliasing to allow early introduction of imports like 'from tg import request'
34 import pylons
35 sys.modules['tg'] = pylons
36
37 33 VERSION = (0, 3, 99)
38 34 BACKENDS = {
39 35 'hg': 'Mercurial repository',
@@ -11,76 +11,112 b''
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
15 Global configuration file for TurboGears2 specific settings in Kallithea.
14 16
15 import os
16 import kallithea
17 This file complements the .ini file.
18 """
19
17 20 import platform
18
19 import pylons
20 import mako.lookup
21 import formencode
21 import os, sys
22 22
23 import kallithea.lib.app_globals as app_globals
24
25 from kallithea.config.routing import make_map
23 import tg
24 from tg import hooks
25 from tg.configuration import AppConfig
26 from tg.support.converters import asbool
26 27
27 from kallithea.lib import helpers
28 from kallithea.lib.middleware.https_fixup import HttpsFixup
29 from kallithea.lib.middleware.simplegit import SimpleGit
30 from kallithea.lib.middleware.simplehg import SimpleHg
31 from kallithea.config.routing import make_map
28 32 from kallithea.lib.auth import set_available_permissions
29 from kallithea.lib.utils import repo2db_mapper, make_ui, set_app_settings, \
30 load_rcextensions, check_git_version, set_vcs_config, set_indexer_config
31 from kallithea.lib.utils2 import engine_from_config, str2bool
32 from kallithea.model.base import init_model
33 from kallithea.lib.db_manage import DbManage
34 from kallithea.lib.utils import load_rcextensions, make_ui, set_app_settings, set_vcs_config, \
35 set_indexer_config, check_git_version, repo2db_mapper
36 from kallithea.lib.utils2 import str2bool
33 37 from kallithea.model.scm import ScmModel
34 38
35 from routes.middleware import RoutesMiddleware
36 from paste.cascade import Cascade
37 from paste.registry import RegistryManager
38 from paste.urlparser import StaticURLParser
39 from paste.deploy.converters import asbool
39 import formencode
40 import kallithea
41
42
43 class KallitheaAppConfig(AppConfig):
44 # Note: AppConfig has a misleading name, as it's not the application
45 # configuration, but the application configurator. The AppConfig values are
46 # used as a template to create the actual configuration, which might
47 # overwrite or extend the one provided by the configurator template.
48
49 # To make it clear, AppConfig creates the config and sets into it the same
50 # values that AppConfig itself has. Then the values from the config file and
51 # gearbox options are loaded and merged into the configuration. Then an
52 # after_init_config(conf) method of AppConfig is called for any change that
53 # might depend on options provided by configuration files.
54
55 def __init__(self):
56 super(KallitheaAppConfig, self).__init__()
57
58 self['package'] = kallithea
59
60 self['prefer_toscawidgets2'] = False
61 self['use_toscawidgets'] = False
62
63 self['renderers'] = []
64
65 # Enable json in expose
66 self['renderers'].append('json')
40 67
41 from pylons.middleware import ErrorHandler, StatusCodeRedirect
42 from pylons.wsgiapp import PylonsApp
68 # Configure template rendering
69 self['renderers'].append('mako')
70 self['default_renderer'] = 'mako'
71 self['use_dotted_templatenames'] = False
72
73 # Configure Sessions, store data as JSON to avoid pickle security issues
74 self['session.enabled'] = True
75 self['session.data_serializer'] = 'json'
76
77 # Configure the base SQLALchemy Setup
78 self['use_sqlalchemy'] = True
79 self['model'] = kallithea.model.base
80 self['DBSession'] = kallithea.model.meta.Session
81
82 # Configure App without an authentication backend.
83 self['auth_backend'] = None
43 84
44 from kallithea.lib.middleware.simplehg import SimpleHg
45 from kallithea.lib.middleware.simplegit import SimpleGit
46 from kallithea.lib.middleware.https_fixup import HttpsFixup
47 from kallithea.lib.middleware.sessionmiddleware import SecureSessionMiddleware
48 from kallithea.lib.middleware.wrapper import RequestWrapper
85 # Use custom error page for these errors. By default, Turbogears2 does not add
86 # 400 in this list.
87 # Explicitly listing all is considered more robust than appending to defaults,
88 # in light of possible future framework changes.
89 self['errorpage.status_codes'] = [400, 401, 403, 404]
49 90
50 def setup_configuration(config, paths, app_conf, test_env, test_index):
91 # Disable transaction manager -- currently Kallithea takes care of transactions itself
92 self['tm.enabled'] = False
93
94 base_config = KallitheaAppConfig()
95
96 # TODO still needed as long as we use pylonslib
97 sys.modules['pylons'] = tg
98
99 def setup_configuration(app):
100 config = app.config
51 101
52 102 # store some globals into kallithea
53 103 kallithea.CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
54 104 kallithea.CELERY_EAGER = str2bool(config['app_conf'].get('celery.always.eager'))
105 kallithea.CONFIG = config
55 106
56 config['routes.map'] = make_map(config)
57 config['pylons.app_globals'] = app_globals.Globals(config)
58 config['pylons.h'] = helpers
59 kallithea.CONFIG = config
107 # Provide routes mapper to the RoutedController
108 root_controller = app.find_controller('root')
109 root_controller.mapper = config['routes.map'] = make_map(config)
60 110
61 111 load_rcextensions(root_path=config['here'])
62 112
63 # Setup cache object as early as possible
64 pylons.cache._push_object(config['pylons.app_globals'].cache)
65
66 # Create the Mako TemplateLookup, with the default auto-escaping
67 config['pylons.app_globals'].mako_lookup = mako.lookup.TemplateLookup(
68 directories=paths['templates'],
69 strict_undefined=True,
70 module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
71 input_encoding='utf-8', default_filters=['escape'],
72 imports=['from webhelpers.html import escape'])
73
74 # sets the c attribute access when don't existing attribute are accessed
75 config['pylons.strict_tmpl_context'] = True
113 # FIXME move test setup code out of here
76 114 test = os.path.split(config['__file__'])[-1] == 'test.ini'
77 115 if test:
78 if test_env is None:
79 test_env = not int(os.environ.get('KALLITHEA_NO_TMP_PATH', 0))
80 if test_index is None:
81 test_index = not int(os.environ.get('KALLITHEA_WHOOSH_TEST_DISABLE', 0))
116 test_env = not int(os.environ.get('KALLITHEA_NO_TMP_PATH', 0))
117 test_index = not int(os.environ.get('KALLITHEA_WHOOSH_TEST_DISABLE', 0))
82 118 if os.environ.get('TEST_DB'):
83 # swap config if we pass enviroment variable
119 # swap config if we pass environment variable
84 120 config['sqlalchemy.url'] = os.environ.get('TEST_DB')
85 121
86 122 from kallithea.tests.fixture import create_test_env, create_test_index
@@ -93,11 +129,6 b' def setup_configuration(config, paths, a'
93 129 if test_index:
94 130 create_test_index(TESTS_TMP_PATH, config, True)
95 131
96 # MULTIPLE DB configs
97 # Setup the SQLAlchemy database engine
98 sa_engine = engine_from_config(config, 'sqlalchemy.')
99 init_model(sa_engine)
100
101 132 set_available_permissions(config)
102 133 repos_path = make_ui('db').configitems('paths')[0][1]
103 134 config['base_path'] = repos_path
@@ -108,78 +139,37 b' def setup_configuration(config, paths, a'
108 139 instance_id = '%s-%s' % (platform.uname()[1], os.getpid())
109 140 kallithea.CONFIG['instance_id'] = instance_id
110 141
111 # CONFIGURATION OPTIONS HERE (note: all config options will override
112 # any Pylons config options)
142 # update kallithea.CONFIG with the meanwhile changed 'config'
143 kallithea.CONFIG.update(config)
113 144
114 # store config reference into our module to skip import magic of
115 # pylons
116 kallithea.CONFIG.update(config)
145 # configure vcs and indexer libraries (they are supposed to be independent
146 # as much as possible and thus avoid importing tg.config or
147 # kallithea.CONFIG).
117 148 set_vcs_config(kallithea.CONFIG)
118 149 set_indexer_config(kallithea.CONFIG)
119 150
120 #check git version
121 151 check_git_version()
122 152
123 153 if str2bool(config.get('initial_repo_scan', True)):
124 154 repo2db_mapper(ScmModel().repo_scan(repos_path),
125 155 remove_obsolete=False, install_git_hooks=False)
156
126 157 formencode.api.set_stdtranslation(languages=[config.get('lang')])
127 158
128 return config
129
130 def setup_application(config, global_conf, full_stack, static_files):
159 hooks.register('configure_new_app', setup_configuration)
131 160
132 # The Pylons WSGI app
133 app = PylonsApp(config=config)
134
135 # Routing/Session/Cache Middleware
136 app = RoutesMiddleware(app, config['routes.map'], use_method_override=False)
137 app = SecureSessionMiddleware(app, config)
138 161
139 # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
140 if asbool(config['pdebug']):
141 from kallithea.lib.profiler import ProfilingMiddleware
142 app = ProfilingMiddleware(app)
143
144 if asbool(full_stack):
145
146 from kallithea.lib.middleware.sentry import Sentry
147 from kallithea.lib.middleware.appenlight import AppEnlight
148 if AppEnlight and asbool(config['app_conf'].get('appenlight')):
149 app = AppEnlight(app, config)
150 elif Sentry:
151 app = Sentry(app, config)
152
153 # Handle Python exceptions
154 app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
162 def setup_application(app):
163 config = app.config
155 164
156 # Display error documents for 401, 403, 404 status codes (and
157 # 500 when debug is disabled)
158 # Note: will buffer the output in memory!
159 if asbool(config['debug']):
160 app = StatusCodeRedirect(app)
161 else:
162 app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])
163
164 # we want our low level middleware to get to the request ASAP. We don't
165 # need any pylons stack middleware in them - especially no StatusCodeRedirect buffering
166 app = SimpleHg(app, config)
167 app = SimpleGit(app, config)
165 # we want our low level middleware to get to the request ASAP. We don't
166 # need any stack middleware in them - especially no StatusCodeRedirect buffering
167 app = SimpleHg(app, config)
168 app = SimpleGit(app, config)
168 169
169 # Enable https redirects based on HTTP_X_URL_SCHEME set by proxy
170 if any(asbool(config.get(x)) for x in ['https_fixup', 'force_https', 'use_htsts']):
171 app = HttpsFixup(app, config)
172
173 app = RequestWrapper(app, config) # logging
174
175 # Establish the Registry for this application
176 app = RegistryManager(app) # thread / request-local module globals / variables
170 # Enable https redirects based on HTTP_X_URL_SCHEME set by proxy
171 if any(asbool(config.get(x)) for x in ['https_fixup', 'force_https', 'use_htsts']):
172 app = HttpsFixup(app, config)
173 return app
177 174
178 if asbool(static_files):
179 # Serve static files
180 static_app = StaticURLParser(config['pylons.paths']['static_files'])
181 app = Cascade([static_app, app])
182
183 app.config = config
184
185 return app
175 hooks.register('before_config', setup_application)
@@ -11,34 +11,11 b''
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
15 Pylons environment configuration
16 """
14 """WSGI environment setup for Kallithea."""
17 15
18 import os
19 import kallithea
20 import pylons
21
22 from kallithea.config.app_cfg import setup_configuration
16 from kallithea.config.app_cfg import base_config
23 17
24 def load_environment(global_conf, app_conf,
25 test_env=None, test_index=None):
26 """
27 Configure the Pylons environment via the ``pylons.config``
28 object
29 """
30 config = pylons.configuration.PylonsConfig()
18 __all__ = ['load_environment']
31 19
32 # Pylons paths
33 root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
34 paths = dict(
35 root=root,
36 controllers=os.path.join(root, 'controllers'),
37 static_files=os.path.join(root, 'public'),
38 templates=[os.path.join(root, 'templates')]
39 )
40
41 # Initialize config with the basic options
42 config.init_app(global_conf, app_conf, package='kallithea', paths=paths)
43
44 return setup_configuration(config, paths, app_conf, test_env, test_index)
20 # Use base_config to setup the environment loader function
21 load_environment = base_config.make_load_environment()
@@ -11,33 +11,35 b''
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
15 Pylons middleware initialization
16 """
14 """WSGI middleware initialization for the Kallithea application."""
17 15
18 from kallithea.config.app_cfg import setup_application
16 from kallithea.config.app_cfg import base_config
19 17 from kallithea.config.environment import load_environment
20 18
21 def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
22 """Create a Pylons WSGI application and return it
19 __all__ = ['make_app']
20
21 # Use base_config to setup the necessary PasteDeploy application factory.
22 # make_base_app will wrap the TurboGears2 app with all the middleware it needs.
23 make_base_app = base_config.setup_tg_wsgi_app(load_environment)
23 24
24 ``global_conf``
25 The inherited configuration for this application. Normally from
26 the [DEFAULT] section of the Paste ini file.
25
26 def make_app(global_conf, full_stack=True, **app_conf):
27 """
28 Set up Kallithea with the settings found in the PasteDeploy configuration
29 file used.
27 30
28 ``full_stack``
29 Whether or not this application provides a full WSGI stack (by
30 default, meaning it handles its own exceptions and errors).
31 Disable full_stack when this application is "managed" by
32 another WSGI middleware.
31 :param global_conf: The global settings for Kallithea (those
32 defined under the ``[DEFAULT]`` section).
33 :type global_conf: dict
34 :param full_stack: Should the whole TurboGears2 stack be set up?
35 :type full_stack: str or bool
36 :return: The Kallithea application with all the relevant middleware
37 loaded.
33 38
34 ``app_conf``
35 The application's local configuration. Normally specified in
36 the [app:<name>] section of the Paste ini file (where <name>
37 defaults to main).
39 This is the PasteDeploy factory for the Kallithea application.
38 40
41 ``app_conf`` contains all the application-specific settings (those defined
42 under ``[app:main]``.
39 43 """
40 # Configure the Pylons environment
41 config = load_environment(global_conf, app_conf)
42
43 return setup_application(config, global_conf, full_stack, static_files)
44 app = make_base_app(global_conf, full_stack=full_stack, **app_conf)
45 return app
@@ -28,7 +28,7 b" ADMIN_PREFIX = '/_admin'"
28 28
29 29 def make_map(config):
30 30 """Create, configure and return the routes Mapper"""
31 rmap = Mapper(directory=config['pylons.paths']['controllers'],
31 rmap = Mapper(directory=config['paths']['controllers'],
32 32 always_scan=config['debug'])
33 33 rmap.minimization = False
34 34 rmap.explicit = False
@@ -46,7 +46,7 b' def make_map(config):'
46 46 repo_name = match_dict.get('repo_name')
47 47
48 48 if match_dict.get('f_path'):
49 #fix for multiple initial slashes that causes errors
49 # fix for multiple initial slashes that causes errors
50 50 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
51 51
52 52 by_id_match = get_repo_by_id(repo_name)
@@ -90,11 +90,6 b' def make_map(config):'
90 90 def check_int(environ, match_dict):
91 91 return match_dict.get('id').isdigit()
92 92
93 # The ErrorController route (handles 404/500 error pages); it should
94 # likely stay at the top, ensuring it can always be resolved
95 rmap.connect('/error/{action}', controller='error')
96 rmap.connect('/error/{action}/{id}', controller='error')
97
98 93 #==========================================================================
99 94 # CUSTOM ROUTES HERE
100 95 #==========================================================================
@@ -426,8 +421,8 b' def make_map(config):'
426 421 #==========================================================================
427 422 # API V2
428 423 #==========================================================================
429 with rmap.submapper(path_prefix=ADMIN_PREFIX,
430 controller='api/api') as m:
424 with rmap.submapper(path_prefix=ADMIN_PREFIX, controller='api/api',
425 action='_dispatch') as m:
431 426 m.connect('api', '/api')
432 427
433 428 #USER JOURNAL
@@ -32,7 +32,7 b' import itertools'
32 32
33 33 from formencode import htmlfill
34 34
35 from tg import request, tmpl_context as c
35 from tg import request, tmpl_context as c, app_globals
36 36 from tg.i18n import ugettext as _, ungettext
37 37 from webob.exc import HTTPFound, HTTPForbidden, HTTPNotFound, HTTPInternalServerError
38 38
@@ -112,7 +112,7 b' class RepoGroupsController(BaseControlle'
112 112 group_iter = RepoGroupList(_list, perm_level='admin')
113 113 repo_groups_data = []
114 114 total_records = len(group_iter)
115 _tmpl_lookup = kallithea.CONFIG['pylons.app_globals'].mako_lookup
115 _tmpl_lookup = app_globals.mako_lookup
116 116 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
117 117
118 118 repo_group_name = lambda repo_group_name, children_groups: (
@@ -30,7 +30,7 b' import traceback'
30 30 import formencode
31 31
32 32 from formencode import htmlfill
33 from tg import request, tmpl_context as c, config
33 from tg import request, tmpl_context as c, config, app_globals
34 34 from tg.i18n import ugettext as _
35 35 from webob.exc import HTTPFound
36 36
@@ -94,7 +94,7 b' class UserGroupsController(BaseControlle'
94 94 group_iter = UserGroupList(_list, perm_level='admin')
95 95 user_groups_data = []
96 96 total_records = len(group_iter)
97 _tmpl_lookup = kallithea.CONFIG['pylons.app_globals'].mako_lookup
97 _tmpl_lookup = app_globals.mako_lookup
98 98 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
99 99
100 100 user_group_name = lambda user_group_id, user_group_name: (
@@ -30,7 +30,7 b' import traceback'
30 30 import formencode
31 31
32 32 from formencode import htmlfill
33 from tg import request, tmpl_context as c, config
33 from tg import request, tmpl_context as c, config, app_globals
34 34 from tg.i18n import ugettext as _
35 35 from sqlalchemy.sql.expression import func
36 36 from webob.exc import HTTPFound, HTTPNotFound
@@ -73,7 +73,7 b' class UsersController(BaseController):'
73 73
74 74 users_data = []
75 75 total_records = len(c.users_list)
76 _tmpl_lookup = kallithea.CONFIG['pylons.app_globals'].mako_lookup
76 _tmpl_lookup = app_globals.mako_lookup
77 77 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
78 78
79 79 grav_tmpl = '<div class="gravatar">%s</div>'
@@ -32,12 +32,9 b' import traceback'
32 32 import time
33 33 import itertools
34 34
35 from paste.response import replace_header
36 from pylons.controllers import WSGIController
37 from pylons.controllers.util import Response
38 from tg import request
35 from tg import Response, response, request, TGController
39 36
40 from webob.exc import HTTPError
37 from webob.exc import HTTPError, HTTPException, WSGIHTTPException
41 38
42 39 from kallithea.model.db import User
43 40 from kallithea.model import meta
@@ -59,19 +56,20 b' class JSONRPCError(BaseException):'
59 56 return safe_str(self.message)
60 57
61 58
62 class JSONRPCErrorResponse(Response, Exception):
59 class JSONRPCErrorResponse(Response, HTTPException):
63 60 """
64 61 Generate a Response object with a JSON-RPC error body
65 62 """
66 63
67 64 def __init__(self, message=None, retid=None, code=None):
65 HTTPException.__init__(self, message, self)
68 66 Response.__init__(self,
69 body=json.dumps(dict(id=retid, result=None, error=message)),
67 json_body=dict(id=retid, result=None, error=message),
70 68 status=code,
71 69 content_type='application/json')
72 70
73 71
74 class JSONRPCController(WSGIController):
72 class JSONRPCController(TGController):
75 73 """
76 74 A WSGI-speaking JSON-RPC controller class
77 75
@@ -95,19 +93,15 b' class JSONRPCController(WSGIController):'
95 93 """
96 94 return self._rpc_args
97 95
98 def __call__(self, environ, start_response):
96 def _dispatch(self, state, remainder=None):
99 97 """
100 98 Parse the request body as JSON, look up the method on the
101 99 controller and if it exists, dispatch to it.
102 100 """
103 try:
104 return self._handle_request(environ, start_response)
105 except JSONRPCErrorResponse as e:
106 return e
107 finally:
108 meta.Session.remove()
101 # Since we are here we should respond as JSON
102 response.content_type = 'application/json'
109 103
110 def _handle_request(self, environ, start_response):
104 environ = state.request.environ
111 105 start = time.time()
112 106 ip_addr = request.ip_addr = self._get_ip_addr(environ)
113 107 self._req_id = None
@@ -218,39 +212,26 b' class JSONRPCController(WSGIController):'
218 212 )
219 213
220 214 self._rpc_args = {}
221
222 215 self._rpc_args.update(self._request_params)
223
224 216 self._rpc_args['action'] = self._req_method
225 217 self._rpc_args['environ'] = environ
226 self._rpc_args['start_response'] = start_response
227 218
228 status = []
229 headers = []
230 exc_info = []
231
232 def change_content(new_status, new_headers, new_exc_info=None):
233 status.append(new_status)
234 headers.extend(new_headers)
235 exc_info.append(new_exc_info)
236
237 output = WSGIController.__call__(self, environ, change_content)
238 output = list(output) # expand iterator - just to ensure exact timing
239 replace_header(headers, 'Content-Type', 'application/json')
240 start_response(status[0], headers, exc_info[0])
241 219 log.info('IP: %s Request to %s time: %.3fs' % (
242 220 self._get_ip_addr(environ),
243 221 safe_unicode(_get_access_path(environ)), time.time() - start)
244 222 )
245 return output
246 223
247 def _dispatch_call(self):
224 state.set_action(self._rpc_call, [])
225 state.set_params(self._rpc_args)
226 return state
227
228 def _rpc_call(self, action, environ, **rpc_args):
248 229 """
249 Implement dispatch interface specified by WSGIController
230 Call the specified RPC Method
250 231 """
251 232 raw_response = ''
252 233 try:
253 raw_response = self._inspect_call(self._func)
234 raw_response = getattr(self, action)(**rpc_args)
254 235 if isinstance(raw_response, HTTPError):
255 236 self._error = str(raw_response)
256 237 except JSONRPCError as e:
@@ -29,9 +29,8 b' import os'
29 29 import cgi
30 30 import logging
31 31
32 from tg import tmpl_context as c, request, config
32 from tg import tmpl_context as c, request, config, expose
33 33 from tg.i18n import ugettext as _
34 from pylons.middleware import media_path
35 34
36 35 from kallithea.lib.base import BaseController, render
37 36
@@ -52,8 +51,9 b' class ErrorController(BaseController):'
52 51 # disable all base actions since we don't need them here
53 52 pass
54 53
55 def document(self):
56 resp = request.environ.get('pylons.original_response')
54 @expose('/errors/error_document.html')
55 def document(self, *args, **kwargs):
56 resp = request.environ.get('tg.original_response')
57 57 c.site_name = config.get('title')
58 58
59 59 log.debug('### %s ###', resp and resp.status or 'no response')
@@ -70,7 +70,7 b' class ErrorController(BaseController):'
70 70 c.error_message = _('No response')
71 71 c.error_explanation = _('Unknown error')
72 72
73 return render('/errors/error_document.html')
73 return dict()
74 74
75 75 def get_error_explanation(self, code):
76 76 """ get the error explanations of int codes
@@ -25,9 +25,8 b' Original author and date, and relevant c'
25 25 :copyright: (c) 2013 RhodeCode GmbH, and others.
26 26 :license: GPLv3, see LICENSE.md for more details.
27 27 """
28
29 from beaker.cache import CacheManager
30 from beaker.util import parse_cache_config_options
28 import tg
29 from tg import config
31 30
32 31
33 32 class Globals(object):
@@ -36,11 +35,18 b' class Globals(object):'
36 35 life of the application
37 36 """
38 37
39 def __init__(self, config):
38 def __init__(self):
40 39 """One instance of Globals is created during application
41 40 initialization and is available during requests via the
42 41 'app_globals' variable
43 42
44 43 """
45 self.cache = CacheManager(**parse_cache_config_options(config))
46 44 self.available_permissions = None # propagated after init_model
45
46 @property
47 def cache(self):
48 return tg.cache
49
50 @property
51 def mako_lookup(self):
52 return config['render_functions']['mako'].normal_loader
@@ -41,9 +41,8 b' import paste.auth.basic'
41 41 import paste.httpheaders
42 42 from webhelpers.pylonslib import secure_form
43 43
44 from tg import config, tmpl_context as c, request, response, session
45 from pylons.controllers import WSGIController
46 from pylons.templating import render_mako as render # don't remove this import
44 from tg import config, tmpl_context as c, request, response, session, render_template
45 from tg import TGController
47 46 from tg.i18n import ugettext as _
48 47
49 48 from kallithea import __version__, BACKENDS
@@ -66,6 +65,10 b' from kallithea.model.scm import ScmModel'
66 65 log = logging.getLogger(__name__)
67 66
68 67
68 def render(template_path):
69 return render_template({'url': url}, 'mako', template_path)
70
71
69 72 def _filter_proxy(ip):
70 73 """
71 74 HEADERS can have multiple ips inside the left-most being the original
@@ -101,7 +104,7 b' def _get_ip_addr(environ):'
101 104
102 105 def _get_access_path(environ):
103 106 path = environ.get('PATH_INFO')
104 org_req = environ.get('pylons.original_request')
107 org_req = environ.get('tg.original_request')
105 108 if org_req:
106 109 path = org_req.environ.get('PATH_INFO')
107 110 return path
@@ -375,14 +378,11 b' class BaseVCSController(object):'
375 378 meta.Session.remove()
376 379
377 380
378 class BaseController(WSGIController):
381 class BaseController(TGController):
379 382
380 383 def _before(self, *args, **kwargs):
381 pass
382
383 def __before__(self):
384 384 """
385 __before__ is called before controller methods and after __call__
385 _before is called before controller methods and after __call__
386 386 """
387 387 c.kallithea_version = __version__
388 388 rc_config = Setting.get_app_settings()
@@ -437,13 +437,6 b' class BaseController(WSGIController):'
437 437
438 438 self.scm_model = ScmModel()
439 439
440 # __before__ in Pylons is called _before in TurboGears2. As preparation
441 # to the migration to TurboGears2, all __before__ methods were already
442 # renamed to _before. We call them from here to keep the behavior.
443 # This is a temporary call that will be removed in the real TurboGears2
444 # migration commit.
445 self._before()
446
447 440 @staticmethod
448 441 def _determine_auth_user(api_key, bearer_token, session_authuser):
449 442 """
@@ -530,12 +523,7 b' class BaseController(WSGIController):'
530 523 log.error('%r request with payload parameters; WebOb should have stopped this', request.method)
531 524 raise webob.exc.HTTPBadRequest()
532 525
533 def __call__(self, environ, start_response):
534 """Invoke the Controller"""
535
536 # WSGIController.__call__ dispatches to the Controller method
537 # the request is routed to. This routing information is
538 # available in environ['pylons.routes_dict']
526 def __call__(self, environ, context):
539 527 try:
540 528 request.ip_addr = _get_ip_addr(environ)
541 529 # make sure that we update permissions each time we call controller
@@ -564,11 +552,9 b' class BaseController(WSGIController):'
564 552 request.ip_addr, request.authuser,
565 553 safe_unicode(_get_access_path(environ)),
566 554 )
567 return WSGIController.__call__(self, environ, start_response)
555 return super(BaseController, self).__call__(environ, context)
568 556 except webob.exc.HTTPException as e:
569 return e(environ, start_response)
570 finally:
571 meta.Session.remove()
557 return e
572 558
573 559
574 560 class BaseRepoController(BaseController):
@@ -82,12 +82,12 b' class BasePasterCommand(gearbox.command.'
82 82 """
83 83 Read the config file and initialize logging and the application.
84 84 """
85 from tg import config as pylonsconfig
85 from kallithea.config.middleware import make_app
86 86
87 87 path_to_ini_file = os.path.realpath(config_file)
88 88 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
89 89 logging.config.fileConfig(path_to_ini_file)
90 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
90 make_app(conf.global_conf, **conf.local_conf)
91 91
92 92 def _init_session(self):
93 93 """
@@ -40,7 +40,7 b' class Command(BasePasterCommand):'
40 40 "Kallithea: Create or update full text search index"
41 41
42 42 def take_action(self, args):
43 from pylons import config
43 from tg import config
44 44 index_location = config['index_dir']
45 45 load_rcextensions(config['here'])
46 46
@@ -44,7 +44,7 b' class Command(BasePasterCommand):'
44 44 takes_config_file = False
45 45
46 46 def take_action(self, args):
47 from pylons import config
47 from tg import config
48 48
49 49 here = config['here']
50 50 content = pkg_resources.resource_string(
@@ -516,7 +516,7 b' script_location = kallithea:alembic'
516 516 <%text>################################</%text>
517 517
518 518 [loggers]
519 keys = root, routes, kallithea, sqlalchemy, gearbox, beaker, templates, whoosh_indexer
519 keys = root, routes, kallithea, sqlalchemy, tg, gearbox, beaker, templates, whoosh_indexer
520 520
521 521 [handlers]
522 522 keys = console, console_sql
@@ -557,6 +557,12 b' handlers ='
557 557 qualname = kallithea
558 558 propagate = 1
559 559
560 [logger_tg]
561 level = DEBUG
562 handlers =
563 qualname = tg
564 propagate = 1
565
560 566 [logger_gearbox]
561 567 level = DEBUG
562 568 handlers =
@@ -32,6 +32,7 b' import datetime'
32 32 import traceback
33 33 import beaker
34 34
35 from tg import request, response
35 36 from webhelpers.text import collapse, remove_formatting, strip_tags
36 37 from beaker.cache import _cache_decorate
37 38
@@ -29,7 +29,7 b' Original author and date, and relevant c'
29 29 import logging
30 30 import traceback
31 31
32 from tg import tmpl_context as c
32 from tg import tmpl_context as c, app_globals
33 33 from tg.i18n import ugettext as _
34 34 from sqlalchemy.orm import joinedload, subqueryload
35 35
@@ -274,8 +274,8 b' class EmailNotificationModel(object):'
274 274
275 275 def __init__(self):
276 276 super(EmailNotificationModel, self).__init__()
277 self._template_root = kallithea.CONFIG['pylons.paths']['templates'][0]
278 self._tmpl_lookup = kallithea.CONFIG['pylons.app_globals'].mako_lookup
277 self._template_root = kallithea.CONFIG['paths']['templates'][0]
278 self._tmpl_lookup = app_globals.mako_lookup
279 279 self.email_types = {
280 280 self.TYPE_CHANGESET_COMMENT: 'changeset_comment',
281 281 self.TYPE_PASSWORD_RESET: 'password_reset',
@@ -153,10 +153,10 b' class RepoModel(object):'
153 153 @classmethod
154 154 def _render_datatable(cls, tmpl, *args, **kwargs):
155 155 import kallithea
156 from tg import tmpl_context as c, request
156 from tg import tmpl_context as c, request, app_globals
157 157 from tg.i18n import ugettext as _
158 158
159 _tmpl_lookup = kallithea.CONFIG['pylons.app_globals'].mako_lookup
159 _tmpl_lookup = app_globals.mako_lookup
160 160 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
161 161
162 162 tmpl = template.get_def(tmpl)
@@ -21,14 +21,9 b' import tempfile'
21 21 import time
22 22
23 23 from tg import config
24 import pylons
25 from pylons import url
26 from pylons.i18n.translation import _get_translator
27 from pylons.util import ContextObj
28 from routes.util import URLGenerator
29 24 from webtest import TestApp
30 25
31 from kallithea import is_windows
26 from kallithea import is_windows, model
32 27 from kallithea.model.db import Notification, User, UserNotification
33 28 from kallithea.model.meta import Session
34 29 from kallithea.lib.utils2 import safe_str
@@ -42,6 +37,10 b' log = logging.getLogger(__name__)'
42 37 skipif = pytest.mark.skipif
43 38 parametrize = pytest.mark.parametrize
44 39
40 # Hack: These module global values MUST be set to actual values before running any tests. This is currently done by conftest.py.
41 url = None
42 testapp = None
43
45 44 __all__ = [
46 45 'skipif', 'parametrize', 'environ', 'url', 'TestController',
47 46 'ldap_lib_installed', 'pam_lib_installed', 'invalidate_all_caches',
@@ -147,17 +146,9 b' class TestController(object):'
147 146
148 147 @pytest.fixture(autouse=True)
149 148 def app_fixture(self):
150 config = pylons.test.pylonsapp.config
151 url._push_object(URLGenerator(config['routes.map'], environ))
152 pylons.app_globals._push_object(config['pylons.app_globals'])
153 pylons.config._push_object(config)
154 pylons.tmpl_context._push_object(ContextObj())
155 # Initialize a translator for tests that utilize i18n
156 translator = _get_translator(pylons.config.get('lang'))
157 pylons.translator._push_object(translator)
158 149 h = NullHandler()
159 150 logging.getLogger("kallithea").addHandler(h)
160 self.app = TestApp(pylons.test.pylonsapp)
151 self.app = TestApp(testapp)
161 152 return self.app
162 153
163 154 def remove_all_notifications(self):
@@ -1,18 +1,20 b''
1 1 import os
2 2 import sys
3 3 import logging
4 import pkg_resources
4 5
5 import pkg_resources
6 6 from paste.deploy import loadapp
7 import pylons.test
8 from pylons.i18n.translation import _get_translator
7 from routes.util import URLGenerator
8 from tg import config
9
9 10 import pytest
10 11 from kallithea.model.user import UserModel
11 12 from kallithea.model.meta import Session
12 13 from kallithea.model.db import Setting, User, UserIpMap
13 14 from kallithea.tests.base import invalidate_all_caches, TEST_USER_REGULAR_LOGIN
15 import kallithea.tests.base # FIXME: needed for setting testapp instance!!!
14 16
15 from kallithea.tests.test_context import test_context
17 from tg.util.webtest import test_context
16 18
17 19 def pytest_configure():
18 20 path = os.getcwd()
@@ -21,14 +23,10 b' def pytest_configure():'
21 23
22 24 # Disable INFO logging of test database creation, restore with NOTSET
23 25 logging.disable(logging.INFO)
24 pylons.test.pylonsapp = loadapp('config:kallithea/tests/test.ini', relative_to=path)
26 kallithea.tests.base.testapp = loadapp('config:kallithea/tests/test.ini', relative_to=path)
25 27 logging.disable(logging.NOTSET)
26 28
27 # Initialize a translator for tests that utilize i18n
28 translator = _get_translator(pylons.config.get('lang'))
29 pylons.translator._push_object(translator)
30
31 return pylons.test.pylonsapp
29 kallithea.tests.base.url = URLGenerator(config['routes.map'], kallithea.tests.base.environ)
32 30
33 31
34 32 @pytest.fixture
@@ -6,7 +6,7 b' from kallithea.model.notification import'
6 6 from kallithea.model.meta import Session
7 7 from kallithea.lib import helpers as h
8 8
9 from kallithea.tests.test_context import test_context
9 from tg.util.webtest import test_context
10 10
11 11 class TestNotificationsController(TestController):
12 12 def setup_method(self, method):
@@ -5,7 +5,7 b' from kallithea.model.user import UserMod'
5 5 from kallithea.model.meta import Session
6 6 from kallithea.tests.base import *
7 7
8 from kallithea.tests.test_context import test_context
8 from tg.util.webtest import test_context
9 9
10 10
11 11 class TestAdminPermissionsController(TestController):
@@ -17,7 +17,6 b' from sqlalchemy.orm.exc import NoResultF'
17 17 import pytest
18 18 from kallithea.tests.base import *
19 19 from kallithea.tests.fixture import Fixture
20 from kallithea.tests.test_context import test_context
21 20 from kallithea.controllers.admin.users import UsersController
22 21 from kallithea.model.db import User, Permission, UserIpMap, UserApiKeys
23 22 from kallithea.lib.auth import check_password
@@ -27,6 +26,8 b' from kallithea.lib import helpers as h'
27 26 from kallithea.model.meta import Session
28 27 from webob.exc import HTTPNotFound
29 28
29 from tg.util.webtest import test_context
30
30 31 fixture = Fixture()
31 32
32 33 @pytest.fixture
@@ -16,7 +16,7 b' from kallithea.model.db import User, Not'
16 16 from kallithea.model.meta import Session
17 17 from kallithea.model.user import UserModel
18 18
19 from kallithea.tests.test_context import test_context
19 from tg.util.webtest import test_context
20 20
21 21 fixture = Fixture()
22 22
@@ -7,7 +7,7 b' from kallithea.lib import helpers as h'
7 7 from kallithea.model.user import UserModel
8 8 from kallithea.model.meta import Session
9 9
10 from kallithea.tests.test_context import test_context
10 from tg.util.webtest import test_context
11 11
12 12 fixture = Fixture()
13 13
@@ -1,7 +1,7 b''
1 1 import re
2 2 import pytest
3 3
4 from kallithea.tests.test_context import test_context
4 from tg.util.webtest import test_context
5 5
6 6 from kallithea.tests.base import *
7 7 from kallithea.tests.fixture import Fixture
@@ -14,7 +14,7 b' from kallithea.model.notification import'
14 14 import kallithea.lib.celerylib
15 15 import kallithea.lib.celerylib.tasks
16 16
17 from kallithea.tests.test_context import test_context
17 from tg.util.webtest import test_context
18 18
19 19 class TestNotifications(TestController):
20 20
@@ -31,7 +31,7 b' import mock'
31 31 from kallithea.tests.base import *
32 32 from kallithea.lib.utils2 import AttributeDict
33 33 from kallithea.model.db import Repository
34 from kallithea.tests.test_context import test_context
34 from tg.util.webtest import test_context
35 35
36 36 proto = 'http'
37 37 TEST_URLS = [
@@ -224,7 +224,7 b' class TestLibs(TestController):'
224 224 from kallithea.lib.helpers import gravatar_url
225 225 _md5 = lambda s: hashlib.md5(s).hexdigest()
226 226
227 #mock pylons.tmpl_context
227 # mock tg.tmpl_context
228 228 def fake_tmpl_context(_url):
229 229 _c = AttributeDict()
230 230 _c.visual = AttributeDict()
@@ -236,31 +236,31 b' class TestLibs(TestController):'
236 236 fake_url = FakeUrlGenerator(current_url='https://example.com')
237 237 with mock.patch('kallithea.config.routing.url', fake_url):
238 238 fake = fake_tmpl_context(_url='http://example.com/{email}')
239 with mock.patch('pylons.tmpl_context', fake):
239 with mock.patch('tg.tmpl_context', fake):
240 240 from kallithea.config.routing import url
241 241 assert url.current() == 'https://example.com'
242 242 grav = gravatar_url(email_address='test@example.com', size=24)
243 243 assert grav == 'http://example.com/test@example.com'
244 244
245 245 fake = fake_tmpl_context(_url='http://example.com/{email}')
246 with mock.patch('pylons.tmpl_context', fake):
246 with mock.patch('tg.tmpl_context', fake):
247 247 grav = gravatar_url(email_address='test@example.com', size=24)
248 248 assert grav == 'http://example.com/test@example.com'
249 249
250 250 fake = fake_tmpl_context(_url='http://example.com/{md5email}')
251 with mock.patch('pylons.tmpl_context', fake):
251 with mock.patch('tg.tmpl_context', fake):
252 252 em = 'test@example.com'
253 253 grav = gravatar_url(email_address=em, size=24)
254 254 assert grav == 'http://example.com/%s' % (_md5(em))
255 255
256 256 fake = fake_tmpl_context(_url='http://example.com/{md5email}/{size}')
257 with mock.patch('pylons.tmpl_context', fake):
257 with mock.patch('tg.tmpl_context', fake):
258 258 em = 'test@example.com'
259 259 grav = gravatar_url(email_address=em, size=24)
260 260 assert grav == 'http://example.com/%s/%s' % (_md5(em), 24)
261 261
262 262 fake = fake_tmpl_context(_url='{scheme}://{netloc}/{md5email}/{size}')
263 with mock.patch('pylons.tmpl_context', fake):
263 with mock.patch('tg.tmpl_context', fake):
264 264 em = 'test@example.com'
265 265 grav = gravatar_url(email_address=em, size=24)
266 266 assert grav == 'https://example.com/%s/%s' % (_md5(em), 24)
@@ -517,7 +517,7 b' script_location = kallithea:alembic'
517 517 ################################
518 518
519 519 [loggers]
520 keys = root, routes, kallithea, sqlalchemy, gearbox, beaker, templates, whoosh_indexer
520 keys = root, routes, kallithea, sqlalchemy, tg, gearbox, beaker, templates, whoosh_indexer
521 521
522 522 [handlers]
523 523 keys = console, console_sql
@@ -558,6 +558,12 b' handlers ='
558 558 qualname = kallithea
559 559 propagate = 1
560 560
561 [logger_tg]
562 level = DEBUG
563 handlers =
564 qualname = tg
565 propagate = 1
566
561 567 [logger_gearbox]
562 568 level = DEBUG
563 569 handlers =
@@ -39,7 +39,9 b' requirements = ['
39 39 "GearBox<1",
40 40 "waitress>=0.8.8,<1.0",
41 41 "webob>=1.7,<2",
42 "Pylons>=1.0.0,<=1.0.2",
42 "backlash >= 0.1.1, < 1.0.0",
43 "TurboGears2 >= 2.3.10, < 3.0.0",
44 "tgext.routes >= 0.2.0, < 1.0.0",
43 45 "Beaker>=1.7.0,<2",
44 46 "WebHelpers==1.3",
45 47 "formencode>=1.2.4,<=1.2.6",
@@ -56,6 +58,8 b' requirements = ['
56 58 "Routes==1.13",
57 59 "dulwich>=0.14.1",
58 60 "mercurial>=2.9,<4.2",
61 "decorator >= 3.3.2",
62 "Paste >= 2.0.3, < 3.0",
59 63 ]
60 64
61 65 if sys.version_info < (2, 7):
@@ -151,9 +155,6 b' setuptools.setup('
151 155 [paste.app_factory]
152 156 main = kallithea.config.middleware:make_app
153 157
154 [paste.app_install]
155 main = pylons.util:PylonsInstaller
156
157 158 [gearbox.commands]
158 159 make-config=kallithea.lib.paster_commands.make_config:Command
159 160 setup-db=kallithea.lib.paster_commands.setup_db:Command
General Comments 0
You need to be logged in to leave comments. Login now