#!/usr/bin/env python3 import sys import re from markdown import markdown import strictyaml from ipo import read, write, map, dictmap, starstarmap, join, sort, ipo md_to_html = join("\n") | ipo(markdown) yaml_to_dict = join("\n") | ipo(strictyaml.load) | ipo(lambda x: x.data) @ipo def parted(data, regex): """ Two parts of data, delimited by the first element on which regex fullmatches. If a delimiter is found, the second part is an iterator over the remaining elements. Else it is None. """ first = [] it = iter(data) try: while True: item = next(it) if re.fullmatch(regex, item): break first.append(item) except StopIteration: return (first, None) return (first, it) LIST_ITEM_TEMPLATE = """
  • {title}
    {summary}
  • """ def blog_page_template(): with open("templates/blog.html") as fh: return fh.read() # FIXME what a mess def blog_page(file, read_body=True): """ Keep the file open if you want to be able to read the body. Good: >>> with open(…) as file: >>> metadata, body = blog_page(file) >>> [smthng(line) for line in body] >>> print("\n".join(hello)) Bad: >>> with open(…) as file: >>> metadata, body = blog_page(file) >>> hello = (smthng(line) for line in body) # Lazy generator, the body wasn't read from file yet >>> print("\n".join(hello)) """ if isinstance(file, str): assert not read_body, "Can't read body when giving a filename, need a file for that." filename = file file = open(filename) else: filename = None try: metadata_yaml, body_md = read(file) | parted("---+") metadata = { **(metadata_yaml | yaml_to_dict), } if not read_body: metadata["path"] = re.sub(r".md$", "", filename) body = ( body_md | md_to_html(extensions=["abbr", "toc", "smarty", "fenced_code", "codehilite"]) if read_body else None ) return (metadata, body) finally: if filename: file.close() def safe_metadata(metadata): return { k: ( v if k in ("published", "path") else markdown(v, extensions=["smarty"]).replace("

    ", "").replace("

    ", "") ) for k, v in metadata.items() } if sys.argv[1] == "--index": blog_post_list = ( sys.argv[4:] | map(lambda filename: { **blog_page(filename, read_body=False)[0], }) | sort(key=lambda x: x["published"]) | map(safe_metadata) | map(lambda metadata: LIST_ITEM_TEMPLATE.format(**metadata)) | join("") ) with open(sys.argv[2]) as file_in, open(sys.argv[3], "w") as file_out: file_in.read().format(blog_posts=blog_post_list) | write(file=file_out) else: with open(sys.argv[1]) as file_in, open(sys.argv[2], "w") as file_out: metadata, body = blog_page(file_in) blog_page_template().format(**safe_metadata(metadata), body=body) | write(file=file_out)