Exemplo n.º 1
0
def init(confdict, webassets, tpl, http):
    """
    Initializes this module acoording to :ref:`our module initialization
    guidelines <module_initialization>` with the following configuration keys:

    :confkey:`rootdir` :confdefault:`None`
        Denotes the root folder containing all css files. Will fall
        back to a sub-folder of the folder in :mod:`score.tpl`'s
        configuration, as described in :func:`score.tpl.init`.

    :confkey:`cachedir` :confdefault:`None`
        A dedicated cache folder for this module. It is generally sufficient
        to provide a ``cachedir`` for :mod:`score.tpl`, as this module will
        use a sub-folder of that by default.

    :confkey:`combine` :confdefault:`False`
        Whether css files should be delivered as a single file. If this
        value is `true` (as defined by :func:`score.init.parse_bool`), the
        default url will point to the combined css file.

    :confkey:`minify` :confdefault:`False`
        Whether css assets should be minified.

    :confkey:`group.*`
        Keys starting with ``group.*`` will register :term:`asset groups
        <asset group>`. The following configuration will create a
        :term:`virtual asset` called ``meal`` which has the combined content
        of the assets 'bacon.css' and 'spam.css'::

            group.meal =
                bacon.css
                spam.css

        Note that groups defined this way *must* reference real assets, i.e.
        at the point this value is interpreted, there are no virtual assets
        yet! If you need to add virtual assets to a group, you will need to
        create the asset group after all required virtual assets have been
        registered.

    """
    conf = dict(defaults.items())
    conf.update(confdict)
    if not conf['rootdir']:
        conf['rootdir'] = os.path.join(tpl.rootdir, 'css')
    conf['minify'] = parse_bool(conf['minify'])
    conf['combine'] = parse_bool(conf['combine'])
    if not conf['cachedir'] and tpl.cachedir:
        conf['cachedir'] = os.path.join(tpl.cachedir, 'css')
    if conf['cachedir']:
        init_cache_folder(conf, 'cachedir', autopurge=True)
    else:
        warnings.warn('No cachedir configured, SCSS rendering will not work.')
    cssconf = ConfiguredCssModule(
        webassets, tpl, http, conf['rootdir'], conf['cachedir'],
        conf['combine'], conf['minify'])
    for name, pathlist in extract_conf(conf, 'group.').items():
        paths = parse_list(pathlist)
        _create_group(cssconf, webassets, name + '.css', paths)
    return cssconf
Exemplo n.º 2
0
def parse_connect_conf(conf):
    conf = conf.copy()
    if 'hosts' in conf:
        conf['hosts'] = parse_list(conf['hosts'])
    if 'verify_certs' in conf:
        conf['verify_certs'] = parse_bool(conf['verify_certs'])
    if 'use_ssl' in conf:
        conf['use_ssl'] = parse_bool(conf['use_ssl'])
    if 'timeout' in conf:
        conf['timeout'] = parse_time_interval(conf['timeout'])
    return conf
Exemplo n.º 3
0
def parse_cookie_kwargs(conf):
    if not conf['cookie'] or conf['cookie'] == 'None':
        return None
    cookie_kwargs = {
        'name': conf['cookie'],
        'path': conf['cookie.path'],
        'domain': conf['cookie.domain'],
        'secure': parse_bool(conf['cookie.secure']),
        'httponly': parse_bool(conf['cookie.httponly']),
    }
    if conf['cookie.max_age']:
        cookie_kwargs['max_age'] = \
            parse_time_interval(conf['cookie.max_age'])
    return cookie_kwargs
Exemplo n.º 4
0
def init(confdict, ctx=None):
    """
    Initializes this module acoording to :ref:`our module initialization
    guidelines <module_initialization>` with the following configuration keys:

    :confkey:`sqlalchemy.*`
        All configuration values under this key will be passed to
        :func:`engine_from_config`, which in turn calls
        :func:`sqlalchemy.create_engine` with these configuration values as
        keyword arguments. Usually the following is sufficient::

            sqlalchemy.url = postgresql://dbuser@localhost/projname

    :confkey:`destroyable` :confdefault:`False`
        Whether destructive operations may be performed on the database. This
        value prevents accidental deletion of important data on live servers.

        Note that any application feature destroying data must consult this
        flag before proceeding!

    :confkey:`ctx.member` :confdefault:`db`
        The name of the :term:`context member` providing an
        :class:`sqlalchemy.engine.Connection`. Can be the string `None` to
        indicate, that no context member should be registered.

        This value is only relevant, if the optional :mod:`score.ctx`
        dependency was configured.

    :confkey:`ctx.transaction` :confdefault:`True`
        Whether the context member providing the context-scoped database
        connection should wrap the connection in a transaction. The transaction
        will be committed at the end of the context object's lifecycle (or
        rolled back, if the context was terminated with an uncaught exception).

        This value is only relevant if *ctx.member* is not `None`.

    """
    conf = defaults.copy()
    conf.update(confdict)
    engine = engine_from_config(conf)
    ctx_member = None
    if conf['ctx.member'] and conf['ctx.member'] != 'None':
        ctx_member = conf['ctx.member']
    if conf['ctx.transaction']:
        ctx_transaction = parse_bool(conf['ctx.transaction'])
    return ConfiguredSaDbModule(
        ctx, engine, parse_bool(conf['destroyable']),
        ctx_member, ctx_transaction)
