Skip to content

Commit

Permalink
Gave command --section and --no-section options
Browse files Browse the repository at this point in the history
  • Loading branch information
jwodder committed Mar 14, 2021
1 parent 4729fa4 commit f2582da
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ v0.2.0 (in development)
-----------------------
- Require the `port` field of `SMTPSender` to be non-negative
- Mark `Sender` as `runtime_checkable` and export it
- Gave the `outgoing` command `--section` and `--no-section` options

v0.1.0 (2021-03-06)
-------------------
Expand Down
3 changes: 3 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Changelog
v0.2.0 (in development)
-----------------------
- Require the ``port`` field of ``SMTPSender`` to be non-negative
- Mark `Sender` as ``runtime_checkable`` and export it
- Gave the :command:`outgoing` command ``--section`` and ``--no-section``
options

v0.1.0 (2021-03-06)
-------------------
Expand Down
26 changes: 26 additions & 0 deletions docs/command.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. index:: outgoing (command)

Command-Line Program
====================

Expand All @@ -13,3 +15,27 @@ file (or specify another config file with the ``--config`` option). Multiple
files can be passed to the command at once to send multiple e-mails. If no
files are specified on the command line, the command reads an e-mail from
standard input.

Options
-------

.. program:: outgoing

.. option:: -c <file>, --config <file>

Specify a :ref:`configuration file <configfile>` to use instead of the
default configuration file

.. option:: -s <key>, --section <key>

.. versionadded:: 0.2.0

Read the configuration from the given table or key in the configuration
file; defaults to "``outgoing``"

.. option:: --no-section

.. versionadded:: 0.2.0

Read the configuration fields from the top level of the configuration file
instead of expecting them to all be contained below a certain table/key
2 changes: 2 additions & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Configuration
=============

.. _configfile:

The Configuration File
----------------------

Expand Down
41 changes: 36 additions & 5 deletions src/outgoing/__main__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
from email import message_from_binary_file, policy
from email.message import EmailMessage
from typing import IO, List, Optional
from typing import Any, IO, List, Optional
import click
from . import __version__, from_config_file, get_default_configpath
from . import (
DEFAULT_CONFIG_SECTION,
__version__,
from_config_file,
get_default_configpath,
)
from .errors import Error

NO_SECTION = object()


@click.command()
@click.version_option(
Expand All @@ -21,19 +28,43 @@
help="Specify the outgoing configuration file to use",
show_default=True,
)
@click.option(
"-s",
"--section",
help=(
"Read configuration from the given key/section of the config file"
f" [default: {DEFAULT_CONFIG_SECTION}]"
),
metavar="KEY",
)
@click.option(
"--no-section",
"section",
flag_value=NO_SECTION,
help="Read configuration from the root of the config file",
)
@click.argument("message", type=click.File("rb"), nargs=-1)
@click.pass_context
def main(ctx: click.Context, message: List[IO[bytes]], config: Optional[str]) -> None:
def main(
ctx: click.Context, message: List[IO[bytes]], config: Optional[str], section: Any
) -> None:
"""
Common interface for different e-mail methods.
Visit <https://github.com/jwodder/outgoing> for more information.
"""

sectname: Optional[str]
if section is NO_SECTION:
sectname = None
elif section is None:
sectname = DEFAULT_CONFIG_SECTION
else:
assert isinstance(section, str)
sectname = section
if not message:
message = [click.get_binary_stream("stdin")]
try:
with from_config_file(config, fallback=False) as sender:
with from_config_file(config, section=sectname, fallback=False) as sender:
for fp in message:
with fp:
msg = message_from_binary_file(fp, policy=policy.default)
Expand Down
48 changes: 48 additions & 0 deletions test/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,51 @@ def test_main_args(
sent2 = instance.send.call_args_list[1][0][0]
assert isinstance(sent2, EmailMessage)
assert email2dict(sent2) == email2dict(test_email2)


def test_main_custom_section(mocker: MockerFixture, test_email1: EmailMessage) -> None:
m = mocker.patch("outgoing.senders.null.NullSender", autospec=True)
runner = CliRunner()
with runner.isolated_filesystem():
Path("cfg.toml").write_text(
"[outgoing]\n" 'method = "command"\n' "\n" "[test]\n" 'method = "null"\n'
)
r = runner.invoke(
main,
["--config", "cfg.toml", "--section", "test"],
input=bytes(test_email1),
standalone_mode=False,
)
assert r.exit_code == 0, show_result(r)
assert r.output == ""
m.assert_called_once_with(method="null", configpath=Path("cfg.toml"))
instance = m.return_value.__enter__.return_value
assert instance.send.call_count == 1
sent = instance.send.call_args[0][0]
assert isinstance(sent, EmailMessage)
assert email2dict(sent) == email2dict(test_email1)


def test_main_no_section(mocker: MockerFixture, test_email1: EmailMessage) -> None:
m = mocker.patch("outgoing.senders.null.NullSender", autospec=True)
runner = CliRunner()
with runner.isolated_filesystem():
Path("cfg.toml").write_text(
'method = "null"\n\n[outgoing]\nmethod = "command"\n'
)
r = runner.invoke(
main,
["--config", "cfg.toml", "--no-section"],
input=bytes(test_email1),
standalone_mode=False,
)
assert r.exit_code == 0, show_result(r)
assert r.output == ""
m.assert_called_once_with(
method="null", configpath=Path("cfg.toml"), outgoing={"method": "command"}
)
instance = m.return_value.__enter__.return_value
assert instance.send.call_count == 1
sent = instance.send.call_args[0][0]
assert isinstance(sent, EmailMessage)
assert email2dict(sent) == email2dict(test_email1)

0 comments on commit f2582da

Please sign in to comment.