##// END OF EJS Templates
search: cython compatible configurator
ergo -
r7:e5ee6e0d
parent child Browse files
Show More
@@ -0,0 +1,61 b''
1 import inspect
2
3 from pyramid.config import Configurator
4
5
6 class InspectProxy(object):
7 """
8 Proxy to the `inspect` module that allows us to use the pyramid include
9 mechanism for cythonized modules without source file.
10 """
11
12 def _get_cyfunction_func_code(self, cyfunction):
13 """
14 Unpack the `func_code` attribute of a cython function.
15 """
16 if inspect.ismethod(cyfunction):
17 cyfunction = cyfunction.im_func
18 return getattr(cyfunction, 'func_code')
19
20 def getmodule(self, *args, **kwds):
21 """
22 Simple proxy to `inspect.getmodule`.
23 """
24 return inspect.getmodule(*args, **kwds)
25
26 def getsourcefile(self, obj):
27 """
28 Proxy to `inspect.getsourcefile` or `inspect.getfile` depending on if
29 it's called to look up the source file that contains the magic pyramid
30 `includeme` callable.
31
32 For cythonized modules the source file may be deleted. Therefore we
33 return the result of `inspect.getfile` instead. In the case of the
34 `configurator.include` method this is OK, because the result is passed
35 to `os.path.dirname` which strips the file name. So it doesn't matter
36 if we return the path to the source file or another file in the same
37 directory.
38 """
39 # Check if it's called to look up the source file that contains the
40 # magic pyramid `includeme` callable.
41 if getattr(obj, '__name__') == 'includeme':
42 try:
43 return inspect.getfile(obj)
44 except TypeError as e:
45 # Cython functions are not recognized as functions by the
46 # inspect module. We have to unpack the func_code attribute
47 # ourself.
48 if 'cyfunction' in e.message:
49 obj = self._get_cyfunction_func_code(obj)
50 return inspect.getfile(obj)
51 raise
52 else:
53 return inspect.getsourcefile(obj)
54
55
56 class CythonCompatConfigurator(Configurator):
57 """
58 Customized configurator to replace the inspect class attribute with
59 a custom one that is cython compatible.
60 """
61 inspect = InspectProxy()
@@ -1,286 +1,288 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # App Enlight Enterprise Edition, including its added features, Support
18 # App Enlight Enterprise Edition, including its added features, Support
19 # services, and proprietary license terms, please see
19 # services, and proprietary license terms, please see
20 # https://rhodecode.com/licenses/
20 # https://rhodecode.com/licenses/
21
21
22 import datetime
22 import datetime
23 import logging
23 import logging
24 import pyelasticsearch
24 import pyelasticsearch
25 import redis
25 import redis
26 import os
26 import os
27 from pkg_resources import iter_entry_points
27 from pkg_resources import iter_entry_points
28
28
29 import appenlight.lib.jinja2_filters as jinja2_filters
29 import appenlight.lib.jinja2_filters as jinja2_filters
30 import appenlight.lib.encryption as encryption
30 import appenlight.lib.encryption as encryption
31
31
32 from authomatic.providers import oauth2, oauth1
32 from authomatic.providers import oauth2, oauth1
33 from authomatic import Authomatic
33 from authomatic import Authomatic
34 from pyramid.config import Configurator, PHASE3_CONFIG
34 from pyramid.config import PHASE3_CONFIG
35 from pyramid.authentication import AuthTktAuthenticationPolicy
35 from pyramid.authentication import AuthTktAuthenticationPolicy
36 from pyramid.authorization import ACLAuthorizationPolicy
36 from pyramid.authorization import ACLAuthorizationPolicy
37 from pyramid_mailer.mailer import Mailer
37 from pyramid_mailer.mailer import Mailer
38 from pyramid.renderers import JSON
38 from pyramid.renderers import JSON
39 from pyramid_redis_sessions import session_factory_from_settings
39 from pyramid_redis_sessions import session_factory_from_settings
40 from pyramid.settings import asbool, aslist
40 from pyramid.settings import asbool, aslist
41 from pyramid.security import AllPermissionsList
41 from pyramid.security import AllPermissionsList
42 from pyramid_authstack import AuthenticationStackPolicy
42 from pyramid_authstack import AuthenticationStackPolicy
43 from redlock import Redlock
43 from redlock import Redlock
44 from sqlalchemy import engine_from_config
44 from sqlalchemy import engine_from_config
45
45
46 from appenlight.celery import configure_celery
46 from appenlight.celery import configure_celery
47 from appenlight.lib.configurator import CythonCompatConfigurator
47 from appenlight.lib import cache_regions
48 from appenlight.lib import cache_regions
48 from appenlight.lib.ext_json import json
49 from appenlight.lib.ext_json import json
49 from appenlight.security import groupfinder, AuthTokenAuthenticationPolicy
50 from appenlight.security import groupfinder, AuthTokenAuthenticationPolicy
50
51
51 json_renderer = JSON(serializer=json.dumps, indent=4)
52 json_renderer = JSON(serializer=json.dumps, indent=4)
52
53
53 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
54
55
55
56
56 def datetime_adapter(obj, request):
57 def datetime_adapter(obj, request):
57 return obj.isoformat()
58 return obj.isoformat()
58
59
59
60
60 def all_permissions_adapter(obj, request):
61 def all_permissions_adapter(obj, request):
61 return '__all_permissions__'
62 return '__all_permissions__'
62
63
63
64
64 json_renderer.add_adapter(datetime.datetime, datetime_adapter)
65 json_renderer.add_adapter(datetime.datetime, datetime_adapter)
65 json_renderer.add_adapter(AllPermissionsList, all_permissions_adapter)
66 json_renderer.add_adapter(AllPermissionsList, all_permissions_adapter)
66
67
67
68
68 def main(global_config, **settings):
69 def main(global_config, **settings):
69 """ This function returns a Pyramid WSGI application.
70 """ This function returns a Pyramid WSGI application.
70 """
71 """
71 auth_tkt_policy = AuthTktAuthenticationPolicy(
72 auth_tkt_policy = AuthTktAuthenticationPolicy(
72 settings['authtkt.secret'],
73 settings['authtkt.secret'],
73 hashalg='sha512',
74 hashalg='sha512',
74 callback=groupfinder,
75 callback=groupfinder,
75 max_age=2592000,
76 max_age=2592000,
76 secure=asbool(settings.get('authtkt.secure', 'false')))
77 secure=asbool(settings.get('authtkt.secure', 'false')))
77 auth_token_policy = AuthTokenAuthenticationPolicy(
78 auth_token_policy = AuthTokenAuthenticationPolicy(
78 callback=groupfinder
79 callback=groupfinder
79 )
80 )
80 authorization_policy = ACLAuthorizationPolicy()
81 authorization_policy = ACLAuthorizationPolicy()
81 authentication_policy = AuthenticationStackPolicy()
82 authentication_policy = AuthenticationStackPolicy()
82 authentication_policy.add_policy('auth_tkt', auth_tkt_policy)
83 authentication_policy.add_policy('auth_tkt', auth_tkt_policy)
83 authentication_policy.add_policy('auth_token', auth_token_policy)
84 authentication_policy.add_policy('auth_token', auth_token_policy)
84 # set crypto key
85 # set crypto key
85 encryption.ENCRYPTION_SECRET = settings.get('encryption_secret')
86 encryption.ENCRYPTION_SECRET = settings.get('encryption_secret')
86 # import this later so encyption key can be monkeypatched
87 # import this later so encyption key can be monkeypatched
87 from appenlight.models import DBSession, register_datastores
88 from appenlight.models import DBSession, register_datastores
88 # update config with cometd info
89 # update config with cometd info
89 settings['cometd_servers'] = {'server': settings['cometd.server'],
90 settings['cometd_servers'] = {'server': settings['cometd.server'],
90 'secret': settings['cometd.secret']}
91 'secret': settings['cometd.secret']}
91
92
92 # Create the Pyramid Configurator.
93 # Create the Pyramid Configurator.
93 settings['_mail_url'] = settings['mailing.app_url']
94 settings['_mail_url'] = settings['mailing.app_url']
94 config = Configurator(settings=settings,
95 config = CythonCompatConfigurator(
95 authentication_policy=authentication_policy,
96 settings=settings,
96 authorization_policy=authorization_policy,
97 authentication_policy=authentication_policy,
97 root_factory='appenlight.security.RootFactory',
98 authorization_policy=authorization_policy,
98 default_permission='view')
99 root_factory='appenlight.security.RootFactory',
100 default_permission='view')
99 config.set_default_csrf_options(require_csrf=True, header='X-XSRF-TOKEN')
101 config.set_default_csrf_options(require_csrf=True, header='X-XSRF-TOKEN')
100 config.add_view_deriver('appenlight.predicates.csrf_view',
102 config.add_view_deriver('appenlight.predicates.csrf_view',
101 name='csrf_view')
103 name='csrf_view')
102
104
103 # later, when config is available
105 # later, when config is available
104 dogpile_config = {'url': settings['redis.url'],
106 dogpile_config = {'url': settings['redis.url'],
105 "redis_expiration_time": 86400,
107 "redis_expiration_time": 86400,
106 "redis_distributed_lock": True}
108 "redis_distributed_lock": True}
107 cache_regions.regions = cache_regions.CacheRegions(dogpile_config)
109 cache_regions.regions = cache_regions.CacheRegions(dogpile_config)
108 config.registry.cache_regions = cache_regions.regions
110 config.registry.cache_regions = cache_regions.regions
109 engine = engine_from_config(settings, 'sqlalchemy.',
111 engine = engine_from_config(settings, 'sqlalchemy.',
110 json_serializer=json.dumps)
112 json_serializer=json.dumps)
111 DBSession.configure(bind=engine)
113 DBSession.configure(bind=engine)
112
114
113 # json rederer that serializes datetime
115 # json rederer that serializes datetime
114 config.add_renderer('json', json_renderer)
116 config.add_renderer('json', json_renderer)
115 config.set_request_property('appenlight.lib.request.es_conn', 'es_conn')
117 config.set_request_property('appenlight.lib.request.es_conn', 'es_conn')
116 config.set_request_property('appenlight.lib.request.get_user', 'user',
118 config.set_request_property('appenlight.lib.request.get_user', 'user',
117 reify=True)
119 reify=True)
118 config.set_request_property('appenlight.lib.request.get_csrf_token',
120 config.set_request_property('appenlight.lib.request.get_csrf_token',
119 'csrf_token', reify=True)
121 'csrf_token', reify=True)
120 config.set_request_property('appenlight.lib.request.safe_json_body',
122 config.set_request_property('appenlight.lib.request.safe_json_body',
121 'safe_json_body', reify=True)
123 'safe_json_body', reify=True)
122 config.set_request_property('appenlight.lib.request.unsafe_json_body',
124 config.set_request_property('appenlight.lib.request.unsafe_json_body',
123 'unsafe_json_body', reify=True)
125 'unsafe_json_body', reify=True)
124 config.add_request_method('appenlight.lib.request.add_flash_to_headers',
126 config.add_request_method('appenlight.lib.request.add_flash_to_headers',
125 'add_flash_to_headers')
127 'add_flash_to_headers')
126
128
127 config.include('pyramid_redis_sessions')
129 config.include('pyramid_redis_sessions')
128 config.include('pyramid_tm')
130 config.include('pyramid_tm')
129 config.include('pyramid_jinja2')
131 config.include('pyramid_jinja2')
130 config.include('appenlight_client.ext.pyramid_tween')
132 config.include('appenlight_client.ext.pyramid_tween')
131 config.include('ziggurat_foundations.ext.pyramid.sign_in')
133 config.include('ziggurat_foundations.ext.pyramid.sign_in')
132 es_server_list = aslist(settings['elasticsearch.nodes'])
134 es_server_list = aslist(settings['elasticsearch.nodes'])
133 redis_url = settings['redis.url']
135 redis_url = settings['redis.url']
134 log.info('Elasticsearch server list: {}'.format(es_server_list))
136 log.warning('Elasticsearch server list: {}'.format(es_server_list))
135 log.info('Redis server: {}'.format(redis_url))
137 log.warning('Redis server: {}'.format(redis_url))
136 config.registry.es_conn = pyelasticsearch.ElasticSearch(es_server_list)
138 config.registry.es_conn = pyelasticsearch.ElasticSearch(es_server_list)
137 config.registry.redis_conn = redis.StrictRedis.from_url(redis_url)
139 config.registry.redis_conn = redis.StrictRedis.from_url(redis_url)
138
140
139 config.registry.redis_lockmgr = Redlock([settings['redis.redlock.url'], ],
141 config.registry.redis_lockmgr = Redlock([settings['redis.redlock.url'], ],
140 retry_count=0, retry_delay=0)
142 retry_count=0, retry_delay=0)
141 # mailer
143 # mailer
142 config.registry.mailer = Mailer.from_settings(settings)
144 config.registry.mailer = Mailer.from_settings(settings)
143
145
144 # Configure sessions
146 # Configure sessions
145 session_factory = session_factory_from_settings(settings)
147 session_factory = session_factory_from_settings(settings)
146 config.set_session_factory(session_factory)
148 config.set_session_factory(session_factory)
147
149
148 # Configure renderers and event subscribers
150 # Configure renderers and event subscribers
149 config.add_jinja2_extension('jinja2.ext.loopcontrols')
151 config.add_jinja2_extension('jinja2.ext.loopcontrols')
150 config.add_jinja2_search_path('appenlight:templates')
152 config.add_jinja2_search_path('appenlight:templates')
151 # event subscribers
153 # event subscribers
152 config.add_subscriber("appenlight.subscribers.application_created",
154 config.add_subscriber("appenlight.subscribers.application_created",
153 "pyramid.events.ApplicationCreated")
155 "pyramid.events.ApplicationCreated")
154 config.add_subscriber("appenlight.subscribers.add_renderer_globals",
156 config.add_subscriber("appenlight.subscribers.add_renderer_globals",
155 "pyramid.events.BeforeRender")
157 "pyramid.events.BeforeRender")
156 config.add_subscriber('appenlight.subscribers.new_request',
158 config.add_subscriber('appenlight.subscribers.new_request',
157 'pyramid.events.NewRequest')
159 'pyramid.events.NewRequest')
158 config.add_view_predicate('context_type_class',
160 config.add_view_predicate('context_type_class',
159 'appenlight.predicates.contextTypeClass')
161 'appenlight.predicates.contextTypeClass')
160
162
161 register_datastores(es_conn=config.registry.es_conn,
163 register_datastores(es_conn=config.registry.es_conn,
162 redis_conn=config.registry.redis_conn,
164 redis_conn=config.registry.redis_conn,
163 redis_lockmgr=config.registry.redis_lockmgr)
165 redis_lockmgr=config.registry.redis_lockmgr)
164
166
165 # base stuff and scan
167 # base stuff and scan
166
168
167 # need to ensure webassets exists otherwise config.override_asset()
169 # need to ensure webassets exists otherwise config.override_asset()
168 # throws exception
170 # throws exception
169 if not os.path.exists(settings['webassets.dir']):
171 if not os.path.exists(settings['webassets.dir']):
170 os.mkdir(settings['webassets.dir'])
172 os.mkdir(settings['webassets.dir'])
171 config.add_static_view(path='appenlight:webassets',
173 config.add_static_view(path='appenlight:webassets',
172 name='static', cache_max_age=3600)
174 name='static', cache_max_age=3600)
173 config.override_asset(to_override='appenlight:webassets/',
175 config.override_asset(to_override='appenlight:webassets/',
174 override_with=settings['webassets.dir'])
176 override_with=settings['webassets.dir'])
175
177
176 config.include('appenlight.views')
178 config.include('appenlight.views')
177 config.include('appenlight.views.admin')
179 config.include('appenlight.views.admin')
178 config.scan(ignore=['appenlight.migrations',
180 config.scan(ignore=['appenlight.migrations',
179 'appenlight.scripts',
181 'appenlight.scripts',
180 'appenlight.tests'])
182 'appenlight.tests'])
181
183
182 # authomatic social auth
184 # authomatic social auth
183 authomatic_conf = {
185 authomatic_conf = {
184 # callback http://yourapp.com/social_auth/twitter
186 # callback http://yourapp.com/social_auth/twitter
185 'twitter': {
187 'twitter': {
186 'class_': oauth1.Twitter,
188 'class_': oauth1.Twitter,
187 'consumer_key': settings.get('authomatic.pr.twitter.key', 'X'),
189 'consumer_key': settings.get('authomatic.pr.twitter.key', 'X'),
188 'consumer_secret': settings.get('authomatic.pr.twitter.secret',
190 'consumer_secret': settings.get('authomatic.pr.twitter.secret',
189 'X'),
191 'X'),
190 },
192 },
191 # callback http://yourapp.com/social_auth/facebook
193 # callback http://yourapp.com/social_auth/facebook
192 'facebook': {
194 'facebook': {
193 'class_': oauth2.Facebook,
195 'class_': oauth2.Facebook,
194 'consumer_key': settings.get('authomatic.pr.facebook.app_id', 'X'),
196 'consumer_key': settings.get('authomatic.pr.facebook.app_id', 'X'),
195 'consumer_secret': settings.get('authomatic.pr.facebook.secret',
197 'consumer_secret': settings.get('authomatic.pr.facebook.secret',
196 'X'),
198 'X'),
197 'scope': ['email'],
199 'scope': ['email'],
198 },
200 },
199 # callback http://yourapp.com/social_auth/google
201 # callback http://yourapp.com/social_auth/google
200 'google': {
202 'google': {
201 'class_': oauth2.Google,
203 'class_': oauth2.Google,
202 'consumer_key': settings.get('authomatic.pr.google.key', 'X'),
204 'consumer_key': settings.get('authomatic.pr.google.key', 'X'),
203 'consumer_secret': settings.get(
205 'consumer_secret': settings.get(
204 'authomatic.pr.google.secret', 'X'),
206 'authomatic.pr.google.secret', 'X'),
205 'scope': ['profile', 'email'],
207 'scope': ['profile', 'email'],
206 },
208 },
207 'github': {
209 'github': {
208 'class_': oauth2.GitHub,
210 'class_': oauth2.GitHub,
209 'consumer_key': settings.get('authomatic.pr.github.key', 'X'),
211 'consumer_key': settings.get('authomatic.pr.github.key', 'X'),
210 'consumer_secret': settings.get(
212 'consumer_secret': settings.get(
211 'authomatic.pr.github.secret', 'X'),
213 'authomatic.pr.github.secret', 'X'),
212 'scope': ['repo', 'public_repo', 'user:email'],
214 'scope': ['repo', 'public_repo', 'user:email'],
213 'access_headers': {'User-Agent': 'AppEnlight'},
215 'access_headers': {'User-Agent': 'AppEnlight'},
214 },
216 },
215 'bitbucket': {
217 'bitbucket': {
216 'class_': oauth1.Bitbucket,
218 'class_': oauth1.Bitbucket,
217 'consumer_key': settings.get('authomatic.pr.bitbucket.key', 'X'),
219 'consumer_key': settings.get('authomatic.pr.bitbucket.key', 'X'),
218 'consumer_secret': settings.get(
220 'consumer_secret': settings.get(
219 'authomatic.pr.bitbucket.secret', 'X')
221 'authomatic.pr.bitbucket.secret', 'X')
220 }
222 }
221 }
223 }
222 config.registry.authomatic = Authomatic(
224 config.registry.authomatic = Authomatic(
223 config=authomatic_conf, secret=settings['authomatic.secret'])
225 config=authomatic_conf, secret=settings['authomatic.secret'])
224
226
225 # resource type information
227 # resource type information
226 config.registry.resource_types = ['resource', 'application']
228 config.registry.resource_types = ['resource', 'application']
227
229
228 # plugin information
230 # plugin information
229 config.registry.appenlight_plugins = {}
231 config.registry.appenlight_plugins = {}
230
232
231 def register_appenlight_plugin(config, plugin_name, plugin_config):
233 def register_appenlight_plugin(config, plugin_name, plugin_config):
232 def register():
234 def register():
233 log.warning('Registering plugin: {}'.format(plugin_name))
235 log.warning('Registering plugin: {}'.format(plugin_name))
234 if plugin_name not in config.registry.appenlight_plugins:
236 if plugin_name not in config.registry.appenlight_plugins:
235 config.registry.appenlight_plugins[plugin_name] = {
237 config.registry.appenlight_plugins[plugin_name] = {
236 'javascript': None,
238 'javascript': None,
237 'static': None,
239 'static': None,
238 'css': None,
240 'css': None,
239 'top_nav': None,
241 'top_nav': None,
240 'celery_tasks': None,
242 'celery_tasks': None,
241 'celery_beats': None,
243 'celery_beats': None,
242 'fulltext_indexer': None,
244 'fulltext_indexer': None,
243 'sqlalchemy_migrations': None,
245 'sqlalchemy_migrations': None,
244 'default_values_setter': None,
246 'default_values_setter': None,
245 'resource_types': [],
247 'resource_types': [],
246 'url_gen': None
248 'url_gen': None
247 }
249 }
248 config.registry.appenlight_plugins[plugin_name].update(
250 config.registry.appenlight_plugins[plugin_name].update(
249 plugin_config)
251 plugin_config)
250 # inform AE what kind of resource types we have available
252 # inform AE what kind of resource types we have available
251 # so we can avoid failing when a plugin is removed but data
253 # so we can avoid failing when a plugin is removed but data
252 # is still present in the db
254 # is still present in the db
253 if plugin_config.get('resource_types'):
255 if plugin_config.get('resource_types'):
254 config.registry.resource_types.extend(
256 config.registry.resource_types.extend(
255 plugin_config['resource_types'])
257 plugin_config['resource_types'])
256
258
257 config.action('appenlight_plugin={}'.format(plugin_name), register)
259 config.action('appenlight_plugin={}'.format(plugin_name), register)
258
260
259 config.add_directive('register_appenlight_plugin',
261 config.add_directive('register_appenlight_plugin',
260 register_appenlight_plugin)
262 register_appenlight_plugin)
261
263
262 for entry_point in iter_entry_points(group='appenlight.plugins'):
264 for entry_point in iter_entry_points(group='appenlight.plugins'):
263 plugin = entry_point.load()
265 plugin = entry_point.load()
264 plugin.includeme(config)
266 plugin.includeme(config)
265
267
266 # include other appenlight plugins explictly if needed
268 # include other appenlight plugins explictly if needed
267 includes = aslist(settings.get('appenlight.includes', []))
269 includes = aslist(settings.get('appenlight.includes', []))
268 for inc in includes:
270 for inc in includes:
269 config.include(inc)
271 config.include(inc)
270
272
271 # run this after everything registers in configurator
273 # run this after everything registers in configurator
272
274
273 def pre_commit():
275 def pre_commit():
274 jinja_env = config.get_jinja2_environment()
276 jinja_env = config.get_jinja2_environment()
275 jinja_env.filters['tojson'] = json.dumps
277 jinja_env.filters['tojson'] = json.dumps
276 jinja_env.filters['toJSONUnsafe'] = jinja2_filters.toJSONUnsafe
278 jinja_env.filters['toJSONUnsafe'] = jinja2_filters.toJSONUnsafe
277
279
278 config.action(None, pre_commit, order=PHASE3_CONFIG + 999)
280 config.action(None, pre_commit, order=PHASE3_CONFIG + 999)
279
281
280 def wrap_config_celery():
282 def wrap_config_celery():
281 configure_celery(config.registry)
283 configure_celery(config.registry)
282
284
283 config.action(None, wrap_config_celery, order=PHASE3_CONFIG + 999)
285 config.action(None, wrap_config_celery, order=PHASE3_CONFIG + 999)
284
286
285 app = config.make_wsgi_app()
287 app = config.make_wsgi_app()
286 return app
288 return app
General Comments 0
You need to be logged in to leave comments. Login now