Exemplo n.º 5
0
def init(confdict):
    """
    Initializes this module according to :ref:`our module initialization
    guidelines <module_initialization>` with the following configuration keys:

    :confkey:`hosts`
        A list of Varnish hosts passed to :func:`parse_host_port
        <score.init.parse_host_port>` and :func:`parse_list
        <score.init.parse_list>`.

    :confkey:`soft` :faint:`[default=true]`
        Whether to purge :term:`soft <soft purge>` or :term:`hard <hard purge>`.

    :confkey:`timeout` :faint:`[default=5s]`
        The timeout for sending requests to a Varnish host passed to
        :func:`parse_time_interval <score.init.parse_time_interval>`.

    :confkey:`header.domain` :faint:`[default=X-Purge-Domain]`
        The name of the header used for purging a domain.

    :confkey:`header.path` :faint:`[default=X-Purge-Path]`
        The name of the header used for purging a path.

    :confkey:`header.soft` :faint:`[default=X-Purge-Soft]`
        The name of the header used for triggering a :term:`soft purge`.
    """
    conf = dict(defaults.items())
    conf.update(confdict)
    hosts = [parse_host_port(host) for host in parse_list(conf['hosts'])]
    soft = parse_bool(conf['soft'])
    timeout = parse_time_interval(conf['timeout'])
    header_mapping = extract_conf(conf, 'header.')
    return ConfiguredVarnishModule(hosts, soft, timeout, header_mapping)
Exemplo n.º 6
0
def init(confdict):
    """
    Initializes this module acoording to :ref:`our module initialization
    guidelines <module_initialization>` with the following configuration keys:

    :confkey:`autoreload` :confdefault:`False`
        When set to :func:`true <score.init.parse_bool>`, the server will
        automatically reload whenever it detects a change in one of the python
        files, that are in use.

    :confkey:`modules`
        The :func:`list <score.init.parse_list>` of modules to serve. This need
        to be a list of module aliases, i.e. the same name, with which you
        configured the module with ("score.http" becomes "http" if not specified
        otherwise.)

    """
    conf = defaults.copy()
    conf.update(confdict)
    modules = parse_list(conf['modules'])
    if not modules:
        import score.serve
        raise InitializationError(score.serve, 'No modules configured')
    autoreload = parse_bool(conf['autoreload'])
    return ConfiguredServeModule(conf['conf'], modules, autoreload)
Exemplo n.º 7
0
def init(confdict):
    """
    Initializes this module acoording to :ref:`our module initialization
    guidelines <module_initialization>` with the following configuration keys:

    :confkey:`autoreload` :confdefault:`False`
        When set to :func:`true <score.init.parse_bool>`, the server will
        automatically reload whenever it detects a change in one of the python
        files, that are in use.

    :confkey:`modules`
        The :func:`list <score.init.parse_list>` of modules to serve. This need
        to be a list of module aliases, i.e. the same name, with which you
        configured the module with ("score.http" becomes "http" if not
        specified otherwise.)

    """
    conf = defaults.copy()
    conf.update(confdict)
    modules = parse_list(conf['modules'])
    if not modules:
        import score.serve
        raise InitializationError(score.serve, 'No modules configured')
    autoreload = parse_bool(conf['autoreload'])
    monitor_host_port = None
    if conf['monitor']:
        monitor_host_port = parse_host_port(conf['monitor'])
    return ConfiguredServeModule(conf['conf'], modules, autoreload,
                                 monitor_host_port)
Exemplo n.º 8
0
def engine_from_config(config):
    """
    A wrapper around :func:`sqlalchemy.engine_from_config`, that converts
    certain configuration values. Currently, the following configurations are
    processed:

    - ``sqlalchemy.echo`` (using :func:`score.init.parse_bool`)
    - ``sqlalchemy.echo_pool`` (using :func:`score.init.parse_bool`)
    - ``sqlalchemy.case_sensitive`` (using :func:`score.init.parse_bool`)
    - ``sqlalchemy.module`` (using :func:`score.init.parse_dotted_path`)
    - ``sqlalchemy.poolclass`` (using :func:`score.init.parse_dotted_path`)
    - ``sqlalchemy.pool`` (using :func:`score.init.parse_call`)
    - ``sqlalchemy.pool_size`` (converted to `int`)
    - ``sqlalchemy.pool_recycle`` (converted to `int`)
    """
    global _registered_utf8mb4
    conf = dict()
    for key in config:
        if key in ('sqlalchemy.echo', 'sqlalchemy.echo_pool',
                   'sqlalchemy.case_sensitive'):
            conf[key] = parse_bool(config[key])
        elif key in ('sqlalchemy.module', 'sqlalchemy.poolclass'):
            conf[key] = parse_dotted_path(config[key])
        elif key == 'sqlalchemy.pool':
            conf[key] = parse_call(config[key])
        elif key in ('sqlalchemy.pool_size', 'sqlalchemy.pool_recycle'):
            conf[key] = int(config[key])
        else:
            conf[key] = config[key]
    if not _registered_utf8mb4 and 'utf8mb4' in conf.get('sqlalchemy.url', ''):
        import codecs
        codecs.register(lambda name: codecs.lookup('utf8')
                        if name == 'utf8mb4' else None)
        _registered_utf8mb4 = True
    return sa.engine_from_config(conf)
