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

 vial.py

View raw Download
text/x-script.python • 4.39 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
class Document:
21
def __init__(self, file_name):
22
self.file_name = file_name
23
self.encoding = "utf-8"
24
# If the file is text, read it.
25
self.front_matter = YAML()
26
try:
27
with open(file_name, "r", encoding=self.encoding) as f:
28
print("!" + f"Loading document {file_name}".center(80, "-") + "!")
29
30
# Parse front matter if available.
31
front_matter = ""
32
initial_line = f.readline()
33
if initial_line == "---\n":
34
print("!" + "Front matter found".center(80, "-") + "!")
35
line = ""
36
while line != "---\n":
37
line = f.readline()
38
if line != "---\n":
39
front_matter += line
40
print("!" + "Front matter loaded".center(80, "-") + "!")
41
print(front_matter)
42
43
if front_matter:
44
self.front_matter = self.front_matter.load(front_matter)
45
46
print("!" + "Reading content".center(80, "-") + "!")
47
48
self.content = initial_line + f.read()
49
50
print("!" + "Content loaded".center(80, "-") + "!")
51
print(self.content)
52
except UnicodeDecodeError:
53
self.encoding = None
54
with open(file_name, "rb") as f:
55
self.content = f.read()
56
57
58
class Index:
59
def __init__(self, directory):
60
self.directory = directory
61
# Temporarily move to the specified directory in order to read the files.
62
with in_directory(directory):
63
self.file_names = [i for i in os.listdir() if os.path.isfile(i)]
64
self.documents = [Document(i) for i in self.file_names]
65
self.__current_index = 0
66
67
def __iter__(self):
68
return self
69
70
def __next__(self):
71
if self.__current_index >= len(self.documents):
72
raise StopIteration
73
else:
74
self.__current_index += 1
75
return self.documents[self.__current_index - 1]
76
77
78
class Site:
79
def __init__(self, build_dir):
80
self.build_dir = build_dir
81
self.template_engine = jinja2.Environment(loader=jinja2.FileSystemLoader("templates"))
82
self.pages = {}
83
84
def add_page(self, location, page):
85
if location.endswith("/"):
86
location += "index.html"
87
location = location.lstrip("/") # interpret it as site root, not OS root
88
self.pages[location] = page
89
90
def add_from_index(self, index, location, template, static=False, **kwargs):
91
location = location.lstrip("/") # interpret it as site root, not OS root
92
if static:
93
for document in index:
94
self.pages[os.path.join(location, document.file_name)] = Static(self, document)
95
else:
96
for document in index:
97
self.pages[os.path.join(location, document.file_name)] = Page(self, template, os.path.join(index.directory, document), **kwargs)
98
99
def filter(self, name):
100
def decorator(func):
101
self.template_engine.filters[name] = func
102
return func
103
104
return decorator
105
106
def build(self):
107
# Clear the build directory if it exists.
108
if os.path.isdir(self.build_dir):
109
shutil.rmtree(self.build_dir)
110
for location, page in self.pages.items():
111
# Create the required directories.
112
os.makedirs(os.path.join(self.build_dir, os.path.dirname(location)), exist_ok=True)
113
if isinstance(page, str):
114
with open(os.path.join(self.build_dir, location), "w") as f:
115
f.write(page)
116
elif isinstance(page, bytes):
117
with open(os.path.join(self.build_dir, location), "wb") as f:
118
f.write(page)
119
else:
120
raise ValueError(f"{type(page)} cannot be used as a document")
121
122
123
class Page(str):
124
def __new__(cls, site, template, document, **kwargs):
125
return site.template_engine.get_template(template).render(document=document, **kwargs)
126
127
128
class Static(bytes):
129
def __new__(cls, site, document):
130
return document.content
131