summaryrefslogtreecommitdiff
path: root/src/mailman/utilities
diff options
context:
space:
mode:
authorBarry Warsaw2016-07-16 15:44:07 -0400
committerBarry Warsaw2016-07-16 15:44:07 -0400
commitdbde6231ec897379ed38ed4cd015b8ab20ed5fa1 (patch)
tree1226d06a238314262a1d04d0bbf9c4dc0b72c309 /src/mailman/utilities
parent3387791beb7112dbe07664041f117fdcc20df53d (diff)
downloadmailman-dbde6231ec897379ed38ed4cd015b8ab20ed5fa1.tar.gz
mailman-dbde6231ec897379ed38ed4cd015b8ab20ed5fa1.tar.zst
mailman-dbde6231ec897379ed38ed4cd015b8ab20ed5fa1.zip
Diffstat (limited to 'src/mailman/utilities')
-rw-r--r--src/mailman/utilities/i18n.py45
-rw-r--r--src/mailman/utilities/importer.py52
-rw-r--r--src/mailman/utilities/protocols.py101
-rw-r--r--src/mailman/utilities/string.py31
-rw-r--r--src/mailman/utilities/tests/test_import.py78
-rw-r--r--src/mailman/utilities/tests/test_protocols.py174
-rw-r--r--src/mailman/utilities/tests/test_templates.py64
7 files changed, 367 insertions, 178 deletions
diff --git a/src/mailman/utilities/i18n.py b/src/mailman/utilities/i18n.py
index e289a92be..1df8ede7e 100644
--- a/src/mailman/utilities/i18n.py
+++ b/src/mailman/utilities/i18n.py
@@ -24,9 +24,7 @@ from itertools import product
from mailman import public
from mailman.config import config
from mailman.core.constants import system_preferences
-from mailman.core.i18n import _
from mailman.interfaces.errors import MailmanError
-from mailman.utilities.string import expand, wrap as wrap_text
from pkg_resources import resource_filename
@@ -159,46 +157,3 @@ def find(template_file, mlist=None, language=None, _trace=False):
print(' FOUND:', path, file=sys.stderr)
return path, fp
raise TemplateNotFoundError(template_file)
-
-
-@public
-def make(template_file, mlist=None, language=None, wrap=True,
- _trace=False, **kw):
- """Locate and 'make' a template file.
-
- The template file is located as with `find()`, and the resulting text is
- optionally wrapped and interpolated with the keyword argument dictionary.
-
- :param template_file: The name of the template file to search for.
- :type template_file: string
- :param mlist: Optional mailing list used as the context for
- searching for the template file. The list's preferred language will
- influence the search, as will the list's data directory.
- :type mlist: `IMailingList`
- :param language: Optional language code, which influences the search.
- :type language: string
- :param wrap: When True, wrap the text.
- :type wrap: bool
- :param _trace: Passed through to ``find()``, this enables printing of
- debugging information during template search.
- :type _trace: bool
- :param **kw: Keyword arguments for template interpolation.
- :return: The interpolated text.
- :rtype: string
- :raises TemplateNotFoundError: when the template could not be found.
- """
- path, fp = find(template_file, mlist, language, _trace)
- try:
- # XXX Removing the trailing newline is a hack carried over from
- # Mailman 2. The (stripped) template text is then passed through the
- # translation catalog. This ensures that the translated text is
- # unicode, and also allows for volunteers to translate the templates
- # into the language catalogs.
- template = _(fp.read()[:-1])
- finally:
- fp.close()
- assert isinstance(template, str), 'Translated template is not a string'
- text = expand(template, kw)
- if wrap:
- return wrap_text(text)
- return text
diff --git a/src/mailman/utilities/importer.py b/src/mailman/utilities/importer.py
index 1f350205f..c908a2ada 100644
--- a/src/mailman/utilities/importer.py
+++ b/src/mailman/utilities/importer.py
@@ -20,13 +20,12 @@
import os
import re
import sys
-import codecs
import logging
import datetime
from mailman import public
from mailman.config import config
-from mailman.handlers.decorate import decorate, decorate_template
+from mailman.handlers.decorate import decorate_template
from mailman.interfaces.action import Action, FilterAction
from mailman.interfaces.address import IEmailValidator
from mailman.interfaces.archiver import ArchivePolicy
@@ -41,11 +40,11 @@ from mailman.interfaces.mailinglist import (
SubscriptionPolicy)
from mailman.interfaces.member import DeliveryMode, DeliveryStatus, MemberRole
from mailman.interfaces.nntp import NewsgroupModeration
+from mailman.interfaces.template import ITemplateManager
from mailman.interfaces.usermanager import IUserManager
from mailman.utilities.filesystem import makedirs
from mailman.utilities.i18n import search
from sqlalchemy import Boolean
-from urllib.error import URLError
from zope.component import getUtility
log = logging.getLogger('mailman.error')
@@ -376,44 +375,28 @@ def import_config_pck(mlist, config_dict):
# special `mailman:` scheme indicating a file system path. What we do
# here is look to see if the list's decoration is different than the
# default, and if so, we'll write the new decoration template to a
- # `mailman:` scheme path.
+ # `mailman:` scheme path, then add the template to the template manager.
convert_to_uri = {
- 'welcome_msg': 'welcome_message_uri',
- 'goodbye_msg': 'goodbye_message_uri',
- 'msg_header': 'header_uri',
- 'msg_footer': 'footer_uri',
- 'digest_header': 'digest_header_uri',
- 'digest_footer': 'digest_footer_uri',
+ 'welcome_msg': 'list:user:notice:welcome',
+ 'goodbye_msg': 'list:user:notice:goodbye',
+ 'msg_header': 'list:member:regular:header',
+ 'msg_footer': 'list:member:regular:footer',
+ 'digest_header': 'list:member:digest:header',
+ 'digest_footer': 'list:member:digest:footer',
}
# The best we can do is convert only the most common ones. These are
# order dependent; the longer substitution with the common prefix must
# show up earlier.
convert_placeholders = [
- ('%(real_name)s@%(host_name)s', '$fqdn_listname'),
+ ('%(real_name)s@%(host_name)s', '$listname'),
('%(real_name)s', '$display_name'),
- ('%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s',
- '$listinfo_uri'),
+ # The generic footers no longer have URLs in them.
+ ('%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s\n', ''),
]
# Collect defaults.
+ manager = getUtility(ITemplateManager)
defaults = {}
for oldvar, newvar in convert_to_uri.items():
- default_value = getattr(mlist, newvar, None)
- if not default_value:
- continue
- # Check if the value changed from the default.
- try:
- default_text = decorate(mlist, default_value)
- except (URLError, KeyError):
- # Use case: importing the old a@ex.com into b@ex.com. We can't
- # check if it changed from the default so don't import, we may do
- # more harm than good and it's easy to change if needed.
- # TESTME
- print('Unable to convert mailing list attribute:', oldvar,
- 'with old value "{}"'.format(default_value),
- file=sys.stderr)
- continue
- defaults[newvar] = (default_value, default_text)
- for oldvar, newvar in convert_to_uri.items():
if oldvar not in config_dict:
continue
text = config_dict[oldvar]
@@ -442,17 +425,18 @@ def import_config_pck(mlist, config_dict):
expanded_text.strip() == default_text.strip()):
# Keep the default.
continue
- # Write the custom value to the right file.
+ # Write the custom value to the right file and add it to the template
+ # manager for real.
base_uri = 'mailman:///$listname/$language/'
if default_value:
filename = default_value.rpartition('/')[2]
else:
- filename = '{}.txt'.format(newvar[:-4])
+ filename = '{}.txt'.format(newvar.replace(':', '_'))
if not default_value or not default_value.startswith(base_uri):
- setattr(mlist, newvar, base_uri + filename)
+ manager.set(newvar, mlist.list_id, base_uri + filename)
filepath = list(search(filename, mlist))[0]
makedirs(os.path.dirname(filepath))
- with codecs.open(filepath, 'w', encoding='utf-8') as fp:
+ with open(filepath, 'w', encoding='utf-8') as fp:
fp.write(text)
# Import rosters.
regulars_set = set(config_dict.get('members', {}))
diff --git a/src/mailman/utilities/protocols.py b/src/mailman/utilities/protocols.py
new file mode 100644
index 000000000..5f447c465
--- /dev/null
+++ b/src/mailman/utilities/protocols.py
@@ -0,0 +1,101 @@
+# Copyright (C) 2016 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 <http://www.gnu.org/licenses/>.
+
+"""Various URL protocol support."""
+
+import requests
+
+from mailman import public
+from mailman.interfaces.languages import ILanguageManager
+from mailman.interfaces.listmanager import IListManager
+from mailman.utilities.i18n import TemplateNotFoundError, find
+from urllib.error import URLError
+from urllib.parse import urlparse
+from zope.component import getUtility
+
+COMMASPACE = ', '
+
+
+@public
+def get(url, **kws):
+ parsed = urlparse(url)
+ if parsed.scheme in ('http', 'https'):
+ response = requests.get(url, **kws)
+ response.raise_for_status()
+ return response.text
+ if parsed.scheme == 'file':
+ mode = kws.pop('mode', 'r')
+ arguments = dict(mode=mode)
+ if 'encoding' in kws or 'b' not in mode:
+ arguments['encoding'] = kws.pop('encoding', 'utf-8')
+ if len(kws) > 0:
+ raise ValueError('Unexpected arguments: {}'.format(
+ COMMASPACE.join(sorted(kws))))
+ with open(parsed.path, **arguments) as fp:
+ return fp.read()
+ if parsed.scheme == 'mailman':
+ mlist = code = None
+ if len(kws) > 0:
+ raise ValueError('Unexpected arguments: {}'.format(
+ COMMASPACE.join(sorted(kws))))
+ # The path can contain one, two, or three components. Since no empty
+ # path components are legal, filter them out.
+ parts = [p for p in parsed.path.split('/') if p]
+ if len(parts) == 0:
+ raise URLError('No template specified')
+ elif len(parts) == 1:
+ template = parts[0]
+ elif len(parts) == 2:
+ part0, template = parts
+ # Is part0 a language code or a mailing list? This is rather
+ # tricky because if it's a mailing list, it could be a list-id and
+ # that will contain dots, as could the language code.
+ language = getUtility(ILanguageManager).get(part0)
+ if language is None:
+ list_manager = getUtility(IListManager)
+ # part0 must be a fqdn-listname or list-id.
+ mlist = (list_manager.get(part0)
+ if '@' in part0 else
+ list_manager.get_by_list_id(part0))
+ if mlist is None:
+ raise URLError('Bad language or list name')
+ else:
+ code = language.code
+ elif len(parts) == 3:
+ part0, code, template = parts
+ # part0 could be an fqdn-listname or a list-id.
+ mlist = (getUtility(IListManager).get(part0)
+ if '@' in part0 else
+ getUtility(IListManager).get_by_list_id(part0))
+ if mlist is None:
+ raise URLError('Missing list')
+ language = getUtility(ILanguageManager).get(code)
+ if language is None:
+ raise URLError('No such language')
+ code = language.code
+ else:
+ raise URLError('No such file')
+ # Find the template, mutating any missing template exception.
+ try:
+ path, fp = find(template, mlist, code)
+ except TemplateNotFoundError:
+ raise URLError('No such file')
+ try:
+ return fp.read()
+ finally:
+ fp.close()
+ raise URLError(url)
diff --git a/src/mailman/utilities/string.py b/src/mailman/utilities/string.py
index 8394aa5e6..2694877ee 100644
--- a/src/mailman/utilities/string.py
+++ b/src/mailman/utilities/string.py
@@ -22,6 +22,7 @@ import logging
from email.errors import HeaderParseError
from email.header import decode_header, make_header
from mailman import public
+from mailman.config import config
from string import Template, whitespace
from textwrap import TextWrapper, dedent
@@ -33,18 +34,40 @@ log = logging.getLogger('mailman.error')
@public
-def expand(template, substitutions, template_class=Template):
+def expand(template, mlist=None, extras=None, template_class=Template):
"""Expand string template with substitutions.
:param template: A PEP 292 $-string template.
:type template: string
- :param substitutions: The substitutions dictionary.
- :type substitutions: dict
+ :param mlist: Optional mailing list. If given, the standard set of
+ list-specific substitution variables are used automatically.
+ :type mlist: `IMailingList`
+ :param extras: An additional substitutions dictionary. These are used to
+ augment any standard, list-specific substitutions.
+ :type extras: dict
:param template_class: The template class to use.
:type template_class: class
:return: The substituted string.
:rtype: string
"""
+ substitutions = dict(
+ site_email=config.mailman.site_owner,
+ )
+ if mlist is not None:
+ substitutions.update(dict(
+ listname=mlist.fqdn_listname,
+ list_id=mlist.list_id,
+ display_name=mlist.display_name,
+ short_listname=mlist.list_name,
+ domain=mlist.mail_host,
+ description=mlist.description,
+ info=mlist.info,
+ request_email=mlist.request_address,
+ owner_email=mlist.owner_address,
+ language=mlist.preferred_language.code,
+ ))
+ if extras is not None:
+ substitutions.update(extras)
return template_class(template).safe_substitute(substitutions)
@@ -119,10 +142,12 @@ def wrap(text, column=70, honor_leading_ws=True):
wrapped_paragraphs = []
# The dedented wrapper.
wrapper = TextWrapper(width=column,
+ break_on_hyphens=False,
fix_sentence_endings=True)
# The indented wrapper. For this one, we'll clobber initial_indent and
# subsequent_indent as needed per indented chunk of text.
iwrapper = TextWrapper(width=column,
+ break_on_hyphens=False,
fix_sentence_endings=True,
)
add_paragraph_break = False
diff --git a/src/mailman/utilities/tests/test_import.py b/src/mailman/utilities/tests/test_import.py
index b2d48891b..d2729e8e1 100644
--- a/src/mailman/utilities/tests/test_import.py
+++ b/src/mailman/utilities/tests/test_import.py
@@ -31,22 +31,23 @@ from mailman.interfaces.archiver import ArchivePolicy
from mailman.interfaces.autorespond import ResponseAction
from mailman.interfaces.bans import IBanManager
from mailman.interfaces.bounce import UnrecognizedBounceDisposition
+from mailman.interfaces.domain import IDomainManager
from mailman.interfaces.languages import ILanguageManager
from mailman.interfaces.mailinglist import (
IAcceptableAliasSet, SubscriptionPolicy)
from mailman.interfaces.member import DeliveryMode, DeliveryStatus
from mailman.interfaces.nntp import NewsgroupModeration
-from mailman.interfaces.templates import ITemplateLoader
+from mailman.interfaces.template import ITemplateLoader, ITemplateManager
from mailman.interfaces.usermanager import IUserManager
from mailman.testing.helpers import LogFileMark
from mailman.testing.layers import ConfigLayer
from mailman.utilities.filesystem import makedirs
from mailman.utilities.importer import (
Import21Error, check_language_code, import_config_pck)
-from mailman.utilities.string import expand
from pickle import load
from pkg_resources import resource_filename
from unittest import mock
+from urllib.error import URLError
from zope.component import getUtility
@@ -638,16 +639,17 @@ class TestConvertToURI(unittest.TestCase):
# -> %(listinfo_uri)s
layer = ConfigLayer
+ maxDiff = None
def setUp(self):
self._mlist = create_list('blank@example.com')
self._conf_mapping = dict(
- welcome_msg='welcome_message_uri',
- goodbye_msg='goodbye_message_uri',
- msg_header='header_uri',
- msg_footer='footer_uri',
- digest_header='digest_header_uri',
- digest_footer='digest_footer_uri',
+ welcome_msg='list:user:notice:welcome',
+ goodbye_msg='list:user:notice:goodbye',
+ msg_header='list:member:regular:header',
+ msg_footer='list:member:regular:footer',
+ digest_header='list:member:digest:header',
+ digest_footer='list:member:digest:footer',
)
self._pckdict = dict()
@@ -655,8 +657,7 @@ class TestConvertToURI(unittest.TestCase):
for oldvar, newvar in self._conf_mapping.items():
self._pckdict[str(oldvar)] = b'TEST VALUE'
import_config_pck(self._mlist, self._pckdict)
- newattr = getattr(self._mlist, newvar)
- text = decorate(self._mlist, newattr)
+ text = decorate(newvar, self._mlist)
self.assertEqual(
text, 'TEST VALUE',
'Old variable %s was not properly imported to %s'
@@ -664,21 +665,13 @@ class TestConvertToURI(unittest.TestCase):
def test_substitutions(self):
test_text = ('UNIT TESTING %(real_name)s mailing list\n'
- '%(real_name)s@%(host_name)s\n'
- '%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s')
+ '%(real_name)s@%(host_name)s')
expected_text = ('UNIT TESTING $display_name mailing list\n'
- '$fqdn_listname\n'
- '$listinfo_uri')
+ '$listname')
for oldvar, newvar in self._conf_mapping.items():
self._pckdict[str(oldvar)] = str(test_text)
import_config_pck(self._mlist, self._pckdict)
- newattr = getattr(self._mlist, newvar)
- template_uri = expand(newattr, dict(
- listname=self._mlist.fqdn_listname,
- language=self._mlist.preferred_language.code,
- ))
- loader = getUtility(ITemplateLoader)
- text = loader.get(template_uri)
+ text = getUtility(ITemplateLoader).get(newvar, self._mlist)
self.assertEqual(
text, expected_text,
'Old variables were not converted for %s' % newvar)
@@ -689,35 +682,53 @@ class TestConvertToURI(unittest.TestCase):
'_______________________________________________\n'
'%(real_name)s mailing list\n'
'%(real_name)s@%(host_name)s\n'
- '%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s'
+ '%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s\n'
)
+ loader = getUtility(ITemplateLoader)
for oldvar in ('msg_footer', 'digest_footer'):
newvar = self._conf_mapping[oldvar]
self._pckdict[str(oldvar)] = str(default_msg_footer)
- old_value = getattr(self._mlist, newvar)
+ try:
+ old_value = loader.get(newvar, self._mlist)
+ except URLError:
+ old_value = None
import_config_pck(self._mlist, self._pckdict)
- new_value = getattr(self._mlist, newvar)
+ try:
+ new_value = loader.get(newvar, self._mlist)
+ except URLError:
+ new_value = None
self.assertEqual(
old_value, new_value,
- 'Default value was not preserved for %s' % newvar)
+ '{} changed unexpectedly: {} != {}'.format(
+ newvar, old_value, new_value))
def test_keep_default_if_fqdn_changed(self):
# Use case: importing the old a@ex.com into b@ex.com. We can't check
# if it changed from the default so don't import. We may do more harm
# than good and it's easy to change if needed.
test_value = b'TEST-VALUE'
+ # We need an IDomain for this mail_host.
+ getUtility(IDomainManager).add('test.example.com')
+ manager = getUtility(ITemplateManager)
for oldvar, newvar in self._conf_mapping.items():
self._mlist.mail_host = 'example.com'
self._pckdict['mail_host'] = b'test.example.com'
self._pckdict[str(oldvar)] = test_value
- old_value = getattr(self._mlist, newvar)
+ try:
+ old_value = manager.get(newvar, 'blank.example.com')
+ except URLError:
+ old_value = None
# Suppress warning messages in the test output.
with mock.patch('sys.stderr'):
import_config_pck(self._mlist, self._pckdict)
- new_value = getattr(self._mlist, newvar)
+ try:
+ new_value = manager.get(newvar, 'test.example.com')
+ except URLError:
+ new_value = None
self.assertEqual(
old_value, new_value,
- 'Default value was not preserved for %s' % newvar)
+ '{} changed unexpectedly: {} != {}'.format(
+ newvar, old_value, new_value))
def test_unicode(self):
# non-ascii templates
@@ -725,10 +736,11 @@ class TestConvertToURI(unittest.TestCase):
self._pckdict[str(oldvar)] = b'Ol\xe1!'
import_config_pck(self._mlist, self._pckdict)
for oldvar, newvar in self._conf_mapping.items():
- newattr = getattr(self._mlist, newvar)
- text = decorate(self._mlist, newattr)
+ text = decorate(newvar, self._mlist)
expected = u'Ol\ufffd!'
- self.assertEqual(text, expected)
+ self.assertEqual(
+ text, expected,
+ '{} -> {} did not get converted'.format(oldvar, newvar))
def test_unicode_in_default(self):
# What if the default template is already in UTF-8? For example, if
@@ -736,13 +748,13 @@ class TestConvertToURI(unittest.TestCase):
footer = b'\xe4\xb8\xad $listinfo_uri'
footer_path = os.path.join(
config.VAR_DIR, 'templates', 'lists',
- 'blank@example.com', 'en', 'footer-generic.txt')
+ 'blank@example.com', 'en', 'footer.txt')
makedirs(os.path.dirname(footer_path))
with open(footer_path, 'wb') as fp:
fp.write(footer)
self._pckdict['msg_footer'] = b'NEW-VALUE'
import_config_pck(self._mlist, self._pckdict)
- text = decorate(self._mlist, self._mlist.footer_uri)
+ text = decorate('list:member:regular:footer', self._mlist)
self.assertEqual(text, 'NEW-VALUE')
diff --git a/src/mailman/utilities/tests/test_protocols.py b/src/mailman/utilities/tests/test_protocols.py
new file mode 100644
index 000000000..dc5e46e6a
--- /dev/null
+++ b/src/mailman/utilities/tests/test_protocols.py
@@ -0,0 +1,174 @@
+# Copyright (C) 2016 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 <http://www.gnu.org/licenses/>.
+
+"""Test the protocol support.
+
+For convenience, we currently don't test the http: and https: schemes here.
+These are tested fairly well in the template cache tests. We probably
+eventually want to refactor that for test isolation.
+"""
+
+
+import os
+import unittest
+
+from contextlib import ExitStack
+from mailman.app.lifecycle import create_list
+from mailman.config import config
+from mailman.testing.layers import ConfigLayer
+from mailman.utilities import protocols
+from tempfile import TemporaryDirectory
+from urllib.error import URLError
+
+
+class TestProtocols(unittest.TestCase):
+ layer = ConfigLayer
+
+ def setUp(self):
+ resources = ExitStack()
+ self.addCleanup(resources.close)
+ self.var_dir = resources.enter_context(TemporaryDirectory())
+ config.push('template config', """\
+ [paths.testing]
+ var_dir: {}
+ """.format(self.var_dir))
+ resources.callback(config.pop, 'template config')
+ # Put a demo template in the site directory.
+ path = os.path.join(self.var_dir, 'templates', 'site', 'en')
+ os.makedirs(path)
+ with open(os.path.join(path, 'demo.txt'), 'w') as fp:
+ print('Test content', end='', file=fp)
+ self._mlist = create_list('test@example.com')
+
+ def test_file(self):
+ with TemporaryDirectory() as tempdir:
+ path = os.path.join(tempdir, 'my-file')
+ with open(path, 'w', encoding='utf-8') as fp:
+ print('Some contents', end='', file=fp)
+ contents = protocols.get('file:///{}'.format(path))
+ self.assertEqual(contents, 'Some contents')
+
+ def test_file_binary(self):
+ with TemporaryDirectory() as tempdir:
+ path = os.path.join(tempdir, 'my-file')
+ with open(path, 'wb') as fp:
+ fp.write(b'xxx')
+ contents = protocols.get('file:///{}'.format(path), mode='rb')
+ self.assertEqual(contents, b'xxx')
+
+ def test_file_ascii(self):
+ with TemporaryDirectory() as tempdir:
+ path = os.path.join(tempdir, 'my-file')
+ with open(path, 'w', encoding='us-ascii') as fp:
+ print('Some contents', end='', file=fp)
+ contents = protocols.get('file:///{}'.format(path),
+ encoding='us-ascii')
+ self.assertEqual(contents, 'Some contents')
+
+ def test_mailman_internal_uris(self):
+ # mailman://demo.txt
+ content = protocols.get('mailman:///demo.txt')
+ self.assertEqual(content, 'Test content')
+
+ def test_mailman_internal_uris_twice(self):
+ # mailman:///demo.txt
+ content = protocols.get('mailman:///demo.txt')
+ self.assertEqual(content, 'Test content')
+ content = protocols.get('mailman:///demo.txt')
+ self.assertEqual(content, 'Test content')
+
+ def test_mailman_uri_with_language(self):
+ content = protocols.get('mailman:///en/demo.txt')
+ self.assertEqual(content, 'Test content')
+
+ def test_mailman_uri_with_english_fallback(self):
+ content = protocols.get('mailman:///it/demo.txt')
+ self.assertEqual(content, 'Test content')
+
+ def test_mailman_uri_with_list_name(self):
+ content = protocols.get('mailman:///test@example.com/demo.txt')
+ self.assertEqual(content, 'Test content')
+
+ def test_mailman_full_uri(self):
+ content = protocols.get('mailman:///test@example.com/en/demo.txt')
+ self.assertEqual(content, 'Test content')
+
+ def test_mailman_full_uri_with_english_fallback(self):
+ content = protocols.get('mailman:///test@example.com/it/demo.txt')
+ self.assertEqual(content, 'Test content')
+
+ def test_uri_not_found(self):
+ with self.assertRaises(URLError) as cm:
+ protocols.get('mailman:///missing.txt')
+ self.assertEqual(cm.exception.reason, 'No such file')
+
+ def test_shorter_url_error(self):
+ with self.assertRaises(URLError) as cm:
+ protocols.get('mailman:///')
+ self.assertEqual(cm.exception.reason, 'No template specified')
+
+ def test_short_url_error(self):
+ with self.assertRaises(URLError) as cm:
+ protocols.get('mailman://')
+ self.assertEqual(cm.exception.reason, 'No template specified')
+
+ def test_bad_language(self):
+ with self.assertRaises(URLError) as cm:
+ protocols.get('mailman:///xx/demo.txt')
+ self.assertEqual(cm.exception.reason, 'Bad language or list name')
+
+ def test_bad_mailing_list(self):
+ with self.assertRaises(URLError) as cm:
+ protocols.get('mailman:///missing@example.com/demo.txt')
+ self.assertEqual(cm.exception.reason, 'Bad language or list name')
+
+ def test_missing_mailing_list(self):
+ with self.assertRaises(URLError) as cm:
+ protocols.get('mailman:///missing@example.com/it/demo.txt')
+ self.assertEqual(cm.exception.reason, 'Missing list')
+
+ def test_no_such_language(self):
+ with self.assertRaises(URLError) as cm:
+ protocols.get('mailman:///test@example.com/xx/demo.txt')
+ self.assertEqual(cm.exception.reason, 'No such language')
+
+ def test_too_many_path_components(self):
+ with self.assertRaises(URLError) as cm:
+ protocols.get('mailman:///missing@example.com/en/foo/demo.txt')
+ self.assertEqual(cm.exception.reason, 'No such file')
+
+ def test_non_ascii(self):
+ # mailman://demo.txt with non-ascii content.
+ test_text = b'\xe4\xb8\xad'
+ path = os.path.join(self.var_dir, 'templates', 'site', 'it')
+ os.makedirs(path)
+ with open(os.path.join(path, 'demo.txt'), 'wb') as fp:
+ fp.write(test_text)
+ content = protocols.get('mailman:///it/demo.txt')
+ self.assertIsInstance(content, str)
+ self.assertEqual(content, test_text.decode('utf-8'))
+
+ def test_bad_file_keyword(self):
+ self.assertRaises(ValueError, protocols.get, 'file:///etc/passwd',
+ invalid_keyword='yes')
+
+ def test_bad_mailman_keyword(self):
+ self.assertRaises(ValueError, protocols.get, 'mailman:///demo.text',
+ invalid_keyword='yes')
+
+ def test_bad_protocol(self):
+ self.assertRaises(URLError, protocols.get, 'unknown:///demo.text')
diff --git a/src/mailman/utilities/tests/test_templates.py b/src/mailman/utilities/tests/test_templates.py
index fcb0ecc1c..4e05f13c6 100644
--- a/src/mailman/utilities/tests/test_templates.py
+++ b/src/mailman/utilities/tests/test_templates.py
@@ -26,7 +26,7 @@ from mailman.app.lifecycle import create_list
from mailman.config import config
from mailman.interfaces.languages import ILanguageManager
from mailman.testing.layers import ConfigLayer
-from mailman.utilities.i18n import TemplateNotFoundError, find, make, search
+from mailman.utilities.i18n import TemplateNotFoundError, find, search
from pkg_resources import resource_filename
from zope.component import getUtility
@@ -219,65 +219,3 @@ class TestFind(unittest.TestCase):
with self.assertRaises(TemplateNotFoundError) as cm:
find('missing.txt', self.mlist)
self.assertEqual(cm.exception.template_file, 'missing.txt')
-
-
-class TestMake(unittest.TestCase):
- """Test template interpolation."""
-
- layer = ConfigLayer
-
- def setUp(self):
- self.var_dir = tempfile.mkdtemp()
- self.addCleanup(shutil.rmtree, self.var_dir)
- config.push('template config', """\
- [paths.testing]
- var_dir: {}
- """.format(self.var_dir))
- self.addCleanup(config.pop, 'template config')
- # The following MUST happen AFTER the push() above since pushing a new
- # config also clears out the language manager.
- getUtility(ILanguageManager).add('xx', 'utf-8', 'Xlandia')
- self.mlist = create_list('test@example.com')
- self.mlist.preferred_language = 'xx'
- # Populate the template directories with a few fake templates.
- path = os.path.join(self.var_dir, 'templates', 'site', 'xx')
- os.makedirs(path)
- with open(os.path.join(path, 'nosub.txt'), 'w') as fp:
- print("""\
-This is a global template.
-It has no substitutions.
-It will be wrapped.
-""", file=fp)
- with open(os.path.join(path, 'subs.txt'), 'w') as fp:
- print("""\
-This is a $kind template.
-It has $howmany substitutions.
-It will be wrapped.
-""", file=fp)
- with open(os.path.join(path, 'nowrap.txt'), 'w') as fp:
- print("""\
-This is a $kind template.
-It has $howmany substitutions.
-It will not be wrapped.
-""", file=fp)
-
- def test_no_substitutions(self):
- self.assertEqual(make('nosub.txt', self.mlist), """\
-This is a global template. It has no substitutions. It will be
-wrapped.""")
-
- def test_substitutions(self):
- self.assertEqual(make('subs.txt', self.mlist,
- kind='very nice',
- howmany='a few'), """\
-This is a very nice template. It has a few substitutions. It will be
-wrapped.""")
-
- def test_substitutions_no_wrap(self):
- self.assertEqual(make('nowrap.txt', self.mlist, wrap=False,
- kind='very nice',
- howmany='a few'), """\
-This is a very nice template.
-It has a few substitutions.
-It will not be wrapped.
-""")