Exemplo n.º 9
0
    def rename(self, project, newname):
        """
        Changes the name of a given *project* to *newname*. The *project* can be
        anything accepted by :meth:`get`.

        .. note::

            Since the folder path of a project's virtual environment depends on
            its name, the function will also delete the project's old virtualenv
            and create a new one.
        """
        project = self.get(project)
        oldname = project.name
        try:
            shutil.rmtree(project.venvdir)
        except FileNotFoundError:
            pass
        project.name = newname
        settings = self._read_conf()
        try:
            site_packages = parse_bool(settings[oldname]['site_packages'])
        except:
            site_packages = False
        project.recreate_venv(site_packages=site_packages)
        del settings[oldname]
        settings[newname] = {'folder': project.folder}
        self._write_conf(settings)
Exemplo n.º 10
0
def init(confdict, ctx=None):
    """
    Initializes this module acoording to :ref:`our module initialization
    guidelines <module_initialization>` with the following configuration keys:

    :confkey:`sqlalchemy.*`
        All configuration values under this key will be passed to
        :func:`engine_from_config`, which in turn calls
        :func:`sqlalchemy.create_engine` with these configuration values as
        keyword arguments. Usually the following is sufficient::

            sqlalchemy.url = postgresql://dbuser@localhost/projname

    :confkey:`base` :faint:`[default=None]`
        The dotted python path to the :ref:`base class <db_base>` to
        configure. See :func:`parse_dotted_path` for the syntax.

    :confkey:`destroyable` :faint:`[default=False]`
        Whether destructive operations may be performed on the database. This
        value prevents accidental deletion of important data on live servers.

    :confkey:`ctx.member` :faint:`[default=db]`
        The name of the :term:`context member`, that should be registered with
        the configured :mod:`score.ctx` module (if there is one). The default
        value allows you to always access a valid session within a
        :class:`score.ctx.Context` like this:

        >>> ctx.db.query(User).first()

        Providing the special value 'None' will disable registration of the
        context member, even if a configured :mod:`ctx module <score.ctx>` was
        provided.

    This function will initialize an sqlalchemy
    :ref:`Engine <sqlalchemy:engines_toplevel>` and the provided
    :ref:`base class <db_base>`.

    """
    conf = defaults.copy()
    conf.update(confdict)
    engine = engine_from_config(conf)
    Base = None
    if 'base' in conf:
        Base = parse_dotted_path(conf['base'])
        Base.metadata.bind = engine
    ctx_member = None
    if ctx and conf['ctx.member'] not in (None, 'None'):
        ctx_member = conf['ctx.member']
    db_conf = ConfiguredDbModule(
        engine, Base, parse_bool(conf['destroyable']), ctx_member)
    if ctx_member:

        def constructor(ctx):
            zope_tx = ZopeTransactionExtension(
                transaction_manager=ctx.tx_manager)
            return db_conf.Session(extension=zope_tx)

        ctx.register(ctx_member, constructor)
    return db_conf
Exemplo n.º 11
0
def init(confdict, ctx, http, jslib=None):
    """
    Initializes this module acoording to :ref:`our module initialization
    guidelines <module_initialization>` with the following configuration keys:

    :confkey:`endpoints` :confdefault:`list()`
        A :func:`list <score.init.parse_list>` of :func:`dotted paths
        <score.init.parse_dotted_path>` pointing to any amount of
        :class:`Endpoints <.Endpoint>`. The registered functions of these
        Endpoints will be available in javascript.

    :confkey:`expose` :confdefault:`False`
        Whether security critical data may be exposed through the API. This
        value should be left at its default value in production, but may be
        switched to `True` during development to receive Exceptions and
        stacktraces in the browser console.

    :confkey:`jslib.require` :confdefault:`score.jsapi`
        The name of the require.js module to create the virtual javascript with.
        When left at its default value, the resulting javascript can be included
        like the following:

        .. code-block:: javascript

            require(['score.jsapi'], function(Api) {
                var api = new Api();
                // ... use api here ...
            });
    """
    conf = dict(defaults.items())
    conf.update(confdict)
    endpoints = list(map(parse_dotted_path, parse_list(conf['endpoints'])))
    expose = parse_bool(conf['expose'])
    jsapi = ConfiguredJsapiModule(ctx, http, jslib, expose,
                                  conf['jslib.require'])
    for endpoint in endpoints:
        jsapi.add_endpoint(endpoint)

    if jslib:
        import score.jsapi

        version = score.jsapi.__version__
        dependencies = {
            conf['jslib.require'] + '/excformat': version,
            'bluebird': '3.X.X',
            'score.init': '0.X.X',
            'score.oop': '0.4.X'
        }

        @jslib.virtlib(conf['jslib.require'] + '/excformat', version, {})
        def exc2json(ctx):
            return gen_excformat_js(ctx)

        @jslib.virtlib(conf['jslib.require'], version, dependencies)
        def api(ctx):
            return jsapi.generate_js()

    return jsapi
Exemplo n.º 12
0
def _init_kvcache_backend(conf, session, kvcache):
    if not kvcache:
        return None
    from ._kvcache import KvcacheSession
    return type('ConfiguredKvcacheSession', (KvcacheSession,), {
        '_conf': session,
        '_livedata': parse_bool(conf['kvcache.livedata']),
        '_container': kvcache[conf['kvcache.container']],
    })
Exemplo n.º 13
0
def _init_kvcache_backend(conf, session, kvcache):
    if not kvcache:
        return None
    from ._kvcache import KvcacheSession
    return type(
        'ConfiguredKvcacheSession', (KvcacheSession, ), {
            '_conf': session,
            '_livedata': parse_bool(conf['kvcache.livedata']),
            '_container': kvcache[conf['kvcache.container']],
        })
