Compare commits
2 commits
5aa6a0cea4
...
e71fb28295
Author | SHA1 | Date | |
---|---|---|---|
|
e71fb28295 | ||
|
b5df95a2dd |
8 changed files with 122 additions and 47 deletions
|
@ -116,6 +116,10 @@ class BaseOption: # pylint: disable=too-many-instance-attributes
|
|||
|
||||
self._set = True
|
||||
|
||||
def set_default(self, default_value):
|
||||
"""Set option default value"""
|
||||
self.default = default_value
|
||||
|
||||
@property
|
||||
def parser_action(self):
|
||||
"""Get action as accept by argparse.ArgumentParser"""
|
||||
|
@ -511,6 +515,16 @@ class ConfigSection:
|
|||
assert self.defined(option), f"Option {option} unknown"
|
||||
return self.options[option].set(value)
|
||||
|
||||
def set_default(self, option, default_value):
|
||||
"""Set default option value"""
|
||||
assert self.defined(option), f"Option {option} unknown"
|
||||
return self.options[option].set_default(default_value)
|
||||
|
||||
def set_defaults(self, **default_values):
|
||||
"""Set default options value"""
|
||||
for option, default_value in default_values.items():
|
||||
self.set_default(option, default_value)
|
||||
|
||||
def add_options_to_parser(self, parser):
|
||||
"""Add section to argparse.ArgumentParser"""
|
||||
assert isinstance(parser, argparse.ArgumentParser)
|
||||
|
@ -660,6 +674,18 @@ class Config: # pylint: disable=too-many-instance-attributes
|
|||
self._init_config_parser()
|
||||
self.sections[section].set(option, value)
|
||||
|
||||
def set_default(self, section, option, default_value):
|
||||
"""Set default option value"""
|
||||
assert self.defined(section, option), f"Unknown option {section}.{option}"
|
||||
self._init_config_parser()
|
||||
self.sections[section].set_default(option, default_value)
|
||||
|
||||
def set_defaults(self, section, **default_values):
|
||||
"""Set default options value"""
|
||||
assert section in self.sections, f"Unknown section {section}"
|
||||
self._init_config_parser()
|
||||
self.sections[section].set_defaults(**default_values)
|
||||
|
||||
def _init_config_parser(self, force=False):
|
||||
"""Initialize ConfigParser object"""
|
||||
if not self.config_parser or force:
|
||||
|
|
|
@ -46,15 +46,18 @@ class EmailClient(
|
|||
"encoding": "utf-8",
|
||||
"catch_all_addr": None,
|
||||
"just_try": False,
|
||||
"templates_path": None,
|
||||
}
|
||||
|
||||
templates = {}
|
||||
|
||||
def __init__(self, templates=None, **kwargs):
|
||||
def __init__(self, templates=None, initialize=False, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
assert templates is None or isinstance(templates, dict)
|
||||
self.templates = templates if templates else {}
|
||||
if initialize:
|
||||
self.initialize()
|
||||
|
||||
# pylint: disable=arguments-differ,arguments-renamed
|
||||
def configure(self, use_smtp=True, just_try=True, **kwargs):
|
||||
|
@ -137,8 +140,40 @@ class EmailClient(
|
|||
comment="Just-try mode: do not really send emails",
|
||||
)
|
||||
|
||||
section.add_option(
|
||||
StringOption,
|
||||
"templates_path",
|
||||
comment="Path to templates directory",
|
||||
)
|
||||
|
||||
return section
|
||||
|
||||
def initialize(self, *args, **kwargs): # pylint: disable=arguments-differ
|
||||
"""Configuration initialized hook"""
|
||||
super().initialize(*args, **kwargs)
|
||||
self.load_templates_directory()
|
||||
|
||||
def load_templates_directory(self, templates_path=None):
|
||||
"""Load templates from specified directory"""
|
||||
if templates_path is None:
|
||||
templates_path = self._get_option("templates_path")
|
||||
if not templates_path:
|
||||
return
|
||||
log.debug("Load email templates from %s directory", templates_path)
|
||||
for filename in os.listdir(templates_path):
|
||||
filepath = os.path.join(templates_path, filename)
|
||||
if not os.path.isfile(filepath):
|
||||
continue
|
||||
template_name, template_type = os.path.splitext(filename)
|
||||
if template_type not in [".html", ".txt", ".subject"]:
|
||||
continue
|
||||
template_type = "text" if template_type == ".txt" else template_type[1:]
|
||||
if template_name not in self.templates:
|
||||
self.templates[template_name] = {}
|
||||
log.debug("Load email template %s %s from %s", template_name, template_type, filepath)
|
||||
with open(filepath, encoding="utf8") as file_desc:
|
||||
self.templates[template_name][template_type] = MakoTemplate(file_desc.read())
|
||||
|
||||
def forge_message(
|
||||
self,
|
||||
rcpt_to,
|
||||
|
@ -179,7 +214,11 @@ class EmailClient(
|
|||
)
|
||||
)
|
||||
if subject:
|
||||
msg["Subject"] = subject.format(**template_vars)
|
||||
msg["Subject"] = (
|
||||
subject.render(**template_vars)
|
||||
if isinstance(subject, MakoTemplate)
|
||||
else subject.format(**template_vars)
|
||||
)
|
||||
msg["Date"] = email.utils.formatdate(None, True)
|
||||
encoding = encoding if encoding else self._get_option("encoding")
|
||||
if template:
|
||||
|
@ -189,7 +228,11 @@ class EmailClient(
|
|||
assert self.templates[template].get(
|
||||
"subject"
|
||||
), f"No subject defined in template {template}"
|
||||
msg["Subject"] = self.templates[template]["subject"].format(**template_vars)
|
||||
msg["Subject"] = (
|
||||
self.templates[template]["subject"].render(**template_vars)
|
||||
if isinstance(self.templates[template]["subject"], MakoTemplate)
|
||||
else self.templates[template]["subject"].format(**template_vars)
|
||||
)
|
||||
|
||||
# Put HTML part in last one to prefered it
|
||||
parts = []
|
||||
|
|
1
mylib/scripts/email_templates/test.html
Normal file
1
mylib/scripts/email_templates/test.html
Normal file
|
@ -0,0 +1 @@
|
|||
<strong>Just a test email.</strong> <small>(sent at ${sent_date})</small>
|
1
mylib/scripts/email_templates/test.subject
Normal file
1
mylib/scripts/email_templates/test.subject
Normal file
|
@ -0,0 +1 @@
|
|||
Test email
|
1
mylib/scripts/email_templates/test.txt
Normal file
1
mylib/scripts/email_templates/test.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Just a test email sent at ${sent_date}.
|
|
@ -2,10 +2,9 @@
|
|||
import datetime
|
||||
import getpass
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from mako.template import Template as MakoTemplate
|
||||
|
||||
from mylib.scripts.helpers import add_email_opts, get_opts_parser, init_email_client, init_logging
|
||||
|
||||
log = logging.getLogger("mylib.scripts.email_test")
|
||||
|
@ -18,7 +17,10 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
|
|||
|
||||
# Options parser
|
||||
parser = get_opts_parser(just_try=True)
|
||||
add_email_opts(parser)
|
||||
add_email_opts(
|
||||
parser,
|
||||
templates_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "email_templates"),
|
||||
)
|
||||
|
||||
test_opts = parser.add_argument_group("Test email options")
|
||||
|
||||
|
@ -31,6 +33,15 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
|
|||
help="Test email recipient",
|
||||
)
|
||||
|
||||
test_opts.add_argument(
|
||||
"-T",
|
||||
"--template",
|
||||
action="store_true",
|
||||
dest="template",
|
||||
help="Template name to send (default: test)",
|
||||
default="test",
|
||||
)
|
||||
|
||||
test_opts.add_argument(
|
||||
"-m",
|
||||
"--mako",
|
||||
|
@ -51,26 +62,7 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
|
|||
if options.email_smtp_user and not options.email_smtp_password:
|
||||
options.email_smtp_password = getpass.getpass("Please enter SMTP password: ")
|
||||
|
||||
email_client = init_email_client(
|
||||
options,
|
||||
templates=dict(
|
||||
test=dict(
|
||||
subject="Test email",
|
||||
text=(
|
||||
"Just a test email sent at {sent_date}."
|
||||
if not options.test_mako
|
||||
else MakoTemplate("Just a test email sent at ${sent_date}.")
|
||||
),
|
||||
html=(
|
||||
"<strong>Just a test email.</strong> <small>(sent at {sent_date})</small>"
|
||||
if not options.test_mako
|
||||
else MakoTemplate(
|
||||
"<strong>Just a test email.</strong> <small>(sent at ${sent_date})</small>"
|
||||
)
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
email_client = init_email_client(options)
|
||||
|
||||
log.info("Send a test email to %s", options.test_to)
|
||||
if email_client.send(options.test_to, template="test", sent_date=datetime.datetime.now()):
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
""" Test Email client using mylib.config.Config for configuration """
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from mako.template import Template as MakoTemplate
|
||||
|
||||
from mylib.config import Config
|
||||
from mylib.email import EmailClient
|
||||
|
||||
|
@ -20,6 +19,11 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
|
|||
|
||||
email_client = EmailClient(config=config)
|
||||
email_client.configure()
|
||||
config.set_default(
|
||||
"email",
|
||||
"templates_path",
|
||||
os.path.join(os.path.dirname(os.path.realpath(__file__)), "email_templates"),
|
||||
)
|
||||
|
||||
# Options parser
|
||||
parser = config.get_arguments_parser(description=__doc__)
|
||||
|
@ -35,6 +39,15 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
|
|||
help="Test email recipient",
|
||||
)
|
||||
|
||||
test_opts.add_argument(
|
||||
"-T",
|
||||
"--template",
|
||||
action="store_true",
|
||||
dest="template",
|
||||
help="Template name to send (default: test)",
|
||||
default="test",
|
||||
)
|
||||
|
||||
test_opts.add_argument(
|
||||
"-m",
|
||||
"--mako",
|
||||
|
@ -49,24 +62,6 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
|
|||
parser.error("You must specify test email recipient using -t/--to parameter")
|
||||
sys.exit(1)
|
||||
|
||||
email_client.templates = dict(
|
||||
test=dict(
|
||||
subject="Test email",
|
||||
text=(
|
||||
"Just a test email sent at {sent_date}."
|
||||
if not options.test_mako
|
||||
else MakoTemplate("Just a test email sent at ${sent_date}.")
|
||||
),
|
||||
html=(
|
||||
"<strong>Just a test email.</strong> <small>(sent at {sent_date})</small>"
|
||||
if not options.test_mako
|
||||
else MakoTemplate(
|
||||
"<strong>Just a test email.</strong> <small>(sent at ${sent_date})</small>"
|
||||
)
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
logging.info("Send a test email to %s", options.test_to)
|
||||
if email_client.send(options.test_to, template="test", sent_date=datetime.datetime.now()):
|
||||
logging.info("Test email sent")
|
||||
|
|
|
@ -87,7 +87,7 @@ def get_opts_parser(desc=None, just_try=False, just_one=False, progress=False, c
|
|||
return parser
|
||||
|
||||
|
||||
def add_email_opts(parser, config=None):
|
||||
def add_email_opts(parser, config=None, **defaults):
|
||||
"""Add email options"""
|
||||
email_opts = parser.add_argument_group("Email options")
|
||||
|
||||
|
@ -103,7 +103,9 @@ def add_email_opts(parser, config=None):
|
|||
sender_name=getpass.getuser(),
|
||||
sender_email=f"{getpass.getuser()}@{socket.gethostname()}",
|
||||
catch_all=False,
|
||||
templates_path=None,
|
||||
)
|
||||
default_config.update(defaults)
|
||||
|
||||
email_opts.add_argument(
|
||||
"--smtp-host",
|
||||
|
@ -220,6 +222,18 @@ def add_email_opts(parser, config=None):
|
|||
default=get_default_opt_value(config, default_config, "catch_all"),
|
||||
)
|
||||
|
||||
email_opts.add_argument(
|
||||
"--templates-path",
|
||||
action="store",
|
||||
type=str,
|
||||
dest="email_templates_path",
|
||||
help=(
|
||||
"Load templates from specify directory "
|
||||
f'(default: {get_default_opt_value(config, default_config, "templates_path")})'
|
||||
),
|
||||
default=get_default_opt_value(config, default_config, "templates_path"),
|
||||
)
|
||||
|
||||
|
||||
def init_email_client(options, **kwargs):
|
||||
"""Initialize email client from calling script options"""
|
||||
|
@ -239,6 +253,8 @@ def init_email_client(options, **kwargs):
|
|||
catch_all_addr=options.email_catch_all,
|
||||
just_try=options.just_try if hasattr(options, "just_try") else False,
|
||||
encoding=options.email_encoding,
|
||||
templates_path=options.email_templates_path,
|
||||
initialize=True,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in a new issue