By using this site, you agree to have cookies stored on your device, strictly for functional purposes, such as storing your session and preferences.

Dismiss

 __init__.py

View raw Download
text/x-script.python • 5.29 kiB
Python script, ASCII text executable
        
            
1
#!/usr/bin/env python3
2
3
import os
4
import jinja2
5
from ruamel.yaml import YAML
6
import shutil
7
import contextlib
8
9
10
@contextlib.contextmanager
11
def in_directory(directory):
12
cwd = os.getcwd()
13
os.chdir(directory)
14
try:
15
yield
16
finally:
17
os.chdir(cwd)
18
19
20
def delete_directory_contents(directory):
21
for root, dirs, files in os.walk(directory):
22
for file in files:
23
os.remove(os.path.join(root, file))
24
for dir in dirs:
25
shutil.rmtree(os.path.join(root, dir))
26
27
28
class Document:
29
def __init__(self, file_name):
30
self.file_name = file_name
31
self.encoding = "utf-8"
32
# If the file is text, read it.
33
self.front_matter = YAML()
34
self.content = ""
35
try:
36
with open(file_name, "r", encoding=self.encoding) as f:
37
print("!" + f"Loading document {file_name}".center(80, "-") + "!")
38
39
# Parse front matter if available.
40
front_matter = ""
41
initial_line = f.readline()
42
if initial_line == "---\n":
43
print("!" + "Front matter found".center(80, "-") + "!")
44
line = ""
45
while line != "---\n":
46
line = f.readline()
47
if line != "---\n":
48
front_matter += line
49
print("!" + "Front matter loaded".center(80, "-") + "!")
50
print(front_matter)
51
52
if front_matter:
53
self.front_matter = self.front_matter.load(front_matter)
54
else: # put it back
55
self.content = initial_line
56
57
print("!" + "Reading content".center(80, "-") + "!")
58
59
self.content += f.read()
60
61
print("!" + "Content loaded".center(80, "-") + "!")
62
print(self.content[:256] + "..." if len(self.content) > 256 else self.content)
63
except UnicodeDecodeError:
64
print("!" + "Text decoding failed, assuming binary.".center(80, "-") + "!")
65
self.encoding = None
66
with open(file_name, "rb") as f:
67
self.content = f.read()
68
print("!" + "Binary content loaded".center(80, "-") + "!")
69
70
71
class Index:
72
def __init__(self, directory, recursive=False, url_transform=lambda x: x):
73
self.directory = directory
74
# Temporarily move to the specified directory in order to read the files.
75
with in_directory(directory):
76
if recursive:
77
self.file_names = [os.path.join(dir_path, f) for dir_path, dir_name, filenames in os.walk(".") for f in filenames]
78
else:
79
self.file_names = [i for i in os.listdir() if os.path.isfile(i)]
80
self.documents = [Document(url_transform(i)) for i in self.file_names]
81
self.__current_index = 0
82
83
def __iter__(self):
84
return self
85
86
def __next__(self):
87
if self.__current_index >= len(self.documents):
88
raise StopIteration
89
else:
90
self.__current_index += 1
91
return self.documents[self.__current_index - 1]
92
93
94
class Site:
95
def __init__(self, build_dir, template_dir="templates"):
96
self.build_dir = build_dir
97
self.template_engine = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir))
98
self.pages = {}
99
self.context = {}
100
101
def add_page(self, location, page):
102
if location.endswith("/"):
103
location += "index.html"
104
location = location.lstrip("/") # interpret it as site root, not OS root
105
self.pages[location] = page
106
107
def add_from_index(self, index, location, template, static=False, **kwargs):
108
location = location.lstrip("/") # interpret it as site root, not OS root
109
kwargs = {**self.context, **kwargs}
110
if static:
111
for document in index:
112
self.pages[os.path.join(location, document.file_name)] = Static(self, document)
113
else:
114
for document in index:
115
self.pages[os.path.join(location, document.file_name)] = Page(self, template, document, **kwargs)
116
117
def filter(self, name):
118
def decorator(func):
119
self.template_engine.filters[name] = func
120
return func
121
122
return decorator
123
124
def build(self):
125
# Clear the build directory if it exists.
126
if os.path.isdir(self.build_dir):
127
delete_directory_contents(self.build_dir)
128
for location, page in self.pages.items():
129
# Create the required directories.
130
os.makedirs(os.path.join(self.build_dir, os.path.dirname(location)), exist_ok=True)
131
if isinstance(page, str):
132
with open(os.path.join(self.build_dir, location), "w") as f:
133
f.write(page)
134
elif isinstance(page, bytes):
135
with open(os.path.join(self.build_dir, location), "wb") as f:
136
f.write(page)
137
else:
138
raise ValueError(f"{type(page)} cannot be used as a document")
139
140
141
class Page(str):
142
def __new__(cls, site, template, document=None, **kwargs):
143
kwargs = {**site.context, **kwargs}
144
return site.template_engine.get_template(template).render(document=document, **kwargs)
145
146
147
class Static(bytes):
148
def __new__(cls, site, document):
149
return document.content
150