Exemplo n.º 14
0
def init(confdict, ctx=None):
    """
    Initializes this module acoording to :ref:`our module initialization
    guidelines <module_initialization>` with the following configuration keys:

    :confkey:`args.hosts`
        A list of hosts (as read by :func:`score.init.parse_list`) to pass to
        the :class:`Elasticsearch <elasticsearch.Elasticsearch>` constructor.

    :confkey:`args.*`
        Any other arguments to be passed to the :class:`Elasticsearch
        <elasticsearch.Elasticsearch>` constructor.

    :confkey:`index` :confdefault:`score`
        The index to use in all operations.

    :confkey:`keep_source` :confdefault:`False`
        Whether the `_source` field should be enabled. The default is `False`,
        since the canonical representation of objects should be in a
        database.

    :confkey:`ctx.member` :confdefault:`es`
        The name of the :term:`context member`, that should be registered with
        the configured :mod:`score.ctx` module (if there is one). The default
        value allows one to conveniently query the index:

        >>> response = ctx.es.client.search(body={"query": {"match_all": {}}})
        >>> for hit in response['hits']['hits']:
        ...     print(hit['_source']['title'])

    :confkey:`ctx.extensions`
        Additional extension classes for the context proxy.

    """
    conf = defaults.copy()
    conf.update(confdict)
    connect_kwargs = parse_connect_conf(extract_conf(conf, 'args.'))
    if 'index' not in conf:
        conf['index'] = 'score'
    keep_source = parse_bool(conf['keep_source'])
    ctx_extensions = [DslExtension]
    for path in parse_list(conf['ctx.extensions']):
        class_ = parse_dotted_path(path)
        if not issubclass(class_, CtxProxy):
            raise InitializationError(
                'score.es7',
                'Ctx extensions classes must be sub-classes of CtxProxy')
        ctx_extensions.append(class_)
    es_conf = ConfiguredEs7Module(connect_kwargs, conf['index'], keep_source,
                                  ctx_extensions)
    if ctx and conf['ctx.member'] not in (None, 'None'):
        ctx.register(conf['ctx.member'], es_conf.get_ctx_proxy)
    return es_conf
Exemplo n.º 15
0
def engine_from_config(config):
    """
    A wrapper around :func:`sqlalchemy.engine_from_config`, that converts
    certain configuration values. Currently, the following configurations are
    processed:

    - ``sqlalchemy.echo`` (using :func:`score.init.parse_bool`)
    - ``sqlalchemy.echo_pool`` (using :func:`score.init.parse_bool`)
    - ``sqlalchemy.case_sensitive`` (using :func:`score.init.parse_bool`)
    - ``sqlalchemy.module`` (using :func:`score.init.parse_dotted_path`)
    - ``sqlalchemy.poolclass`` (using :func:`score.init.parse_dotted_path`)
    - ``sqlalchemy.pool`` (using :func:`score.init.parse_call`)
    - ``sqlalchemy.pool_size`` (converted to `int`)
    - ``sqlalchemy.pool_recycle`` (converted to `int`)
    """
    if 'sqlalchemy.echo' in config:
        config['sqlalchemy.echo'] = parse_bool(config['sqlalchemy.echo'])
    if 'sqlalchemy.echo_pool' in config:
        config['sqlalchemy.echo_pool'] = \
            parse_bool(config['sqlalchemy.echo_pool'])
    if 'sqlalchemy.case_sensitive' in config:
        config['sqlalchemy.case_sensitive'] = \
            parse_bool(config['sqlalchemy.case_sensitive'])
    if 'sqlalchemy.module' in config:
        config['sqlalchemy.module'] = \
            parse_dotted_path(config['sqlalchemy.module'])
    if 'sqlalchemy.poolclass' in config:
        config['sqlalchemy.poolclass'] = \
            parse_dotted_path(config['sqlalchemy.poolclass'])
    if 'sqlalchemy.pool' in config:
        config['sqlalchemy.pool'] = parse_call(config['sqlalchemy.pool'])
    if 'sqlalchemy.pool_size' in config:
        config['sqlalchemy.pool_size'] = \
            int(config['sqlalchemy.pool_size'])
    if 'sqlalchemy.pool_recycle' in config:
        config['sqlalchemy.pool_recycle'] = \
            int(config['sqlalchemy.pool_recycle'])
    return sa.engine_from_config(config)
Exemplo n.º 16
0
def parse_cookie_kwargs(conf):
    if not conf['cookie'] or conf['cookie'] == 'None':
        return None
    samesite = conf['cookie.samesite']
    if samesite and samesite.strip().lower() != 'none':
        samesite = samesite.strip()
        samesite = samesite[0].upper() + samesite[1:].lower()
        if samesite not in ('Strict', 'Lax'):
            raise ValueError('cookie.samesite must be "Strict" or "Lax"')
    else:
        samesite = None
    cookie_kwargs = {
        'name': conf['cookie'],
        'path': conf['cookie.path'],
        'domain': conf['cookie.domain'],
        'secure': parse_bool(conf['cookie.secure']),
        'httponly': parse_bool(conf['cookie.httponly']),
        'samesite': samesite,
    }
    if conf['cookie.max_age']:
        cookie_kwargs['max_age'] = \
            parse_time_interval(conf['cookie.max_age'])
    return cookie_kwargs
