Skip to content

Commit

Permalink
Compile template directories.
Browse files Browse the repository at this point in the history
  • Loading branch information
bartfeenstra committed Nov 10, 2020
1 parent 37193c4 commit 2004843
Show file tree
Hide file tree
Showing 20 changed files with 236 additions and 78 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{ {
'$schema': 'schema.json#/definitions/eventCollection' | static_url(absolute=True),
'collection': file_resources | list,
} | json }}
} | json | safe }}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{ {
'$schema': 'schema.json#/definitions/personCollection' | static_url(absolute=True),
'collection': file_resources | list,
} | json }}
} | json | safe }}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{ {
'$schema': 'schema.json#/definitions/placeCollection' | static_url(absolute=True),
'collection': file_resources | list,
} | json }}
} | json | safe }}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{ {
'$schema': 'schema.json#/definitions/sourceCollection' | static_url(absolute=True),
'collection': file_resources | list,
} | json }}
} | json | safe }}
2 changes: 1 addition & 1 deletion betty/assets/templates/resource/citation/index.json.j2
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{ file_resource | json }}
{{ file_resource | json | safe }}
2 changes: 1 addition & 1 deletion betty/assets/templates/resource/event/index.json.j2
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{ file_resource | json }}
{{ file_resource | json | safe }}
2 changes: 1 addition & 1 deletion betty/assets/templates/resource/file/index.json.j2
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{ file_resource | json }}
{{ file_resource | json | safe }}
2 changes: 1 addition & 1 deletion betty/assets/templates/resource/person/index.json.j2
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{ file_resource | json }}
{{ file_resource | json | safe }}
2 changes: 1 addition & 1 deletion betty/assets/templates/resource/place/index.json.j2
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{ file_resource | json }}
{{ file_resource | json | safe }}
2 changes: 1 addition & 1 deletion betty/assets/templates/resource/source/index.json.j2
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{ file_resource | json }}
{{ file_resource | json | safe }}
39 changes: 14 additions & 25 deletions betty/fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from collections import deque
from contextlib import suppress
from os import walk, path
from tempfile import mkdtemp, TemporaryDirectory
from typing import Iterable
from tempfile import mkdtemp
from typing import Iterable, Dict


def iterfiles(file_path: str) -> Iterable[str]:
Expand All @@ -22,7 +22,7 @@ def hashfile(file_path: str) -> str:
return hashlib.md5(':'.join([str(path.getmtime(file_path)), file_path]).encode('utf-8')).hexdigest()


async def copy_tree(source_path: str, destination_path: str):
async def copy_directory(source_path: str, destination_path: str):
for file_source_path in iterfiles(source_path):
file_destination_path = path.join(destination_path, path.relpath(file_source_path, source_path))
try:
Expand All @@ -32,23 +32,6 @@ async def copy_tree(source_path: str, destination_path: str):
shutil.copy2(file_source_path, file_destination_path)


class CopyTreeTo:
def __init__(self, file_system: 'FileSystem', source_path: str):
self._file_system = file_system
self._source_path = source_path

async def __aenter__(self) -> 'CopyTreeTo':
self._intermediate_directory = TemporaryDirectory()
await self._file_system.copy_tree(self._source_path, self._intermediate_directory.name)
return self

async def __call__(self, destination_path: str) -> None:
await copy_tree(self._intermediate_directory.name, destination_path)

async def __aexit__(self, exc_type, exc_val, exc_tb):
self._intermediate_directory.cleanup()


