boop.py 4.26 KB
Newer Older
1
2
3
#!/bin/env python3

import os
4
import sys
5
import shutil
6
import distutils.dir_util
7
import locale
8

9
10
import markdown

11
12
13
import boopsy


14
locale.setlocale(locale.LC_ALL, "")
15

16

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class ProgramError(Exception):
    """Exception raised during the program execution.

    Examples:

    >>> try:
    ...     raise ProgramError("Oops")
    ... except ProgramError as e:
    ...     print(e.args[0])
    Oops
    """

    pass


32
class Article:
33
    def __init__(self, slug, article_content, configuration={}):
34
35
        # We convert the file's content into HTML
        md = markdown.Markdown(extensions=["meta", "fenced_code", "attr_list"])
36
        content = md.convert(article_content)
37
38
39
40

        # We get the local variables from the Markdown file (metadata)
        # which will be accessible in the article template
        self.meta = {}
41
42
        self.meta.update(configuration)
        self.meta["ARTICLE_SLUG"] = slug
43
44
45
46
47
48
49
        for key, values in md.Meta.items():
            article_key = f"ARTICLE_{key.upper()}"
            # The Markdown's meta extension extract all the values in arrays, even
            # if there is only one value. Because there is no iteration system in
            # the template system, for the moment we just get the first value of
            # the array.
            self.meta[article_key] = values[0]
50
        self.meta["ARTICLE_CONTENT"] = content
51
52
53
54
55
56

        # And we initialize a template for the article
        article_template_filepath = os.path.join("theme", "article.html")
        self.template = boopsy.Template(article_template_filepath)

    def render(self):
57
58
59
60
61
62
63
64
65
66
        return self.template.render(self.meta)

    def slug(self):
        return self.meta["ARTICLE_SLUG"]

    def title(self):
        return self.meta.get("ARTICLE_TITLE", "An article")

    def content(self):
        return self.meta["ARTICLE_CONTENT"]
67
68


69
70
71
72
73
74
75
76
77
78
79
80
def dir_tree(path):
    """List all the tree directory under the given path.
    """
    for filename in os.listdir(path):
        full_path = os.path.join(path, filename)
        if os.path.isdir(full_path):
            for sub_filename in dir_tree(full_path):
                yield os.path.join(filename, sub_filename)
        else:
            yield filename


81
def main():
82
    # Make sure ./site folder is empty
berumuron's avatar
berumuron committed
83
    output_path = os.path.join(os.curdir, "site")
84
    if os.path.isdir(output_path):
85
        # it should be a directory...
86
        shutil.rmtree(output_path)
87
88
89
    elif os.path.exists(output_path):
        # ... but it also can be a simple file if user created it manually!
        os.remove(output_path)
90
    os.mkdir(output_path)
91
92
93
94
95
96
97
98

    # Check that index file exists and copy it under the ./site folder
    index_filepath = os.path.join(os.curdir, "index.html")
    if not os.path.exists(index_filepath):
        raise ProgramError("./index.html file does not exist")
    index_output_filepath = os.path.join(output_path, "index.html")
    shutil.copyfile(index_filepath, index_output_filepath)

99
100
101
102
103
104
    # Check that a ./theme/static folder exists and copy its content under the
    # ./site folder
    static_dirpath = os.path.join(os.curdir, "theme", "static")
    if os.path.isdir(static_dirpath):
        distutils.dir_util.copy_tree(static_dirpath, output_path)

105
106
107
    # Check that articles directory exists on the filesystem
    articles_path = os.path.join(os.curdir, "articles")
    if not os.path.isdir(articles_path):
108
109
        # If not, just stop here
        return
110

111
112
    # List files from ./articles
    articles = []
113
    for filename in dir_tree(articles_path):
114
        slug, ext = os.path.splitext(filename)
115
116
        if ext != ".md":
            # Don't consider files which are not Markdown
117
            continue
118

119
        # We read the article file and initialize an Article from it
120
        article_filepath = os.path.join(articles_path, filename)
121
        with open(article_filepath, "r") as article_file:
122
123
            article = Article(slug, article_file.read())
            articles.append(article)
124

125
126
    # And write them in the ./site folder
    for article in articles:
127
        output_filepath = os.path.join(output_path, f"{article.slug()}.html")
128
        with open(output_filepath, "w") as output_file:
129
            output_file.write(article.render())
130
131
132


if __name__ == "__main__":
133
134
135
136
137
    try:
        main()
        print("Boop!")
    except ProgramError as e:
        print(e.args[0], file=sys.stderr)