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
|
self._set = True
|
||||||
|
|
||||||
|
def set_default(self, default_value):
|
||||||
|
"""Set option default value"""
|
||||||
|
self.default = default_value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def parser_action(self):
|
def parser_action(self):
|
||||||
"""Get action as accept by argparse.ArgumentParser"""
|
"""Get action as accept by argparse.ArgumentParser"""
|
||||||
|
@ -511,6 +515,16 @@ class ConfigSection:
|
||||||
assert self.defined(option), f"Option {option} unknown"
|
assert self.defined(option), f"Option {option} unknown"
|
||||||
return self.options[option].set(value)
|
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):
|
def add_options_to_parser(self, parser):
|
||||||
"""Add section to argparse.ArgumentParser"""
|
"""Add section to argparse.ArgumentParser"""
|
||||||
assert isinstance(parser, argparse.ArgumentParser)
|
assert isinstance(parser, argparse.ArgumentParser)
|
||||||
|
@ -660,6 +674,18 @@ class Config: # pylint: disable=too-many-instance-attributes
|
||||||
self._init_config_parser()
|
self._init_config_parser()
|
||||||
self.sections[section].set(option, value)
|
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):
|
def _init_config_parser(self, force=False):
|
||||||
"""Initialize ConfigParser object"""
|
"""Initialize ConfigParser object"""
|
||||||
if not self.config_parser or force:
|
if not self.config_parser or force:
|
||||||
|
|
|
@ -46,15 +46,18 @@ class EmailClient(
|
||||||
"encoding": "utf-8",
|
"encoding": "utf-8",
|
||||||
"catch_all_addr": None,
|
"catch_all_addr": None,
|
||||||
"just_try": False,
|
"just_try": False,
|
||||||
|
"templates_path": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
templates = {}
|
templates = {}
|
||||||
|
|
||||||
def __init__(self, templates=None, **kwargs):
|
def __init__(self, templates=None, initialize=False, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
assert templates is None or isinstance(templates, dict)
|
assert templates is None or isinstance(templates, dict)
|
||||||
self.templates = templates if templates else {}
|
self.templates = templates if templates else {}
|
||||||
|
if initialize:
|
||||||
|
self.initialize()
|
||||||
|
|
||||||
# pylint: disable=arguments-differ,arguments-renamed
|
# pylint: disable=arguments-differ,arguments-renamed
|
||||||
def configure(self, use_smtp=True, just_try=True, **kwargs):
|
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",
|
comment="Just-try mode: do not really send emails",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
section.add_option(
|
||||||
|
StringOption,
|
||||||
|
"templates_path",
|
||||||
|
comment="Path to templates directory",
|
||||||
|
)
|
||||||
|
|
||||||
return section
|
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(
|
def forge_message(
|
||||||
self,
|
self,
|
||||||
rcpt_to,
|
rcpt_to,
|
||||||
|
@ -179,7 +214,11 @@ class EmailClient(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if subject:
|
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)
|
msg["Date"] = email.utils.formatdate(None, True)
|
||||||
encoding = encoding if encoding else self._get_option("encoding")
|
encoding = encoding if encoding else self._get_option("encoding")
|
||||||
if template:
|
if template:
|
||||||
|
@ -189,7 +228,11 @@ class EmailClient(
|
||||||
assert self.templates[template].get(
|
assert self.templates[template].get(
|
||||||
"subject"
|
"subject"
|
||||||
), f"No subject defined in template {template}"
|
), 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
|
# Put HTML part in last one to prefered it
|
||||||
parts = []
|
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 datetime
|
||||||
import getpass
|
import getpass
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import sys
|
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
|
from mylib.scripts.helpers import add_email_opts, get_opts_parser, init_email_client, init_logging
|
||||||
|
|
||||||
log = logging.getLogger("mylib.scripts.email_test")
|
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
|
# Options parser
|
||||||
parser = get_opts_parser(just_try=True)
|
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")
|
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",
|
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(
|
test_opts.add_argument(
|
||||||
"-m",
|
"-m",
|
||||||
"--mako",
|
"--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:
|
if options.email_smtp_user and not options.email_smtp_password:
|
||||||
options.email_smtp_password = getpass.getpass("Please enter SMTP password: ")
|
options.email_smtp_password = getpass.getpass("Please enter SMTP password: ")
|
||||||
|
|
||||||
email_client = init_email_client(
|
email_client = init_email_client(options)
|
||||||
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>"
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
log.info("Send a test email to %s", options.test_to)
|
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()):
|
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 """
|
""" Test Email client using mylib.config.Config for configuration """
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from mako.template import Template as MakoTemplate
|
|
||||||
|
|
||||||
from mylib.config import Config
|
from mylib.config import Config
|
||||||
from mylib.email import EmailClient
|
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 = EmailClient(config=config)
|
||||||
email_client.configure()
|
email_client.configure()
|
||||||
|
config.set_default(
|
||||||
|
"email",
|
||||||
|
"templates_path",
|
||||||
|
os.path.join(os.path.dirname(os.path.realpath(__file__)), "email_templates"),
|
||||||
|
)
|
||||||
|
|
||||||
# Options parser
|
# Options parser
|
||||||
parser = config.get_arguments_parser(description=__doc__)
|
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",
|
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(
|
test_opts.add_argument(
|
||||||
"-m",
|
"-m",
|
||||||
"--mako",
|
"--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")
|
parser.error("You must specify test email recipient using -t/--to parameter")
|
||||||
sys.exit(1)
|
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)
|
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()):
|
if email_client.send(options.test_to, template="test", sent_date=datetime.datetime.now()):
|
||||||
logging.info("Test email sent")
|
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
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def add_email_opts(parser, config=None):
|
def add_email_opts(parser, config=None, **defaults):
|
||||||
"""Add email options"""
|
"""Add email options"""
|
||||||
email_opts = parser.add_argument_group("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_name=getpass.getuser(),
|
||||||
sender_email=f"{getpass.getuser()}@{socket.gethostname()}",
|
sender_email=f"{getpass.getuser()}@{socket.gethostname()}",
|
||||||
catch_all=False,
|
catch_all=False,
|
||||||
|
templates_path=None,
|
||||||
)
|
)
|
||||||
|
default_config.update(defaults)
|
||||||
|
|
||||||
email_opts.add_argument(
|
email_opts.add_argument(
|
||||||
"--smtp-host",
|
"--smtp-host",
|
||||||
|
@ -220,6 +222,18 @@ def add_email_opts(parser, config=None):
|
||||||
default=get_default_opt_value(config, default_config, "catch_all"),
|
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):
|
def init_email_client(options, **kwargs):
|
||||||
"""Initialize email client from calling script options"""
|
"""Initialize email client from calling script options"""
|
||||||
|
@ -239,6 +253,8 @@ def init_email_client(options, **kwargs):
|
||||||
catch_all_addr=options.email_catch_all,
|
catch_all_addr=options.email_catch_all,
|
||||||
just_try=options.just_try if hasattr(options, "just_try") else False,
|
just_try=options.just_try if hasattr(options, "just_try") else False,
|
||||||
encoding=options.email_encoding,
|
encoding=options.email_encoding,
|
||||||
|
templates_path=options.email_templates_path,
|
||||||
|
initialize=True,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue