Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/source/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,13 @@ type of database, set :attr:`~CoreSection.db_type` to one of these values:
* ``firebird``
* ``sybase``

.. note::

In certain environments, specifying the :attr:`~CoreSection.db_url`
setting via :ref:`environment variable <Overriding individual settings>`
may be more convenient. Doing so will supersede all of the other options
described in this section.

SQLite
------

Expand Down
19 changes: 19 additions & 0 deletions sopel/config/core_section.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,25 @@ class CoreSection(StaticSection):

"""

db_url = ValidatedAttribute('db_url')
"""A raw database URL.

If this option is present, Sopel will ignore **all** other ``db_*``
settings and use this option's value only.

Any supported database type *other than SQLite* can be used this way.

.. note::

Specifying this option via the ``SOPEL_CORE_DB_URL`` :ref:`environment
variable <Overriding individual settings>` may prove especially useful
in certain cloud environments, avoiding the need to split a database
URI provided by the platform at runtime into its components with a
startup script.

.. versionadded:: 8.0
"""

db_user = ValidatedAttribute('db_user')
"""The user for Sopel's database.

Expand Down
68 changes: 38 additions & 30 deletions sopel/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import traceback

from sqlalchemy import Column, create_engine, ForeignKey, Integer, String
from sqlalchemy.engine.url import URL
from sqlalchemy.engine.url import make_url, URL
from sqlalchemy.exc import OperationalError, SQLAlchemyError
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
Expand Down Expand Up @@ -112,32 +112,36 @@ class SopelDB(object):
"""

def __init__(self, config):
# MySQL - mysql://username:password@localhost/db
# SQLite - sqlite:////home/sopel/.sopel/default.db
self.type = config.core.db_type

# Handle SQLite explicitly as a default
if self.type == 'sqlite':
path = config.core.db_filename
if path is None:
path = os.path.join(config.core.homedir, config.basename + '.db')
path = os.path.expanduser(path)
if not os.path.isabs(path):
path = os.path.normpath(os.path.join(config.core.homedir, path))
if not os.path.isdir(os.path.dirname(path)):
raise OSError(
errno.ENOENT,
'Cannot create database file. '
'No such directory: "{}". Check that configuration setting '
'core.db_filename is valid'.format(os.path.dirname(path)),
path
)
self.filename = path
self.url = 'sqlite:///%s' % path
# Otherwise, handle all other database engines
if config.core.db_url is not None:
self.url = make_url(config.core.db_url)

# TODO: there's no way to get `config.core.db_type.choices`, but
# it would be nice to validate this type name somehow. Shouldn't
# affect anything, since the only thing it's ever used for is
# checking whether the configured database is 'sqlite'.
self.type = self.url.drivername.split('+', 1)[0]
else:
self.type = config.core.db_type

query = {}
if self.type == 'mysql':
if self.type == 'sqlite':
drivername = config.core.db_driver or 'sqlite'
path = config.core.db_filename
if path is None:
path = os.path.join(config.core.homedir, config.basename + '.db')
path = os.path.expanduser(path)
if not os.path.isabs(path):
path = os.path.normpath(os.path.join(config.core.homedir, path))
if not os.path.isdir(os.path.dirname(path)):
raise OSError(
errno.ENOENT,
'Cannot create database file. '
'No such directory: "{}". Check that configuration setting '
'core.db_filename is valid'.format(os.path.dirname(path)),
path
)
self.filename = path
elif self.type == 'mysql':
drivername = config.core.db_driver or 'mysql'
query = {'charset': 'utf8mb4'}
elif self.type == 'postgres':
Expand All @@ -153,14 +157,18 @@ def __init__(self, config):
else:
raise Exception('Unknown db_type')

db_user = config.core.db_user
db_pass = config.core.db_pass
db_host = config.core.db_host
db_user = config.core.db_user # Sometimes empty
db_pass = config.core.db_pass # Sometimes empty
db_host = config.core.db_host # Sometimes empty
db_port = config.core.db_port # Optional
db_name = config.core.db_name # Optional, depending on DB
db_name = getattr(self, 'filename', None) or config.core.db_name

# Ensure we have all our variables defined
if db_user is None or db_pass is None or db_host is None:
if self.type != 'sqlite' and (
db_user is None or
db_pass is None or
db_host is None
):
raise Exception('Please make sure the following core '
'configuration values are defined: '
'db_user, db_pass, db_host')
Expand Down