Exemplo n.º 17
0
def init(confdict, ctx=None):
    """
    Initializes this module acoording to :ref:`our module initialization
    guidelines <module_initialization>` with the following configuration keys:

    :confkey:`server` :faint:`[default=localhost:14000]`
        The server to connect to for all remote operations. Read using the
        generic :func:`score.init.parse_host_port`.

        The special value ``None`` indicates that all remote operations will
        immediately raise an exception. It is still possible to use higher
        level functions like :meth:`put <.ConfiguredNetfsModule.put>` and
        :meth:`get <.ConfiguredNetfsModule.get>` (although :meth:`get
        <.ConfiguredNetfsModule.get>` will raise an exception if the requested
        file is not present in the local folder.)

    :confkey:`cachedir` :faint:`[default=None]`
        A local folder that will hold downloaded files. If this value is
        omitted, the module will create a new temporary folder on demand, that
        will be used as the local folder for this session.

    :confkey:`deltmpcache` :faint:`[default=True]`
        This option is only relevant if the configuration did not contain a
        ``cachedir``. If this value is `True`, any temporary folder that was
        created for this session—as described for the configuration value
        ``cachedir``, above—will be removed when the
        :class:`.ConfiguredNetfsModule` is freed.

        The only use case where you might need this configuration is when you
        want to operate on a temporary folder, but still keep its contents when
        you are done with this module.

    """
    conf = dict(defaults.items())
    conf.update(confdict)
    if conf['server'] in (None, 'None'):
        host, port = None, None
    else:
        host, port = parse_host_port(conf['server'], defaults['server'])
    cachedir = None
    delcache = False
    if conf['cachedir']:
        cachedir = init_cache_folder(conf, 'cachedir')
    else:
        delcache = parse_bool(conf['deltmpcache'])
    c = ConfiguredNetfsModule(host, port, cachedir, delcache)
    c.ctx_conf = ctx
    if ctx and conf['ctx.member'] not in ('None', None):
        ctx.register(conf['ctx.member'], lambda _: c.connect())
    return c
Exemplo n.º 18
0
 def _readconf(self, file):
     if not file:
         if not self.file:
             raise ValueError('No zergling configuration provided')
         file = self.file
     result = {}
     defaults = {'pause': True}
     confdict = parse_config_file(self.file)
     for section in confdict:
         if section in ['DEFAULT', 'score.init']:
             continue
         result[section] = defaults.copy()
         result[section].update(confdict[section])
         result[section]['name'] = section
         result[section]['pause'] = parse_bool(result[section]['pause'])
     return result
Exemplo n.º 19
0
def init(confdict, ctx_conf, js_conf=None):
    conf = dict(defaults.items())
    conf.update(confdict)
    server = parse_dotted_path(conf['server'])
    server.host = conf['host']
    server.port = int(conf['port'])
    server.url = 'ws://%s:%d' % (server.host, server.port)
    if js_conf and conf['virtjs.path']:

        @js_conf.virtjs(conf['virtjs.path'])
        def ws():
            # TODO: next two lines should be outside of function for increased
            # performance
            tpl = open(os.path.join(os.path.dirname(__file__), 'ws.js')).read()
            virtjs = tpl % conf['virtjs.name']
            return virtjs
    ws_conf = ConfiguredWsModule(ctx_conf, server, parse_bool(conf['expose']))
    server.conf = ws_conf
    return ws_conf
Exemplo n.º 20
0
def engine_from_config(config):
    """
    A wrapper around :func:`sqlalchemy.engine_from_config`, that converts
    certain configuration values. Currently, the following configurations are
    processed:

    - ``sqlalchemy.case_sensitive`` (using :func:`score.init.parse_bool`)
    - ``sqlalchemy.connect_args.cursorclass`` (using
      :func:`score.init.parse_dotted_path`)
    - ``sqlalchemy.echo`` (using :func:`score.init.parse_bool`)
    - ``sqlalchemy.echo_pool`` (using :func:`score.init.parse_bool`)
    - ``sqlalchemy.module`` (using :func:`score.init.parse_dotted_path`)
    - ``sqlalchemy.poolclass`` (using :func:`score.init.parse_dotted_path`)
    - ``sqlalchemy.pool`` (using :func:`score.init.parse_call`)
    - ``sqlalchemy.pool_size`` (converted to `int`)
    - ``sqlalchemy.pool_recycle`` (converted to `int`)

    Any other keys are used without conversion.
    """
    conf = dict()
    for key in config:
        if key in ('sqlalchemy.echo', 'sqlalchemy.echo_pool',
                   'sqlalchemy.case_sensitive'):
            conf[key] = parse_bool(config[key])
        elif key in ('sqlalchemy.module', 'sqlalchemy.poolclass'):
            conf[key] = parse_dotted_path(config[key])
        elif key.startswith('sqlalchemy.connect_args.'):
            if 'sqlalchemy.connect_args' not in conf:
                conf['sqlalchemy.connect_args'] = {}
            value = config[key]
            key = key[len('sqlalchemy.connect_args.'):]
            if key == 'cursorclass':
                value = parse_dotted_path(value)
            conf['sqlalchemy.connect_args'][key] = value
        elif key == 'sqlalchemy.pool':
            conf[key] = parse_call(config[key])
        elif key in ('sqlalchemy.pool_size', 'sqlalchemy.pool_recycle'):
            conf[key] = int(config[key])
        else:
            conf[key] = config[key]
    return sa.engine_from_config(conf)
