__init__.py 9.22 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# copyright 2017 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# copyright 2014-2016 UNLISH S.A.S. (Montpellier, FRANCE), all rights reserved.
#
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
#
# CubicWeb is free software: you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option)
# any later version.
#
# CubicWeb 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 Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.

"""Pyramid interface to CubicWeb"""

23
import atexit
24
from configparser import ConfigParser
25
import os
26
import warnings
27

28
import wsgicors
29
30

from cubicweb.cwconfig import CubicWebConfiguration as cwcfg
31
32
from cubicweb.pyramid.debug_source_code import debug_display_source_code, DEBUG_DISPLAY_SOURCE_CODE_PATH

33
from pyramid.config import Configurator
34
from pyramid.exceptions import ConfigurationError
35
36
from pyramid.settings import asbool, aslist

37

38
def config_from_cwconfig(cwconfig, settings=None, debugtoolbar=False):
39
40
    """Return a Pyramid Configurator instance built from a CubicWeb config and
    Pyramid-specific configuration files (pyramid.ini).
Christophe de Vienne's avatar
Christophe de Vienne committed
41
42
43

    :param cwconfig: A CubicWeb configuration
    :returns: A Pyramid config object
44
    """
45
    settings = dict(settings) if settings else {}
46
    settings.update(settings_from_cwconfig(cwconfig, debugtoolbar=debugtoolbar))
47
48
    config = Configurator(settings=settings)
    config.registry['cubicweb.config'] = cwconfig
Yann Voté's avatar
Yann Voté committed
49
    config.include('cubicweb.pyramid')
50
51
52
53

    if debugtoolbar:
        config.include("pyramid_debugtoolbar")

54
55
    return config

56

57
def settings_from_cwconfig(cwconfig, debugtoolbar=False):
58
59
    '''
    Extract settings from pyramid.ini and pyramid-debug.ini (if in debug)
60

61
62
63
64
65
66
67
    Can be used to configure middleware WSGI with settings from pyramid.ini files

    :param cwconfig: A CubicWeb configuration
    :returns: A settings dictionnary
    '''
    settings_filenames = [os.path.join(cwconfig.apphome, 'pyramid.ini')]
    settings = {}
68
    if cwconfig.debugmode:
69
70
        settings_filenames.insert(
            0, os.path.join(cwconfig.apphome, 'pyramid-debug.ini'))
71

72
73
74
75
        settings.update({
            'pyramid.debug_authorization': True,
            'pyramid.debug_notfound': True,
            'pyramid.debug_routematch': True,
76
            'pyramid.reload_templates': True,
77
        })
78

79
80
81
        if debugtoolbar:
            settings["debugtoolbar.includes"] = ["cubicweb.pyramid.debugtoolbar_panels"]

82
83
    for fname in settings_filenames:
        if os.path.exists(fname):
84
            cp = ConfigParser()
85
86
87
            cp.read(fname)
            settings.update(cp.items('main'))
            break
88

89
    return settings
90
91


92
93
def wsgi_application_from_cwconfig(
        cwconfig,
94
        profile=False, profile_output=None, profile_dump_every=None, debugtoolbar=False):
Christophe de Vienne's avatar
Christophe de Vienne committed
95
96
97
98
99
100
101
    """ Build a WSGI application from a cubicweb configuration

    :param cwconfig: A CubicWeb configuration
    :param profile: Enable profiling. See :ref:`profiling`.
    :param profile_output: Profiling output filename. See :ref:`profiling`.
    :param profile_dump_every: Profiling number of requests before dumping the
                               stats. See :ref:`profiling`.
102
    :param debugtoolbar: Activate pyramid debugtoolbar when True.
Christophe de Vienne's avatar
Christophe de Vienne committed
103
104
105

    :returns: A fully operationnal WSGI application
    """
106
107
    config = config_from_cwconfig(cwconfig, debugtoolbar=debugtoolbar)

108
109
110
111
112
    profile = profile or asbool(config.registry.settings.get(
        'cubicweb.profile.enable', False))
    if profile:
        config.add_route('profile_ping', '_profile/ping')
        config.add_route('profile_cnx', '_profile/cnx')
Yann Voté's avatar
Yann Voté committed
113
        config.scan('cubicweb.pyramid.profile')
114
115
116
117
118

    if debugtoolbar:
        config.add_route('debug_display_source_code', DEBUG_DISPLAY_SOURCE_CODE_PATH)
        config.add_view(debug_display_source_code, route_name='debug_display_source_code')

119
120
    app = config.make_wsgi_app()
    # This replaces completely web/cors.py, which is not used by
Yann Voté's avatar
Yann Voté committed
121
    # cubicweb.pyramid anymore
122
123
    app = wsgicors.CORS(
        app,
124
        origin=' '.join(cwconfig['access-control-allow-origin']),
125
        headers=', '.join(cwconfig['access-control-allow-headers']),
126
        expose_headers=', '.join(cwconfig['access-control-expose-headers']),
127
        methods=', '.join(cwconfig['access-control-allow-methods']),
128
        maxage=cwconfig['access-control-max-age'],
129
        credentials='true')
130
131

    if profile:
