# Copyright © 2017,2018 STRG.AT GmbH, Vienna, Austria
# Copyright © 2019-2023 Necdet Can Ateşman, Vienna, Austria
#
# This file is part of the The SCORE Framework.
#
# The SCORE Framework and all its parts are free software: you can redistribute
# them and/or modify them under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation which is in
# the file named COPYING.LESSER.txt.
#
# The SCORE Framework and all its parts are distributed without any WARRANTY;
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. For more details see the GNU Lesser General Public
# License.
#
# If you have not received a copy of the GNU Lesser General Public License see
# http://www.gnu.org/licenses/.
#
# The License-Agreement realised between you as Licensee and STRG.AT GmbH as
# Licenser including the issue of its valid conclusion and its pre- and
# post-contractual effects is governed by the laws of Austria. Any disputes
# concerning this License-Agreement including the issue of its valid conclusion
# and its pre- and post-contractual effects are exclusively decided by the
# competent court, in whose district STRG.AT GmbH has its registered seat, at
# the discretion of STRG.AT GmbH also the competent court, in whose district
# the Licensee has his registered seat, an establishment or assets.
import sqlalchemy as sa
from score.init import (
ConfiguredModule, parse_dotted_path, parse_bool, parse_call)
defaults = {
'destroyable': False,
'ctx.member': 'db',
'ctx.transaction': True,
}
[docs]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)
_registered_utf8mb4 = False
[docs]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)