Exemplo n.º 21
0
def init(confdict, ctx):
    """
    Initializes this module acoording to :ref:`our module initialization
    guidelines <module_initialization>` with the following configuration keys:

    :confkey:`host` :confdefault:`0.0.0.0`
        The hostname to listen for connnections on.

    :confkey:`port` :confdefault:`8081`
        The port to listen for connnections on.

    :confkey:`stop_timeout` :confdefault:`None`
        Defines how long the module will wait for connections to close
        when pausing the worker. The value will be interpreted through
        a call to :func:`score.init.parse_time_interval`.

        The default value `None` indicates that the module will wait
        indefinitely. If you want to the server to terminate immediately,
        without waiting for open connections at all, you must pass "0".

    :confkey:`reuse_port` :confdefault:`False`
        Whether the ``reuse_port`` keyword argument should be passed to the
        underlying event loop's :meth:`create_server()
        <asyncio.AbstractEventLoop.create_server>` method.

    """
    conf = dict(defaults.items())
    conf.update(confdict)
    host = conf['host']
    port = int(conf['port'])
    stop_timeout = conf['stop_timeout']
    if stop_timeout == 'None':
        stop_timeout = None
    if stop_timeout is not None:
        stop_timeout = parse_time_interval(stop_timeout)
    reuse_port = parse_bool(conf['reuse_port'])
    return ConfiguredWebsocketsModule(ctx, host, port, stop_timeout,
                                      reuse_port)
Exemplo n.º 22
0
def init(confdict, http=None, netfs=None):
    """
    Initializes this module acoording to :ref:`our module initialization
    guidelines <module_initialization>` with the following configuration keys:

    :confkey:`cachedir` :default:`None`
        A writable folder that will be used to cache intermediate values. This
        value is mostly unused in this module, but the initialized value can be
        used by other modules. The :mod:`css module <score.css>`, for example,
        will create a sub-folder beneath this folder, if it was initialized
        without an explicit `cachedir` of its own.

    :confkey:`versionmanager` :default:`score.webassets.versioning.Dummy`
        The :class:`VersionManager <score.webassets.versioning.VersionManager>`
        to use. This value will be converted to an object using
        :func:`score.init.parse_object`.

        See the :mod:`package description <score.webassets.versioning>` for
        available implementations.

    :confkey:`netfs` :default:`True`
        The initializer will upload all webassets to a :mod:`score.netfs`
        server, if one was configured. You can disable this feature by passing a
        `False` value here
    """
    conf = dict(defaults.items())
    conf.update(confdict)
    if conf['cachedir']:
        init_cache_folder(conf, 'cachedir', autopurge=True)
    versionmanager = parse_object(conf, 'versionmanager')
    if netfs and parse_bool(conf['netfs']):
        versionmanager = NetfsVersionManager(versionmanager, netfs)

    def assetnotfound(ctx, exception):
        raise HTTPNotFound()
    if http:
        http.exception_handlers[AssetNotFound] = assetnotfound
    return ConfiguredWebassetsModule(conf['cachedir'], versionmanager)
Exemplo n.º 23
0
def init(confdict, webassets, http, tpl, html=None):
    """
    Initializes this module acoording to :ref:`our module initialization
    guidelines <module_initialization>` with the following configuration keys:

    :confkey:`rootdir` :confdefault:`None`
        Denotes the root folder containing all javascript files. Will fall
        back to a sub-folder of the folder in :mod:`score.tpl`'s
        configuration, as described in :func:`score.tpl.init`.

    :confkey:`cachedir` :confdefault:`None`
        A dedicated cache folder for this module. It is generally sufficient
        to provide a ``cachedir`` for :mod:`score.tpl`, as this module will
        use a sub-folder of that by default.

    :confkey:`minifier` :confdefault:`None`
        The minifier to use for minification. Will be initialized using
        :func:`score.init.init_object`. See :mod:`score.tpl.minifier` for
        available minifiers.

    :confkey:`combine` :confdefault:`False`
        Whether javascript files should be delivered as a single file. If this
        value is `true` (as defined by :func:`score.init.parse_bool`), the
        default url will point to the combined javascript file.
    """
    conf = dict(defaults.items())
    conf.update(confdict)
    if conf['minifier']:
        conf['minifier'] = init_object(conf, 'minifier')
    if not conf['cachedir'] and webassets.cachedir:
        conf['cachedir'] = os.path.join(webassets.cachedir, 'js')
    if conf['cachedir']:
        init_cache_folder(conf, 'cachedir', autopurge=True)
    conf['combine'] = parse_bool(conf['combine'])
    return ConfiguredJsModule(
        http, tpl, webassets, conf['rootdir'], conf['cachedir'],
        conf['combine'], conf['minifier'])
