Skip to content

Commit

Permalink
Render documentation using Sphinx.
Browse files Browse the repository at this point in the history
  • Loading branch information
bartfeenstra committed Jan 9, 2024
1 parent 3982ba5 commit ad3c3d3
Show file tree
Hide file tree
Showing 52 changed files with 961 additions and 32 deletions.
2 changes: 2 additions & 0 deletions .pydocstyle.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pydocstyle]
ignore = D100, D101, D102, D103, D104, D105, D107, D200, D203, D212
2 changes: 1 addition & 1 deletion betty/_resizeimage.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
This is a partial, simplified fork of the abandoned python-resize-image package.
A partial, simplified fork of the abandoned python-resize-image package.
This module exists purely because existing Betty code depended on an abandoned package, and that code has not been
refactored yet.
Expand Down
7 changes: 7 additions & 0 deletions betty/about.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Provide information about (this version of) Betty."""

from __future__ import annotations

import platform
Expand Down Expand Up @@ -40,6 +42,11 @@ def _indent_mapping_item(key: str, value: str, max_indentation: int) -> Iterator


def report() -> str:
"""
Produce a human-readable report about the current Betty installation.
:returns: A human-readable string in US English, using monospace indentation.
"""
return _indent_mapping({
'Betty': version_label(),
'Operating system': platform.platform(),
Expand Down
2 changes: 1 addition & 1 deletion betty/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ def servers(self) -> Mapping[str, Server]:
if isinstance(extension, serve.ServerProvider)
for server in extension.servers
),
serve.BuiltinServer(self),
serve.BuiltinAppServer(self),
DemoServer(),
]
}
Expand Down
2 changes: 2 additions & 0 deletions betty/app/extension/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class ExtensionTypeImportError(ExtensionTypeError, ImportError):
"""
Raised when an alleged extension type cannot be imported.
"""

def __init__(self, extension_type_name: str):
super().__init__(f'Cannot find and import an extension with name "{extension_type_name}".')

Expand All @@ -42,6 +43,7 @@ class ExtensionTypeInvalidError(ExtensionTypeError, ImportError):
"""
Raised for types that are not valid extension types.
"""

def __init__(self, extension_type: type):
super().__init__(f'{extension_type.__module__}.{extension_type.__name__} is not an extension type class. Extension types must extend {Extension.__module__}.{Extension.__name__}.')

Expand Down
3 changes: 2 additions & 1 deletion betty/app/extension/requirement.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ def localize(self, localizer: Localizer) -> str:

def reduce(self) -> Requirement | None:
"""
Removes unnecessary components of this requirement.
Remove unnecessary components of this requirement.
- Collections can flatten unnecessary hierarchies.
- Empty decorators or collections can 'dissolve' themselves and return None.
Expand Down
5 changes: 4 additions & 1 deletion betty/assets/betty.pot
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Betty VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-01-08 19:21+0000\n"
"POT-Creation-Date: 2024-01-09 12:39+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
Expand Down Expand Up @@ -168,6 +168,9 @@ msgstr ""
msgid "Beneficiary"
msgstr ""

msgid "Betty documentation"
msgstr ""

msgid "Betty helps you visualize and publish your family history by building interactive genealogy websites out of your <a href=\"{gramps_url}\">Gramps</a> and <a href=\"{gedcom_url}\">GEDCOM</a> family trees."
msgstr ""

Expand Down
5 changes: 4 additions & 1 deletion betty/assets/locale/fr-FR/betty.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-01-08 19:21+0000\n"
"POT-Creation-Date: 2024-01-09 12:39+0000\n"
"PO-Revision-Date: 2020-11-27 19:49+0100\n"
"Last-Translator: \n"
"Language: fr\n"
Expand Down Expand Up @@ -198,6 +198,9 @@ msgstr "Baptême"
msgid "Beneficiary"
msgstr "Bénéficiaire"

msgid "Betty documentation"
msgstr "Documentation de Betty"

msgid ""
"Betty helps you visualize and publish your family history by building "
"interactive genealogy websites out of your <a "
Expand Down
5 changes: 4 additions & 1 deletion betty/assets/locale/nl-NL/betty.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-01-08 19:21+0000\n"
"POT-Creation-Date: 2024-01-09 12:39+0000\n"
"PO-Revision-Date: 2022-04-08 01:58+0100\n"
"Last-Translator: \n"
"Language: nl\n"
Expand Down Expand Up @@ -211,6 +211,9 @@ msgstr "Doping"
msgid "Beneficiary"
msgstr "Begunstigde"

