From e9ab3f31504a176f483c8340b3ad2ba7679fe285 Mon Sep 17 00:00:00 2001
From: Abhilash Raj
Date: Wed, 24 Sep 2014 23:13:12 +0530
Subject: added support for migrations via alembic
---
src/mailman/testing/layers.py | 4 ++++
1 file changed, 4 insertions(+)
(limited to 'src/mailman/testing')
diff --git a/src/mailman/testing/layers.py b/src/mailman/testing/layers.py
index eb51e309f..3d2a50782 100644
--- a/src/mailman/testing/layers.py
+++ b/src/mailman/testing/layers.py
@@ -190,6 +190,10 @@ class ConfigLayer(MockAndMonkeyLayer):
@classmethod
def tearDown(cls):
assert cls.var_dir is not None, 'Layer not set up'
+ # Reset the test database after the tests are done so that there
+ # is no data incase the tests are rerun with a database layer like
+ # mysql or postgresql which are not deleted in teardown.
+ reset_the_world()
config.pop('test config')
shutil.rmtree(cls.var_dir)
cls.var_dir = None
--
cgit v1.3.1
From 95fc64b4894e5985bb8d0e5e944b2cda38c9a58c Mon Sep 17 00:00:00 2001
From: Abhilash Raj
Date: Thu, 25 Sep 2014 18:36:24 +0530
Subject: Add support for postgresql
* revert changes in message_id_has encoding by barry
* Change message_id_hash column to LargeBinary
(from previously mistaken one i.e.unicode)
* add missing import in database/types.py
* fix a bug in database/Model.py, transaction has no
method abort(), instead it is rollback()
---
src/mailman/database/model.py | 2 +-
src/mailman/database/postgresql.py | 9 ++++-----
src/mailman/database/types.py | 3 ++-
src/mailman/model/docs/messagestore.rst | 5 ++---
src/mailman/model/message.py | 4 ++--
src/mailman/model/messagestore.py | 10 +++++++---
src/mailman/model/pending.py | 2 +-
src/mailman/styles/base.py | 4 ++--
src/mailman/testing/testing.cfg | 6 +++---
src/mailman/utilities/importer.py | 9 +++++++++
10 files changed, 33 insertions(+), 21 deletions(-)
(limited to 'src/mailman/testing')
diff --git a/src/mailman/database/model.py b/src/mailman/database/model.py
index 06705dfe9..b65d7d5eb 100644
--- a/src/mailman/database/model.py
+++ b/src/mailman/database/model.py
@@ -49,7 +49,7 @@ class ModelMeta:
for table in reversed(Model.metadata.sorted_tables):
connection.execute(table.delete())
except:
- transaction.abort()
+ transaction.rollback()
raise
else:
transaction.commit()
diff --git a/src/mailman/database/postgresql.py b/src/mailman/database/postgresql.py
index 59fff0865..ca1068302 100644
--- a/src/mailman/database/postgresql.py
+++ b/src/mailman/database/postgresql.py
@@ -40,15 +40,14 @@ class PostgreSQLDatabase(SABaseDatabase):
restart from zero for new tests.
"""
super(PostgreSQLDatabase, self)._post_reset(store)
- from mailman.database.model import ModelMeta
- classes = sorted(ModelMeta._class_registry,
- key=attrgetter('__storm_table__'))
+ from mailman.database.model import Model
+ tables = reversed(Model.metadata.sorted_tables)
# Recipe adapted from
# http://stackoverflow.com/questions/544791/
# django-postgresql-how-to-reset-primary-key
- for model_class in classes:
+ for table in tables:
store.execute("""\
SELECT setval('"{0}_id_seq"', coalesce(max("id"), 1),
max("id") IS NOT null)
FROM "{0}";
- """.format(model_class.__storm_table__))
+ """.format(table))
diff --git a/src/mailman/database/types.py b/src/mailman/database/types.py
index 641e065ba..eec6df6d5 100644
--- a/src/mailman/database/types.py
+++ b/src/mailman/database/types.py
@@ -30,6 +30,7 @@ import uuid
from sqlalchemy import Integer
from sqlalchemy.types import TypeDecorator, CHAR
+from sqlalchemy.dialects import postgresql
@@ -68,7 +69,7 @@ class UUID(TypeDecorator):
def load_dialect_impl(self, dialect):
if dialect.name == 'postgresql':
- return dialect.type_descriptor(UUID())
+ return dialect.type_descriptor(postgresql.UUID())
else:
return dialect.type_descriptor(CHAR(32))
diff --git a/src/mailman/model/docs/messagestore.rst b/src/mailman/model/docs/messagestore.rst
index f2f2ca9d2..bb853e575 100644
--- a/src/mailman/model/docs/messagestore.rst
+++ b/src/mailman/model/docs/messagestore.rst
@@ -28,9 +28,8 @@ header, you will get an exception.
However, if the message has a ``Message-ID`` header, it can be stored.
>>> msg['Message-ID'] = '<87myycy5eh.fsf@uwakimon.sk.tsukuba.ac.jp>'
- >>> x_message_id_hash = message_store.add(msg)
- >>> print(x_message_id_hash)
- AGDWSNXXKCWEILKKNYTBOHRDQGOX3Y35
+ >>> message_store.add(msg)
+ 'AGDWSNXXKCWEILKKNYTBOHRDQGOX3Y35'
>>> print(msg.as_string())
Subject: An important message
Message-ID: <87myycy5eh.fsf@uwakimon.sk.tsukuba.ac.jp>
diff --git a/src/mailman/model/message.py b/src/mailman/model/message.py
index 74a76ac30..c03d4bbd6 100644
--- a/src/mailman/model/message.py
+++ b/src/mailman/model/message.py
@@ -41,8 +41,8 @@ class Message(Model):
id = Column(Integer, primary_key=True)
message_id = Column(Unicode)
- message_id_hash = Column(Unicode)
- path = Column(LargeBinary) # TODO : was RawStr()
+ message_id_hash = Column(LargeBinary)
+ path = Column(LargeBinary)
# This is a Messge-ID field representation, not a database row id.
@dbconnection
diff --git a/src/mailman/model/messagestore.py b/src/mailman/model/messagestore.py
index 225d3d1ce..0b8a0ac78 100644
--- a/src/mailman/model/messagestore.py
+++ b/src/mailman/model/messagestore.py
@@ -66,7 +66,7 @@ class MessageStore:
'Message ID already exists in message store: {0}'.format(
message_id))
shaobj = hashlib.sha1(message_id)
- hash32 = base64.b32encode(shaobj.digest()).decode('ascii')
+ hash32 = base64.b32encode(shaobj.digest())
del message['X-Message-ID-Hash']
message['X-Message-ID-Hash'] = hash32
# Calculate the path on disk where we're going to store this message
@@ -115,8 +115,12 @@ class MessageStore:
@dbconnection
def get_message_by_hash(self, store, message_id_hash):
- if isinstance(message_id_hash, bytes):
- message_id_hash = message_id_hash.decode('utf-8')
+ # It's possible the hash came from a message header, in which case it
+ # will be a Unicode. However when coming from source code, it may be
+ # an 8-string. Coerce to the latter if necessary; it must be
+ # US-ASCII.
+ if isinstance(message_id_hash, unicode):
+ message_id_hash = message_id_hash.encode('ascii')
row = store.query(Message).filter_by(
message_id_hash=message_id_hash).first()
if row is None:
diff --git a/src/mailman/model/pending.py b/src/mailman/model/pending.py
index 68a8cd63e..691e94fd9 100644
--- a/src/mailman/model/pending.py
+++ b/src/mailman/model/pending.py
@@ -71,7 +71,7 @@ class Pended(Model):
__tablename__ = 'pended'
id = Column(Integer, primary_key=True)
- token = Column(LargeBinary) # TODO : was RawStr()
+ token = Column(LargeBinary)
expiration_date = Column(DateTime)
key_values = relationship('PendedKeyValue')
diff --git a/src/mailman/styles/base.py b/src/mailman/styles/base.py
index d83b2e2a5..4f051c13c 100644
--- a/src/mailman/styles/base.py
+++ b/src/mailman/styles/base.py
@@ -67,9 +67,9 @@ class Identity:
# Set this to Never if the list's preferred language uses us-ascii,
# otherwise set it to As Needed.
if mlist.preferred_language.charset == 'us-ascii':
- mlist.encode_ascii_prefixes = 0
+ mlist.encode_ascii_prefixes = False
else:
- mlist.encode_ascii_prefixes = 2
+ mlist.encode_ascii_prefixes = True
diff --git a/src/mailman/testing/testing.cfg b/src/mailman/testing/testing.cfg
index fc76aa361..eb9de5513 100644
--- a/src/mailman/testing/testing.cfg
+++ b/src/mailman/testing/testing.cfg
@@ -18,9 +18,9 @@
# A testing configuration.
# For testing against PostgreSQL.
-# [database]
-# class: mailman.database.postgresql.PostgreSQLDatabase
-# url: postgres://barry:barry@localhost/mailman
+[database]
+class: mailman.database.postgresql.PostgreSQLDatabase
+url: postgresql://maxking:maxking@localhost/mailman_test
[mailman]
site_owner: noreply@example.com
diff --git a/src/mailman/utilities/importer.py b/src/mailman/utilities/importer.py
index 26b7261e3..99531194f 100644
--- a/src/mailman/utilities/importer.py
+++ b/src/mailman/utilities/importer.py
@@ -128,6 +128,12 @@ def nonmember_action_mapping(value):
}[value]
+def int_to_bool(value):
+ if value:
+ return True
+ else:
+ return False
+
def check_language_code(code):
if code is None:
@@ -172,6 +178,9 @@ TYPES = dict(
personalize=Personalization,
preferred_language=check_language_code,
reply_goes_to_list=ReplyToMunging,
+ allow_list_posts=int_to_bool,
+ include_rfc2369_headers=int_to_bool,
+ nntp_prefix_subject_too=int_to_bool,
)
--
cgit v1.3.1
From 19850b6abd93a27fe22f80b20b8c7ba9bcb24205 Mon Sep 17 00:00:00 2001
From: Aurélien Bompard
Date: Fri, 3 Oct 2014 17:08:11 +0200
Subject: New DB testing layer that does not auto-create the DB
---
src/mailman/testing/layers.py | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
(limited to 'src/mailman/testing')
diff --git a/src/mailman/testing/layers.py b/src/mailman/testing/layers.py
index 3d2a50782..304f9a891 100644
--- a/src/mailman/testing/layers.py
+++ b/src/mailman/testing/layers.py
@@ -47,6 +47,7 @@ import tempfile
from lazr.config import as_boolean
from pkg_resources import resource_string
+from sqlalchemy import MetaData
from textwrap import dedent
from zope.component import getUtility
@@ -54,6 +55,7 @@ from mailman.config import config
from mailman.core import initialize
from mailman.core.initialize import INHIBIT_CONFIG_FILE
from mailman.core.logging import get_handler
+from mailman.database.model import Model
from mailman.database.transaction import transaction
from mailman.interfaces.domain import IDomainManager
from mailman.testing.helpers import (
@@ -309,6 +311,34 @@ class RESTLayer(SMTPLayer):
cls.server = None
+
+class DatabaseLayer(MockAndMonkeyLayer):
+ """Layer for database tests"""
+
+ @classmethod
+ def _drop_all_tables(cls):
+ Model.metadata.drop_all(config.db.engine)
+ md = MetaData()
+ md.reflect(bind=config.db.engine)
+ if "alembic_version" in md.tables:
+ md.tables["alembic_version"].drop(config.db.engine)
+
+ @classmethod
+ def setUp(cls):
+ # Set up the basic configuration stuff. Turn off path creation.
+ config.create_paths = False
+ initialize.initialize_1(INHIBIT_CONFIG_FILE)
+ # Don't initialize the database.
+
+ @classmethod
+ def tearDown(cls):
+ cls._drop_all_tables()
+
+ @classmethod
+ def testTearDown(cls):
+ cls._drop_all_tables()
+
+
def is_testing():
"""Return a 'testing' flag for use with the predictable factories.
--
cgit v1.3.1
From 061799ef5031977bd343bbe54a6ad809138bdb45 Mon Sep 17 00:00:00 2001
From: Abhilash Raj
Date: Fri, 3 Oct 2014 21:56:50 +0530
Subject: add central alembic config
---
src/mailman/commands/cli_migrate.py | 5 +----
src/mailman/config/alembic.cfg | 8 ++++----
src/mailman/database/alembic/__init__.py | 32 ++++++++++++++++++++++++++++++++
src/mailman/database/alembic/env.py | 17 ++++-------------
src/mailman/database/base.py | 5 +----
src/mailman/database/factory.py | 8 +++-----
src/mailman/testing/testing.cfg | 2 +-
7 files changed, 46 insertions(+), 31 deletions(-)
(limited to 'src/mailman/testing')
diff --git a/src/mailman/commands/cli_migrate.py b/src/mailman/commands/cli_migrate.py
index ca9d5e9d4..82bf4a708 100644
--- a/src/mailman/commands/cli_migrate.py
+++ b/src/mailman/commands/cli_migrate.py
@@ -26,11 +26,11 @@ __all__ = [
from alembic import command
-from alembic.config import Config
from zope.interface import implementer
from mailman.config import config
from mailman.core.i18n import _
+from mailman.database.alembic import alembic_cfg
from mailman.interfaces.command import ICLISubCommand
from mailman.utilities.modules import expand_path
@@ -54,9 +54,6 @@ class Migrate:
help=('Produce less output.'))
def process(self, args):
- alembic_cfg = Config()
- alembic_cfg.set_main_option(
- 'script_location', expand_path(config.database['alembic_scripts']))
if args.autogenerate:
command.revision(alembic_cfg, autogenerate=True)
else:
diff --git a/src/mailman/config/alembic.cfg b/src/mailman/config/alembic.cfg
index 46ac90b61..b1f53a3d2 100644
--- a/src/mailman/config/alembic.cfg
+++ b/src/mailman/config/alembic.cfg
@@ -38,18 +38,18 @@ qualname =
[logger_sqlalchemy]
level = WARN
-handlers =
+handlers = console
qualname = sqlalchemy.engine
[logger_alembic]
-level = INFO
-handlers =
+level = WARN
+handlers = console
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
-level = NOTSET
+level = WARN
formatter = generic
[formatter_generic]
diff --git a/src/mailman/database/alembic/__init__.py b/src/mailman/database/alembic/__init__.py
index e69de29bb..73f30832e 100644
--- a/src/mailman/database/alembic/__init__.py
+++ b/src/mailman/database/alembic/__init__.py
@@ -0,0 +1,32 @@
+# Copyright (C) 2014 by the Free Software Foundation, Inc.
+#
+# This file is part of GNU Mailman.
+#
+# GNU Mailman is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# GNU Mailman 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 General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# GNU Mailman. If not, see .
+
+"Alembic config init."
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'alembic_cfg'
+]
+
+
+from alembic.config import Config
+from mailman.utilities.modules import expand_path
+
+
+alembic_cfg=Config(expand_path("python:mailman.config.alembic"))
diff --git a/src/mailman/database/alembic/env.py b/src/mailman/database/alembic/env.py
index 5e4f6bfdb..d1caec58d 100644
--- a/src/mailman/database/alembic/env.py
+++ b/src/mailman/database/alembic/env.py
@@ -27,17 +27,20 @@ __all__ = [
from alembic import context
-from alembic.config import Config
from contextlib import closing
+from logging.config import fileConfig
from sqlalchemy import create_engine
from mailman.core import initialize
from mailman.config import config
+from mailman.database.alembic import alembic_cfg
from mailman.database.model import Model
from mailman.utilities.modules import expand_path
from mailman.utilities.string import expand
+fileConfig(alembic_cfg.config_file_name)
+
def run_migrations_offline():
"""Run migrations in 'offline' mode.
@@ -49,11 +52,6 @@ def run_migrations_offline():
Calls to context.execute() here emit the given string to the script
output.
"""
- if not config.initialized:
- initialize.initialize_1(context.config.config_file_name)
- alembic_cfg= Config()
- alembic_cfg.set_main_option(
- "script_location", config.alembic['script_location'])
url = expand(config.database.url, config.paths)
context.configure(url=url, target_metadata=Model.metadata)
with context.begin_transaction():
@@ -66,13 +64,6 @@ def run_migrations_online():
In this scenario we need to create an Engine and associate a
connection with the context.
"""
-
- if not config.initialized:
- initialize.initialize_1(context.config.config_file_name)
- alembic_cfg = Config()
- alembic_cfg.set_main_option(
- 'script_location', expand_path(config.database['alembic_scripts']))
- alembic_cfg.set_section_option('logger_alembic' ,'level' , 'ERROR')
url = expand(config.database.url, config.paths)
engine = create_engine(url)
diff --git a/src/mailman/database/base.py b/src/mailman/database/base.py
index 26b6ddbbb..dcaedade0 100644
--- a/src/mailman/database/base.py
+++ b/src/mailman/database/base.py
@@ -26,12 +26,12 @@ __all__ = [
import logging
from alembic import command
-from alembic.config import Config
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from zope.interface import implementer
from mailman.config import config
+from mailman.database.alembic import alembic_cfg
from mailman.interfaces.database import IDatabase
from mailman.utilities.string import expand
@@ -97,9 +97,6 @@ class SABaseDatabase:
# create_all() ceates the latest schema. This patches the database
# with the latest Alembic version to add an entry in the
# alembic_version table.
- alembic_cfg = Config()
- alembic_cfg.set_main_option(
- 'script_location', config.database['alembic_scripts'])
command.stamp(alembic_cfg, 'head')
diff --git a/src/mailman/database/factory.py b/src/mailman/database/factory.py
index 6111be8c5..469ed5d18 100644
--- a/src/mailman/database/factory.py
+++ b/src/mailman/database/factory.py
@@ -30,7 +30,6 @@ import os
import types
from alembic import command
-from alembic.config import Config as AlembicConfig
from alembic.migration import MigrationContext
from alembic.script import ScriptDirectory
from flufl.lock import Lock
@@ -40,8 +39,9 @@ from zope.interface.verify import verifyObject
from mailman.config import config
from mailman.database.model import Model
+from mailman.database.alembic import alembic_cfg
from mailman.interfaces.database import IDatabase, IDatabaseFactory
-from mailman.utilities.modules import call_name
+from mailman.utilities.modules import call_name, expand_path
@@ -70,9 +70,7 @@ class SchemaManager:
def __init__(self, database):
self.database = database
- self.alembic_cfg = AlembicConfig()
- self.alembic_cfg.set_main_option(
- "script_location", config.alembic['script_location'])
+ self.alembic_cfg = alembic_cfg
self.script = ScriptDirectory.from_config(self.alembic_cfg)
def get_storm_schema_version(self):
diff --git a/src/mailman/testing/testing.cfg b/src/mailman/testing/testing.cfg
index fc76aa361..39d1d922a 100644
--- a/src/mailman/testing/testing.cfg
+++ b/src/mailman/testing/testing.cfg
@@ -20,7 +20,7 @@
# For testing against PostgreSQL.
# [database]
# class: mailman.database.postgresql.PostgreSQLDatabase
-# url: postgres://barry:barry@localhost/mailman
+# url: postgres://maxking:maxking@localhost/mailman_test
[mailman]
site_owner: noreply@example.com
--
cgit v1.3.1
From cbbac03083357ca928d104d386d9e3008c937581 Mon Sep 17 00:00:00 2001
From: Aurélien Bompard
Date: Mon, 6 Oct 2014 14:47:38 +0200
Subject: Fix DB unit tests
---
src/mailman/database/tests/test_factory.py | 54 +++++++++++-------------------
src/mailman/testing/layers.py | 38 ++++++++++++++++++---
2 files changed, 53 insertions(+), 39 deletions(-)
(limited to 'src/mailman/testing')
diff --git a/src/mailman/database/tests/test_factory.py b/src/mailman/database/tests/test_factory.py
index 7becdbfde..42a05fe1b 100644
--- a/src/mailman/database/tests/test_factory.py
+++ b/src/mailman/database/tests/test_factory.py
@@ -40,6 +40,7 @@ from mailman.database.model import Model
class TestSchemaManager(unittest.TestCase):
+
layer = DatabaseLayer
def setUp(self):
@@ -55,23 +56,22 @@ class TestSchemaManager(unittest.TestCase):
Model.metadata.remove(version)
- def _db_is_setup(self):
- md = MetaData()
- md.reflect(bind=config.db.engine)
- return "mailinglist" in md.tables and "alembic_version" in md.tables
-
def _table_exists(self, tablename):
md = MetaData()
md.reflect(bind=config.db.engine)
return tablename in md.tables
- def _create_storm_version_table(self):
- table = Table("version", Model.metadata,
- Column("id", Integer, primary_key=True),
- Column("component", Unicode),
- Column("version", Unicode),
- )
- table.create(config.db.engine)
+ def _create_storm_version_table(self, revision):
+ version_table = Table("version", Model.metadata,
+ Column("id", Integer, primary_key=True),
+ Column("component", Unicode),
+ Column("version", Unicode),
+ )
+ version_table.create(config.db.engine)
+ config.db.store.execute(version_table.insert().values(
+ component='schema', version=revision))
+ config.db.commit()
+
def test_current_db(self):
"""The database is already at the latest version"""
@@ -84,27 +84,19 @@ class TestSchemaManager(unittest.TestCase):
def test_initial(self):
"""No existing database"""
- self.assertFalse(self._table_exists("mailinglist")
- and self._table_exists("alembic_version"))
+ self.assertFalse(self._table_exists("mailinglist"))
+ self.assertFalse(self._table_exists("alembic_version"))
self.schema_mgr._upgrade = Mock()
self.schema_mgr.setup_db()
self.assertFalse(self.schema_mgr._upgrade.called)
- self.assertTrue(self._table_exists("mailinglist")
- and self._table_exists("alembic_version"))
+ self.assertTrue(self._table_exists("mailinglist"))
+ self.assertTrue(self._table_exists("alembic_version"))
def test_storm(self):
"""Existing Storm database"""
Model.metadata.create_all(config.db.engine)
- version_table = Table("version", Model.metadata,
- Column("id", Integer, primary_key=True),
- Column("component", Unicode),
- Column("version", Unicode),
- )
- version_table.create(config.db.engine)
- config.db.store.execute(version_table.insert().values(
- component='schema',
- version=self.schema_mgr.LAST_STORM_SCHEMA_VERSION))
- config.db.commit()
+ self._create_storm_version_table(
+ self.schema_mgr.LAST_STORM_SCHEMA_VERSION)
self.schema_mgr._create = Mock()
self.schema_mgr.setup_db()
self.assertFalse(self.schema_mgr._create.called)
@@ -115,15 +107,7 @@ class TestSchemaManager(unittest.TestCase):
def test_old_storm(self):
"""Existing Storm database in an old version"""
Model.metadata.create_all(config.db.engine)
- version_table = Table("version", Model.metadata,
- Column("id", Integer, primary_key=True),
- Column("component", Unicode),
- Column("version", Unicode),
- )
- version_table.create(config.db.engine)
- config.db.store.execute(version_table.insert().values(
- component='schema', version='001'))
- config.db.commit()
+ self._create_storm_version_table("001")
self.schema_mgr._create = Mock()
self.assertRaises(RuntimeError, self.schema_mgr.setup_db)
self.assertFalse(self.schema_mgr._create.called)
diff --git a/src/mailman/testing/layers.py b/src/mailman/testing/layers.py
index 304f9a891..68cb07f8c 100644
--- a/src/mailman/testing/layers.py
+++ b/src/mailman/testing/layers.py
@@ -49,6 +49,7 @@ from lazr.config import as_boolean
from pkg_resources import resource_string
from sqlalchemy import MetaData
from textwrap import dedent
+from zope import event
from zope.component import getUtility
from mailman.config import config
@@ -100,7 +101,9 @@ class ConfigLayer(MockAndMonkeyLayer):
# Set up the basic configuration stuff. Turn off path creation until
# we've pushed the testing config.
config.create_paths = False
- initialize.initialize_1(INHIBIT_CONFIG_FILE)
+ if not event.subscribers:
+ # only if not yet initialized by another layer
+ initialize.initialize_1(INHIBIT_CONFIG_FILE)
assert cls.var_dir is None, 'Layer already set up'
# Calculate a temporary VAR_DIR directory so that run-time artifacts
# of the tests won't tread on the installation's data. This also
@@ -312,9 +315,11 @@ class RESTLayer(SMTPLayer):
-class DatabaseLayer(MockAndMonkeyLayer):
+class DatabaseLayer:
"""Layer for database tests"""
+ var_dir = None
+
@classmethod
def _drop_all_tables(cls):
Model.metadata.drop_all(config.db.engine)
@@ -327,12 +332,37 @@ class DatabaseLayer(MockAndMonkeyLayer):
def setUp(cls):
# Set up the basic configuration stuff. Turn off path creation.
config.create_paths = False
- initialize.initialize_1(INHIBIT_CONFIG_FILE)
+ if not event.subscribers:
+ # only if not yet initialized by another layer
+ initialize.initialize_1(INHIBIT_CONFIG_FILE)
# Don't initialize the database.
+ cls.var_dir = tempfile.mkdtemp()
+ test_config = dedent("""
+ [mailman]
+ layout: testing
+ [paths.testing]
+ var_dir: {0}
+ [devmode]
+ testing: yes
+ """.format(cls.var_dir))
+ # Read the testing config and push it.
+ test_config += resource_string('mailman.testing', 'testing.cfg')
+ config.create_paths = True
+ config.push('test config', test_config)
+ # Write the configuration file for subprocesses and set up the config
+ # object to pass that properly on the -C option.
+ config_file = os.path.join(cls.var_dir, 'test.cfg')
+ with open(config_file, 'w') as fp:
+ fp.write(test_config)
+ print(file=fp)
+ config.filename = config_file
@classmethod
def tearDown(cls):
- cls._drop_all_tables()
+ assert cls.var_dir is not None, 'Layer not set up'
+ shutil.rmtree(cls.var_dir)
+ config.pop('test config')
+ cls.var_dir = None
@classmethod
def testTearDown(cls):
--
cgit v1.3.1
From b023009538019927b5fe67f129469ee8d4951f91 Mon Sep 17 00:00:00 2001
From: Aurélien Bompard
Date: Mon, 6 Oct 2014 19:17:50 +0200
Subject: Don't use a testing layer for database tests
---
src/mailman/database/tests/test_factory.py | 15 +++++---
src/mailman/testing/layers.py | 55 ------------------------------
2 files changed, 10 insertions(+), 60 deletions(-)
(limited to 'src/mailman/testing')
diff --git a/src/mailman/database/tests/test_factory.py b/src/mailman/database/tests/test_factory.py
index 81febbde5..a87bca7be 100644
--- a/src/mailman/database/tests/test_factory.py
+++ b/src/mailman/database/tests/test_factory.py
@@ -32,7 +32,7 @@ from mock import Mock
from sqlalchemy import MetaData, Table, Column, Integer, Unicode
from mailman.config import config
-from mailman.testing.layers import DatabaseLayer
+from mailman.testing.layers import ConfigLayer
from mailman.database.factory import SchemaManager, _reset
from mailman.database.sqlite import SQLiteDatabase
from mailman.database.alembic import alembic_cfg
@@ -42,12 +42,15 @@ from mailman.database.model import Model
class TestSchemaManager(unittest.TestCase):
- layer = DatabaseLayer
+ layer = ConfigLayer
def setUp(self):
- config.db = SQLiteDatabase()
- config.db.initialize()
- config.db._reset = types.MethodType(_reset, config.db)
+ # Drop the existing database
+ Model.metadata.drop_all(config.db.engine)
+ md = MetaData()
+ md.reflect(bind=config.db.engine)
+ if "alembic_version" in md.tables:
+ md.tables["alembic_version"].drop(config.db.engine)
self.schema_mgr = SchemaManager(config.db)
def tearDown(self):
@@ -55,6 +58,8 @@ class TestSchemaManager(unittest.TestCase):
version = Model.metadata.tables["version"]
version.drop(config.db.engine, checkfirst=True)
Model.metadata.remove(version)
+ # Restore a virgin DB
+ Model.metadata.create_all(config.db.engine)
def _table_exists(self, tablename):
diff --git a/src/mailman/testing/layers.py b/src/mailman/testing/layers.py
index c1346b29f..99852e135 100644
--- a/src/mailman/testing/layers.py
+++ b/src/mailman/testing/layers.py
@@ -314,61 +314,6 @@ class RESTLayer(SMTPLayer):
cls.server = None
-
-class DatabaseLayer:
- """Layer for database tests"""
-
- var_dir = None
-
- @classmethod
- def _drop_all_tables(cls):
- Model.metadata.drop_all(config.db.engine)
- md = MetaData()
- md.reflect(bind=config.db.engine)
- if "alembic_version" in md.tables:
- md.tables["alembic_version"].drop(config.db.engine)
-
- @classmethod
- def setUp(cls):
- # Set up the basic configuration stuff. Turn off path creation.
- config.create_paths = False
- if not event.subscribers:
- # only if not yet initialized by another layer
- initialize.initialize_1(INHIBIT_CONFIG_FILE)
- # Don't initialize the database.
- cls.var_dir = tempfile.mkdtemp()
- test_config = dedent("""
- [mailman]
- layout: testing
- [paths.testing]
- var_dir: {0}
- [devmode]
- testing: yes
- """.format(cls.var_dir))
- # Read the testing config and push it.
- test_config += resource_string('mailman.testing', 'testing.cfg')
- config.create_paths = True
- config.push('test config', test_config)
- # Write the configuration file for subprocesses and set up the config
- # object to pass that properly on the -C option.
- config_file = os.path.join(cls.var_dir, 'test.cfg')
- with open(config_file, 'w') as fp:
- fp.write(test_config)
- print(file=fp)
- config.filename = config_file
-
- @classmethod
- def tearDown(cls):
- assert cls.var_dir is not None, 'Layer not set up'
- shutil.rmtree(cls.var_dir)
- config.pop('test config')
- cls.var_dir = None
-
- @classmethod
- def testTearDown(cls):
- cls._drop_all_tables()
-
-
def is_testing():
"""Return a 'testing' flag for use with the predictable factories.
--
cgit v1.3.1
From 9cc31df3831298fcc1d698306a74cad6f82a1dc1 Mon Sep 17 00:00:00 2001
From: Abhilash Raj
Date: Fri, 10 Oct 2014 10:29:43 +0530
Subject: * remove migrate command * remove alembic.cfg, move contents to
schema.cfg * fix import errors in src/mailman/model/language.py * add indexes
* change the previously wrong written tablename autoresponserecord * change
alembic_cfg to use schema.cfg instead of alembic.cfg
---
src/mailman/commands/cli_migrate.py | 62 ----------------------
src/mailman/commands/docs/conf.rst | 2 +-
src/mailman/config/alembic.cfg | 21 --------
src/mailman/config/schema.cfg | 4 ++
src/mailman/database/alembic/__init__.py | 2 +-
src/mailman/database/alembic/env.py | 3 ++
.../alembic/versions/429e08420177_initial.py | 29 ----------
.../alembic/versions/51b7f92bd06c_initial.py | 32 +++++++++++
src/mailman/model/address.py | 4 +-
src/mailman/model/autorespond.py | 6 +--
src/mailman/model/language.py | 4 +-
src/mailman/model/mailinglist.py | 12 +++--
src/mailman/model/mime.py | 2 +-
src/mailman/model/pending.py | 2 +-
src/mailman/model/requests.py | 4 +-
src/mailman/model/uid.py | 2 +-
src/mailman/model/user.py | 4 +-
src/mailman/testing/testing.cfg | 6 +--
18 files changed, 66 insertions(+), 135 deletions(-)
delete mode 100644 src/mailman/commands/cli_migrate.py
delete mode 100644 src/mailman/config/alembic.cfg
delete mode 100644 src/mailman/database/alembic/versions/429e08420177_initial.py
create mode 100644 src/mailman/database/alembic/versions/51b7f92bd06c_initial.py
(limited to 'src/mailman/testing')
diff --git a/src/mailman/commands/cli_migrate.py b/src/mailman/commands/cli_migrate.py
deleted file mode 100644
index 82bf4a708..000000000
--- a/src/mailman/commands/cli_migrate.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free
-# Software Foundation, either version 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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 General Public License for
-# more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# GNU Mailman. If not, see .
-
-"""bin/mailman migrate."""
-
-from __future__ import absolute_import, print_function, unicode_literals
-
-__metaclass__ = type
-__all__ = [
- 'Migrate',
- ]
-
-
-from alembic import command
-from zope.interface import implementer
-
-from mailman.config import config
-from mailman.core.i18n import _
-from mailman.database.alembic import alembic_cfg
-from mailman.interfaces.command import ICLISubCommand
-from mailman.utilities.modules import expand_path
-
-
-
-@implementer(ICLISubCommand)
-class Migrate:
- """Migrate the Mailman database to the latest schema."""
-
- name = 'migrate'
-
- def add(self, parser, command_parser):
- """See `ICLISubCommand`."""
- command_parser.add_argument(
- '-a', '--autogenerate',
- action='store_true', help=_("""\
- Autogenerate the migration script using Alembic."""))
- command_parser.add_argument(
- '-q', '--quiet',
- action='store_true', default=False,
- help=('Produce less output.'))
-
- def process(self, args):
- if args.autogenerate:
- command.revision(alembic_cfg, autogenerate=True)
- else:
- command.upgrade(alembic_cfg, 'head')
- if not args.quiet:
- print('Updated the database schema.')
diff --git a/src/mailman/commands/docs/conf.rst b/src/mailman/commands/docs/conf.rst
index b3fef9db7..fb4c3eeed 100644
--- a/src/mailman/commands/docs/conf.rst
+++ b/src/mailman/commands/docs/conf.rst
@@ -22,7 +22,7 @@ To get a list of all key-value pairs of any section, you need to call the
command without any options.
>>> command.process(FakeArgs)
- [logging.dbmigration] path: mailman.log
+ [alembic] script_location: mailman.database:alembic
...
[passwords] password_length: 8
...
diff --git a/src/mailman/config/alembic.cfg b/src/mailman/config/alembic.cfg
deleted file mode 100644
index 78c047379..000000000
--- a/src/mailman/config/alembic.cfg
+++ /dev/null
@@ -1,21 +0,0 @@
-# A generic, single database configuration.
-
-[alembic]
-# path to migration scripts
-script_location = mailman.database:alembic
-
-# template used to generate migration files
-# file_template = %%(rev)s_%%(slug)s
-
-# max length of characters to apply to the
-# "slug" field
-#truncate_slug_length = 40
-
-# set to 'true' to run the environment during
-# the 'revision' command, regardless of autogenerate
-# revision_environment = false
-
-# set to 'true' to allow .pyc and .pyo files without
-# a source .py file to be detected as revisions in the
-# versions/ directory
-# sourceless = false
diff --git a/src/mailman/config/schema.cfg b/src/mailman/config/schema.cfg
index 3316f54e0..3e628fa47 100644
--- a/src/mailman/config/schema.cfg
+++ b/src/mailman/config/schema.cfg
@@ -645,3 +645,7 @@ rewrite_duplicate_headers:
CC X-Original-CC
Content-Transfer-Encoding X-Original-Content-Transfer-Encoding
MIME-Version X-MIME-Version
+
+[alembic]
+# path to migration scripts
+script_location = mailman.database:alembic
diff --git a/src/mailman/database/alembic/__init__.py b/src/mailman/database/alembic/__init__.py
index 73f30832e..a2f7418ba 100644
--- a/src/mailman/database/alembic/__init__.py
+++ b/src/mailman/database/alembic/__init__.py
@@ -29,4 +29,4 @@ from alembic.config import Config
from mailman.utilities.modules import expand_path
-alembic_cfg=Config(expand_path("python:mailman.config.alembic"))
+alembic_cfg=Config(expand_path("python:mailman.config.schema"))
diff --git a/src/mailman/database/alembic/env.py b/src/mailman/database/alembic/env.py
index 56f673803..d1a1c557c 100644
--- a/src/mailman/database/alembic/env.py
+++ b/src/mailman/database/alembic/env.py
@@ -36,6 +36,9 @@ from mailman.database.alembic import alembic_cfg
from mailman.database.model import Model
from mailman.utilities.string import expand
+if not config.initialized:
+ initialize.initialize_1(context.config.config_file_name)
+
def run_migrations_offline():
diff --git a/src/mailman/database/alembic/versions/429e08420177_initial.py b/src/mailman/database/alembic/versions/429e08420177_initial.py
deleted file mode 100644
index e8d612676..000000000
--- a/src/mailman/database/alembic/versions/429e08420177_initial.py
+++ /dev/null
@@ -1,29 +0,0 @@
-"""Initial migration
-
-This empty migration file makes sure there is always an alembic_version in the
-database. As a consequence, if the DB version is reported as None, it means the
-database needs to be created from scratch with SQLAlchemy itself.
-
-It also removes the "version" table left over from Storm (if it exists).
-
-
-Revision ID: 429e08420177
-Revises: None
-Create Date: 2014-10-02 10:18:17.333354
-
-"""
-
-# revision identifiers, used by Alembic.
-revision = '429e08420177'
-down_revision = None
-
-from alembic import op
-import sqlalchemy as sa
-
-
-def upgrade():
- op.drop_table('version')
-
-
-def downgrade():
- pass
diff --git a/src/mailman/database/alembic/versions/51b7f92bd06c_initial.py b/src/mailman/database/alembic/versions/51b7f92bd06c_initial.py
new file mode 100644
index 000000000..c5b3e01c5
--- /dev/null
+++ b/src/mailman/database/alembic/versions/51b7f92bd06c_initial.py
@@ -0,0 +1,32 @@
+"""initial
+
+Revision ID: 51b7f92bd06c
+Revises: None
+Create Date: 2014-10-10 09:53:35.624472
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '51b7f92bd06c'
+down_revision = None
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table('version')
+ op.drop_column('mailinglist', 'acceptable_aliases_id')
+ op.create_index(op.f('ix_user__user_id'), 'user', ['_user_id'], unique=False)
+ op.drop_index('ix_user_user_id', table_name='user')
+ ### end Alembic commands ###
+
+
+def downgrade():
+ ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('version')
+ op.create_index('ix_user_user_id', 'user', ['_user_id'], unique=False)
+ op.drop_index(op.f('ix_user__user_id'), table_name='user')
+ op.add_column('mailinglist', sa.Column('acceptable_aliases_id', sa.INTEGER(), nullable=True))
+ ### end Alembic commands ###
diff --git a/src/mailman/model/address.py b/src/mailman/model/address.py
index 20bd631f5..cc4ab6fd0 100644
--- a/src/mailman/model/address.py
+++ b/src/mailman/model/address.py
@@ -53,9 +53,9 @@ class Address(Model):
_verified_on = Column('verified_on', DateTime)
registered_on = Column(DateTime)
- user_id = Column(Integer, ForeignKey('user.id'))
+ user_id = Column(Integer, ForeignKey('user.id'), index=True)
- preferences_id = Column(Integer, ForeignKey('preferences.id'))
+ preferences_id = Column(Integer, ForeignKey('preferences.id'), index=True)
preferences = relationship(
'Preferences', backref=backref('address', uselist=False))
diff --git a/src/mailman/model/autorespond.py b/src/mailman/model/autorespond.py
index c74434f7b..2293f5dcd 100644
--- a/src/mailman/model/autorespond.py
+++ b/src/mailman/model/autorespond.py
@@ -44,14 +44,14 @@ from mailman.utilities.datetime import today
class AutoResponseRecord(Model):
"""See `IAutoResponseRecord`."""
- __tablename__ = 'autorespondrecord'
+ __tablename__ = 'autoresponserecord'
id = Column(Integer, primary_key=True)
- address_id = Column(Integer, ForeignKey('address.id'))
+ address_id = Column(Integer, ForeignKey('address.id'), index=True)
address = relationship('Address')
- mailing_list_id = Column(Integer, ForeignKey('mailinglist.id'))
+ mailing_list_id = Column(Integer, ForeignKey('mailinglist.id'), index=True)
mailing_list = relationship('MailingList')
response_type = Column(Enum(Response))
diff --git a/src/mailman/model/language.py b/src/mailman/model/language.py
index 15450c936..f4d48fc97 100644
--- a/src/mailman/model/language.py
+++ b/src/mailman/model/language.py
@@ -28,8 +28,8 @@ __all__ = [
from sqlalchemy import Column, Integer, Unicode
from zope.interface import implementer
-from mailman.database import Model
-from mailman.interfaces import ILanguage
+from mailman.database.model import Model
+from mailman.interfaces.languages import ILanguage
diff --git a/src/mailman/model/mailinglist.py b/src/mailman/model/mailinglist.py
index d00cf3d31..fe84ff5b5 100644
--- a/src/mailman/model/mailinglist.py
+++ b/src/mailman/model/mailinglist.py
@@ -504,9 +504,12 @@ class AcceptableAlias(Model):
id = Column(Integer, primary_key=True)
- mailing_list_id = Column(Integer, ForeignKey('mailinglist.id'))
+ mailing_list_id = Column(Integer,
+ ForeignKey('mailinglist.id'),
+ index=True,
+ nullable=False)
mailing_list = relationship('MailingList', backref='acceptable_alias')
- alias = Column(Unicode)
+ alias = Column(Unicode, index=True, nullable=False)
def __init__(self, mailing_list, alias):
self.mailing_list = mailing_list
@@ -558,9 +561,10 @@ class ListArchiver(Model):
id = Column(Integer, primary_key=True)
- mailing_list_id = Column(Integer, ForeignKey('mailinglist.id'))
+ mailing_list_id = Column(Integer, ForeignKey('mailinglist.id'),
+ index=True, nullable=False)
mailing_list = relationship('MailingList')
- name = Column(Unicode)
+ name = Column(Unicode, nullable=False)
_is_enabled = Column(Boolean)
def __init__(self, mailing_list, archiver_name, system_archiver):
diff --git a/src/mailman/model/mime.py b/src/mailman/model/mime.py
index 906af91ea..dc6a54437 100644
--- a/src/mailman/model/mime.py
+++ b/src/mailman/model/mime.py
@@ -43,7 +43,7 @@ class ContentFilter(Model):
id = Column(Integer, primary_key=True)
- mailing_list_id = Column(Integer, ForeignKey('mailinglist.id'))
+ mailing_list_id = Column(Integer, ForeignKey('mailinglist.id'), index=True)
mailing_list = relationship('MailingList')
filter_type = Column(Enum(FilterType))
diff --git a/src/mailman/model/pending.py b/src/mailman/model/pending.py
index 691e94fd9..49b12c16a 100644
--- a/src/mailman/model/pending.py
+++ b/src/mailman/model/pending.py
@@ -56,7 +56,7 @@ class PendedKeyValue(Model):
id = Column(Integer, primary_key=True)
key = Column(Unicode)
value = Column(Unicode)
- pended_id = Column(Integer, ForeignKey('pended.id'))
+ pended_id = Column(Integer, ForeignKey('pended.id'), index=True)
def __init__(self, key, value):
self.key = key
diff --git a/src/mailman/model/requests.py b/src/mailman/model/requests.py
index 7f996dded..6b130196d 100644
--- a/src/mailman/model/requests.py
+++ b/src/mailman/model/requests.py
@@ -149,14 +149,14 @@ class ListRequests:
class _Request(Model):
"""Table for mailing list hold requests."""
- __tablename__ = 'request'
+ __tablename__ = '_request'
id = Column(Integer, primary_key=True)
key = Column(Unicode)
request_type = Column(Enum(RequestType))
data_hash = Column(LargeBinary)
- mailing_list_id = Column(Integer, ForeignKey('mailinglist.id'))
+ mailing_list_id = Column(Integer, ForeignKey('mailinglist.id'), index=True)
mailing_list = relationship('MailingList')
def __init__(self, key, request_type, mailing_list, data_hash):
diff --git a/src/mailman/model/uid.py b/src/mailman/model/uid.py
index 29d8e7021..72ddd7b5a 100644
--- a/src/mailman/model/uid.py
+++ b/src/mailman/model/uid.py
@@ -50,7 +50,7 @@ class UID(Model):
__tablename__ = 'uid'
id = Column(Integer, primary_key=True)
- uid = Column(UUID)
+ uid = Column(UUID, index=True)
@dbconnection
def __init__(self, store, uid):
diff --git a/src/mailman/model/user.py b/src/mailman/model/user.py
index ffd52fdfb..5fe61ddd4 100644
--- a/src/mailman/model/user.py
+++ b/src/mailman/model/user.py
@@ -57,7 +57,7 @@ class User(Model):
id = Column(Integer, primary_key=True)
display_name = Column(Unicode)
_password = Column('password', LargeBinary) # TODO : was RawStr()
- _user_id = Column(UUID)
+ _user_id = Column(UUID, index=True)
_created_on = Column(DateTime)
addresses = relationship(
@@ -74,7 +74,7 @@ class User(Model):
'Address', primaryjoin=(_preferred_address_id==Address.id),
post_update=True)
- preferences_id = Column(Integer, ForeignKey('preferences.id'))
+ preferences_id = Column(Integer, ForeignKey('preferences.id'), index=True)
preferences = relationship(
'Preferences', backref=backref('user', uselist=False))
diff --git a/src/mailman/testing/testing.cfg b/src/mailman/testing/testing.cfg
index eb9de5513..ea2df6988 100644
--- a/src/mailman/testing/testing.cfg
+++ b/src/mailman/testing/testing.cfg
@@ -18,9 +18,9 @@
# A testing configuration.
# For testing against PostgreSQL.
-[database]
-class: mailman.database.postgresql.PostgreSQLDatabase
-url: postgresql://maxking:maxking@localhost/mailman_test
+# [database]
+# class: mailman.database.postgresql.PostgreSQLDatabase
+# url: postgresql://maxking:maxking@localhost/mailman_test
[mailman]
site_owner: noreply@example.com
--
cgit v1.3.1
From 24e0cf6a5971454c21145d7af319fe5e15de7839 Mon Sep 17 00:00:00 2001
From: Barry Warsaw
Date: Sat, 11 Oct 2014 22:43:07 -0400
Subject: Merge Abhilash's latest revisions.
---
MANIFEST.in | 5 +-
src/mailman/commands/cli_migrate.py | 2 -
src/mailman/config/config.py | 4 +-
src/mailman/database/alembic/__init__.py | 8 +--
src/mailman/database/alembic/env.py | 3 +-
.../alembic/versions/429e08420177_initial.py | 43 +++++++++---
src/mailman/database/base.py | 1 -
src/mailman/database/factory.py | 79 +++++++++++-----------
src/mailman/testing/testing.cfg | 2 +-
9 files changed, 81 insertions(+), 66 deletions(-)
(limited to 'src/mailman/testing')
diff --git a/MANIFEST.in b/MANIFEST.in
index c119f141e..75488eb77 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,4 @@
-include *.py *.rc
+include *.py *.rc *.mako
include COPYING
recursive-include .buildout *
recursive-include contrib *
@@ -6,12 +6,9 @@ recursive-include cron *
recursive-include data *
global-include *.txt *.rst *.po *.mo *.cfg *.sql *.zcml *.html
global-exclude *.egg-info
-exclude MANIFEST.in
prune src/attic
prune src/web
prune eggs
prune parts
include MANIFEST.in
include src/mailman/testing/config.pck
-include src/mailman/databases/alembic/scripts.py.mako
-include src/mailman/databases/alembic/versions/*.py
diff --git a/src/mailman/commands/cli_migrate.py b/src/mailman/commands/cli_migrate.py
index 82bf4a708..d51aaa209 100644
--- a/src/mailman/commands/cli_migrate.py
+++ b/src/mailman/commands/cli_migrate.py
@@ -28,11 +28,9 @@ __all__ = [
from alembic import command
from zope.interface import implementer
-from mailman.config import config
from mailman.core.i18n import _
from mailman.database.alembic import alembic_cfg
from mailman.interfaces.command import ICLISubCommand
-from mailman.utilities.modules import expand_path
diff --git a/src/mailman/config/config.py b/src/mailman/config/config.py
index 0ba88e089..d8b844f65 100644
--- a/src/mailman/config/config.py
+++ b/src/mailman/config/config.py
@@ -87,7 +87,7 @@ class Configuration:
self.pipelines = {}
self.commands = {}
self.password_context = None
- self.initialized = False
+ #self.initialized = False
def _clear(self):
"""Clear the cached configuration variables."""
@@ -137,7 +137,7 @@ class Configuration:
# Expand and set up all directories.
self._expand_paths()
self.ensure_directories_exist()
- self.initialized = True
+ #self.initialized = True
notify(ConfigurationUpdatedEvent(self))
def _expand_paths(self):
diff --git a/src/mailman/database/alembic/__init__.py b/src/mailman/database/alembic/__init__.py
index 73f30832e..ffd3af6df 100644
--- a/src/mailman/database/alembic/__init__.py
+++ b/src/mailman/database/alembic/__init__.py
@@ -15,18 +15,18 @@
# You should have received a copy of the GNU General Public License along with
# GNU Mailman. If not, see .
-"Alembic config init."
+"""Alembic configuration initization."""
from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
- 'alembic_cfg'
-]
+ 'alembic_cfg',
+ ]
from alembic.config import Config
from mailman.utilities.modules import expand_path
-alembic_cfg=Config(expand_path("python:mailman.config.alembic"))
+alembic_cfg = Config(expand_path('python:mailman.config.alembic'))
diff --git a/src/mailman/database/alembic/env.py b/src/mailman/database/alembic/env.py
index d1caec58d..1f4722038 100644
--- a/src/mailman/database/alembic/env.py
+++ b/src/mailman/database/alembic/env.py
@@ -31,16 +31,15 @@ from contextlib import closing
from logging.config import fileConfig
from sqlalchemy import create_engine
-from mailman.core import initialize
from mailman.config import config
from mailman.database.alembic import alembic_cfg
from mailman.database.model import Model
-from mailman.utilities.modules import expand_path
from mailman.utilities.string import expand
fileConfig(alembic_cfg.config_file_name)
+
def run_migrations_offline():
"""Run migrations in 'offline' mode.
diff --git a/src/mailman/database/alembic/versions/429e08420177_initial.py b/src/mailman/database/alembic/versions/429e08420177_initial.py
index e8d612676..14f22079b 100644
--- a/src/mailman/database/alembic/versions/429e08420177_initial.py
+++ b/src/mailman/database/alembic/versions/429e08420177_initial.py
@@ -1,24 +1,45 @@
-"""Initial migration
-
-This empty migration file makes sure there is always an alembic_version in the
-database. As a consequence, if the DB version is reported as None, it means the
-database needs to be created from scratch with SQLAlchemy itself.
-
-It also removes the "version" table left over from Storm (if it exists).
-
+# Copyright (C) 2014 by the Free Software Foundation, Inc.
+#
+# This file is part of GNU Mailman.
+#
+# GNU Mailman is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# GNU Mailman 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 General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# GNU Mailman. If not, see .
+
+"""Initial migration.
+
+This empty migration file makes sure there is always an alembic_version
+in the database. As a consequence, if the database version is reported
+as None, it means the database needs to be created from scratch with
+SQLAlchemy itself.
+
+It also removes the `version` table left over from Storm (if it exists).
Revision ID: 429e08420177
Revises: None
Create Date: 2014-10-02 10:18:17.333354
-
"""
-# revision identifiers, used by Alembic.
+from __future__ import absolute_import, print_function, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ ]
+
+# Revision identifiers, used by Alembic.
revision = '429e08420177'
down_revision = None
from alembic import op
-import sqlalchemy as sa
def upgrade():
diff --git a/src/mailman/database/base.py b/src/mailman/database/base.py
index dcaedade0..2f69ed6ad 100644
--- a/src/mailman/database/base.py
+++ b/src/mailman/database/base.py
@@ -99,7 +99,6 @@ class SABaseDatabase:
# alembic_version table.
command.stamp(alembic_cfg, 'head')
-
def initialize(self, debug=None):
"""See `IDatabase`."""
# Calculate the engine url.
diff --git a/src/mailman/database/factory.py b/src/mailman/database/factory.py
index 469ed5d18..9cbe1088f 100644
--- a/src/mailman/database/factory.py
+++ b/src/mailman/database/factory.py
@@ -38,10 +38,14 @@ from zope.interface import implementer
from zope.interface.verify import verifyObject
from mailman.config import config
-from mailman.database.model import Model
from mailman.database.alembic import alembic_cfg
-from mailman.interfaces.database import IDatabase, IDatabaseFactory
-from mailman.utilities.modules import call_name, expand_path
+from mailman.database.model import Model
+from mailman.interfaces.database import (
+ DatabaseError, IDatabase, IDatabaseFactory)
+from mailman.utilities.modules import call_name
+
+
+LAST_STORM_SCHEMA_VERSION = '20130406000000'
@@ -57,58 +61,55 @@ class DatabaseFactory:
database = call_name(database_class)
verifyObject(IDatabase, database)
database.initialize()
- schema_mgr = SchemaManager(database)
- schema_mgr.setup_db()
+ SchemaManager(database).setup_database()
database.commit()
return database
class SchemaManager:
-
- LAST_STORM_SCHEMA_VERSION = '20130406000000'
+ "Manage schema migrations."""
def __init__(self, database):
- self.database = database
- self.alembic_cfg = alembic_cfg
- self.script = ScriptDirectory.from_config(self.alembic_cfg)
-
- def get_storm_schema_version(self):
- md = MetaData()
- md.reflect(bind=self.database.engine)
- if "version" not in md.tables:
+ self._database = database
+ self._script = ScriptDirectory.from_config(alembic_cfg)
+
+ def _get_storm_schema_version(self):
+ metadata = MetaData()
+ metadata.reflect(bind=self._database.engine)
+ if 'version' not in metadata.tables:
+ # There are no Storm artifacts left.
return None
- Version = md.tables["version"]
- last_version = self.database.store.query(Version.c.version).filter(
- Version.c.component == "schema"
- ).order_by(Version.c.version.desc()).first()
+ Version = metadata.tables['version']
+ last_version = self._database.store.query(Version.c.version).filter(
+ Version.c.component == 'schema'
+ ).order_by(Version.c.version.desc()).first()
return last_version
- def setup_db(self):
- context = MigrationContext.configure(self.database.store.connection())
+ def setup_database(self):
+ context = MigrationContext.configure(self._database.store.connection())
current_rev = context.get_current_revision()
- head_rev = self.script.get_current_head()
+ head_rev = self._script.get_current_head()
if current_rev == head_rev:
- return head_rev # already at the latest revision, nothing to do
- if current_rev == None:
- # no alembic information
- storm_version = self.get_storm_schema_version()
+ # We're already at the latest revision so there's nothing to do.
+ return head_rev
+ if current_rev is None:
+ # No Alembic information is available.
+ storm_version = self._get_storm_schema_version()
if storm_version is None:
- # initial DB creation
- Model.metadata.create_all(self.database.engine)
- command.stamp(self.alembic_cfg, "head")
+ # Initial database creation.
+ Model.metadata.create_all(self._database.engine)
+ command.stamp(alembic_cfg, 'head')
else:
- # DB from a previous version managed by Storm
- if storm_version.version < self.LAST_STORM_SCHEMA_VERSION:
- raise RuntimeError(
- "Upgrading while skipping beta version is "
- "unsupported, please install the previous "
- "Mailman beta release")
- # Run migrations to remove the Storm-specific table and
- # upgrade to SQLAlchemy & Alembic
- command.upgrade(self.alembic_cfg, "head")
+ # The database was previously managed by Storm.
+ if storm_version.version < LAST_STORM_SCHEMA_VERSION:
+ raise DatabaseError(
+ 'Upgrades skipping beta versions is not supported.')
+ # Run migrations to remove the Storm-specific table and upgrade
+ # to SQLAlchemy and Alembic.
+ command.upgrade(alembic_cfg, 'head')
elif current_rev != head_rev:
- command.upgrade(self.alembic_cfg, "head")
+ command.upgrade(alembic_cfg, 'head')
return head_rev
diff --git a/src/mailman/testing/testing.cfg b/src/mailman/testing/testing.cfg
index 39d1d922a..eba10dd3b 100644
--- a/src/mailman/testing/testing.cfg
+++ b/src/mailman/testing/testing.cfg
@@ -20,7 +20,7 @@
# For testing against PostgreSQL.
# [database]
# class: mailman.database.postgresql.PostgreSQLDatabase
-# url: postgres://maxking:maxking@localhost/mailman_test
+# url: postgres://$USER:$USER@localhost/mailman_test
[mailman]
site_owner: noreply@example.com
--
cgit v1.3.1
From d20af910b287f8a7f13b51ee07c89e79af35f8a4 Mon Sep 17 00:00:00 2001
From: Barry Warsaw
Date: Mon, 13 Oct 2014 17:13:38 -0400
Subject: Remove some unnecessary code.
---
src/mailman/testing/layers.py | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
(limited to 'src/mailman/testing')
diff --git a/src/mailman/testing/layers.py b/src/mailman/testing/layers.py
index 38aa3907f..4a9841fc2 100644
--- a/src/mailman/testing/layers.py
+++ b/src/mailman/testing/layers.py
@@ -48,7 +48,6 @@ import tempfile
from lazr.config import as_boolean
from pkg_resources import resource_string
from textwrap import dedent
-from zope import event
from zope.component import getUtility
from mailman.config import config
@@ -99,9 +98,7 @@ class ConfigLayer(MockAndMonkeyLayer):
# Set up the basic configuration stuff. Turn off path creation until
# we've pushed the testing config.
config.create_paths = False
- if not event.subscribers:
- # Only if not yet initialized by another layer.
- initialize.initialize_1(INHIBIT_CONFIG_FILE)
+ initialize.initialize_1(INHIBIT_CONFIG_FILE)
assert cls.var_dir is None, 'Layer already set up'
# Calculate a temporary VAR_DIR directory so that run-time artifacts
# of the tests won't tread on the installation's data. This also
--
cgit v1.3.1
From 4e19b227ab4c29e59801b38f12f80e1948817053 Mon Sep 17 00:00:00 2001
From: Barry Warsaw
Date: Thu, 30 Oct 2014 23:12:00 -0400
Subject: Merge abompard's fixes to the Postgres test suite.
---
src/mailman/app/docs/moderator.rst | 77 +++++++++++++++-------------------
src/mailman/commands/docs/withlist.rst | 4 +-
src/mailman/database/base.py | 6 +++
src/mailman/interfaces/domain.py | 2 +
src/mailman/interfaces/listmanager.py | 6 ++-
src/mailman/model/docs/mailinglist.rst | 15 ++++---
src/mailman/model/domain.py | 4 +-
src/mailman/model/listmanager.py | 3 +-
src/mailman/rest/docs/moderation.rst | 32 +++++++-------
src/mailman/testing/layers.py | 7 ++--
src/mailman/testing/testing.cfg | 6 +--
11 files changed, 83 insertions(+), 79 deletions(-)
(limited to 'src/mailman/testing')
diff --git a/src/mailman/app/docs/moderator.rst b/src/mailman/app/docs/moderator.rst
index ee9df8eb5..490c9630a 100644
--- a/src/mailman/app/docs/moderator.rst
+++ b/src/mailman/app/docs/moderator.rst
@@ -211,9 +211,9 @@ moderators.
...
... Here's something important about our mailing list.
... """)
- >>> hold_message(mlist, msg, {}, 'Needs approval')
- 2
- >>> handle_message(mlist, 2, Action.discard, forward=['zack@example.com'])
+ >>> req_id = hold_message(mlist, msg, {}, 'Needs approval')
+ >>> handle_message(mlist, req_id, Action.discard,
+ ... forward=['zack@example.com'])
The forwarded message is in the virgin queue, destined for the moderator.
::
@@ -243,10 +243,9 @@ choosing and their preferred language.
>>> from mailman.app.moderator import hold_subscription
>>> from mailman.interfaces.member import DeliveryMode
- >>> hold_subscription(mlist,
- ... 'fred@example.org', 'Fred Person',
+ >>> req_id = hold_subscription(
+ ... mlist, 'fred@example.org', 'Fred Person',
... '{NONE}abcxyz', DeliveryMode.regular, 'en')
- 2
Disposing of membership change requests
@@ -257,26 +256,27 @@ dispositions for this membership change request. The most trivial is to
simply defer a decision for now.
>>> from mailman.app.moderator import handle_subscription
- >>> handle_subscription(mlist, 2, Action.defer)
- >>> requests.get_request(2) is not None
+ >>> handle_subscription(mlist, req_id, Action.defer)
+ >>> requests.get_request(req_id) is not None
True
The held subscription can also be discarded.
- >>> handle_subscription(mlist, 2, Action.discard)
- >>> print(requests.get_request(2))
+ >>> handle_subscription(mlist, req_id, Action.discard)
+ >>> print(requests.get_request(req_id))
None
Gwen tries to subscribe to the mailing list, but...
- >>> hold_subscription(mlist,
- ... 'gwen@example.org', 'Gwen Person',
+ >>> req_id = hold_subscription(
+ ... mlist, 'gwen@example.org', 'Gwen Person',
... '{NONE}zyxcba', DeliveryMode.regular, 'en')
- 2
+
...her request is rejected...
- >>> handle_subscription(mlist, 2, Action.reject, 'This is a closed list')
+ >>> handle_subscription(
+ ... mlist, req_id, Action.reject, 'This is a closed list')
>>> messages = get_queue_messages('virgin')
>>> len(messages)
1
@@ -304,14 +304,13 @@ The subscription can also be accepted. This subscribes the address to the
mailing list.
>>> mlist.send_welcome_message = False
- >>> hold_subscription(mlist,
- ... 'herb@example.org', 'Herb Person',
+ >>> req_id = hold_subscription(
+ ... mlist, 'herb@example.org', 'Herb Person',
... 'abcxyz', DeliveryMode.regular, 'en')
- 2
The moderators accept the subscription request.
- >>> handle_subscription(mlist, 2, Action.accept)
+ >>> handle_subscription(mlist, req_id, Action.accept)
And now Herb is a member of the mailing list.
@@ -328,29 +327,27 @@ the unsubscribing address is required.
Herb now wants to leave the mailing list, but his request must be approved.
>>> from mailman.app.moderator import hold_unsubscription
- >>> hold_unsubscription(mlist, 'herb@example.org')
- 2
+ >>> req_id = hold_unsubscription(mlist, 'herb@example.org')
As with subscription requests, the unsubscription request can be deferred.
>>> from mailman.app.moderator import handle_unsubscription
- >>> handle_unsubscription(mlist, 2, Action.defer)
+ >>> handle_unsubscription(mlist, req_id, Action.defer)
>>> print(mlist.members.get_member('herb@example.org').address)
Herb Person
The held unsubscription can also be discarded, and the member will remain
subscribed.
- >>> handle_unsubscription(mlist, 2, Action.discard)
+ >>> handle_unsubscription(mlist, req_id, Action.discard)
>>> print(mlist.members.get_member('herb@example.org').address)
Herb Person
The request can be rejected, in which case a message is sent to the member,
and the person remains a member of the mailing list.
- >>> hold_unsubscription(mlist, 'herb@example.org')
- 2
- >>> handle_unsubscription(mlist, 2, Action.reject, 'No can do')
+ >>> req_id = hold_unsubscription(mlist, 'herb@example.org')
+ >>> handle_unsubscription(mlist, req_id, Action.reject, 'No can do')
>>> print(mlist.members.get_member('herb@example.org').address)
Herb Person
@@ -381,10 +378,9 @@ Herb gets a rejection notice.
The unsubscription request can also be accepted. This removes the member from
the mailing list.
- >>> hold_unsubscription(mlist, 'herb@example.org')
- 2
+ >>> req_id = hold_unsubscription(mlist, 'herb@example.org')
>>> mlist.send_goodbye_message = False
- >>> handle_unsubscription(mlist, 2, Action.accept)
+ >>> handle_unsubscription(mlist, req_id, Action.accept)
>>> print(mlist.members.get_member('herb@example.org'))
None
@@ -403,9 +399,8 @@ list is configured to send them.
Iris tries to subscribe to the mailing list.
- >>> hold_subscription(mlist, 'iris@example.org', 'Iris Person',
+ >>> req_id = hold_subscription(mlist, 'iris@example.org', 'Iris Person',
... 'password', DeliveryMode.regular, 'en')
- 2
There's now a message in the virgin queue, destined for the list owner.
@@ -429,8 +424,7 @@ There's now a message in the virgin queue, destined for the list owner.
Similarly, the administrator gets notifications on unsubscription requests.
Jeff is a member of the mailing list, and chooses to unsubscribe.
- >>> hold_unsubscription(mlist, 'jeff@example.org')
- 3
+ >>> unsub_req_id = hold_unsubscription(mlist, 'jeff@example.org')
>>> messages = get_queue_messages('virgin')
>>> len(messages)
1
@@ -457,7 +451,7 @@ receive a membership change notice.
>>> mlist.admin_notify_mchanges = True
>>> mlist.admin_immed_notify = False
- >>> handle_subscription(mlist, 2, Action.accept)
+ >>> handle_subscription(mlist, req_id, Action.accept)
>>> messages = get_queue_messages('virgin')
>>> len(messages)
1
@@ -474,9 +468,8 @@ receive a membership change notice.
Similarly when an unsubscription request is accepted, the administrators can
get a notification.
- >>> hold_unsubscription(mlist, 'iris@example.org')
- 4
- >>> handle_unsubscription(mlist, 4, Action.accept)
+ >>> req_id = hold_unsubscription(mlist, 'iris@example.org')
+ >>> handle_unsubscription(mlist, req_id, Action.accept)
>>> messages = get_queue_messages('virgin')
>>> len(messages)
1
@@ -498,10 +491,9 @@ can get a welcome message.
>>> mlist.admin_notify_mchanges = False
>>> mlist.send_welcome_message = True
- >>> hold_subscription(mlist, 'kate@example.org', 'Kate Person',
- ... 'password', DeliveryMode.regular, 'en')
- 4
- >>> handle_subscription(mlist, 4, Action.accept)
+ >>> req_id = hold_subscription(mlist, 'kate@example.org', 'Kate Person',
+ ... 'password', DeliveryMode.regular, 'en')
+ >>> handle_subscription(mlist, req_id, Action.accept)
>>> messages = get_queue_messages('virgin')
>>> len(messages)
1
@@ -523,9 +515,8 @@ Similarly, when the member's unsubscription request is approved, she'll get a
goodbye message.
>>> mlist.send_goodbye_message = True
- >>> hold_unsubscription(mlist, 'kate@example.org')
- 4
- >>> handle_unsubscription(mlist, 4, Action.accept)
+ >>> req_id = hold_unsubscription(mlist, 'kate@example.org')
+ >>> handle_unsubscription(mlist, req_id, Action.accept)
>>> messages = get_queue_messages('virgin')
>>> len(messages)
1
diff --git a/src/mailman/commands/docs/withlist.rst b/src/mailman/commands/docs/withlist.rst
index 827d246cd..e915eb04c 100644
--- a/src/mailman/commands/docs/withlist.rst
+++ b/src/mailman/commands/docs/withlist.rst
@@ -90,13 +90,13 @@ must start with a caret.
>>> args.listname = '^.*example.com'
>>> command.process(args)
The list's display name is Aardvark
- The list's display name is Badger
The list's display name is Badboys
+ The list's display name is Badger
>>> args.listname = '^bad.*'
>>> command.process(args)
- The list's display name is Badger
The list's display name is Badboys
+ The list's display name is Badger
>>> args.listname = '^foo'
>>> command.process(args)
diff --git a/src/mailman/database/base.py b/src/mailman/database/base.py
index e360dcedf..beb9c260d 100644
--- a/src/mailman/database/base.py
+++ b/src/mailman/database/base.py
@@ -114,3 +114,9 @@ class SABaseDatabase:
session = sessionmaker(bind=self.engine)
self.store = session()
self.store.commit()
+
+ # XXX BAW Why doesn't model.py _reset() do this?
+ def destroy(self):
+ """Drop all database tables"""
+ from mailman.database.model import Model
+ Model.metadata.drop_all(self.engine)
diff --git a/src/mailman/interfaces/domain.py b/src/mailman/interfaces/domain.py
index e8610fd76..e7cb0d901 100644
--- a/src/mailman/interfaces/domain.py
+++ b/src/mailman/interfaces/domain.py
@@ -166,6 +166,8 @@ class IDomainManager(Interface):
def __iter__():
"""An iterator over all the domains.
+ Domains are returned sorted by `mail_host`.
+
:return: iterator over `IDomain`.
"""
diff --git a/src/mailman/interfaces/listmanager.py b/src/mailman/interfaces/listmanager.py
index 22d7b3418..0c641fb91 100644
--- a/src/mailman/interfaces/listmanager.py
+++ b/src/mailman/interfaces/listmanager.py
@@ -130,8 +130,10 @@ class IListManager(Interface):
"""
mailing_lists = Attribute(
- """An iterator over all the mailing list objects managed by this list
- manager.""")
+ """An iterator over all the mailing list objects.
+
+ The mailing lists are returned in order sorted by `list_id`.
+ """)
def __iter__():
"""An iterator over all the mailing lists.
diff --git a/src/mailman/model/docs/mailinglist.rst b/src/mailman/model/docs/mailinglist.rst
index 53ba99575..3d01710c5 100644
--- a/src/mailman/model/docs/mailinglist.rst
+++ b/src/mailman/model/docs/mailinglist.rst
@@ -50,7 +50,10 @@ receive a copy of any message sent to the mailing list.
Both addresses appear on the roster of members.
- >>> for member in mlist.members.members:
+ >>> from operator import attrgetter
+ >>> sort_key = attrgetter('address.email')
+
+ >>> for member in sorted(mlist.members.members, key=sort_key):
... print(member)
@@ -72,7 +75,7 @@ A Person is now both a member and an owner of the mailing list. C Person is
an owner and a moderator.
::
- >>> for member in mlist.owners.members:
+ >>> for member in sorted(mlist.owners.members, key=sort_key):
... print(member)
@@ -87,13 +90,13 @@ All rosters can also be accessed indirectly.
::
>>> roster = mlist.get_roster(MemberRole.member)
- >>> for member in roster.members:
+ >>> for member in sorted(roster.members, key=sort_key):
... print(member)
>>> roster = mlist.get_roster(MemberRole.owner)
- >>> for member in roster.members:
+ >>> for member in sorted(roster.members, key=sort_key):
... print(member)
@@ -122,7 +125,7 @@ just by changing their preferred address.
>>> mlist.subscribe(user)
on aardvark@example.com
as MemberRole.member>
- >>> for member in mlist.members.members:
+ >>> for member in sorted(mlist.members.members, key=sort_key):
... print(member)
@@ -133,7 +136,7 @@ just by changing their preferred address.
>>> new_address.verified_on = now()
>>> user.preferred_address = new_address
- >>> for member in mlist.members.members:
+ >>> for member in sorted(mlist.members.members, key=sort_key):
... print(member)
diff --git a/src/mailman/model/domain.py b/src/mailman/model/domain.py
index 083e1cf51..ef8b1f761 100644
--- a/src/mailman/model/domain.py
+++ b/src/mailman/model/domain.py
@@ -48,7 +48,7 @@ class Domain(Model):
id = Column(Integer, primary_key=True)
- mail_host = Column(Unicode)
+ mail_host = Column(Unicode) # TODO: add index?
base_url = Column(Unicode)
description = Column(Unicode)
contact_address = Column(Unicode)
@@ -170,7 +170,7 @@ class DomainManager:
@dbconnection
def __iter__(self, store):
"""See `IDomainManager`."""
- for domain in store.query(Domain).all():
+ for domain in store.query(Domain).order_by(Domain.mail_host).all():
yield domain
@dbconnection
diff --git a/src/mailman/model/listmanager.py b/src/mailman/model/listmanager.py
index 43a2b8f2a..261490a92 100644
--- a/src/mailman/model/listmanager.py
+++ b/src/mailman/model/listmanager.py
@@ -86,7 +86,8 @@ class ListManager:
@dbconnection
def mailing_lists(self, store):
"""See `IListManager`."""
- for mlist in store.query(MailingList).all():
+ for mlist in store.query(MailingList).order_by(
+ MailingList._list_id).all():
yield mlist
@dbconnection
diff --git a/src/mailman/rest/docs/moderation.rst b/src/mailman/rest/docs/moderation.rst
index 44182eb23..6e2dbb43c 100644
--- a/src/mailman/rest/docs/moderation.rst
+++ b/src/mailman/rest/docs/moderation.rst
@@ -226,10 +226,9 @@ moderator approval.
>>> from mailman.app.moderator import hold_subscription
>>> from mailman.interfaces.member import DeliveryMode
- >>> hold_subscription(
+ >>> sub_req_id = hold_subscription(
... ant, 'anne@example.com', 'Anne Person',
... 'password', DeliveryMode.regular, 'en')
- 1
>>> transaction.commit()
The subscription request is available from the mailing list.
@@ -242,7 +241,7 @@ The subscription request is available from the mailing list.
http_etag: "..."
language: en
password: password
- request_id: 1
+ request_id: ...
type: subscription
when: 2005-08-01T07:49:23
http_etag: "..."
@@ -259,8 +258,7 @@ Bart tries to leave a mailing list, but he may not be allowed to.
>>> from mailman.app.moderator import hold_unsubscription
>>> bart = add_member(ant, 'bart@example.com', 'Bart Person',
... 'password', DeliveryMode.regular, 'en')
- >>> hold_unsubscription(ant, 'bart@example.com')
- 2
+ >>> unsub_req_id = hold_unsubscription(ant, 'bart@example.com')
>>> transaction.commit()
The unsubscription request is also available from the mailing list.
@@ -273,13 +271,13 @@ The unsubscription request is also available from the mailing list.
http_etag: "..."
language: en
password: password
- request_id: 1
+ request_id: ...
type: subscription
when: 2005-08-01T07:49:23
entry 1:
address: bart@example.com
http_etag: "..."
- request_id: 2
+ request_id: ...
type: unsubscription
http_etag: "..."
start: 0
@@ -292,23 +290,25 @@ Viewing individual requests
You can view an individual membership change request by providing the
request id. Anne's subscription request looks like this.
- >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/requests/1')
+ >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/'
+ ... 'requests/{}'.format(sub_req_id))
address: anne@example.com
delivery_mode: regular
display_name: Anne Person
http_etag: "..."
language: en
password: password
- request_id: 1
+ request_id: ...
type: subscription
when: 2005-08-01T07:49:23
Bart's unsubscription request looks like this.
- >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/requests/2')
+ >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/'
+ ... 'requests/{}'.format(unsub_req_id))
address: bart@example.com
http_etag: "..."
- request_id: 2
+ request_id: ...
type: unsubscription
@@ -328,9 +328,8 @@ data requires an action of one of the following:
Anne's subscription request is accepted.
>>> dump_json('http://localhost:9001/3.0/lists/'
- ... 'ant@example.com/requests/1', {
- ... 'action': 'accept',
- ... })
+ ... 'ant@example.com/requests/{}'.format(sub_req_id),
+ ... {'action': 'accept'})
content-length: 0
date: ...
server: ...
@@ -347,9 +346,8 @@ Anne is now a member of the mailing list.
Bart's unsubscription request is discarded.
>>> dump_json('http://localhost:9001/3.0/lists/'
- ... 'ant@example.com/requests/2', {
- ... 'action': 'discard',
- ... })
+ ... 'ant@example.com/requests/{}'.format(unsub_req_id),
+ ... {'action': 'discard'})
content-length: 0
date: ...
server: ...
diff --git a/src/mailman/testing/layers.py b/src/mailman/testing/layers.py
index 4a9841fc2..6104e64f7 100644
--- a/src/mailman/testing/layers.py
+++ b/src/mailman/testing/layers.py
@@ -190,10 +190,11 @@ class ConfigLayer(MockAndMonkeyLayer):
@classmethod
def tearDown(cls):
assert cls.var_dir is not None, 'Layer not set up'
- # Reset the test database after the tests are done so that there is no
- # data in case the tests are rerun with a database layer like mysql or
- # postgresql which are not deleted in teardown.
reset_the_world()
+ # Destroy the test database after the tests are done so that there is
+ # no data in case the tests are rerun with a database layer like mysql
+ # or postgresql which are not deleted in teardown.
+ config.db.destroy()
config.pop('test config')
shutil.rmtree(cls.var_dir)
cls.var_dir = None
diff --git a/src/mailman/testing/testing.cfg b/src/mailman/testing/testing.cfg
index 43ae6e67e..ff2034069 100644
--- a/src/mailman/testing/testing.cfg
+++ b/src/mailman/testing/testing.cfg
@@ -18,9 +18,9 @@
# A testing configuration.
# For testing against PostgreSQL.
-# [database]
-# class: mailman.database.postgresql.PostgreSQLDatabase
-# url: postgresql://$USER:$USER@localhost/mailman_test
+[database]
+class: mailman.database.postgresql.PostgreSQLDatabase
+url: postgresql://barry:barry@localhost:5433/mailman
[mailman]
site_owner: noreply@example.com
--
cgit v1.3.1
From 98ddb4d8ec662bcff039d60b4b5afd4279231b97 Mon Sep 17 00:00:00 2001
From: Barry Warsaw
Date: Fri, 31 Oct 2014 23:35:02 -0400
Subject: Merge in the last of Aurelien's changes, and make the test suite pass
with PostgreSQL.
---
src/mailman/config/config.py | 2 +-
src/mailman/core/docs/runner.rst | 4 ++++
src/mailman/interfaces/domain.py | 5 ++++-
src/mailman/model/domain.py | 3 ++-
src/mailman/testing/testing.cfg | 2 +-
5 files changed, 12 insertions(+), 4 deletions(-)
(limited to 'src/mailman/testing')
diff --git a/src/mailman/config/config.py b/src/mailman/config/config.py
index 52cac414f..649d6c5e1 100644
--- a/src/mailman/config/config.py
+++ b/src/mailman/config/config.py
@@ -141,7 +141,7 @@ class Configuration:
def _expand_paths(self):
"""Expand all configuration paths."""
# Set up directories.
- bin_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
+ bin_dir = os.path.abspath(os.path.dirname(sys.executable))
# Now that we've loaded all the configuration files we're going to
# load, set up some useful directories based on the settings in the
# configuration file.
diff --git a/src/mailman/core/docs/runner.rst b/src/mailman/core/docs/runner.rst
index 28eab9203..e9fd21c57 100644
--- a/src/mailman/core/docs/runner.rst
+++ b/src/mailman/core/docs/runner.rst
@@ -73,3 +73,7 @@ on instance variables.
version : 3
XXX More of the Runner API should be tested.
+
+..
+ Clean up.
+ >>> config.pop('test-runner')
diff --git a/src/mailman/interfaces/domain.py b/src/mailman/interfaces/domain.py
index e7cb0d901..d51b1a702 100644
--- a/src/mailman/interfaces/domain.py
+++ b/src/mailman/interfaces/domain.py
@@ -97,7 +97,10 @@ class IDomain(Interface):
E.g. postmaster@example.com""")
mailing_lists = Attribute(
- 'All mailing lists for this domain.')
+ """All mailing lists for this domain.
+
+ The mailing lists are returned in order sorted by list-id.
+ """)
def confirm_url(token=''):
"""The url used for various forms of confirmation.
diff --git a/src/mailman/model/domain.py b/src/mailman/model/domain.py
index ef8b1f761..8290cb755 100644
--- a/src/mailman/model/domain.py
+++ b/src/mailman/model/domain.py
@@ -95,7 +95,8 @@ class Domain(Model):
def mailing_lists(self, store):
"""See `IDomain`."""
mailing_lists = store.query(MailingList).filter(
- MailingList.mail_host == self.mail_host)
+ MailingList.mail_host == self.mail_host
+ ).order_by(MailingList._list_id)
for mlist in mailing_lists:
yield mlist
diff --git a/src/mailman/testing/testing.cfg b/src/mailman/testing/testing.cfg
index ff2034069..12646c680 100644
--- a/src/mailman/testing/testing.cfg
+++ b/src/mailman/testing/testing.cfg
@@ -20,7 +20,7 @@
# For testing against PostgreSQL.
[database]
class: mailman.database.postgresql.PostgreSQLDatabase
-url: postgresql://barry:barry@localhost:5433/mailman
+url: postgresql://barry:barry@localhost:5432/mailman
[mailman]
site_owner: noreply@example.com
--
cgit v1.3.1
From fb38e482aa42edd4032a23e7c1f727066991fa62 Mon Sep 17 00:00:00 2001
From: Barry Warsaw
Date: Fri, 31 Oct 2014 23:48:13 -0400
Subject: SQLite by default
---
src/mailman/testing/testing.cfg | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
(limited to 'src/mailman/testing')
diff --git a/src/mailman/testing/testing.cfg b/src/mailman/testing/testing.cfg
index 12646c680..b61b36604 100644
--- a/src/mailman/testing/testing.cfg
+++ b/src/mailman/testing/testing.cfg
@@ -18,9 +18,9 @@
# A testing configuration.
# For testing against PostgreSQL.
-[database]
-class: mailman.database.postgresql.PostgreSQLDatabase
-url: postgresql://barry:barry@localhost:5432/mailman
+#[database]
+#class: mailman.database.postgresql.PostgreSQLDatabase
+#url: postgresql://barry:barry@localhost:5432/mailman
[mailman]
site_owner: noreply@example.com
--
cgit v1.3.1
From 1d9f6970b9a26ee576838b53f485b96365e3a6c2 Mon Sep 17 00:00:00 2001
From: Barry Warsaw
Date: Sat, 1 Nov 2014 13:46:36 -0400
Subject: Remove some unnecessary code, and revert back to SQLite by default
for the test suite.
---
src/mailman/database/base.py | 6 ------
src/mailman/testing/layers.py | 3 ---
src/mailman/testing/testing.cfg | 6 +++---
3 files changed, 3 insertions(+), 12 deletions(-)
(limited to 'src/mailman/testing')
diff --git a/src/mailman/database/base.py b/src/mailman/database/base.py
index 2b86899bc..55edf6005 100644
--- a/src/mailman/database/base.py
+++ b/src/mailman/database/base.py
@@ -114,9 +114,3 @@ class SABaseDatabase:
session = sessionmaker(bind=self.engine)
self.store = session()
self.store.commit()
-
- # XXX BAW Why doesn't model.py _reset() do this?
- def destroy(self):
- """Drop all database tables"""
- from mailman.database.model import Model
- Model.metadata.drop_all(self.engine)
diff --git a/src/mailman/testing/layers.py b/src/mailman/testing/layers.py
index 006feef9c..74ad99dc8 100644
--- a/src/mailman/testing/layers.py
+++ b/src/mailman/testing/layers.py
@@ -194,9 +194,6 @@ class ConfigLayer(MockAndMonkeyLayer):
# Destroy the test database after the tests are done so that there is
# no data in case the tests are rerun with a database layer like mysql
# or postgresql which are not deleted in teardown.
- #
- # XXX 2014-11-01 BAW: Shouldn't reset_the_world() take care of this?
- config.db.destroy()
config.pop('test config')
shutil.rmtree(cls.var_dir)
cls.var_dir = None
diff --git a/src/mailman/testing/testing.cfg b/src/mailman/testing/testing.cfg
index 12646c680..b61b36604 100644
--- a/src/mailman/testing/testing.cfg
+++ b/src/mailman/testing/testing.cfg
@@ -18,9 +18,9 @@
# A testing configuration.
# For testing against PostgreSQL.
-[database]
-class: mailman.database.postgresql.PostgreSQLDatabase
-url: postgresql://barry:barry@localhost:5432/mailman
+#[database]
+#class: mailman.database.postgresql.PostgreSQLDatabase
+#url: postgresql://barry:barry@localhost:5432/mailman
[mailman]
site_owner: noreply@example.com
--
cgit v1.3.1