Exemplo n.º 24
0
def init(confdict, db, ctx=None):
    """
    Initializes this module acoording to :ref:`our module initialization
    guidelines <module_initialization>` with the following configuration keys:

    :confkey:`args.hosts`
        A list of hosts (as read by :func:`score.init.parse_list`) to pass to
        the :class:`Elasticsearch <elasticsearch.Elasticsearch>` constructor.

    :confkey:`args.*`
        Any other arguments to be passed to the :class:`Elasticsearch
        <elasticsearch.Elasticsearch>` constructor.

    :confkey:`index` :confdefault:`score`
        The index to use in all operations.

    :confkey:`ctx.member` :confdefault:`es`
        The name of the :term:`context member`, that should be registered with
        the configured :mod:`score.ctx` module (if there is one). The default
        value allows one to conveniently query the index:

        >>> for knight in ctx.es.query(User, 'name:sir*')
        ...     print(knight.name)
    """
    conf = defaults.copy()
    conf.update(confdict)
    kwargs = extract_conf(confdict, 'args.')
    if 'hosts' in kwargs:
        kwargs['hosts'] = parse_list(kwargs['hosts'])
    if 'verify_certs' in kwargs:
        kwargs['verify_certs'] = parse_bool(kwargs['verify_certs'])
    if 'use_ssl' in kwargs:
        kwargs['use_ssl'] = parse_bool(kwargs['use_ssl'])
    es = Elasticsearch(**kwargs)
    if 'index' not in confdict:
        confdict['index'] = 'score'
    es_conf = ConfiguredEsModule(db, es, confdict['index'])
    to_insert = []
    to_delete = []

    @event.listens_for(db.Session, 'before_flush')
    def before_flush(session, flush_context, instances):
        """
        Stores the list of new and altered objects in ``to_insert``, and
        deleted objects in ``to_delete``. The actual storing can only be done
        *after* the flush operation (in ``after_flush``, below), since new
        objects don't have an id at this point. But we cannot move the whole
        logic into the ``after_flush``, since we might miss the optional
        *instances* argument to this function.
        """
        nonlocal to_insert, to_delete
        to_insert = []
        to_delete = []
        for obj in session.new:
            if not instances or obj in instances:
                if es_conf.get_es_class(obj) is not None:
                    to_insert.append(obj)
        for obj in session.dirty:
            if not session.is_modified(obj):
                # might actually be unaltered, see docs of Session.dirty:
                # http://docs.sqlalchemy.org/en/latest/orm/session_api.html#sqlalchemy.orm.session.Session.dirty
                continue
            if not instances or obj in instances:
                if es_conf.get_es_class(obj) is not None:
                    to_insert.append(obj)
        for obj in session.deleted:
            if not instances or obj in instances:
                if es_conf.get_es_class(obj) is not None:
                    to_delete.append(obj)

    @event.listens_for(db.Session, 'after_flush')
    def after_flush(session, flush_context):
        for obj in to_insert:
            es_conf.insert(obj)
        for obj in to_delete:
            es_conf.delete(obj)

    if ctx and conf['ctx.member'] not in (None, 'None'):
        ctx.register(conf['ctx.member'], lambda ctx: CtxProxy(es_conf, ctx))
    return es_conf
Exemplo n.º 25
0
def init(confdict, ctx=None):
    """
    Initializes this module acoording to :ref:`our module initialization
    guidelines <module_initialization>` with the following configuration keys:

    :confkey:`sqlalchemy.*`
        All configuration values under this key will be passed to
        :func:`engine_from_config`, which in turn calls
        :func:`sqlalchemy.create_engine` with these configuration values as
        keyword arguments. Usually the following is sufficient::

            sqlalchemy.url = postgresql://dbuser@localhost/projname

    :confkey:`base` :faint:`[default=None]`
        The dotted python path to the :ref:`base class <db_base_class>` to
        configure, as interpreted by func:`score.init.parse_dotted_path`.

    :confkey:`destroyable` :faint:`[default=False]`
        Whether destructive operations may be performed on the database. This
        value prevents accidental deletion of important data on live servers.

    :confkey:`ctx.member` :faint:`[default=db]`
        The name of the :term:`context member`, that should be registered with
        the configured :mod:`score.ctx` module (if there is one). The default
        value allows you to always access a valid session within a
        :class:`score.ctx.Context` like this:

        >>> ctx.db.query(User).first()

    This function will initialize an sqlalchemy
    :ref:`Engine <sqlalchemy:engines_toplevel>` and the provided
    :ref:`base class <db_base_class>`.

    """
    conf = defaults.copy()
    conf.update(confdict)
    engine = engine_from_config(conf)
    if not conf['base']:
        import score.db
        raise ConfigurationError(score.db, 'No base class configured')
    Base = parse_dotted_path(conf['base'])
    Base.metadata.bind = engine
    ctx_member = None
    if ctx and conf['ctx.member']:
        ctx_member = conf['ctx.member']
    if engine.dialect.name == 'sqlite':
        @sa.event.listens_for(engine, "connect")
        def set_sqlite_pragma(dbapi_connection, connection_record):
            cursor = dbapi_connection.cursor()
            cursor.execute("PRAGMA foreign_keys=ON")
            cursor.close()
    db_conf = ConfiguredDbModule(
        engine, Base, parse_bool(conf['destroyable']), ctx_member)
    if ctx_member:

        def constructor(ctx):
            zope_tx = ZopeTransactionExtension(
                transaction_manager=ctx.tx_manager)
            return db_conf.Session(extension=zope_tx)

        ctx.register(ctx_member, constructor)
    return db_conf