Yann Voté's avatar
Yann Voté committed
132
        from cubicweb.pyramid.profile import wsgi_profile
133
134
135
136
137
        filename = profile_output or config.registry.settings.get(
            'cubicweb.profile.output', 'program.prof')
        dump_every = profile_dump_every or config.registry.settings.get(
            'cubicweb.profile.dump_every', 100)
        app = wsgi_profile(app, filename=filename, dump_every=dump_every)
138
    return app
139
140
141


def wsgi_application(instance_name=None, debug=None):
Christophe de Vienne's avatar
Christophe de Vienne committed
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
    """ Build a WSGI application from a cubicweb instance name

    :param instance_name: Name of the cubicweb instance (optional). If not
                          provided, :envvar:`CW_INSTANCE` must exists.
    :param debug: Enable/disable the debug mode. If defined to True or False,
                  overrides :envvar:`CW_DEBUG`.

    The following environment variables are used if they exist:

    .. envvar:: CW_INSTANCE

        A CubicWeb instance name.

    .. envvar:: CW_DEBUG

        If defined, the debugmode is enabled.

    The function can be used as an entry-point for third-party wsgi containers.
    Below is a sample uswgi configuration file:

    .. code-block:: ini

        [uwsgi]
        http = 127.0.1.1:8080
        env = CW_INSTANCE=myinstance
        env = CW_DEBUG=1
Yann Voté's avatar
Yann Voté committed
168
        module = cubicweb.pyramid:wsgi_application()
Christophe de Vienne's avatar
Christophe de Vienne committed
169
170
171
172
173
174
175
        virtualenv = /home/user/.virtualenvs/myvirtualenv
        processes = 1
        threads = 8
        stats = 127.0.0.1:9191
        plugins = http,python

    """
176
    if instance_name is None:
177
        instance_name = os.environ['CW_INSTANCE']
178
179
180
181
182
183
    if debug is None:
        debug = 'CW_DEBUG' in os.environ

    cwconfig = cwcfg.config_for(instance_name, debugmode=debug)

    return wsgi_application_from_cwconfig(cwconfig)
184
185


186
187
188
189
190
191
192
def pyramid_app(global_config, **settings):
    """Return a Pyramid WSGI application bound to a CubicWeb repository."""
    config = Configurator(settings=settings)
    config.include('cubicweb.pyramid')
    return config.make_wsgi_app()


193
194
195
196
197
def includeme(config):
    """Set-up a CubicWeb instance.

    The CubicWeb instance can be set in several ways:

198
199
200
201
202
203
    -   Provide an already loaded CubicWeb repository in the registry:

        .. code-block:: python

            config.registry['cubicweb.repository'] = your_repo_instance

204
205
206
207
208
209
210
211
212
    -   Provide an already loaded CubicWeb config instance in the registry:

        .. code-block:: python

            config.registry['cubicweb.config'] = your_config_instance

    -   Provide an instance name in the pyramid settings with
        :confval:`cubicweb.instance`.

213
214
215
216
217
    A CubicWeb repository is instantiated and attached in
    'cubicweb.repository' registry key if not already present.

    The CubicWeb instance registry is attached in 'cubicweb.registry' registry
    key.
218
219
    """
    cwconfig = config.registry.get('cubicweb.config')
220
221
222
223
224
225
226
227
228
229
230
    repo = config.registry.get('cubicweb.repository')

    if repo is not None:
        if cwconfig is None:
            config.registry['cubicweb.config'] = cwconfig = repo.config
        elif cwconfig is not repo.config:
            raise ConfigurationError(
                'CubicWeb config instance (found in "cubicweb.config" '
                'registry key) mismatches with that of the repository '
                '(registry["cubicweb.repository"])'
            )
231
232
233
234
235
236
237
238

    if cwconfig is None:
        debugmode = asbool(
            config.registry.settings.get('cubicweb.debug', False))
        cwconfig = cwcfg.config_for(
            config.registry.settings['cubicweb.instance'], debugmode=debugmode)
        config.registry['cubicweb.config'] = cwconfig

239
240
    if repo is None:
        repo = config.registry['cubicweb.repository'] = cwconfig.repository()
241
242
    config.registry['cubicweb.registry'] = repo.vreg

243
    if cwconfig.mode != 'test':
244
245
246
247
248
        @atexit.register
        def shutdown_repo():
            if repo.shutting_down:
                return
            repo.shutdown
249

250
    if asbool(config.registry.settings.get('cubicweb.defaults', True)):
Yann Voté's avatar
Yann Voté committed
251
        config.include('cubicweb.pyramid.defaults')
252
253
254
255

    for name in aslist(config.registry.settings.get('cubicweb.includes', [])):
        config.include(name)

Yann Voté's avatar
Yann Voté committed
256
    config.include('cubicweb.pyramid.core')
257

258
259
260
261
262
263
264
265
    if asbool(config.registry.settings.get('cubicweb.bwcompat',
                                           cwconfig.name == 'all-in-one')):
        if cwconfig.name != 'all-in-one':
            warnings.warn('"cubicweb.bwcompat" setting only applies to '
                          '"all-in-one" instance configuration',
                          UserWarning)
        else:
            config.include('cubicweb.pyramid.bwcompat')