class FileSystem:
def __init__(self, *paths: str):
"""
Expand All @@ -60,7 +43,7 @@ def __init__(self, *paths: str):
def paths(self) -> deque:
return self._paths

async def copy(self, source_path: str, destination_path: str) -> None:
async def copy_file(self, source_path: str, destination_path: str) -> None:
makedirs(path.dirname(destination_path))

for fs_path in self._paths:
Expand All @@ -70,13 +53,13 @@ async def copy(self, source_path: str, destination_path: str) -> None:
tried_paths = [path. join(fs_path, source_path) for fs_path in self._paths]
raise FileNotFoundError('Could not find any of %s.' % ', '.join(tried_paths))

async def copy_tree(self, source_path: str, destination_path: str) -> None:
async def copy_directory(self, source_path: str, destination_path: str) -> None:
makedirs(destination_path)

tries = []
for fs_path in reversed(self._paths):
try:
await copy_tree(path.join(fs_path, source_path), destination_path)
await copy_directory(path.join(fs_path, source_path), destination_path)
tries.append(True)
except FileNotFoundError:
tries.append(False)

Check warning on line 65 in betty/fs.py

View check run for this annotation

Codecov / codecov/patch

betty/fs.py#L64-L65

Added lines #L64 - L65 were not covered by tests
Expand All @@ -85,8 +68,14 @@ async def copy_tree(self, source_path: str, destination_path: str) -> None:
tried_paths = [path. join(fs_path, source_path) for fs_path in self._paths]
raise FileNotFoundError('Could not find any of %s.' % ', '.join(tried_paths))

Check warning on line 69 in betty/fs.py

View check run for this annotation

Codecov / codecov/patch

betty/fs.py#L68-L69

Added lines #L68 - L69 were not covered by tests

def copy_tree_to(self, source_path: str) -> CopyTreeTo:
return CopyTreeTo(self, source_path)
def iterfiles(self, directory_path: str) -> Dict[str, str]:
file_paths = {}
for fs_path in reversed(self._paths):
for dir_path, _, filenames in walk(directory_path):
for filename in filenames:
file_path = path.join(dir_path, filename)
file_paths[path.relpath(file_path, fs_path)] = file_path
return file_paths

Check warning on line 78 in betty/fs.py

View check run for this annotation

Codecov / codecov/patch

betty/fs.py#L72-L78

Added lines #L72 - L78 were not covered by tests


class DirectoryBackup:
Expand Down
22 changes: 12 additions & 10 deletions betty/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
from json import dump
from os import chmod, path
from tempfile import TemporaryDirectory
from typing import Iterable, Union

from babel import Locale
Expand All @@ -26,9 +27,9 @@ async def post_generate(self) -> None:

async def generate(site: Site) -> None:
logger = logging.getLogger()
await site.assets.copy_tree(path.join('public', 'static'),
site.configuration.www_directory_path)
await site.renderer.render_tree(site.configuration.www_directory_path)
await site.assets.copy_directory(path.join('public', 'static'),
site.configuration.www_directory_path)
await site.renderer.render_directory(site.configuration.www_directory_path)
await site.dispatcher.dispatch(PostStaticGenerator, 'post_static_generate')()
for locale, locale_configuration in site.configuration.locales.items():
async with site.with_locale(locale) as site:
Expand All @@ -38,8 +39,8 @@ async def generate(site: Site) -> None:
else:
www_directory_path = site.configuration.www_directory_path

await site.assets.copy_tree(path.join('public', 'localized'), www_directory_path)
await site.renderer.render_tree(www_directory_path)
await site.assets.copy_directory(path.join('public', 'localized'), www_directory_path)
await site.renderer.render_directory(www_directory_path)

locale_label = Locale.parse(locale, '-').get_display_name()
resources_by_type = {
Expand Down Expand Up @@ -68,18 +69,19 @@ async def generate(site: Site) -> None:
async def _generate_resource_type(www_directory_path: str, resources: Iterable[IdentifiableResource], resource_type_name: str, site: Site) -> None:
resources_template_directory_path = path.join('templates', 'resource-collection', resource_type_name)
resources_destination_path = path.join(www_directory_path, resource_type_name)
await site.assets.copy_tree(resources_template_directory_path, resources_destination_path)
await site.assets.copy_directory(resources_template_directory_path, resources_destination_path)
if path.exists(resources_destination_path):
await site.renderer.render_tree(resources_destination_path, {
await site.renderer.render_directory(resources_destination_path, {
'file_resources': resources,
})

resource_template_directory_path = path.join('templates', 'resource', resource_type_name)
async with site.assets.copy_tree_to(resource_template_directory_path) as copy_tree_to:
with TemporaryDirectory() as intermediate_directory_path:
await site.assets.copy_directory(resource_template_directory_path, intermediate_directory_path)
render_directory_to = await site.renderer.render_directory_to(intermediate_directory_path)
for resource in resources:
resource_destination_path = path.join(www_directory_path, resource.resource_type_name, resource.id)
await copy_tree_to(resource_destination_path)
await site.renderer.render_tree(resource_destination_path, {
await render_directory_to(resource_destination_path, {
'file_resource': resource,
})

Expand Down
37 changes: 26 additions & 11 deletions betty/jinja2.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from betty.media_type import MediaType
from betty.path import extension
from betty.plugin import Plugin
from betty.render import Renderer, RenderArguments
from betty.render import Renderer, TemplateArguments
from betty.search import Index
from betty.site import Site

Expand Down Expand Up @@ -201,33 +201,48 @@ def _filter_url(context, resource, media_type=None, locale=None, **kwargs):


class Jinja2Renderer(Renderer):
_EXTENSIONS = {'j2'}

def __init__(self, environment: Environment, configuration: Configuration):
self._environment = environment
self._configuration = configuration

async def _render_file(self, file_path: str, file_arguments: RenderArguments = None) -> None:
if not file_path.endswith('.j2'):
return
def _assert_file_path(self, file_path: str) -> None:
if not self.consumes_file_path(file_path):
raise ValueError('Cannot consume "%s".' % file_path)

Check warning on line 212 in betty/jinja2.py

View check run for this annotation

Codecov / codecov/patch

betty/jinja2.py#L212

Added line #L212 was not covered by tests

def consumes_file_path(self, file_path: str) -> bool:
return file_path.endswith('.j2')

def update_file_path(self, file_path: str) -> str:
self._assert_file_path(file_path)
return file_path[:-3]

async def render_string(self, template: str, template_arguments: TemplateArguments = None) -> str:
return await self._environment.from_string(template).render_async(**template_arguments)

async def render_file(self, file_path: str, template_arguments: TemplateArguments = None) -> None:
self._assert_file_path(file_path)
file_destination_path = file_path[:-3]
template = _root_loader.load(self._environment, file_path, self._environment.globals)
if file_arguments is None:
file_arguments = {}
if 'file_resource' not in file_arguments and file_destination_path.startswith(self._configuration.www_directory_path):
if template_arguments is None:
template_arguments = {}
if 'file_resource' not in template_arguments and file_destination_path.startswith(self._configuration.www_directory_path):
# Unix-style paths use forward slashes, so they are valid URL paths.
resource = file_destination_path[len(
self._configuration.www_directory_path):]
if self._configuration.multilingual:
resource_parts = resource.lstrip('/').split('/')
if resource_parts[0] in map(lambda x: x.alias, self._configuration.locales.values()):
resource = '/'.join(resource_parts[1:])
file_arguments['file_resource'] = resource
template_arguments['file_resource'] = resource
with open(file_destination_path, 'w') as f:
f.write(await template.render_async(**file_arguments))
f.write(await template.render_async(**template_arguments))
os.remove(file_path)

async def render_tree(self, render_path: str, file_arguments: RenderArguments = None) -> None:
async def render_directory(self, directory_path: str, template_arguments: TemplateArguments = None) -> None:
await asyncio.gather(
*[self._render_file(file_path, file_arguments) for file_path in iterfiles(render_path)],
*[self.render_file(file_path, template_arguments) for file_path in iterfiles(directory_path) if self.consumes_file_path(file_path)],
)


Expand Down
2 changes: 1 addition & 1 deletion betty/plugin/maps/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ async def _render(self) -> None:
with suppress(FileNotFoundError):
shutil.rmtree(build_directory_path)
shutil.copytree(path.join(self.assets_directory_path, 'js'), build_directory_path)
await self._site.renderer.render_tree(build_directory_path)
await self._site.renderer.render_directory(build_directory_path)

self._site.executor.submit(_do_render, build_directory_path, self._site.configuration.www_directory_path)

Expand Down
4 changes: 2 additions & 2 deletions betty/plugin/nginx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ async def _generate_config(self) -> None:
# Render the ngnix configuration.
file_name = 'nginx.conf.j2'
destination_file_path = os.path.join(output_directory_path, file_name)
await self._site.assets.copy(file_name, destination_file_path)
await self._site.renderer.render_tree(output_directory_path)
await self._site.assets.copy_file(file_name, destination_file_path)
await self._site.renderer.render_directory(output_directory_path)

# Render the Dockerfile.
copyfile(os.path.join(DOCKER_PATH, 'Dockerfile'), os.path.join(output_directory_path, 'Dockerfile'))
2 changes: 1 addition & 1 deletion betty/plugin/trees/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ async def _render(self) -> None:
with suppress(FileNotFoundError):
shutil.rmtree(build_directory_path)
shutil.copytree(path.join(self.assets_directory_path, 'js'), build_directory_path)
await self._site.renderer.render_tree(build_directory_path)
await self._site.renderer.render_directory(build_directory_path)

self._site.executor.submit(_do_render, build_directory_path, self._site.configuration.www_directory_path)

Expand Down
Loading

0 comments on commit 2004843

Please sign in to comment.