Exemplo n.º 26
0
def init(confdict, ctx, db=None):
    """
    Initializes this module acoording to :ref:`our module initialization
    guidelines <module_initialization>` with the following configuration keys:

    :confkey:`router`
        Path to the :class:`RouteConfiguration` containing the list of routes to
        compile.

    :confkey:`preroutes` :confdefault:`list()`
        List of :term:`preroute` functions to call before invoking the actual
        route.  See :ref:`http_routing` for details.

    :confkey:`handler.*`
        Keys starting with "``handler.``" are interpreted as :ref:`error
        handlers <http_error_handler>`.

    :confkey:`debug` :confdefault:`False`
        Setting this to `True` will enable the `werkzeug debugger`_ for your
        application.

    :confkey:`urlbase` :confdefault:`None`
        This will be the prefix for all URLs generated by the module. The module
        will create relative URLs by default (i.e. `/Sir%20Lancelot`), but you
        can make it create absolute URLs by default by paassing this
        configuration value.

        If you configure this to be 'http://example.net/', your URL would be
        'http://example.net/Sir%20Lancelot'.

        Note that you can always decide, whether a *certain* URL should be
        absolute or relative, by passing the appropriate argument to
        :meth:`ConfiguredHttpModule.url`.

    :confkey:`ctx.member.url` :confdefault:`url`
        The name of the :term:`context member` function for generating URLs.

    :confkey:`serve.ip` :confdefault:`0.0.0.0`
        This will be the ip address your HTTP server will bind_ to, when using
        :mod:`score.serve` to serve your application.

    :confkey:`serve.port` :confdefault:`8080`
        This will be the port of your HTTP server, when using
        :mod:`score.serve` to serve your application.

    :confkey:`serve.threaded` :confdefault:`False`
        Setting this to `True` will make your HTTP server threaded, which should
        increase its performance. Note that your application will need to be
        thread-safe_, if you want to enable this feature.

    .. _werkzeug debugger: http://werkzeug.pocoo.org/docs/0.11/debug/#using-the-debugger
    .. _bind: http://www.xeams.com/bindtoaddress.htm
    .. _thread-safe: https://en.wikipedia.org/wiki/Thread_safety
    """
    conf = dict(defaults.items())
    conf.update(confdict)
    if 'router' not in conf:
        import score.http
        raise ConfigurationError(score.http, 'No router provided')
    router = parse_dotted_path(conf['router'])
    preroutes = list(map(parse_dotted_path, parse_list(conf['preroutes'])))
    error_handlers = {}
    exception_handlers = {}
    for error, handler in extract_conf(conf, 'handler.').items():
        if re.match('\d(\d\d|XX)', error):
            error_handlers[error] = parse_dotted_path(handler)
        else:
            error = parse_dotted_path(error)
            exception_handlers[error] = handler
    debug = parse_bool(conf['debug'])
    if not conf['urlbase']:
        conf['urlbase'] = ''
    http = ConfiguredHttpModule(
        ctx, db, router, preroutes, error_handlers, exception_handlers, debug,
        conf['urlbase'], conf['serve.ip'], int(conf['serve.port']),
        parse_bool(conf['serve.threaded']))

    def constructor(ctx):
        def url(*args, **kwargs):
            return http.url(ctx, *args, **kwargs)
        return url

    ctx.register(conf['ctx.member.url'], constructor)
    return http
Exemplo n.º 27
0
def init(confdict, db, ctx=None):
    """
    Initializes this module acoording to :ref:`our module initialization
    guidelines <module_initialization>` with the following configuration keys:

    :confkey:`args.hosts`
        A list of hosts (as read by :func:`score.init.parse_list`) to pass to
        the :class:`Elasticsearch <elasticsearch.Elasticsearch>` constructor.

    :confkey:`args.*`
        Any other arguments to be passed to the :class:`Elasticsearch
        <elasticsearch.Elasticsearch>` constructor.

    :confkey:`index` :confdefault:`score`
        The index to use in all operations.

    :confkey:`ctx.member` :confdefault:`es`
        The name of the :term:`context member`, that should be registered with
        the configured :mod:`score.ctx` module (if there is one). The default
        value allows one to conveniently query the index:

        >>> for knight in ctx.es.query(User, 'name:sir*')
        ...     print(knight.name)
    """
    conf = defaults.copy()
    conf.update(confdict)
    kwargs = extract_conf(confdict, 'args.')
    if 'hosts' in kwargs:
        kwargs['hosts'] = parse_list(kwargs['hosts'])
    if 'verify_certs' in kwargs:
        kwargs['verify_certs'] = parse_bool(kwargs['verify_certs'])
    if 'use_ssl' in kwargs:
        kwargs['use_ssl'] = parse_bool(kwargs['use_ssl'])
    es = Elasticsearch(**kwargs)
    if 'index' not in confdict:
        confdict['index'] = 'score'
    es_conf = ConfiguredEsModule(db, es, confdict['index'])
    to_insert = []
    to_delete = []

    @event.listens_for(db.Session, 'before_flush')
    def before_flush(session, flush_context, instances):
        """
        Stores the list of new and altered objects in ``to_insert``, and
        deleted objects in ``to_delete``. The actual storing can only be done
        *after* the flush operation (in ``after_flush``, below), since new
        objects don't have an id at this point. But we cannot move the whole
        logic into the ``after_flush``, since we might miss the optional
        *instances* argument to this function.
        """
        nonlocal to_insert, to_delete
        to_insert = []
        to_delete = []
        for obj in session.new:
            if not instances or obj in instances:
                if es_conf.get_es_class(obj) is not None:
                    to_insert.append(obj)
        for obj in session.dirty:
            if not session.is_modified(obj):
                # might actually be unaltered, see docs of Session.dirty:
                # http://docs.sqlalchemy.org/en/latest/orm/session_api.html#sqlalchemy.orm.session.Session.dirty
                continue
            if not instances or obj in instances:
                if es_conf.get_es_class(obj) is not None:
                    to_insert.append(obj)
        for obj in session.deleted:
            if not instances or obj in instances:
                if es_conf.get_es_class(obj) is not None:
                    to_delete.append(obj)

    @event.listens_for(db.Session, 'after_flush')
    def after_flush(session, flush_context):
        for obj in to_insert:
            es_conf.insert(obj)
        for obj in to_delete:
            es_conf.delete(obj)
    if ctx and conf['ctx.member'] not in (None, 'None'):
        ctx.register(conf['ctx.member'], lambda ctx: CtxProxy(es_conf, ctx))
    return es_conf