diff --git a/betty/cache.py b/betty/cache.py index 1e77fafc7..813ef7f38 100644 --- a/betty/cache.py +++ b/betty/cache.py @@ -1,6 +1,7 @@ """ Provide the Cache API. """ +import asyncio import logging import shutil from contextlib import suppress @@ -22,5 +23,5 @@ def __init__(self, localizer: Localizer): async def clear(self) -> None: with suppress(FileNotFoundError): - shutil.rmtree(fs.CACHE_DIRECTORY_PATH) + await asyncio.to_thread(shutil.rmtree, fs.CACHE_DIRECTORY_PATH) logging.getLogger(__name__).info(self._localizer._('All caches cleared.')) diff --git a/betty/extension/cotton_candy/__init__.py b/betty/extension/cotton_candy/__init__.py index 2e9e0f600..b642eadb1 100644 --- a/betty/extension/cotton_candy/__init__.py +++ b/betty/extension/cotton_candy/__init__.py @@ -3,6 +3,7 @@ """ from __future__ import annotations +import asyncio import logging import re from collections import defaultdict @@ -224,8 +225,8 @@ async def npm_build(self, working_directory_path: Path, assets_directory_path: P async def _copy_npm_build(self, source_directory_path: Path, destination_directory_path: Path) -> None: await makedirs(destination_directory_path, exist_ok=True) - copy2(source_directory_path / 'cotton_candy.css', destination_directory_path / 'cotton_candy.css') - copy2(source_directory_path / 'cotton_candy.js', destination_directory_path / 'cotton_candy.js') + await asyncio.to_thread(copy2, source_directory_path / 'cotton_candy.css', destination_directory_path / 'cotton_candy.css') + await asyncio.to_thread(copy2, source_directory_path / 'cotton_candy.js', destination_directory_path / 'cotton_candy.js') async def generate(self, task_context: GenerationContext) -> None: assets_directory_path = await self.app.extensions[_Npm].ensure_assets(self) diff --git a/betty/extension/http_api_doc/__init__.py b/betty/extension/http_api_doc/__init__.py index 77696647c..83d78466a 100644 --- a/betty/extension/http_api_doc/__init__.py +++ b/betty/extension/http_api_doc/__init__.py @@ -1,6 +1,7 @@ """Integrate Betty with `ReDoc `_.""" from __future__ import annotations +import asyncio import logging from pathlib import Path from shutil import copy2 @@ -21,7 +22,7 @@ def depends_on(cls) -> set[type[Extension]]: async def npm_build(self, working_directory_path: Path, assets_directory_path: Path) -> None: await self.app.extensions[_Npm].install(type(self), working_directory_path) - copy2(working_directory_path / 'node_modules' / 'redoc' / 'bundles' / 'redoc.standalone.js', assets_directory_path / 'http-api-doc.js') + await asyncio.to_thread(copy2, working_directory_path / 'node_modules' / 'redoc' / 'bundles' / 'redoc.standalone.js', assets_directory_path / 'http-api-doc.js') logging.getLogger(__name__).info(self._app.localizer._('Built the HTTP API documentation.')) @classmethod @@ -31,7 +32,7 @@ def npm_cache_scope(cls) -> CacheScope: async def generate(self, task_context: GenerationContext) -> None: assets_directory_path = await self.app.extensions[_Npm].ensure_assets(self) await makedirs(self.app.project.configuration.www_directory_path, exist_ok=True) - copy2(assets_directory_path / 'http-api-doc.js', self.app.project.configuration.www_directory_path / 'http-api-doc.js') + await asyncio.to_thread(copy2, assets_directory_path / 'http-api-doc.js', self.app.project.configuration.www_directory_path / 'http-api-doc.js') @classmethod def assets_directory_path(cls) -> Path | None: diff --git a/betty/extension/maps/__init__.py b/betty/extension/maps/__init__.py index 392cda9da..0c68b19f7 100644 --- a/betty/extension/maps/__init__.py +++ b/betty/extension/maps/__init__.py @@ -1,6 +1,7 @@ """Integrate Betty with `Leaflet.js `_.""" from __future__ import annotations +import asyncio import logging from contextlib import suppress from pathlib import Path @@ -29,10 +30,10 @@ async def npm_build(self, working_directory_path: Path, assets_directory_path: P async def _copy_npm_build(self, source_directory_path: Path, destination_directory_path: Path) -> None: await makedirs(destination_directory_path, exist_ok=True) - copy2(source_directory_path / 'maps.css', destination_directory_path / 'maps.css') - copy2(source_directory_path / 'maps.js', destination_directory_path / 'maps.js') + await asyncio.to_thread(copy2, source_directory_path / 'maps.css', destination_directory_path / 'maps.css') + await asyncio.to_thread(copy2, source_directory_path / 'maps.js', destination_directory_path / 'maps.js') with suppress(FileNotFoundError): - copytree(source_directory_path / 'images', destination_directory_path / 'images') + await asyncio.to_thread(copytree, source_directory_path / 'images', destination_directory_path / 'images') @classmethod def npm_cache_scope(cls) -> CacheScope: diff --git a/betty/extension/npm/__init__.py b/betty/extension/npm/__init__.py index 735b9ee9b..6998c38e0 100644 --- a/betty/extension/npm/__init__.py +++ b/betty/extension/npm/__init__.py @@ -5,6 +5,7 @@ """ from __future__ import annotations +import asyncio import logging import os import shutil @@ -168,7 +169,7 @@ async def _build_assets_to_directory_path(extension: NpmBuilder & Extension, ass assert isinstance(extension, Extension) assert isinstance(extension, NpmBuilder) with suppress(FileNotFoundError): - shutil.rmtree(assets_directory_path) + await asyncio.to_thread(shutil.rmtree, assets_directory_path) os.makedirs(assets_directory_path) async with TemporaryDirectory() as working_directory_path_str: working_directory_path = Path(working_directory_path_str) @@ -202,7 +203,8 @@ async def install(self, extension_type: type[NpmBuilder & Extension], working_di if self._npm_requirement: self._npm_requirement.assert_met() - shutil.copytree( + await asyncio.to_thread( + shutil.copytree, _get_assets_src_directory_path(extension_type), working_directory_path, dirs_exist_ok=True, diff --git a/betty/extension/trees/__init__.py b/betty/extension/trees/__init__.py index 87538a701..848ac928c 100644 --- a/betty/extension/trees/__init__.py +++ b/betty/extension/trees/__init__.py @@ -1,6 +1,7 @@ """Provide interactive family trees by integrating Betty with `Cytoscape.js `_.""" from __future__ import annotations +import asyncio import logging import subprocess from pathlib import Path @@ -29,8 +30,8 @@ async def npm_build(self, working_directory_path: Path, assets_directory_path: P async def _copy_npm_build(self, source_directory_path: Path, destination_directory_path: Path) -> None: await makedirs(destination_directory_path, exist_ok=True) - copy2(source_directory_path / 'trees.css', destination_directory_path / 'trees.css') - copy2(source_directory_path / 'trees.js', destination_directory_path / 'trees.js') + await asyncio.to_thread(copy2, source_directory_path / 'trees.css', destination_directory_path / 'trees.css') + await asyncio.to_thread(copy2, source_directory_path / 'trees.js', destination_directory_path / 'trees.js') @classmethod def npm_cache_scope(cls) -> CacheScope: diff --git a/betty/fs.py b/betty/fs.py index 88d99911d..aa5609e34 100644 --- a/betty/fs.py +++ b/betty/fs.py @@ -93,7 +93,7 @@ def open(self, *file_paths: Path) -> _Open: async def copy2(self, source_path: Path, destination_path: Path) -> Path: for fs_path, _ in self._paths: with suppress(FileNotFoundError): - copy2(fs_path / source_path, destination_path) + await asyncio.to_thread(copy2, fs_path / source_path, destination_path) return destination_path tried_paths = [str(fs_path / source_path) for fs_path, _ in self._paths] raise FileNotFoundError('Could not find any of %s.' % ', '.join(tried_paths)) diff --git a/betty/generate.py b/betty/generate.py index 0140b704f..a3309b895 100644 --- a/betty/generate.py +++ b/betty/generate.py @@ -3,6 +3,7 @@ """ from __future__ import annotations +import asyncio import json import logging import multiprocessing @@ -164,7 +165,7 @@ async def generate(app: App) -> None: task_context = GenerationContext(app) with suppress(FileNotFoundError): - shutil.rmtree(app.project.configuration.output_directory_path) + await asyncio.to_thread(shutil.rmtree, app.project.configuration.output_directory_path) await aiofiles_os.makedirs(app.project.configuration.output_directory_path, exist_ok=True) logger.info(app.localizer._('Generating your site to {output_directory}.').format(output_directory=app.project.configuration.output_directory_path)) diff --git a/betty/os.py b/betty/os.py index 08b145c70..487d18532 100644 --- a/betty/os.py +++ b/betty/os.py @@ -3,6 +3,7 @@ """ from __future__ import annotations +import asyncio import os import shutil from contextlib import suppress @@ -26,7 +27,7 @@ async def link_or_copy(source_path: Path, destination_path: Path) -> None: await link(source_path, destination_path) except OSError: with suppress(shutil.SameFileError): - shutil.copyfile(source_path, destination_path) + await asyncio.to_thread(shutil.copyfile, source_path, destination_path) class ChDir: