summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBarry Warsaw2016-03-25 12:38:59 -0400
committerBarry Warsaw2016-03-25 12:38:59 -0400
commit1f039b2df371a569115b4ddc7bbc3786ef7d135d (patch)
tree5f727128a9f73d75b510fd62cbeca00138ecae58 /src
parentca4259f4abd2802f87b86907c281a8b4cdb8150b (diff)
downloadmailman-1f039b2df371a569115b4ddc7bbc3786ef7d135d.tar.gz
mailman-1f039b2df371a569115b4ddc7bbc3786ef7d135d.tar.zst
mailman-1f039b2df371a569115b4ddc7bbc3786ef7d135d.zip
Diffstat (limited to 'src')
-rw-r--r--src/mailman/rest/addresses.py17
-rw-r--r--src/mailman/rest/bans.py9
-rw-r--r--src/mailman/rest/docs/__init__.py8
-rw-r--r--src/mailman/rest/domains.py10
-rw-r--r--src/mailman/rest/header_matches.py9
-rw-r--r--src/mailman/rest/helpers.py42
-rw-r--r--src/mailman/rest/listconf.py11
-rw-r--r--src/mailman/rest/lists.py19
-rw-r--r--src/mailman/rest/members.py18
-rw-r--r--src/mailman/rest/post_moderation.py12
-rw-r--r--src/mailman/rest/preferences.py13
-rw-r--r--src/mailman/rest/queues.py15
-rw-r--r--src/mailman/rest/root.py14
-rw-r--r--src/mailman/rest/sub_moderation.py11
-rw-r--r--src/mailman/rest/templates.py8
-rw-r--r--src/mailman/rest/tests/test_addresses.py8
-rw-r--r--src/mailman/rest/tests/test_api.py6
-rw-r--r--src/mailman/rest/tests/test_bans.py5
-rw-r--r--src/mailman/rest/tests/test_basic.py6
-rw-r--r--src/mailman/rest/tests/test_domains.py8
-rw-r--r--src/mailman/rest/tests/test_header_matches.py5
-rw-r--r--src/mailman/rest/tests/test_helpers.py6
-rw-r--r--src/mailman/rest/tests/test_listconf.py14
-rw-r--r--src/mailman/rest/tests/test_lists.py9
-rw-r--r--src/mailman/rest/tests/test_membership.py14
-rw-r--r--src/mailman/rest/tests/test_moderation.py8
-rw-r--r--src/mailman/rest/tests/test_owners.py6
-rw-r--r--src/mailman/rest/tests/test_paginate.py9
-rw-r--r--src/mailman/rest/tests/test_preferences.py6
-rw-r--r--src/mailman/rest/tests/test_queues.py6
-rw-r--r--src/mailman/rest/tests/test_root.py6
-rw-r--r--src/mailman/rest/tests/test_systemconf.py7
-rw-r--r--src/mailman/rest/tests/test_uids.py6
-rw-r--r--src/mailman/rest/tests/test_users.py14
-rw-r--r--src/mailman/rest/tests/test_validator.py6
-rw-r--r--src/mailman/rest/users.py31
-rw-r--r--src/mailman/rest/validator.py29
-rw-r--r--src/mailman/rest/wsgiapp.py33
38 files changed, 121 insertions, 343 deletions
diff --git a/src/mailman/rest/addresses.py b/src/mailman/rest/addresses.py
index 421e17bf1..1cf2d2f41 100644
--- a/src/mailman/rest/addresses.py
+++ b/src/mailman/rest/addresses.py
@@ -17,13 +17,7 @@
"""REST for addresses."""
-__all__ = [
- 'AllAddresses',
- 'AnAddress',
- 'UserAddresses',
- ]
-
-
+from mailman import public
from mailman.interfaces.address import (
ExistingAddressError, InvalidEmailAddressError)
from mailman.interfaces.usermanager import IUserManager
@@ -38,7 +32,6 @@ from operator import attrgetter
from zope.component import getUtility
-
class _AddressBase(CollectionMixin):
"""Shared base class for address representations."""
@@ -68,7 +61,7 @@ class _AddressBase(CollectionMixin):
return list(getUtility(IUserManager).addresses)
-
+@public
class AllAddresses(_AddressBase):
"""The addresses."""
@@ -78,7 +71,6 @@ class AllAddresses(_AddressBase):
okay(response, etag(resource))
-
class _VerifyResource:
"""A helper resource for verify/unverify POSTS."""
@@ -96,6 +88,7 @@ class _VerifyResource:
no_content(response)
+@public
class AnAddress(_AddressBase):
"""An address."""
@@ -172,7 +165,7 @@ class AnAddress(_AddressBase):
return AddressUser(self._address)
-
+@public
class UserAddresses(_AddressBase):
"""The addresses of a user."""
@@ -225,12 +218,12 @@ class UserAddresses(_AddressBase):
created(response, location)
-
def membership_key(member):
# Sort first by mailing list, then by address, then by role.
return member.list_id, member.address.email, member.role.value
+@public
class AddressMemberships(MemberCollection):
"""All the memberships of a particular email address."""
diff --git a/src/mailman/rest/bans.py b/src/mailman/rest/bans.py
index ac0a1ca7f..70a61cc73 100644
--- a/src/mailman/rest/bans.py
+++ b/src/mailman/rest/bans.py
@@ -17,12 +17,7 @@
"""REST for banned emails."""
-__all__ = [
- 'BannedEmail',
- 'BannedEmails',
- ]
-
-
+from mailman import public
from mailman.interfaces.bans import IBanManager
from mailman.rest.helpers import (
CollectionMixin, bad_request, child, created, etag, no_content, not_found,
@@ -45,6 +40,7 @@ class _BannedBase:
return self.api.path_to('{}bans/{}'.format(base_location, email))
+@public
class BannedEmail(_BannedBase):
"""A banned email."""
@@ -74,6 +70,7 @@ class BannedEmail(_BannedBase):
not_found(response, 'Email is not banned: {}'.format(self._email))
+@public
class BannedEmails(_BannedBase, CollectionMixin):
"""The list of all banned emails."""
diff --git a/src/mailman/rest/docs/__init__.py b/src/mailman/rest/docs/__init__.py
index cb8282e33..4b83845fc 100644
--- a/src/mailman/rest/docs/__init__.py
+++ b/src/mailman/rest/docs/__init__.py
@@ -17,11 +17,7 @@
"""Doctest layer setup."""
-__all__ = [
- 'layer',
- ]
-
-
-
from mailman.testing.layers import RESTLayer
+
layer = RESTLayer
+__all__ = ['layer']
diff --git a/src/mailman/rest/domains.py b/src/mailman/rest/domains.py
index 0128c9da0..dbf9f94c1 100644
--- a/src/mailman/rest/domains.py
+++ b/src/mailman/rest/domains.py
@@ -17,12 +17,7 @@
"""REST for domains."""
-__all__ = [
- 'ADomain',
- 'AllDomains',
- ]
-
-
+from mailman import public
from mailman.interfaces.domain import (
BadDomainSpecificationError, IDomainManager)
from mailman.rest.helpers import (
@@ -34,7 +29,6 @@ from mailman.rest.validator import Validator, list_of_strings_validator
from zope.component import getUtility
-
class _DomainBase(CollectionMixin):
"""Shared base class for domain representations."""
@@ -53,6 +47,7 @@ class _DomainBase(CollectionMixin):
return list(getUtility(IDomainManager))
+@public
class ADomain(_DomainBase):
"""A domain."""
@@ -100,6 +95,7 @@ class ADomain(_DomainBase):
return NotFound(), []
+@public
class AllDomains(_DomainBase):
"""The domains."""
diff --git a/src/mailman/rest/header_matches.py b/src/mailman/rest/header_matches.py
index f54c181ef..07a1f2c1c 100644
--- a/src/mailman/rest/header_matches.py
+++ b/src/mailman/rest/header_matches.py
@@ -17,12 +17,7 @@
"""REST API for a mailing list's header matches."""
-__all__ = [
- 'HeaderMatch',
- 'HeaderMatches',
- ]
-
-
+from mailman import public
from mailman.interfaces.action import Action
from mailman.interfaces.mailinglist import IHeaderMatchList
from mailman.rest.helpers import (
@@ -59,6 +54,7 @@ class _HeaderMatchBase:
return resource
+@public
class HeaderMatch(_HeaderMatchBase):
"""A header match."""
@@ -129,6 +125,7 @@ class HeaderMatch(_HeaderMatchBase):
self.patch_put(request, response, is_optional=True)
+@public
class HeaderMatches(_HeaderMatchBase, CollectionMixin):
"""The list of all header matches."""
diff --git a/src/mailman/rest/helpers.py b/src/mailman/rest/helpers.py
index a29c0599b..34fff3ba3 100644
--- a/src/mailman/rest/helpers.py
+++ b/src/mailman/rest/helpers.py
@@ -17,24 +17,6 @@
"""Web service helpers."""
-__all__ = [
- 'BadRequest',
- 'ChildError',
- 'CollectionMixin',
- 'GetterSetter',
- 'NotFound',
- 'bad_request',
- 'child',
- 'conflict',
- 'created',
- 'etag',
- 'forbidden',
- 'no_content',
- 'not_found',
- 'okay',
- ]
-
-
import json
import falcon
import hashlib
@@ -42,11 +24,11 @@ import hashlib
from datetime import datetime, timedelta
from enum import Enum
from lazr.config import as_boolean
+from mailman import public
from mailman.config import config
from pprint import pformat
-
class ExtendedEncoder(json.JSONEncoder):
"""An extended JSON encoder which knows about other data types."""
@@ -58,8 +40,8 @@ class ExtendedEncoder(json.JSONEncoder):
# to floating seconds, but only if there are any seconds.
if obj.seconds > 0 or obj.microseconds > 0:
seconds = obj.seconds + obj.microseconds / 1000000.0
- return '{0}d{1}s'.format(obj.days, seconds)
- return '{0}d'.format(obj.days)
+ return '{}d{}s'.format(obj.days, seconds)
+ return '{}d'.format(obj.days)
elif isinstance(obj, Enum):
# It's up to the decoding validator to associate this name with
# the right Enum class.
@@ -67,6 +49,7 @@ class ExtendedEncoder(json.JSONEncoder):
return super().default(obj)
+@public
def etag(resource):
"""Calculate the etag and return a JSON representation.
@@ -94,7 +77,7 @@ def etag(resource):
sort_keys=as_boolean(config.devmode.enabled))
-
+@public
class CollectionMixin:
"""Mixin class for common collection-ish things."""
@@ -164,7 +147,7 @@ class CollectionMixin:
return result
-
+@public
class GetterSetter:
"""Get and set attributes on an object.
@@ -225,9 +208,9 @@ class GetterSetter:
return self.decoder(value)
-
# Falcon REST framework add-ons.
+@public
def child(matcher=None):
def decorator(func):
if matcher is None:
@@ -238,6 +221,7 @@ def child(matcher=None):
return decorator
+@public
class ChildError:
def __init__(self, status):
self._status = status
@@ -252,55 +236,65 @@ class ChildError:
on_delete = _oops
+@public
class BadRequest(ChildError):
def __init__(self):
super().__init__(falcon.HTTP_400)
+@public
class NotFound(ChildError):
def __init__(self):
super().__init__(falcon.HTTP_404)
+@public
def okay(response, body=None):
response.status = falcon.HTTP_200
if body is not None:
response.body = body
+@public
def no_content(response):
response.status = falcon.HTTP_204
+@public
def not_found(response, body=b'404 Not Found'):
response.status = falcon.HTTP_404
if body is not None:
response.body = body
+@public
def accepted(response, body=None):
response.status = falcon.HTTP_202
if body is not None:
response.body = body
+@public
def bad_request(response, body='400 Bad Request'):
response.status = falcon.HTTP_400
if body is not None:
response.body = body
+@public
def created(response, location):
response.status = falcon.HTTP_201
response.location = location
+@public
def conflict(response, body=b'409 Conflict'):
response.status = falcon.HTTP_409
if body is not None:
response.body = body
+@public
def forbidden(response, body=b'403 Forbidden'):
response.status = falcon.HTTP_403
if body is not None:
diff --git a/src/mailman/rest/listconf.py b/src/mailman/rest/listconf.py
index a6c15d428..65ce8c5f7 100644
--- a/src/mailman/rest/listconf.py
+++ b/src/mailman/rest/listconf.py
@@ -17,12 +17,8 @@
"""Mailing list configuration via REST API."""
-__all__ = [
- 'ListConfiguration',
- ]
-
-
from lazr.config import as_boolean, as_timedelta
+from mailman import public
from mailman.config import config
from mailman.interfaces.action import Action
from mailman.interfaces.archiver import ArchivePolicy
@@ -37,7 +33,6 @@ from mailman.rest.validator import (
Validator, enum_validator, list_of_strings_validator)
-
class AcceptableAliases(GetterSetter):
"""Resource for the acceptable aliases of a mailing list."""
@@ -63,7 +58,6 @@ class AcceptableAliases(GetterSetter):
alias_set.add(alias)
-
# Additional validators for converting from web request strings to internal
# data types. See below for details.
@@ -74,7 +68,6 @@ def pipeline_validator(pipeline_name):
raise ValueError('Unknown pipeline: {}'.format(pipeline_name))
-
# This is the list of IMailingList attributes that are exposed through the
# REST API. The values of the keys are the GetterSetter instance holding the
# decoder used to convert the web request string to an internally valid value.
@@ -153,7 +146,7 @@ for attribute, gettersetter in list(VALIDATORS.items()):
del VALIDATORS[attribute]
-
+@public
class ListConfiguration:
"""A mailing list configuration resource."""
diff --git a/src/mailman/rest/lists.py b/src/mailman/rest/lists.py
index 0273c5d3a..2d669a267 100644
--- a/src/mailman/rest/lists.py
+++ b/src/mailman/rest/lists.py
@@ -17,17 +17,8 @@
"""REST for mailing lists."""
-__all__ = [
- 'AList',
- 'AllLists',
- 'ListArchivers',
- 'ListConfiguration',
- 'ListsForDomain',
- 'Styles',
- ]
-
-
from lazr.config import as_boolean
+from mailman import public
from mailman.app.digests import (
bump_digest_number_and_volume, maybe_send_digest_now)
from mailman.app.lifecycle import create_list, remove_list
@@ -118,6 +109,7 @@ class _ListBase(CollectionMixin):
return list(getUtility(IListManager))
+@public
class AList(_ListBase):
"""A mailing list."""
@@ -213,6 +205,7 @@ class AList(_ListBase):
return HeaderMatches(self._mlist)
+@public
class AllLists(_ListBase):
"""The mailing lists."""
@@ -238,6 +231,7 @@ class AllLists(_ListBase):
okay(response, etag(resource))
+@public
class MembersOfList(MemberCollection):
"""The members of a mailing list."""
@@ -255,6 +249,7 @@ class MembersOfList(MemberCollection):
role=self._role)
+@public
class ListsForDomain(_ListBase):
"""The mailing lists for a particular domain."""
@@ -271,6 +266,7 @@ class ListsForDomain(_ListBase):
return list(self._domain.mailing_lists)
+@public
class ArchiverGetterSetter(GetterSetter):
"""Resource for updating archiver statuses."""
@@ -287,6 +283,7 @@ class ArchiverGetterSetter(GetterSetter):
archiver.is_enabled = as_boolean(value)
+@public
class ListArchivers:
"""The archivers for a list, with their enabled flags."""
@@ -323,6 +320,7 @@ class ListArchivers:
self.patch_put(request, response, is_optional=True)
+@public
class ListDigest:
"""Simple resource representing actions on a list's digest."""
@@ -357,6 +355,7 @@ class ListDigest:
accepted(response)
+@public
class Styles:
"""Simple resource representing all list styles."""
diff --git a/src/mailman/rest/members.py b/src/mailman/rest/members.py
index 22db939b3..b56fa3c8d 100644
--- a/src/mailman/rest/members.py
+++ b/src/mailman/rest/members.py
@@ -17,14 +17,7 @@
"""REST for members."""
-__all__ = [
- 'AMember',
- 'AllMembers',
- 'FindMembers',
- 'MemberCollection',
- ]
-
-
+from mailman import public
from mailman.app.membership import add_member, delete_member
from mailman.interfaces.action import Action
from mailman.interfaces.address import IAddress
@@ -47,7 +40,6 @@ from uuid import UUID
from zope.component import getUtility
-
class _MemberBase(CollectionMixin):
"""Shared base class for member representations."""
@@ -85,7 +77,7 @@ class _MemberBase(CollectionMixin):
return list(getUtility(ISubscriptionService))
-
+@public
class MemberCollection(_MemberBase):
"""Abstract class for supporting submemberships.
@@ -103,7 +95,7 @@ class MemberCollection(_MemberBase):
okay(response, etag(resource))
-
+@public
class AMember(_MemberBase):
"""A member."""
@@ -201,7 +193,7 @@ class AMember(_MemberBase):
no_content(response)
-
+@public
class AllMembers(_MemberBase):
"""The members."""
@@ -350,7 +342,6 @@ class AllMembers(_MemberBase):
okay(response, etag(resource))
-
class _FoundMembers(MemberCollection):
"""The found members collection."""
@@ -364,6 +355,7 @@ class _FoundMembers(MemberCollection):
return self._members
+@public
class FindMembers(_MemberBase):
"""/members/find"""
diff --git a/src/mailman/rest/post_moderation.py b/src/mailman/rest/post_moderation.py
index 6283add75..a020b3350 100644
--- a/src/mailman/rest/post_moderation.py
+++ b/src/mailman/rest/post_moderation.py
@@ -17,12 +17,7 @@
"""REST API for held message moderation."""
-__all__ = [
- 'HeldMessage',
- 'HeldMessages',
- ]
-
-
+from mailman import public
from mailman.app.moderator import handle_message
from mailman.interfaces.action import Action
from mailman.interfaces.messages import IMessageStore
@@ -33,7 +28,6 @@ from mailman.rest.validator import Validator, enum_validator
from zope.component import getUtility
-
class _ModerationBase:
"""Common base class."""
@@ -66,7 +60,6 @@ class _ModerationBase:
return resource
-
class _HeldMessageBase(_ModerationBase):
"""Held messages are a little different."""
@@ -95,6 +88,7 @@ class _HeldMessageBase(_ModerationBase):
return resource
+@public
class HeldMessage(_HeldMessageBase):
"""Resource for moderating a held message."""
@@ -135,7 +129,7 @@ class HeldMessage(_HeldMessageBase):
no_content(response)
-
+@public
class HeldMessages(_HeldMessageBase, CollectionMixin):
"""Resource for messages held for moderation."""
diff --git a/src/mailman/rest/preferences.py b/src/mailman/rest/preferences.py
index 694aa47e9..cf26380fe 100644
--- a/src/mailman/rest/preferences.py
+++ b/src/mailman/rest/preferences.py
@@ -17,13 +17,8 @@
"""Preferences."""
-__all__ = [
- 'ReadOnlyPreferences',
- 'Preferences',
- ]
-
-
from lazr.config import as_boolean
+from mailman import public
from mailman.interfaces.member import DeliveryMode, DeliveryStatus
from mailman.rest.helpers import (
GetterSetter, bad_request, etag, no_content, not_found, okay)
@@ -42,7 +37,7 @@ PREFERENCES = (
)
-
+@public
class ReadOnlyPreferences:
""".../<object>/preferences"""
@@ -69,7 +64,7 @@ class ReadOnlyPreferences:
okay(response, etag(resource))
-
+@public
class Preferences(ReadOnlyPreferences):
"""Preferences which can be changed."""
@@ -79,7 +74,7 @@ class Preferences(ReadOnlyPreferences):
return
kws = dict(
acknowledge_posts=GetterSetter(as_boolean),
- hide_address = GetterSetter(as_boolean),
+ hide_address=GetterSetter(as_boolean),
delivery_mode=GetterSetter(enum_validator(DeliveryMode)),
delivery_status=GetterSetter(enum_validator(DeliveryStatus)),
preferred_language=GetterSetter(language_validator),
diff --git a/src/mailman/rest/queues.py b/src/mailman/rest/queues.py
index 4d3c9f58b..69f6df973 100644
--- a/src/mailman/rest/queues.py
+++ b/src/mailman/rest/queues.py
@@ -17,13 +17,7 @@
"""<api>/queues."""
-__all__ = [
- 'AQueue',
- 'AQueueFile',
- 'AllQueues',
- ]
-
-
+from mailman import public
from mailman.config import config
from mailman.app.inject import inject_text
from mailman.interfaces.listmanager import IListManager
@@ -33,7 +27,6 @@ from mailman.rest.validator import Validator
from zope.component import getUtility
-
class _QueuesBase(CollectionMixin):
"""Shared base class for queues."""
@@ -54,7 +47,7 @@ class _QueuesBase(CollectionMixin):
return sorted(config.switchboards)
-
+@public
class AQueue(_QueuesBase):
"""A single queue."""
@@ -94,7 +87,7 @@ class AQueue(_QueuesBase):
created(response, location)
-
+@public
class AQueueFile:
def __init__(self, name, filebase):
self._name = name
@@ -115,7 +108,7 @@ class AQueueFile:
no_content(response)
-
+@public
class AllQueues(_QueuesBase):
"""All queues."""
diff --git a/src/mailman/rest/root.py b/src/mailman/rest/root.py
index 027ed3777..988c228e7 100644
--- a/src/mailman/rest/root.py
+++ b/src/mailman/rest/root.py
@@ -17,14 +17,10 @@
"""The root of the REST API."""
-__all__ = [
- 'Root',
- ]
-
-
import falcon
from base64 import b64decode
+from mailman import public
from mailman.config import config
from mailman.core.api import API30, API31
from mailman.core.constants import system_preferences
@@ -48,7 +44,7 @@ from zope.component import getUtility
SLASH = '/'
-
+@public
class Root:
"""The RESTful root resource.
@@ -90,7 +86,7 @@ class Root:
credentials = b64decode(request.auth[6:]).decode('utf-8')
username, password = credentials.split(':', 1)
if (username != config.webservice.admin_user or
- password != config.webservice.admin_pass):
+ password != config.webservice.admin_pass):
# Not authorized.
raise falcon.HTTPUnauthorized(
'401 Unauthorized',
@@ -98,6 +94,7 @@ class Root:
return TopLevel()
+@public
class Versions:
def on_get(self, request, response):
"""/<api>/system/versions"""
@@ -110,6 +107,7 @@ class Versions:
okay(response, etag(resource))
+@public
class SystemConfiguration:
def __init__(self, section=None):
self._section = section
@@ -131,6 +129,7 @@ class SystemConfiguration:
okay(response, etag(resource))
+@public
class Reserved:
"""Top level API for reserved operations.
@@ -150,6 +149,7 @@ class Reserved:
no_content(response)
+@public
class TopLevel:
"""Top level collections and entries."""
diff --git a/src/mailman/rest/sub_moderation.py b/src/mailman/rest/sub_moderation.py
index e019b7bf4..033f95317 100644
--- a/src/mailman/rest/sub_moderation.py
+++ b/src/mailman/rest/sub_moderation.py
@@ -17,11 +17,7 @@
"""REST API for held subscription requests."""
-__all__ = [
- 'SubscriptionRequests',
- ]
-
-
+from mailman import public
from mailman.app.moderator import send_rejection
from mailman.interfaces.action import Action
from mailman.interfaces.member import AlreadySubscribedError
@@ -35,7 +31,6 @@ from mailman.utilities.i18n import _
from zope.component import getUtility
-
class _ModerationBase:
"""Common base class."""
@@ -52,7 +47,7 @@ class _ModerationBase:
return resource
-
+@public
class IndividualRequest(_ModerationBase):
"""Resource for moderating a membership change."""
@@ -118,7 +113,7 @@ class IndividualRequest(_ModerationBase):
_('[No reason given]'))
-
+@public
class SubscriptionRequests(_ModerationBase, CollectionMixin):
"""Resource for membership change requests."""
diff --git a/src/mailman/rest/templates.py b/src/mailman/rest/templates.py
index a21334e93..fe5e34a8e 100644
--- a/src/mailman/rest/templates.py
+++ b/src/mailman/rest/templates.py
@@ -17,11 +17,7 @@
"""Template finder."""
-__all__ = [
- 'TemplateFinder',
- ]
-
-
+from mailman import public
from mailman.rest.helpers import not_found
from mailman.utilities.i18n import TemplateNotFoundError, find
@@ -33,7 +29,7 @@ EXTENSIONS = {
}
-
+@public
class TemplateFinder:
"""Template finder resource."""
diff --git a/src/mailman/rest/tests/test_addresses.py b/src/mailman/rest/tests/test_addresses.py
index 0af52f607..edcfdfa86 100644
--- a/src/mailman/rest/tests/test_addresses.py
+++ b/src/mailman/rest/tests/test_addresses.py
@@ -17,12 +17,6 @@
"""REST address tests."""
-__all__ = [
- 'TestAPI31Addresses',
- 'TestAddresses',
- ]
-
-
import unittest
from mailman.app.lifecycle import create_list
@@ -35,7 +29,6 @@ from urllib.error import HTTPError
from zope.component import getUtility
-
class TestAddresses(unittest.TestCase):
layer = RESTLayer
@@ -488,7 +481,6 @@ class TestAddresses(unittest.TestCase):
self.assertEqual(cm.exception.code, 404)
-
class TestAPI31Addresses(unittest.TestCase):
"""UUIDs are represented as hex instead of int in API 3.1
diff --git a/src/mailman/rest/tests/test_api.py b/src/mailman/rest/tests/test_api.py
index 1d050ed42..4bca75849 100644
--- a/src/mailman/rest/tests/test_api.py
+++ b/src/mailman/rest/tests/test_api.py
@@ -17,11 +17,6 @@
"""API version tests."""
-__all__ = [
- 'TestAPIVersion',
- ]
-
-
import unittest
from mailman.core.system import system
@@ -30,7 +25,6 @@ from mailman.testing.layers import RESTLayer
from urllib.error import HTTPError
-
class TestAPIVersion(unittest.TestCase):
layer = RESTLayer
diff --git a/src/mailman/rest/tests/test_bans.py b/src/mailman/rest/tests/test_bans.py
index ce6d8c843..e6159d9a4 100644
--- a/src/mailman/rest/tests/test_bans.py
+++ b/src/mailman/rest/tests/test_bans.py
@@ -17,11 +17,6 @@
"""Test address bans."""
-__all__ = [
- 'TestBans',
- ]
-
-
import unittest
from mailman.app.lifecycle import create_list
diff --git a/src/mailman/rest/tests/test_basic.py b/src/mailman/rest/tests/test_basic.py
index c79a3a340..64c60842c 100644
--- a/src/mailman/rest/tests/test_basic.py
+++ b/src/mailman/rest/tests/test_basic.py
@@ -20,11 +20,6 @@
For example, test the integration between Mailman and Falcon.
"""
-__all__ = [
- 'TestBasicREST',
- ]
-
-
import unittest
from mailman.app.lifecycle import create_list
@@ -33,7 +28,6 @@ from mailman.testing.helpers import call_api
from mailman.testing.layers import RESTLayer
-
class TestBasicREST(unittest.TestCase):
"""Test basic REST integration and functionality."""
diff --git a/src/mailman/rest/tests/test_domains.py b/src/mailman/rest/tests/test_domains.py
index cab0def27..e98f75b53 100644
--- a/src/mailman/rest/tests/test_domains.py
+++ b/src/mailman/rest/tests/test_domains.py
@@ -17,12 +17,6 @@
"""REST domain tests."""
-__all__ = [
- 'TestDomainOwners',
- 'TestDomains',
- ]
-
-
import unittest
from mailman.app.lifecycle import create_list
@@ -35,7 +29,6 @@ from urllib.error import HTTPError
from zope.component import getUtility
-
class TestDomains(unittest.TestCase):
layer = RESTLayer
@@ -125,7 +118,6 @@ class TestDomains(unittest.TestCase):
self.assertEqual(cm.exception.code, 404)
-
class TestDomainOwners(unittest.TestCase):
layer = RESTLayer
diff --git a/src/mailman/rest/tests/test_header_matches.py b/src/mailman/rest/tests/test_header_matches.py
index 26baddd34..c8f6615af 100644
--- a/src/mailman/rest/tests/test_header_matches.py
+++ b/src/mailman/rest/tests/test_header_matches.py
@@ -17,11 +17,6 @@
"""Test REST header matches."""
-__all__ = [
- 'TestHeaderMatches',
- ]
-
-
import unittest
from mailman.app.lifecycle import create_list
diff --git a/src/mailman/rest/tests/test_helpers.py b/src/mailman/rest/tests/test_helpers.py
index cab1f06fa..982b97ef3 100644
--- a/src/mailman/rest/tests/test_helpers.py
+++ b/src/mailman/rest/tests/test_helpers.py
@@ -17,11 +17,6 @@
"""Additional tests for helpers."""
-__all__ = [
- 'TestHelpers',
- ]
-
-
import unittest
from datetime import timedelta
@@ -38,7 +33,6 @@ class Unserializable:
pass
-
class TestHelpers(unittest.TestCase):
layer = ConfigLayer
diff --git a/src/mailman/rest/tests/test_listconf.py b/src/mailman/rest/tests/test_listconf.py
index 2132a5387..35bd516e0 100644
--- a/src/mailman/rest/tests/test_listconf.py
+++ b/src/mailman/rest/tests/test_listconf.py
@@ -17,11 +17,6 @@
"""Test list configuration via the REST API."""
-__all__ = [
- 'TestConfiguration',
- ]
-
-
import unittest
from mailman.app.lifecycle import create_list
@@ -80,7 +75,6 @@ RESOURCE = dict(
)
-
class TestConfiguration(unittest.TestCase):
"""Test list configuration via the REST API."""
@@ -168,7 +162,7 @@ class TestConfiguration(unittest.TestCase):
call_api('http://localhost:9001/3.0/lists/ant.example.com'
'/config/mail_host',
dict(mail_host='foo.example.com'),
- 'PUT')
+ 'PUT')
self.assertEqual(cm.exception.code, 400)
self.assertEqual(cm.exception.reason,
b'Read-only attribute: mail_host')
@@ -213,8 +207,8 @@ class TestConfiguration(unittest.TestCase):
def test_unknown_patch_attribute(self):
with self.assertRaises(HTTPError) as cm:
call_api('http://localhost:9001/3.0/lists/ant.example.com/config',
- dict(bogus=1),
- 'PATCH')
+ dict(bogus=1),
+ 'PATCH')
self.assertEqual(cm.exception.code, 400)
self.assertEqual(cm.exception.reason, b'Unknown attribute: bogus')
@@ -223,7 +217,7 @@ class TestConfiguration(unittest.TestCase):
call_api('http://localhost:9001/3.0/lists/ant.example.com'
'/config/mail_host',
dict(mail_host='foo.example.com'),
- 'PATCH')
+ 'PATCH')
self.assertEqual(cm.exception.code, 400)
self.assertEqual(cm.exception.reason,
b'Read-only attribute: mail_host')
diff --git a/src/mailman/rest/tests/test_lists.py b/src/mailman/rest/tests/test_lists.py
index 202725a99..08c29151f 100644
--- a/src/mailman/rest/tests/test_lists.py
+++ b/src/mailman/rest/tests/test_lists.py
@@ -17,15 +17,6 @@
"""REST list tests."""
-__all__ = [
- 'TestListArchivers',
- 'TestListDigests',
- 'TestListPagination',
- 'TestLists',
- 'TestListsMissing',
- ]
-
-
import unittest
from datetime import timedelta
diff --git a/src/mailman/rest/tests/test_membership.py b/src/mailman/rest/tests/test_membership.py
index 57f641007..3d47b13a7 100644
--- a/src/mailman/rest/tests/test_membership.py
+++ b/src/mailman/rest/tests/test_membership.py
@@ -17,13 +17,6 @@
"""REST membership tests."""
-__all__ = [
- 'TestAPI31Members',
- 'TestMembership',
- 'TestNonmembership',
- ]
-
-
import unittest
from mailman.app.lifecycle import create_list
@@ -45,7 +38,6 @@ from urllib.error import HTTPError
from zope.component import getUtility
-
class TestMembership(unittest.TestCase):
layer = RESTLayer
@@ -428,7 +420,6 @@ class TestMembership(unittest.TestCase):
self.assertEqual(cm.exception.reason, b'Membership is banned')
-
class CustomLayer(ConfigLayer):
"""Custom layer which starts both the REST and LMTP servers."""
@@ -487,7 +478,7 @@ Some text.
# Now use the REST API to try to find the nonmember.
response, content = call_api(
'http://localhost:9001/3.0/members/find', {
- #'list_id': 'test.example.com',
+ # 'list_id': 'test.example.com',
'role': 'nonmember',
})
self.assertEqual(response['total_size'], 1)
@@ -518,7 +509,7 @@ Some text.
# Now use the REST API to try to find the nonmember.
response, content = call_api(
'http://localhost:9001/3.0/members/find', {
- #'list_id': 'test.example.com',
+ # 'list_id': 'test.example.com',
'role': 'nonmember',
})
self.assertEqual(response['total_size'], 1)
@@ -534,7 +525,6 @@ Some text.
'http://localhost:9001/3.0/users/1')
-
class TestAPI31Members(unittest.TestCase):
layer = RESTLayer
diff --git a/src/mailman/rest/tests/test_moderation.py b/src/mailman/rest/tests/test_moderation.py
index 9d4bee92a..9f9da6b18 100644
--- a/src/mailman/rest/tests/test_moderation.py
+++ b/src/mailman/rest/tests/test_moderation.py
@@ -17,12 +17,6 @@
"""REST moderation tests."""
-__all__ = [
- 'TestPostModeration',
- 'TestSubscriptionModeration',
- ]
-
-
import unittest
from mailman.app.lifecycle import create_list
@@ -41,7 +35,6 @@ from urllib.error import HTTPError
from zope.component import getUtility
-
class TestPostModeration(unittest.TestCase):
layer = RESTLayer
@@ -150,7 +143,6 @@ Something else.
self.assertEqual(cm.exception.code, 404)
-
class TestSubscriptionModeration(unittest.TestCase):
layer = RESTLayer
maxDiff = None
diff --git a/src/mailman/rest/tests/test_owners.py b/src/mailman/rest/tests/test_owners.py
index 8bc512632..d511a8cae 100644
--- a/src/mailman/rest/tests/test_owners.py
+++ b/src/mailman/rest/tests/test_owners.py
@@ -17,11 +17,6 @@
"""Additional tests for the top-level owners resource."""
-__all__ = [
- 'TestOwners',
- ]
-
-
import unittest
from mailman.testing.helpers import call_api
@@ -29,7 +24,6 @@ from mailman.testing.layers import RESTLayer
from urllib.error import HTTPError
-
class TestOwners(unittest.TestCase):
layer = RESTLayer
diff --git a/src/mailman/rest/tests/test_paginate.py b/src/mailman/rest/tests/test_paginate.py
index a01efc2a2..ad9fb0d2a 100644
--- a/src/mailman/rest/tests/test_paginate.py
+++ b/src/mailman/rest/tests/test_paginate.py
@@ -17,11 +17,6 @@
"""paginate helper tests."""
-__all__ = [
- 'TestPaginateHelper',
- ]
-
-
import unittest
from falcon import HTTPInvalidParam, Request
@@ -31,7 +26,6 @@ from mailman.rest.helpers import CollectionMixin
from mailman.testing.layers import RESTLayer
-
class _FakeRequest(Request):
def __init__(self, count=None, page=None):
self._params = {}
@@ -41,7 +35,6 @@ class _FakeRequest(Request):
self._params['page'] = page
-
class TestPaginateHelper(unittest.TestCase):
"""Test the @paginate decorator."""
@@ -55,7 +48,7 @@ class TestPaginateHelper(unittest.TestCase):
class Resource(CollectionMixin):
def _get_collection(self, request):
return ['one', 'two', 'three', 'four', 'five']
- def _resource_as_dict(self, res):
+ def _resource_as_dict(self, res): # flake8: noqa
return {'value': res}
return Resource()
diff --git a/src/mailman/rest/tests/test_preferences.py b/src/mailman/rest/tests/test_preferences.py
index cd743d960..424ca4f36 100644
--- a/src/mailman/rest/tests/test_preferences.py
+++ b/src/mailman/rest/tests/test_preferences.py
@@ -17,11 +17,6 @@
"""Test various preference functionality."""
-__all__ = [
- 'TestPreferences',
- ]
-
-
import unittest
from mailman.app.lifecycle import create_list
@@ -33,7 +28,6 @@ from urllib.error import HTTPError
from zope.component import getUtility
-
class TestPreferences(unittest.TestCase):
"""Test various preference functionality."""
diff --git a/src/mailman/rest/tests/test_queues.py b/src/mailman/rest/tests/test_queues.py
index 812156e64..d4fd2e7d7 100644
--- a/src/mailman/rest/tests/test_queues.py
+++ b/src/mailman/rest/tests/test_queues.py
@@ -17,11 +17,6 @@
"""Test the `queues` resource."""
-__all__ = [
- 'TestQueues',
- ]
-
-
import unittest
from mailman.app.lifecycle import create_list
@@ -41,7 +36,6 @@ Message-ID: <ant>
"""
-
class TestQueues(unittest.TestCase):
layer = RESTLayer
diff --git a/src/mailman/rest/tests/test_root.py b/src/mailman/rest/tests/test_root.py
index 2f4c9d3b8..ea2f2da5e 100644
--- a/src/mailman/rest/tests/test_root.py
+++ b/src/mailman/rest/tests/test_root.py
@@ -17,11 +17,6 @@
"""REST root object tests."""
-__all__ = [
- 'TestRoot',
- ]
-
-
import os
import json
import unittest
@@ -35,7 +30,6 @@ from mailman.testing.layers import RESTLayer
from urllib.error import HTTPError
-
class TestRoot(unittest.TestCase):
layer = RESTLayer
diff --git a/src/mailman/rest/tests/test_systemconf.py b/src/mailman/rest/tests/test_systemconf.py
index 8555eb7fa..9335480bf 100644
--- a/src/mailman/rest/tests/test_systemconf.py
+++ b/src/mailman/rest/tests/test_systemconf.py
@@ -17,11 +17,6 @@
"""Test system configuration read-only access."""
-__all__ = [
- 'TestSystemConfiguration',
- ]
-
-
import unittest
from mailman.testing.helpers import call_api
@@ -29,7 +24,6 @@ from mailman.testing.layers import RESTLayer
from urllib.error import HTTPError
-
class TestSystemConfiguration(unittest.TestCase):
layer = RESTLayer
maxDiff = None
@@ -88,7 +82,6 @@ class TestSystemConfiguration(unittest.TestCase):
'xref',
])
-
def test_all_sections(self):
# Getting the top level configuration object returns a list of all
# existing sections.
diff --git a/src/mailman/rest/tests/test_uids.py b/src/mailman/rest/tests/test_uids.py
index cbfad8d22..96792f8fa 100644
--- a/src/mailman/rest/tests/test_uids.py
+++ b/src/mailman/rest/tests/test_uids.py
@@ -21,11 +21,6 @@ There is no doctest for this functionality, since it's only useful for testing
of external clients of the REST API.
"""
-__all__ = [
- 'TestUIDs',
- ]
-
-
import unittest
from mailman.config import config
@@ -37,7 +32,6 @@ from mailman.testing.layers import RESTLayer
from zope.component import getUtility
-
class TestUIDs(unittest.TestCase):
layer = RESTLayer
diff --git a/src/mailman/rest/tests/test_users.py b/src/mailman/rest/tests/test_users.py
index 3a6508771..e42bdf623 100644
--- a/src/mailman/rest/tests/test_users.py
+++ b/src/mailman/rest/tests/test_users.py
@@ -17,15 +17,6 @@
"""REST user tests."""
-__all__ = [
- 'TestAPI31Users',
- 'TestLP1074374',
- 'TestLP1419519',
- 'TestLogin',
- 'TestUsers',
- ]
-
-
import os
import unittest
@@ -41,7 +32,6 @@ from zope.component import getUtility
from mailman.model.preferences import Preferences
-
class TestUsers(unittest.TestCase):
layer = RESTLayer
@@ -305,7 +295,6 @@ class TestUsers(unittest.TestCase):
'http://localhost:9001/3.0/users/1/preferences')
-
class TestLogin(unittest.TestCase):
"""Test user 'login' (really just password verification)."""
@@ -384,7 +373,6 @@ schemes = hex_md5
self.assertEqual(self.anne.password, '{plaintext}abc123')
-
class TestLP1074374(unittest.TestCase):
"""LP: #1074374 - deleting a user left their address records active."""
@@ -472,7 +460,6 @@ class TestLP1074374(unittest.TestCase):
self.assertEqual(member['role'], 'member')
-
class TestLP1419519(unittest.TestCase):
# LP: #1419519 - deleting a user with many linked addresses does not delete
# all address records.
@@ -516,7 +503,6 @@ class TestLP1419519(unittest.TestCase):
self.assertEqual(len(emails), 0)
-
class TestAPI31Users(unittest.TestCase):
"""UUIDs are represented as hex in API 3.1."""
diff --git a/src/mailman/rest/tests/test_validator.py b/src/mailman/rest/tests/test_validator.py
index 287928d3c..de2207108 100644
--- a/src/mailman/rest/tests/test_validator.py
+++ b/src/mailman/rest/tests/test_validator.py
@@ -17,11 +17,6 @@
"""Test REST validators."""
-__all__ = [
- 'TestValidators',
- ]
-
-
import unittest
from mailman.interfaces.usermanager import IUserManager
@@ -32,7 +27,6 @@ from mailman.testing.layers import RESTLayer
from zope.component import getUtility
-
class TestValidators(unittest.TestCase):
layer = RESTLayer
diff --git a/src/mailman/rest/users.py b/src/mailman/rest/users.py
index 21ed26745..6ac4462c8 100644
--- a/src/mailman/rest/users.py
+++ b/src/mailman/rest/users.py
@@ -17,16 +17,8 @@
"""REST for users."""
-__all__ = [
- 'AUser',
- 'AddressUser',
- 'AllUsers',
- 'Login',
- 'OwnersForDomain',
- ]
-
-
from lazr.config import as_boolean
+from mailman import public
from mailman.config import config
from mailman.interfaces.address import ExistingAddressError
from mailman.interfaces.usermanager import IUserManager
@@ -42,24 +34,27 @@ from passlib.utils import generate_password as generate
from zope.component import getUtility
-
# Attributes of a user which can be changed via the REST API.
+@public
class PasswordEncrypterGetterSetter(GetterSetter):
def __init__(self):
super().__init__(config.password_context.encrypt)
+
def get(self, obj, attribute):
assert attribute == 'cleartext_password'
super().get(obj, 'password')
+
def put(self, obj, attribute, value):
assert attribute == 'cleartext_password'
super().put(obj, 'password', value)
+@public
class ListOfDomainOwners(GetterSetter):
def get(self, domain, attribute):
assert attribute == 'owner', (
'Unexpected attribute: {}'.format(attribute))
- def sort_key(owner):
+ def sort_key(owner): # flake8: noqa
return owner.addresses[0].email
return sorted(domain.owners, key=sort_key)
@@ -85,7 +80,6 @@ CREATION_FIELDS = dict(
)
-
def create_user(arguments, request, response):
"""Create a new user."""
# We can't pass the 'password' argument to the user creation method, so
@@ -122,7 +116,6 @@ def create_user(arguments, request, response):
return user
-
class _UserBase(CollectionMixin):
"""Shared base class for user representations."""
@@ -152,7 +145,7 @@ class _UserBase(CollectionMixin):
return list(getUtility(IUserManager).users)
-
+@public
class AllUsers(_UserBase):
"""The users."""
@@ -172,7 +165,7 @@ class AllUsers(_UserBase):
create_user(arguments, request, response)
-
+@public
class AUser(_UserBase):
"""A user."""
@@ -284,7 +277,7 @@ class AUser(_UserBase):
return Login(self._user)
-
+@public
class AddressUser(_UserBase):
"""The user linked to an address."""
@@ -381,7 +374,7 @@ class AddressUser(_UserBase):
user.link(self._address)
-
+@public
class Login:
"""<api>/users/<uid>/login"""
@@ -409,7 +402,7 @@ class Login:
forbidden(response)
-
+@public
class OwnersForDomain(_UserBase):
"""Owners for a particular domain."""
@@ -461,7 +454,7 @@ class OwnersForDomain(_UserBase):
return list(self._domain.owners)
-
+@public
class ServerOwners(_UserBase):
"""All server owners."""
diff --git a/src/mailman/rest/validator.py b/src/mailman/rest/validator.py
index 748a63d5c..861b869de 100644
--- a/src/mailman/rest/validator.py
+++ b/src/mailman/rest/validator.py
@@ -17,16 +17,7 @@
"""REST web form validation."""
-__all__ = [
- 'PatchValidator',
- 'Validator',
- 'enum_validator',
- 'language_validator',
- 'list_of_strings_validator',
- 'subscriber_validator',
- ]
-
-
+from mailman import public
from mailman.interfaces.address import IEmailValidator
from mailman.interfaces.errors import MailmanError
from mailman.interfaces.languages import ILanguageManager
@@ -36,10 +27,12 @@ from zope.component import getUtility
COMMASPACE = ', '
+@public
class RESTError(MailmanError):
"""Base class for REST API errors."""
+@public
class UnknownPATCHRequestError(RESTError):
"""A PATCH request contained an unknown attribute."""
@@ -47,6 +40,7 @@ class UnknownPATCHRequestError(RESTError):
self.attribute = attribute
+@public
class ReadOnlyPATCHRequestError(RESTError):
"""A PATCH request contained a read-only attribute."""
@@ -54,7 +48,7 @@ class ReadOnlyPATCHRequestError(RESTError):
self.attribute = attribute
-
+@public
class enum_validator:
"""Convert an enum value name into an enum value."""
@@ -71,6 +65,7 @@ class enum_validator:
raise ValueError(exception.args[0])
+@public
def subscriber_validator(api):
"""Convert an email-or-(int|hex) to an email-or-UUID."""
def _inner(subscriber):
@@ -84,11 +79,13 @@ def subscriber_validator(api):
return _inner
+@public
def language_validator(code):
"""Convert a language code to a Language object."""
return getUtility(ILanguageManager)[code]
+@public
def list_of_strings_validator(values):
"""Turn a list of things, or a single thing, into a list of unicodes."""
if not isinstance(values, (list, tuple)):
@@ -99,7 +96,7 @@ def list_of_strings_validator(values):
return values
-
+@public
class Validator:
"""A validator of parameter input."""
@@ -139,17 +136,17 @@ class Validator:
# Make sure there are no unexpected values.
if len(extras) != 0:
extras = COMMASPACE.join(sorted(extras))
- raise ValueError('Unexpected parameters: {0}'.format(extras))
+ raise ValueError('Unexpected parameters: {}'.format(extras))
# Make sure everything could be converted.
if len(cannot_convert) != 0:
bad = COMMASPACE.join(sorted(cannot_convert))
- raise ValueError('Cannot convert parameters: {0}'.format(bad))
+ raise ValueError('Cannot convert parameters: {}'.format(bad))
# Make sure nothing's missing.
value_keys = set(values)
required_keys = set(self._converters) - self._optional
if value_keys & required_keys != required_keys:
missing = COMMASPACE.join(sorted(required_keys - value_keys))
- raise ValueError('Missing parameters: {0}'.format(missing))
+ raise ValueError('Missing parameters: {}'.format(missing))
return values
def update(self, obj, request):
@@ -167,7 +164,7 @@ class Validator:
self._converters[key].put(obj, key, value)
-
+@public
class PatchValidator(Validator):
"""Create a special validator for PATCH requests.
diff --git a/src/mailman/rest/wsgiapp.py b/src/mailman/rest/wsgiapp.py
index 3155ee510..6184da4ee 100644
--- a/src/mailman/rest/wsgiapp.py
+++ b/src/mailman/rest/wsgiapp.py
@@ -17,18 +17,13 @@
"""Basic WSGI Application object for REST server."""
-__all__ = [
- 'make_application',
- 'make_server',
- ]
-
-
import re
import logging
from falcon import API
from falcon.responders import path_not_found
from falcon.routing import create_http_method_map
+from mailman import public
from mailman.config import config
from mailman.database.transaction import transactional
from mailman.rest.root import Root
@@ -42,7 +37,6 @@ SLASH = '/'
EMPTYSTRING = ''
-
class AdminWSGIServer(WSGIServer):
"""Server class that integrates error handling with our log files."""
@@ -53,6 +47,19 @@ class AdminWSGIServer(WSGIServer):
client_address)
+class StderrLogger:
+ def __init__(self):
+ self._buffer = []
+
+ def write(self, message):
+ self._buffer.append(message)
+
+ def flush(self):
+ self._buffer.insert(0, 'REST request handler error:\n')
+ log.error(EMPTYSTRING.join(self._buffer))
+ self._buffer = []
+
+
class AdminWebServiceWSGIRequestHandler(WSGIRequestHandler):
"""Handler class which just logs output to the right place."""
@@ -63,15 +70,6 @@ class AdminWebServiceWSGIRequestHandler(WSGIRequestHandler):
def get_stderr(self):
# Return a fake stderr object that will actually write its output to
# the log file.
- class StderrLogger:
- def __init__(self):
- self._buffer = []
- def write(self, message):
- self._buffer.append(message)
- def flush(self):
- self._buffer.insert(0, 'REST request handler error:\n')
- log.error(EMPTYSTRING.join(self._buffer))
- self._buffer = []
return StderrLogger()
@@ -182,7 +180,7 @@ class RootedAPI(API):
return path_not_found, {}, None
-
+@public
def make_application():
"""Create the WSGI application.
@@ -192,6 +190,7 @@ def make_application():
return RootedAPI(Root())
+@public
def make_server():
"""Create the Mailman REST server.