help="Markdown extension to add to the list of extensions use",
)
+ parser_setup.add_argument(
+ "--send-message",
+ action="store_true",
+ help="Generate command(s) to send the message after processing",
+ )
+
parser_massage.add_argument(
"--debug-commands",
action="store_true",
return f"<multipart/{self.subtype}> children={len(self.children)}"
-def convert_markdown_to_html(maildraft, *, extensions=None):
- draftpath = pathlib.Path(maildraft)
+
+def convert_markdown_to_html(
+ origtext, draftpath, *, filewriter_fn=None, extensions=None
+):
+ mdwn = markdown.Markdown(extensions=extensions)
+
+ if not filewriter_fn:
+
+ def filewriter_fn(path, content, mode="w", **kwargs):
+ with open(path, mode, **kwargs) as out_f:
+ out_f.write(content)
+
+ filewriter_fn(draftpath, origtext, encoding="utf-8")
textpart = Part(
"text", "plain", draftpath, "Plain-text version", orig=True
)
- with open(draftpath, "r", encoding="utf-8") as textmarkdown:
- text = textmarkdown.read()
-
- mdwn = markdown.Markdown(extensions=extensions)
- html = mdwn.convert(text)
+ htmltext = mdwn.convert(origtext)
htmlpath = draftpath.with_suffix(".html")
+ filewriter_fn(
+ htmlpath, htmltext, encoding="utf-8", errors="xmlcharrefreplace"
+ )
htmlpart = Part("text", "html", htmlpath, "HTML version")
- with open(
- htmlpath, "w", encoding="utf-8", errors="xmlcharrefreplace"
- ) as texthtml:
- texthtml.write(html)
-
logopart = Part(
"image",
"png",
editor = f"{sys.argv[0]} massage --write-commands-to {temppath}"
if extensions:
editor = f'{editor} --extensions {",".join(extensions)}'
+ if debug_commands:
+ editor = f'{editor} --debug-commands'
cmds.cmd('set my_editor="$editor"')
cmds.cmd('set my_edit_headers="$edit_headers"')
cmds.flush()
extensions = extensions.split(",") if extensions else []
- tree = converter(maildraft, extensions=extensions)
+ with open(maildraft, "r") as draft_f:
+ tree = converter(
+ draft_f.read(), pathlib.Path(maildraft), extensions=extensions
+ )
mimetree = MIMETreeDFWalker(debug=debug_walk)
# We've hit a leaf-node, i.e. an alternative or a related part
# with actual content.
- # If the part is not an original part, i.e. doesn't already
- # exist, we must first add it.
- if not item.orig:
+ # Let's add the part
+ if item.orig:
+ # The original source already exists in the NeoMutt tree, but
+ # the underlying file may have been modified, so we need to
+ # update the encoding, but that's it:
+ cmds.push("<update-encoding>")
+ else:
+ # … whereas all other parts need to be added, and they're all
+ # considered to be temporary and inline:
cmds.push(f"<attach-file>{item.path}<enter>")
cmds.push("<toggle-unlink><toggle-disposition>")
- if item.cid:
- cmds.push(f"<edit-content-id>\\Ca\\Ck{item.cid}<enter>")
- # If the item (including the original) comes with a
- # description, then we might just as well update the NeoMutt
+ # If the item (including the original) comes with additional
+ # information, then we might just as well update the NeoMutt
# tree now:
- if item.desc:
- cmds.push(f"<edit-description>\\Ca\\Ck{item.desc}<enter>")
+ if item.cid:
+ cmds.push(f"<edit-content-id>\\Ca\\Ck{item.cid}<enter>")
elif isinstance(item, Multipart):
# This node has children, but we already visited them (see
elif item.subtype == "multilingual":
cmds.push("<group-multilingual>")
- # Again, if there is a description, we might just as well:
- if item.desc:
- cmds.push(f"<edit-description>\\Ca\\Ck{item.desc}<enter>")
-
else:
# We should never get here
assert not "is valid part"
+ # If the item has a description, we might just as well add it
+ if item.desc:
+ cmds.push(f"<edit-description>\\Ca\\Ck{item.desc}<enter>")
+
# Finally, if we're at non-root level, tag the new container,
# as it might itself be part of a container, to be processed
# one level up:
args = parse_cli_args()
if args.mode == "setup":
+ if args.send_message:
+ raise NotImplementedError()
+
do_setup(args.extensions, debug_commands=args.debug_commands)
elif args.mode == "massage":
@pytest.fixture
def basic_mime_tree(self):
return Multipart(
- "related",
+ "relative",
children=[
Multipart(
"alternative",
children=[
- Part("text", "plain", "part.txt", desc="Plain"),
+ Part(
+ "text",
+ "plain",
+ "part.txt",
+ desc="Plain",
+ orig=True,
+ ),
Part("text", "html", "part.html", desc="HTML"),
],
desc="Alternative",
assert items[2][1] == 1
assert items[3][0].subtype == "png"
assert items[3][1] == 1
- assert items[4][0].subtype == "related"
+ assert items[4][0].subtype == "relative"
assert items[4][1] == 0
def test_MIMETreeDFWalker_list_to_mixed(self, basic_mime_tree):
mimetree = MIMETreeDFWalker()
-
items = []
def visitor_fn(item, stack, debugprint):
assert lines[2].endswith(f'{const2},{const1}"')
assert lines[4].endswith(const1)
+ def test_do_massage_basic(self, const1, capsys):
+ def converter(maildraft, extensions):
+ return Part("text", "plain", "/dev/null", orig=True)
+
+ do_massage(maildraft=const1, cmd_f=sys.stdout, converter=converter)
+ captured = capsys.readouterr()
+ assert (
+ captured.out.strip()
+ == """\
+ set editor="$my_editor"
+ set edit_headers="$my_edit_headers"
+ unset my_editor
+ unset my_edit_headers
+ source 'rm -f pytest_internal_file|'
+ unset my_mdwn_postprocess_cmd_file
+ """.replace(
+ " ", ""
+ ).strip()
+ )
+
+ def test_do_massage_fulltree(self, const1, basic_mime_tree, capsys):
+ def converter(maildraft, extensions):
+ return basic_mime_tree
+
+ do_massage(maildraft=const1, cmd_f=sys.stdout, converter=converter)
+ captured = capsys.readouterr()
+ lines = captured.out.splitlines()[4:][::-1]
+ assert "Related" in lines.pop()
+ assert "group-related" in lines.pop()
+ assert "tag-entry" in lines.pop()
+ assert "Logo" in lines.pop()
+ assert "content-id" in lines.pop()
+ assert "toggle-unlink" in lines.pop()
+ assert "logo.png" in lines.pop()
+ assert "tag-entry" in lines.pop()
+ assert "Alternative" in lines.pop()
+ assert "group-alternatives" in lines.pop()
+ assert "tag-entry" in lines.pop()
+ assert "HTML" in lines.pop()
+ assert "toggle-unlink" in lines.pop()
+ assert "part.html" in lines.pop()
+ assert "tag-entry" in lines.pop()
+ assert "Plain" in lines.pop()
+
except ImportError:
pass