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