msgid "Betty documentation"
msgstr "Betty-documentatie"

msgid ""
"Betty helps you visualize and publish your family history by building "
"interactive genealogy websites out of your <a "
Expand Down
5 changes: 4 additions & 1 deletion betty/assets/locale/uk/betty.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Betty VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-01-08 19:21+0000\n"
"POT-Creation-Date: 2024-01-09 12:39+0000\n"
"PO-Revision-Date: 2020-05-02 22:29+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: uk\n"
Expand Down Expand Up @@ -198,6 +198,9 @@ msgstr "Хрещення"
msgid "Beneficiary"
msgstr "Бенефіціар"

msgid "Betty documentation"
msgstr ""

msgid ""
"Betty helps you visualize and publish your family history by building "
"interactive genealogy websites out of your <a "
Expand Down
17 changes: 16 additions & 1 deletion betty/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from PyQt6.QtWidgets import QMainWindow
from click import get_current_context, Context, Option, Command, Parameter

from betty import about, generate, load
from betty import about, generate, load, documentation, fs
from betty.app import App
from betty.asyncio import sync, wait
from betty.error import UserFacingError
Expand Down Expand Up @@ -98,6 +98,7 @@ async def _init_ctx_app(

app = App()
ctx.obj['commands'] = {
'docs': _docs,
'clear-caches': _clear_caches,
'demo': _demo,
'gui': _gui,
Expand Down Expand Up @@ -270,6 +271,20 @@ async def _serve(app: App) -> None:
await asyncio.sleep(999)


@click.command(help='View the documentation.')
@global_command
async def _docs():
async with App() as app:
server = documentation._DocumentationServer(
fs.CACHE_DIRECTORY_PATH,
localizer=app.localizer,
)
async with server:
await server.show()
while True:
await asyncio.sleep(999)


if about.is_development():
@click.command(short_help='Initialize a new translation', help='Initialize a new translation.\n\nThis is available only when developing Betty.')
@click.argument('locale')
Expand Down
1 change: 0 additions & 1 deletion betty/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ def load(
"""
Load dumped configuration into a new configuration instance.
"""

raise NotImplementedError(repr(cls))

@classmethod
Expand Down
71 changes: 71 additions & 0 deletions betty/documentation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import asyncio
import logging
import os
import shutil
from contextlib import suppress, AsyncExitStack
from pathlib import Path
from subprocess import CalledProcessError
from tempfile import TemporaryDirectory

from betty import subprocess, serve
from betty.locale import Str, Localizer
from betty.serve import Server, NoPublicUrlBecauseServerNotStartedError


async def _build_cache(cache_directory_path: Path) -> Path:
cache_directory_path /= 'docs'
if not cache_directory_path.exists():
await _build(cache_directory_path)
return cache_directory_path


async def _build(output_directory_path: Path) -> None:
with suppress(FileExistsError):
await asyncio.to_thread(os.makedirs, output_directory_path)
with TemporaryDirectory() as working_directory_path:
source_directory_path = Path(working_directory_path) / 'source'
await asyncio.to_thread(shutil.copytree, Path(__file__).parent.parent / 'documentation', source_directory_path)
try:
await subprocess.run_exec(['sphinx-apidoc', '--force', '--separate', '-d', '999', '-o', str(source_directory_path), 'betty', 'betty/tests'])
await subprocess.run_exec(['sphinx-build', str(source_directory_path), str(output_directory_path)])
except CalledProcessError as e:
if e.stderr is not None:
logging.getLogger().error(e.stderr)
raise


class _DocumentationServer(Server):
def __init__(
self,
cache_directory_path: Path,
*,
localizer: Localizer,
):
super().__init__(localizer)
self._cache_directory_path = cache_directory_path
self._server: Server | None = None
self._exit_stack = AsyncExitStack()

@classmethod
def label(cls) -> Str:
return Str._('Betty documentation')

@property
def public_url(self) -> str:
if self._server is not None:
return self._server.public_url
raise NoPublicUrlBecauseServerNotStartedError()

async def start(self) -> None:
await super().start()
try:
www_directory_path = await _build_cache(self._cache_directory_path)
self._server = serve.BuiltinServer(www_directory_path, localizer=self._localizer)
await self._exit_stack.enter_async_context(self._server)
except BaseException:
await self.stop()
raise

async def stop(self) -> None:
await self._exit_stack.aclose()
await super().stop()
1 change: 1 addition & 0 deletions betty/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class UserFacingError(Exception, Localizable):
This type of error is fatal, but fixing it does not require knowledge of Betty's internals or the stack trace
leading to the error. It must therefore have an end-user-friendly message, and its stack trace must not be shown.
"""

def __init__(self, message: Localizable):
super().__init__(
# Provide a default localization so this exception can be displayed like any other.
Expand Down
4 changes: 2 additions & 2 deletions betty/extension/demo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ def __init__(self):
from betty.extension import Demo

self._app = App(None, Demo.project())
super().__init__(self._app.localizer)
super().__init__(localizer=self._app.localizer)
self._server: Server | None = None
self._exit_stack = AsyncExitStack()

Expand All @@ -397,7 +397,7 @@ async def start(self) -> None:
try:
await self._exit_stack.enter_async_context(self._app)
await load.load(self._app)
self._server = serve.BuiltinServer(self._app)
self._server = serve.BuiltinAppServer(self._app)
await self._exit_stack.enter_async_context(self._server)
self._app.project.configuration.base_url = self._server.public_url
await generate.generate(self._app)
Expand Down
1 change: 1 addition & 0 deletions betty/gui/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class _LogRecordViewerHandlerObject(QObject):
"""
Provide a signal for logging handlers to log records to a LogRecordViewer in the main (GUI) thread.
"""

log = pyqtSignal(logging.LogRecord)

def __init__(self, viewer: LogRecordViewer):
Expand Down
12 changes: 7 additions & 5 deletions betty/jinja2.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,15 +427,15 @@ def _filter_format_datey(
@pass_context
def _filter_json(context: Context, data: Any, indent: int | None = None) -> str:
"""
Converts a value to a JSON string.
Convert a value to a JSON string.
"""
return stdjson.dumps(data, indent=indent, cls=(context_app(context).json_encoder))


@pass_context
def _filter_tojson(context: Context, data: Any, indent: int | None = None) -> str:
"""
Converts a value to a JSON string safe for use in an HTML document.
Convert a value to a JSON string safe for use in an HTML document.
This mimics Jinja2's built-in JSON filter, but uses Betty's own JSON encoder.
"""
Expand All @@ -457,9 +457,11 @@ def _filter_walk(value: Any, attribute_name: str) -> Iterable[Any]:

@pass_eval_context
def _filter_paragraphs(eval_ctx: EvalContext, text: str) -> str | Markup:
"""Converts newlines to <p> and <br> tags.
"""
Convert newlines to <p> and <br> tags.
Taken from http://jinja.pocoo.org/docs/2.10/api/#custom-filters."""
Taken from http://jinja.pocoo.org/docs/2.10/api/#custom-filters.
"""
result = u'\n\n'.join(u'<p>%s</p>' % p.replace('\n', Markup('<br>\n'))
for p in _paragraph_re.split(escape(text)))
if eval_ctx.autoescape:
Expand Down Expand Up @@ -492,7 +494,7 @@ async def _filter_unique(value: Iterable[T]) -> AsyncIterator[T]:
@pass_context
async def _filter_map(context: Context, values: Iterable[Any], *args: Any, **kwargs: Any) -> Any:
"""
Maps an iterable's values.
Map an iterable's values.
This mimics Jinja2's built-in map filter, but allows macros as callbacks.
"""
Expand Down
2 changes: 1 addition & 1 deletion betty/locale.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ def _date_range_formatters(self) -> DateRangeFormatters:

def format_datey(self, date: Datey) -> str:
"""
Formats a datey value into a human-readable string.
Format a datey value into a human-readable string.
"""
if isinstance(date, Date):
return self.format_date(date)
Expand Down
2 changes: 2 additions & 0 deletions betty/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ class EntityTypeImportError(EntityTypeError, ImportError):
"""
Raised when an alleged entity type cannot be imported.
"""

def __init__(self, entity_type_name: str):
super().__init__(f'Cannot find and import an entity with name "{entity_type_name}".')

Expand All @@ -154,6 +155,7 @@ class EntityTypeInvalidError(EntityTypeError, ImportError):
"""
Raised for types that are not valid entity types.
"""

def __init__(self, entity_type: type):
super().__init__(f'{entity_type.__module__}.{entity_type.__name__} is not an entity type class. Entity types must extend {Entity.__module__}.{Entity.__name__} directly.')

Expand Down
1 change: 0 additions & 1 deletion betty/serde/dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,4 @@ def dump(self) -> VoidableDump:
"""
Dump this instance to a portable format.
"""

raise NotImplementedError(repr(self))
Loading

0 comments on commit ad3c3d3

Please sign in to comment.