Source code for nefelibata.utils

import copy
import json
import logging
import re
import sys
import unicodedata
from contextlib import contextmanager
from pathlib import Path
from typing import Any
from typing import Dict
from typing import Iterator

import yaml
from bs4 import BeautifulSoup
from libgravatar import Gravatar
from nefelibata import config_filename
from rich.logging import RichHandler


[docs]def get_config(root: Path) -> Dict[str, Any]: """Return the configuration file for a weblog. """ with open(root / config_filename) as fp: config: Dict[str, Any] = yaml.full_load(fp) # add gravatar as the default profile picture if "profile_picture" not in config["author"]: config["author"]["profile_picture"] = Gravatar( config["author"]["email"], ).get_image() return config
[docs]def setup_logging(loglevel: str) -> None: """Setup basic logging """ level = getattr(logging, loglevel.upper(), None) if not isinstance(level, int): raise ValueError("Invalid log level: %s" % loglevel) logformat = "[%(asctime)s] %(levelname)s: %(name)s: %(message)s" logging.basicConfig( level=level, format=logformat, datefmt="[%X]", handlers=[RichHandler()], force=True, )
[docs]def find_directory(cwd: Path) -> Path: """Find root of blog, starting from `cwd`. The function will traverse up trying to find a configuration file. """ while not (cwd / config_filename).exists(): if cwd == cwd.parent: raise SystemExit("No configuration found!") cwd = cwd.parent return cwd
[docs]def strip_accents(text: str) -> str: text = unicodedata.normalize("NFD", text).encode("ascii", "ignore").decode("utf-8") return str(text)
[docs]def sanitize(directory: str) -> str: """Sanitize a post title into a directory name. """ directory = directory.lower().replace(" ", "_") directory = re.sub(r"[^\w]", "", directory) directory = strip_accents(directory) return directory
[docs]@contextmanager def json_storage(file_path: Path) -> Iterator[Dict[str, Any]]: """ Open a file and load it as JSON. Save back if modified. """ if file_path.exists(): with open(file_path) as fp: storage = json.load(fp) else: storage = {} original = copy.deepcopy(storage) try: yield storage finally: if storage != original: with open(file_path, "w") as fp: json.dump(storage, fp)
[docs]@contextmanager def modify_html(file_path: Path) -> Iterator[BeautifulSoup]: """ Parse an HTML file to BeautifulSoup. Save back if modified. """ with open(file_path) as fp: html = fp.read() soup = BeautifulSoup(html, "html.parser") original = str(soup) try: yield soup finally: html = str(soup) if html != original: with open(file_path, "w") as fp: fp.write(html)