sigma is a static site generator written in python
-====================
+==================================================
data.toml:
+----------
It is used to define variables for each file. Variables in the root are
-global and are inherited by their children. If a child has a variable of
-the same name, then the child's variable is used instead.
---------------------
+global and are inherited by their children. If a child has a variable
+of the same name, then the child's variable is used instead.
+
+-----------------------------------------------------------------------
Keywords:
-_PAGE_ROOT: specifies the default page root
-_TEMPLATES: specifies the template directory
-_OUT: designates the directory where the output files go
+---------
+- `_PAGE_ROOT` specifies the default page root
+- `_TEMPLATES` specifies the template directory
+- `_OUT` designates the directory where the output files go
+- `_DATE_FORMAT` designates the date format to use when using `_date`
+
++ `self` the file which invoked the template
++ `_extend` the template which the file should invoke
++ `_value` value of a file
+
+* `_name` defaults to file name without extension
+* `_date` defaults to the mtime of the file, in case of a directory,
+ the latest mtime file within the directory is used.
-self: the file which invoked the template
-_extend: the template which the file should invoke
-_value: value of a file
+-----------------------------------------------------------------------
-_name: defaults to file name without extension
-_date: defaults to the mtime of the file
---------------------
+Functionality:
+--------------
+- Ability to use directories as page outputs.
+ ```
+ pages/
+ ├─ index.html
+ ├─ textfile.txt
+ ├─ posts/
+ │ ├─ example.md
+ │ ├─ other.md
+ │ ├─ test.md
+ │ ├─ python-code.html/
+ │ │ ├─ _value.md
+ │ │ ├─ _code.py
+ ├─ about.html/
+ │ ├─ _value.md
+ │ ├─ _title.txt
+ │ ├─ _article.md
+ │ ├─ _test.py
+ ```
+ Where the file names inside the directory become the variables of the
+ file. Python files are not run here, as they will often be used to
+ write code which can then be imported into the _value variable. Files
+ that are meant to be ran should go into the templates directory.
regex_extend = re.compile(r"^{%\s?extend (\"|')(.+)\1\s?%}$")
regex_variable = re.compile(r"{{\s?(\S+)\s?}}")
+regex_variable_no_interpret = re.compile(r"{!\s?(\S+)\s?!}")
regex_execute = re.compile(r"{%\s?file (\"|')(.+)\1\s?%}")
def md_to_html(md: str) -> str:
- return markdown.markdown(md, extensions=["tables"])
+ return markdown.markdown(md, extensions=["extra", "sane_lists"])
+
+
+def get_max_mtime(dir: str) -> float:
+ mtime = 0
+ for f in os.walk(dir):
+ mtime = max(
+ map(
+ os.path.getmtime,
+ map(lambda file: os.path.join(f[0], file), f[2]),
+ )
+ )
+ return mtime
def initialize_values(data: dict):
for dir in os.walk(data["_PAGE_ROOT"]):
+ _, ext = os.path.splitext(dir[0])
+ if ext:
+ rel_path = os.path.relpath(dir[0], data["_PAGE_ROOT"])
+ mtime = get_max_mtime(dir[0])
+ rel_path_no_type, _ = os.path.splitext(rel_path)
+ namespace = split_path(rel_path_no_type)
+ update_value(data, namespace, "_name", namespace[-1], False)
+ update_value(
+ data,
+ namespace,
+ "_date",
+ time.strftime(data["_DATE_FORMAT"], time.localtime(mtime)),
+ False,
+ )
+ continue
for file in dir[2]:
path = os.path.join(dir[0], file)
rel_path = os.path.relpath(path, data["_PAGE_ROOT"])
)
-def get_value(data: dict, namespace: tuple, key: str):
+def get_value_from_file(
+ data: dict, namespace: tuple, key: str, interpret_ok: bool
+) -> str | None:
+ path = os.path.join(data["_PAGE_ROOT"], *namespace[:-1])
+ for dir in os.listdir(path):
+ dir_name, dir_ext = os.path.splitext(dir)
+ if namespace[-1] == dir_name:
+ break
+ else:
+ return
+
+ if not os.path.isdir(os.path.join(path, dir_name + dir_ext)):
+ return
+
+ for file in os.listdir(os.path.join(path, dir_name + dir_ext)):
+ file_name, file_ext = os.path.splitext(file)
+ if key == file_name:
+ with open(os.path.join(path, dir_name + dir_ext, file), "r") as file_in:
+ if interpret_ok:
+ _value = interpret(file_in.read(), data, namespace)
+ else:
+ _value = file_in.read()
+ if file_ext == ".md":
+ _value = md_to_html(_value)
+ return _value
+
+
+def get_value(data: dict, namespace: tuple, key: str, interpret_ok=True):
+ if key != "_value" and (
+ value := get_value_from_file(data, namespace, key, interpret_ok)
+ ):
+ return value
value = data.get(key)
for namespace_item in namespace:
data = data.get(namespace_item, data)
return split_path(rest) + (tail,)
+def interpret_no_recursion(file_value: str, data: dict, namespace: tuple) -> str:
+ start_pos = 0
+ while variable := regex_variable_no_interpret.search(file_value, start_pos):
+ varspace = variable.group(1).split(".")
+ try:
+ varspace.remove("self")
+ except ValueError:
+ varspace = tuple(varspace)
+ else:
+ varspace = namespace + tuple(varspace)
+ repl_value = str(get_value(data, varspace[:-1], varspace[-1], False))
+ start_pos = variable.start() + len(repl_value)
+ regex_variable_no_interpret.search(file_value, start_pos)
+ file_value = file_value.replace(
+ variable.group(0),
+ str(get_value(data, varspace[:-1], varspace[-1], False)),
+ 1,
+ )
+ return file_value
+
+
def interpret(file_value: str, data: dict, namespace: tuple) -> str:
while regex_execute.search(file_value) or regex_variable.search(file_value):
while file_to_run := regex_execute.search(file_value):
file_value = regex_variable.sub(
str(get_value(data, varspace[:-1], varspace[-1])), file_value, 1
)
+
return file_value
+def dir_to_file(file: str) -> dict[str, str]:
+ if not os.path.isdir(file):
+ return {"_value": file}
+ files = {}
+ for f in os.listdir(file):
+ files[os.path.splitext(f)[0]] = os.path.join(file, f)
+ return files
+
+
def generate_output(file: str, data: dict, namespace: tuple) -> str:
- _, file_type = os.path.splitext(file)
- with open(file, "r") as in_file:
+ files = dir_to_file(file)
+ _, _value_file_type = os.path.splitext(files["_value"])
+ with open(files["_value"], "r") as in_file:
_value = in_file.read()
if result := regex_extend.search(_value.splitlines()[0]):
update_value(data, namespace, "_extend", result.group(2))
_value = _value.removeprefix(result.group(0) + "\n")
_value = interpret(_value, data, namespace)
- if file_type == ".md":
+ if _value_file_type == ".md":
_value = md_to_html(_value)
update_value(data, namespace, "_value", _value)
if _extend := get_value(data, namespace, "_extend"):
update_value(data, namespace, "_extend", "")
generate_output(os.path.join(data["_TEMPLATES"], _extend), data, namespace)
+
+ update_value(
+ data,
+ namespace,
+ "_value",
+ interpret_no_recursion(
+ str(get_value(data, namespace, "_value")), data, namespace
+ ),
+ )
+
return str(get_value(data, namespace, "_value"))
if not args.pages:
pages = []
for dir in os.walk(data["_PAGE_ROOT"]):
+ _, ext = os.path.splitext(dir[0])
+ if ext:
+ pages.append(dir[0])
+ continue
for file in dir[2]:
pages.append(os.path.join(dir[0], file))
else: