refactor: organize code in context & extensions
This commit is contained in:
parent
8591ae53fa
commit
2729af8e39
|
|
@ -1,3 +1,4 @@
|
|||
**/__pycache__
|
||||
.coverage
|
||||
jean_website.egg-info
|
||||
tests/**/*.mo
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from pathlib import Path
|
|||
|
||||
from click import group
|
||||
|
||||
from jwebsite.site import Site
|
||||
from jwebsite.context import Context
|
||||
|
||||
|
||||
@group
|
||||
|
|
@ -13,4 +13,4 @@ def main() -> None: ...
|
|||
def build() -> None:
|
||||
cwd = Path.cwd()
|
||||
with open(cwd / "site.py", encoding="utf-8") as site_config:
|
||||
exec(site_config.read(), {"site": Site(cwd)})
|
||||
exec(site_config.read(), {"site": Context(cwd)})
|
||||
|
|
|
|||
|
|
@ -1,114 +1,36 @@
|
|||
from functools import cache
|
||||
from pathlib import Path
|
||||
from typing import Any, Iterable, Iterator
|
||||
|
||||
from markdown import Markdown
|
||||
from yaml import Loader, load
|
||||
from typing import Any, Iterator
|
||||
|
||||
|
||||
class Content:
|
||||
current_language: str | None = None
|
||||
|
||||
def __init__(self, path: Path) -> None:
|
||||
def __init__(self, path: Path, data: Any, language: str | None = None) -> None:
|
||||
self.__path = path
|
||||
self.__data = data
|
||||
self.__language = language
|
||||
|
||||
@property
|
||||
def path(self) -> Path:
|
||||
return self.__path
|
||||
|
||||
@property
|
||||
def language(self) -> str | None:
|
||||
return self.__language
|
||||
|
||||
class ContentDirectory(Content):
|
||||
def load(self, subpath: str | Path) -> Content:
|
||||
subpath = Path(subpath)
|
||||
current: Content = self
|
||||
for part in subpath.parts:
|
||||
if not isinstance(current, ContentDirectory):
|
||||
raise NotADirectoryError(self.path)
|
||||
|
||||
current = current.__load_children(part)
|
||||
|
||||
return current
|
||||
|
||||
def glob(self, pattern: str) -> Iterable[Content]:
|
||||
for item in self.path.glob(pattern):
|
||||
yield self.load(str(item.relative_to(self.path)))
|
||||
|
||||
def __load_children(self, name: str) -> Content:
|
||||
child_path = self.__get_localized_path(self.path / name)
|
||||
return self.__load_path(child_path)
|
||||
|
||||
@cache # noqa: B019
|
||||
def __load_path(self, child_path: Path) -> Content:
|
||||
if not child_path.exists():
|
||||
raise FileNotFoundError(child_path)
|
||||
|
||||
if child_path.is_dir():
|
||||
return ContentDirectory(child_path)
|
||||
|
||||
if child_path.is_file():
|
||||
if child_path.suffix in [".yml", ".yaml", ".json"]:
|
||||
return DataFile(child_path)
|
||||
if child_path.suffix == ".md":
|
||||
return MarkdownFile(child_path)
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@staticmethod
|
||||
def __get_localized_path(path: Path) -> Path:
|
||||
if Content.current_language is None:
|
||||
return path
|
||||
localized_path = path.with_name(f"{path.stem}-{Content.current_language}{path.suffix}")
|
||||
print(localized_path)
|
||||
if not localized_path.exists():
|
||||
return path
|
||||
|
||||
return localized_path
|
||||
|
||||
|
||||
class DataField:
|
||||
def __init__(self, file_path: Path, value: Any) -> None:
|
||||
self.__file_path = file_path
|
||||
self.__value = value
|
||||
|
||||
def as_path(self) -> Path:
|
||||
return self.__file_path.parent / str(self.__value)
|
||||
@property
|
||||
def data(self) -> Any:
|
||||
return self.__data
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.__value)
|
||||
return str(self.__data)
|
||||
|
||||
|
||||
class ContentField(Content):
|
||||
def __getitem__(self, key: Any) -> Any:
|
||||
return DataField(self.__file_path, self.__value.get(key))
|
||||
return ContentField(self.path, self.data.get(key), self.language)
|
||||
|
||||
def __iter__(self) -> Iterator[Any]:
|
||||
for it in self.__value:
|
||||
yield DataField(self.__file_path, it)
|
||||
for it in self.data:
|
||||
yield ContentField(self.path, it, self.language)
|
||||
|
||||
|
||||
class DataFile(Content):
|
||||
def __init__(self, path: Path) -> None:
|
||||
super().__init__(path)
|
||||
with path.open("r", encoding="utf-8") as data_file:
|
||||
self.__data = load(data_file, Loader)
|
||||
|
||||
def __getitem__(self, key: Any) -> Any:
|
||||
return DataField(self.path, self.__data.get(key))
|
||||
|
||||
def __iter__(self) -> Iterator[Any]:
|
||||
for it in self.__data:
|
||||
yield DataField(self.path, it)
|
||||
|
||||
|
||||
class MarkdownFile(Content):
|
||||
def __init__(self, path: Path) -> None:
|
||||
super().__init__(path)
|
||||
with path.open("r", encoding="utf-8") as markdown_file:
|
||||
self.__markdown = Markdown(extensions=["full_yaml_metadata"])
|
||||
self.__html = self.__markdown.convert(markdown_file.read())
|
||||
|
||||
@property
|
||||
def html(self) -> str:
|
||||
return self.__html
|
||||
|
||||
@property
|
||||
def meta(self) -> Any:
|
||||
return DataField(self.path, self.__markdown.Meta) # type: ignore
|
||||
def __str__(self) -> str:
|
||||
return str(self.data)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
import gettext
|
||||
from contextlib import contextmanager
|
||||
from gettext import GNUTranslations, NullTranslations
|
||||
from importlib import import_module
|
||||
from pathlib import Path
|
||||
from typing import Any, Iterator
|
||||
|
||||
from jinja2 import pass_context
|
||||
from jinja2.environment import Environment
|
||||
from jinja2.loaders import FileSystemLoader
|
||||
from jinja2.runtime import Context as JinjaContext
|
||||
|
||||
from jwebsite.content import Content
|
||||
|
||||
_DEFAULT_LANGUAGE = "en"
|
||||
|
||||
|
||||
class Context:
|
||||
def __init__(self, root_directory: Path) -> None:
|
||||
self.__root_directory = root_directory
|
||||
self.__output_directory = root_directory / "build"
|
||||
self.__content_directory = root_directory / "content"
|
||||
self.__environment = Environment(
|
||||
loader=FileSystemLoader(searchpath=root_directory / "src"),
|
||||
)
|
||||
self.__translations: dict[str, GNUTranslations | NullTranslations] = {}
|
||||
self.__current_language: str | None = None
|
||||
|
||||
self.add_filters(load=self.__load, write=self.__write)
|
||||
|
||||
@property
|
||||
def current_language(self) -> str | None:
|
||||
return self.__current_language
|
||||
|
||||
def add_filters(self, **filters: Any) -> None:
|
||||
self.__environment.filters.update(**filters)
|
||||
|
||||
def load_extensions(self, *extensions: str) -> None:
|
||||
for extension in extensions:
|
||||
module = import_module(extension)
|
||||
module.load_extension(self)
|
||||
|
||||
def load_translations(self, domain: str, locale_dir: str | Path, *languages: str) -> None:
|
||||
locale_dir = str(locale_dir)
|
||||
self.__environment.add_extension("jinja2.ext.i18n")
|
||||
self.__translations[_DEFAULT_LANGUAGE] = NullTranslations()
|
||||
for language in languages:
|
||||
self.__translations[language] = gettext.translation(
|
||||
domain, localedir=str(locale_dir), languages=[language]
|
||||
)
|
||||
|
||||
@contextmanager
|
||||
def set_language(self, language: str) -> Iterator[None]:
|
||||
translation = self.__translations[language]
|
||||
old_language = self.__current_language
|
||||
self.__environment.install_gettext_translations(translation, newstyle=True) # type: ignore
|
||||
self.__current_language = language
|
||||
yield
|
||||
self.__current_language = old_language
|
||||
self.__environment.uninstall_gettext_translations(translation) # type: ignore
|
||||
|
||||
def render(self, source: str, output: str | Path, **context: Any) -> None:
|
||||
if self.__translations:
|
||||
for language in self.__translations:
|
||||
with self.set_language(language):
|
||||
self.__render(source, self.__output_directory / language / output, **context)
|
||||
else:
|
||||
self.__render(source, self.__output_directory / output, **context)
|
||||
|
||||
@pass_context
|
||||
def __load(self, context: JinjaContext, path: str | Path) -> Content:
|
||||
current_language = self.current_language
|
||||
if current_language:
|
||||
localized_path = self.__content_directory / current_language / path
|
||||
if not localized_path.exists():
|
||||
localized_path = self.__content_directory / _DEFAULT_LANGUAGE / path
|
||||
else:
|
||||
localized_path = self.__content_directory / path
|
||||
|
||||
with localized_path.open("r", encoding="utf-8") as content_file:
|
||||
return Content(localized_path, content_file.read(), current_language)
|
||||
|
||||
def __write(self, content: Content) -> Path:
|
||||
relative_path = content.path.relative_to(self.__content_directory)
|
||||
output_path = self.__output_directory / relative_path
|
||||
with output_path.open("w") as output_file:
|
||||
output_file.write(content.data)
|
||||
|
||||
return Path(f"/{relative_path}")
|
||||
|
||||
def __render(self, source: str, output_path: Path, **context: Any) -> None:
|
||||
output_path.parent.mkdir(exist_ok=True, parents=True)
|
||||
template = self.__environment.get_template(source)
|
||||
content = template.render(**context)
|
||||
|
||||
with open(output_path, "w") as output_file:
|
||||
output_file.write(content)
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
from datetime import datetime
|
||||
from subprocess import check_output
|
||||
|
||||
from jwebsite.content import Content
|
||||
from jwebsite.context import Context
|
||||
|
||||
|
||||
def load_extension(context: Context) -> None:
|
||||
context.add_filters(git_creation_date=_git_creation_date)
|
||||
|
||||
|
||||
def _git_creation_date(content: Content) -> datetime:
|
||||
git_dir = check_output(["git", "rev-parse", "--show-toplevel"], encoding="utf-8")
|
||||
git_dir = git_dir.strip()
|
||||
log = check_output(
|
||||
[
|
||||
"git",
|
||||
"log",
|
||||
"--pretty=format:%ad",
|
||||
"--date=iso-strict",
|
||||
"--diff-filter=A",
|
||||
"--",
|
||||
str(content.path.relative_to(git_dir)),
|
||||
],
|
||||
encoding="utf-8",
|
||||
)
|
||||
log_lines = log.splitlines()
|
||||
if len(log_lines) == 0:
|
||||
return datetime.now()
|
||||
|
||||
return datetime.fromisoformat(log_lines[0])
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
from typing import Any
|
||||
|
||||
from markdown import Markdown
|
||||
|
||||
from jwebsite.content import Content, ContentField
|
||||
from jwebsite.context import Context
|
||||
|
||||
|
||||
def load_extension(context: Context) -> None:
|
||||
context.add_filters(markdown=_MarkdownDocument)
|
||||
|
||||
|
||||
class _MarkdownDocument:
|
||||
def __init__(self, content: Content) -> None:
|
||||
if not isinstance(content, Content) or not isinstance(content.data, (str, bytes)):
|
||||
raise ValueError("markdown filter can only accept byte or string content")
|
||||
|
||||
data = content.data
|
||||
if isinstance(data, bytes):
|
||||
data = data.decode("utf-8")
|
||||
|
||||
assert isinstance(data, str)
|
||||
|
||||
self.__content = content
|
||||
self.__markdown = Markdown(extensions=["full_yaml_metadata"])
|
||||
self.__html = self.__markdown.convert(data)
|
||||
|
||||
@property
|
||||
def html(self) -> str:
|
||||
return self.__html
|
||||
|
||||
@property
|
||||
def meta(self) -> Any:
|
||||
return ContentField(self.__content.path, self.__markdown.Meta, self.__content.language) # type: ignore
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
from typing import Any
|
||||
|
||||
from yaml import Loader, load
|
||||
|
||||
from jwebsite.content import Content, ContentField
|
||||
from jwebsite.context import Context
|
||||
|
||||
|
||||
def load_extension(context: Context) -> None:
|
||||
context.add_filters(yaml=_load_yaml)
|
||||
|
||||
|
||||
def _load_yaml(content: Any) -> ContentField:
|
||||
if not isinstance(content, Content) or not isinstance(content.data, (str, bytes)):
|
||||
raise ValueError("yaml filter can only accept byte or string content")
|
||||
|
||||
data = content.data
|
||||
if isinstance(data, bytes):
|
||||
data = data.decode("utf-8")
|
||||
|
||||
assert isinstance(data, str)
|
||||
|
||||
return ContentField(content.path, load(data, Loader), content.language)
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
import gettext
|
||||
from gettext import GNUTranslations, NullTranslations
|
||||
from pathlib import Path
|
||||
from shutil import copy
|
||||
from typing import Any
|
||||
|
||||
from jinja2.environment import Environment
|
||||
from jinja2.loaders import FileSystemLoader
|
||||
|
||||
from jwebsite.content import Content, ContentDirectory
|
||||
from jwebsite.git import git_creation_date
|
||||
|
||||
|
||||
class Site:
|
||||
def __init__(self, root_directory: Path) -> None:
|
||||
self.__root_directory = root_directory
|
||||
self.__output_directory = root_directory / "build"
|
||||
self.__environment = Environment(loader=FileSystemLoader(searchpath=root_directory / "src"))
|
||||
self.__environment.filters.update({"output": self.__output, "git_creation_date": git_creation_date})
|
||||
self.__content = ContentDirectory(root_directory / "content")
|
||||
self.__translations: dict[str, GNUTranslations | NullTranslations] = {}
|
||||
|
||||
@property
|
||||
def content(self) -> ContentDirectory:
|
||||
return self.__content
|
||||
|
||||
def set_translations(self, domain: str, locale_dir: str, languages: list[str]) -> None:
|
||||
self.__environment.add_extension("jinja2.ext.i18n")
|
||||
self.__translations["en"] = NullTranslations()
|
||||
for language in languages:
|
||||
self.__translations[language] = gettext.translation(
|
||||
domain, localedir=str(locale_dir), languages=[language]
|
||||
)
|
||||
|
||||
def render(self, source: str, output: str | Path, **context: Any) -> None:
|
||||
if self.__translations:
|
||||
for language, translation in self.__translations.items():
|
||||
Content.current_language = language
|
||||
self.__environment.install_gettext_translations(translation, newstyle=True) # type: ignore
|
||||
self.__render(source, self.__output_directory / language / output, **context)
|
||||
self.__environment.uninstall_gettext_translations(translation) # type: ignore
|
||||
else:
|
||||
self.__render(source, self.__output_directory / output, **context)
|
||||
|
||||
def __render(self, source: str, output_path: Path, **context: Any) -> None:
|
||||
output_path.parent.mkdir(exist_ok=True, parents=True)
|
||||
template = self.__environment.get_template(source)
|
||||
content = template.render(site=self, **context)
|
||||
|
||||
with open(output_path, "w") as output_file:
|
||||
output_file.write(content)
|
||||
|
||||
def __output(self, path: str | Path) -> str:
|
||||
path = Path(path)
|
||||
if path.is_absolute():
|
||||
src_path = path
|
||||
relative_src_path = src_path.relative_to(self.__content.path)
|
||||
else:
|
||||
src_path = self.__content.path / path
|
||||
relative_src_path = path
|
||||
dst_path = self.__output_directory / relative_src_path
|
||||
dst_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
copy(src_path, dst_path, follow_symlinks=True)
|
||||
return f"/{relative_src_path}"
|
||||
32
noxfile.py
32
noxfile.py
|
|
@ -1,7 +1,12 @@
|
|||
"""Nox configuration file."""
|
||||
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
from nox import Session, session
|
||||
|
||||
_LOCALIZED_TESTS = ["tests/test_context"]
|
||||
|
||||
|
||||
@session()
|
||||
def lint(session: Session) -> None:
|
||||
|
|
@ -16,10 +21,37 @@ def mypy(session: Session) -> None:
|
|||
session.run("mypy")
|
||||
|
||||
|
||||
@session()
|
||||
def update_messages(session: Session) -> None:
|
||||
session.install("babel", "jinja2")
|
||||
for directory in _LOCALIZED_TESTS:
|
||||
with TemporaryDirectory() as tmp_dir:
|
||||
messages_file = Path(tmp_dir) / "messages.po"
|
||||
session.run(
|
||||
"pybabel",
|
||||
"extract",
|
||||
"--mapping",
|
||||
f"{directory}/babel.cfg",
|
||||
f"--output-file={messages_file}",
|
||||
str(directory),
|
||||
)
|
||||
|
||||
session.run(
|
||||
"pybabel",
|
||||
"update",
|
||||
"--domain=tests",
|
||||
f"--input-file={messages_file}",
|
||||
f"--output-dir={directory}/locale",
|
||||
)
|
||||
|
||||
|
||||
@session(python=["3.10", "3.11"])
|
||||
def unit_tests(session: Session) -> None:
|
||||
"""Run unit tests."""
|
||||
devenv(session)
|
||||
session.install("babel", "jinja2")
|
||||
for directory in _LOCALIZED_TESTS:
|
||||
session.run("pybabel", "compile", "--domain=tests", f"--directory={directory}/locale", "--use-fuzzy")
|
||||
session.run("python", "-m", "pytest", "--cov=jwebsite", "--cov-report=html")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ requires = ["setuptools>=45"]
|
|||
|
||||
[tool.mypy]
|
||||
strict = true
|
||||
files = "jwebsite/**/*.py,noxfile.py"
|
||||
files = "jwebsite/**/*.py,tests/**/*.py,noxfile.py"
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 110
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
from pathlib import Path
|
||||
|
||||
from pytest import mark, raises
|
||||
|
||||
from jwebsite.content import Content
|
||||
from jwebsite.context import Context
|
||||
|
||||
|
||||
@mark.parametrize("content", ["# Otters", b"# Otters"])
|
||||
def test_load(datadir: Path, content: bytes | str) -> None:
|
||||
context = Context(datadir)
|
||||
context.load_extensions("jwebsite.extensions.markdown")
|
||||
|
||||
context.render("test-load.html", "output.html", content=Content(Path("content.yml"), content, None))
|
||||
with open(datadir / "build" / "output.html", encoding="utf-8") as ouput_file:
|
||||
assert ouput_file.read() == "<h1>Otters</h1>"
|
||||
|
||||
|
||||
def test_load_type_error(datadir: Path) -> None:
|
||||
context = Context(datadir)
|
||||
context.load_extensions("jwebsite.extensions.markdown")
|
||||
|
||||
with raises(ValueError):
|
||||
context.render("test-load.html", "output.html", content=Content(Path("content.yml"), 10, None))
|
||||
|
||||
with raises(ValueError):
|
||||
context.render("test-load.html", "output.html", content=10)
|
||||
|
||||
|
||||
def test_metadata(datadir: Path) -> None:
|
||||
context = Context(datadir)
|
||||
context.load_extensions("jwebsite.extensions.markdown")
|
||||
|
||||
markdown = "---\n" "title: Steven\n" "---\n" "\n" "Content\n"
|
||||
|
||||
context.render("test-metadata.html", "output.html", content=Content(Path("content.yml"), markdown, None))
|
||||
with open(datadir / "build" / "output.html", encoding="utf-8") as ouput_file:
|
||||
assert ouput_file.read() == "Steven"
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{% with document = content | markdown %}
|
||||
{{- document.html }}
|
||||
{%- endwith -%}
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{% with document = content | markdown %}
|
||||
{{- document.meta.title }}
|
||||
{%- endwith -%}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
from pathlib import Path
|
||||
|
||||
from pytest import mark, raises
|
||||
|
||||
from jwebsite.content import Content
|
||||
from jwebsite.context import Context
|
||||
|
||||
|
||||
@mark.parametrize("content", ["[Peter, Steven]", b"[Peter, Steven]"])
|
||||
def test_load(datadir: Path, content: bytes | str) -> None:
|
||||
context = Context(datadir)
|
||||
context.load_extensions("jwebsite.extensions.yaml")
|
||||
|
||||
context.render("test-load.html", "output.html", content=Content(Path("content.yml"), content, None))
|
||||
with open(datadir / "build" / "output.html", encoding="utf-8") as ouput_file:
|
||||
assert ouput_file.read() == "PeterSteven"
|
||||
|
||||
|
||||
def test_load_type_error(datadir: Path) -> None:
|
||||
context = Context(datadir)
|
||||
context.load_extensions("jwebsite.extensions.yaml")
|
||||
|
||||
with raises(ValueError):
|
||||
context.render("test-load.html", "output.html", content=Content(Path("content.yml"), 10, None))
|
||||
|
||||
with raises(ValueError):
|
||||
context.render("test-load.html", "output.html", content=10)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{% for otter in content | yaml -%}
|
||||
{{- otter }}
|
||||
{%- endfor %}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
from pathlib import Path
|
||||
|
||||
from pytest import raises
|
||||
|
||||
from jwebsite.content import Content, ContentDirectory
|
||||
|
||||
|
||||
def test_load_directory(datadir: Path) -> None:
|
||||
content = ContentDirectory(datadir)
|
||||
otters = content.load(Path("otters"))
|
||||
assert isinstance(otters, ContentDirectory)
|
||||
|
||||
|
||||
def test_load_errors(datadir: Path) -> None:
|
||||
directory = ContentDirectory(datadir)
|
||||
|
||||
with raises(FileNotFoundError):
|
||||
directory.load(Path("otters/i-dont-exist"))
|
||||
|
||||
with raises(NotADirectoryError):
|
||||
directory.load(Path("otters/steven.yml/child"))
|
||||
|
||||
|
||||
def test_load_data(datadir: Path) -> None:
|
||||
content = ContentDirectory(datadir)
|
||||
steven = content.load(Path("otters/steven.yml"))
|
||||
|
||||
assert str(steven["name"]) == "Steven"
|
||||
assert str(steven["mood"]) == "Angry"
|
||||
|
||||
otter_list = content.load(Path("otters/otter_list.json"))
|
||||
assert [str(it) for it in otter_list] == ["steven", "peter"]
|
||||
|
||||
|
||||
def test_data_as_path(datadir: Path) -> None:
|
||||
content = ContentDirectory(datadir)
|
||||
paths = content.load(Path("otters/paths.yml"))
|
||||
|
||||
assert paths["steven"].as_path() == datadir / "otters" / "relative-steven"
|
||||
|
||||
|
||||
def test_load_markdown(datadir: Path) -> None:
|
||||
content = ContentDirectory(datadir)
|
||||
page = content.load(Path("page.md"))
|
||||
|
||||
assert page.html == "<p>Content</p>"
|
||||
assert str(page.meta["title"]) == "Title"
|
||||
|
||||
|
||||
def test_localized_content(datadir: Path) -> None:
|
||||
content = ContentDirectory(datadir)
|
||||
Content.current_language = "fr"
|
||||
page = content.load(Path("page.md"))
|
||||
|
||||
assert page.html == "<p>Contenu</p>"
|
||||
assert str(page.meta["title"]) == "Titre"
|
||||
|
|
@ -1 +0,0 @@
|
|||
["steven", "peter"]
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
steven: ./relative-steven
|
||||
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
name: Steven
|
||||
mood: Angry
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
title: Titre
|
||||
---
|
||||
|
||||
Contenu
|
||||
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Title
|
||||
---
|
||||
|
||||
Content
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
from pathlib import Path
|
||||
|
||||
from jwebsite.context import Context
|
||||
|
||||
|
||||
def test_render(datadir: Path) -> None:
|
||||
context = Context(datadir)
|
||||
context.render("test-render.html", "output.html", animal="Otters")
|
||||
with open(datadir / "build" / "output.html", encoding="utf-8") as ouput_file:
|
||||
assert ouput_file.read() == "Otters"
|
||||
|
||||
|
||||
def test_jinja_localization(datadir: Path) -> None:
|
||||
context = Context(datadir)
|
||||
context.load_translations("tests", datadir / "locale", "fr")
|
||||
context.render("test-jinja-localization.html", "output.html")
|
||||
|
||||
with open(datadir / "build" / "en" / "output.html", encoding="utf-8") as ouput_file:
|
||||
assert ouput_file.read() == "Otters"
|
||||
|
||||
with open(datadir / "build" / "fr" / "output.html", encoding="utf-8") as ouput_file:
|
||||
assert ouput_file.read() == "Loutres"
|
||||
|
||||
|
||||
def test_load(datadir: Path) -> None:
|
||||
context = Context(datadir)
|
||||
context.render("test-load.html", "output.html")
|
||||
|
||||
with open(datadir / "build" / "output.html", encoding="utf-8") as ouput_file:
|
||||
assert ouput_file.read() == "Otters\n"
|
||||
|
||||
|
||||
def test_load_localized(datadir: Path) -> None:
|
||||
context = Context(datadir)
|
||||
context.load_translations("tests", datadir / "locale", "fr")
|
||||
context.render("test-load-localized.html", "output.html")
|
||||
|
||||
with open(datadir / "build" / "en" / "output.html", encoding="utf-8") as ouput_file:
|
||||
assert ouput_file.read() == "Otters\nCaimans\n"
|
||||
|
||||
with open(datadir / "build" / "fr" / "output.html", encoding="utf-8") as ouput_file:
|
||||
assert ouput_file.read() == "Loutres\nCaimans\n"
|
||||
|
||||
|
||||
def test_write(datadir: Path) -> None:
|
||||
context = Context(datadir)
|
||||
context.render("test-write.html", "output.html")
|
||||
|
||||
with open(datadir / "build" / "content.txt", encoding="utf-8") as ouput_file:
|
||||
assert ouput_file.read() == "Otters\n"
|
||||
|
||||
with open(datadir / "build" / "output.html", encoding="utf-8") as ouput_file:
|
||||
assert ouput_file.read() == "/content.txt"
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
[jinja2: src/**.html]
|
||||
encoding = utf-8
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
Otters
|
||||
|
|
@ -0,0 +1 @@
|
|||
Otters
|
||||
|
|
@ -0,0 +1 @@
|
|||
Caimans
|
||||
|
|
@ -0,0 +1 @@
|
|||
Loutres
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# French translations for PROJECT.
|
||||
# Copyright (C) 2024 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2024.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2024-05-21 21:13+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: fr\n"
|
||||
"Language-Team: fr <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.15.0\n"
|
||||
|
||||
#: tests/test_context/src/test-jinja-localization.html:1
|
||||
msgid "Otters"
|
||||
msgstr "Loutres"
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
{{ gettext('Otters') }}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{{ 'content.txt' | load -}}
|
||||
{{ 'not-localized-content.txt' | load -}}
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
{{ 'content.txt' | load }}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{{animal}}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{{ 'content.txt' | load | write }}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
from pathlib import Path
|
||||
|
||||
from pyfakefs.fake_filesystem import FakeFilesystem
|
||||
from pytest import fixture
|
||||
|
||||
from jwebsite.site import Site
|
||||
|
||||
|
||||
@fixture
|
||||
def site_dir(datadir: Path, fs: FakeFilesystem):
|
||||
fs.add_real_directory(datadir)
|
||||
yield fs
|
||||
|
||||
|
||||
def test_render_page(datadir: Path, site_dir: FakeFilesystem):
|
||||
site = Site(datadir)
|
||||
site.render("index.j2", "index.html")
|
||||
with open(datadir / "build" / "index.html", encoding="utf-8") as ouput_file:
|
||||
assert ouput_file.read() == "<p>Peter</p><p>Steven</p>"
|
||||
|
||||
|
||||
def test_output_file(datadir: Path, site_dir: FakeFilesystem):
|
||||
site = Site(datadir)
|
||||
build_dir = datadir / "build"
|
||||
|
||||
site.render("output-file.j2", "output-file.html")
|
||||
|
||||
with open(build_dir / "assets/steven-avatar", encoding="utf-8") as ouput_file:
|
||||
assert ouput_file.read() == "Yipee\n"
|
||||
|
||||
with open(build_dir / "output-file.html", encoding="utf-8") as ouput_file:
|
||||
assert ouput_file.read() == "/assets/steven-avatar\n"
|
||||
|
|
@ -1 +0,0 @@
|
|||
Yipee
|
||||
|
|
@ -1 +0,0 @@
|
|||
[Peter, Steven]
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
{%- for otter in site.content.load('otters.yml') -%}
|
||||
<p>{{ otter }}</p>
|
||||
{%- endfor %}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
{{ "assets/steven-avatar" | output }}
|
||||
|
||||
Loading…
Reference in New Issue