Skip to content

Commit

Permalink
Add automatically generated documentation (eclipse-basyx#4)
Browse files Browse the repository at this point in the history
This adds the necessary configuration files for
automatically generating documentation based on 
the code docstrings, similarly to how it works in 
the [BaSyx-Python-SDK Docs]. 

We also add a simple GitHub workflow CI job to 
verify the compilation of docs without errors. 

[BaSyx-Python-SDK Docs](https://github.com/eclipse-basyx/basyx-python-sdk/tree/main/docs)
  • Loading branch information
JAB1305 authored Oct 2, 2024
1 parent 7af7dbe commit 5e5c5e8
Show file tree
Hide file tree
Showing 12 changed files with 270 additions and 40 deletions.
23 changes: 3 additions & 20 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,24 +95,7 @@ jobs:
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
# TODO add autodoc test


package:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ env.X_PYTHON_VERSION }}
uses: actions/setup-python@v2
with:
python-version: ${{ env.X_PYTHON_VERSION }}
- name: Install dependencies
pip install -r docs/docs-requirements.txt
- name: Check documentation for errors
run: |
python -m pip install --upgrade pip
pip install -q build
- name: Create source and wheel dist
run: |
ls
cd ./sdk
python -m build
SPHINXOPTS="-a -E -n -W --keep-going" make -C docs html
16 changes: 16 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

version: 2

build:
os: ubuntu-lts-latest
tools:
python: "3.9"

sphinx:
configuration: docs/source/conf.py

python:
install:
- requirements: docs/docs-requirements.txt
20 changes: 20 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
23 changes: 23 additions & 0 deletions docs/docs-requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# This requirement list is only used by ReadTheDocs to compile the documentation on their side.
# For now the pyproject.toml is unfortunately not an option to specify required dependencies as well as self uploading
# compiled documentations. Until this changes, this file is necessary and should be kept up to date.

################################################
# Basic requirements of basyx-python-framework #
################################################
# These are technically redundant, as they are already kept in the pyproject.toml.
# Please updated these entries here when updating the projects dependencies.

aas-core3.0~=1.0.4


#################################################
# Additional requirements for building the docs #
#################################################

sphinx~=7.1.2
sphinx-rtd-theme~=2.0
sphinx-argparse~=0.4.0
sphinx_autodoc_typehints~=2.0.0
toml~=0.10.2
urllib3>=1.26,<2.0
35 changes: 35 additions & 0 deletions docs/make.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@ECHO OFF

pushd %~dp0

REM Command file for Sphinx documentation

if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build

%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)

if "%1" == "" goto help

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end

:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%

:end
popd
7 changes: 7 additions & 0 deletions docs/source/_static/_static/custom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* Fix white-space wrapping in tables.
* See https://github.com/readthedocs/sphinx_rtd_theme/issues/1505
* This is included via html_static_path and html_style in conf.py
*/
.wy-table-responsive table td {
white-space: normal;
}
5 changes: 5 additions & 0 deletions docs/source/client/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.. basyx-python-framework documentation sub file, regarding the client module
client
======
.. FUTURE: automodule:: sdk.basyx.
100 changes: 100 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
import datetime
import toml

# Load pyproject.toml
with open("../../sdk/pyproject.toml", "r") as f:
pyproject_data = toml.load(f)

# Extract the version
release = pyproject_data["project"]["version"]

# Add the root directory of the project to sys.path
sys.path.insert(0, os.path.abspath('../..'))

# -- Project information -----------------------------------------------------

project = 'Eclipse BaSyx Python Framework'
project_copyright = str(datetime.datetime.now().year) + ', the Eclipse BaSyx Authors'
author = 'The Eclipse BaSyx Authors'

# The full version, including alpha/beta/rc tags
release = "none"


# -- General configuration ---------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.

extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.coverage',
'sphinx.ext.intersphinx',
'sphinx_rtd_theme',
'sphinxarg.ext',
'sphinx_autodoc_typehints' # Allow TypeVars to be compiled properly.
]

# Add any paths that contain templates here, relative to this directory.
templates_path = []

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []

# Don't prepend the name of the current module to all classes.
add_module_names = False

# Include all public documented and undocumented members by default.
autodoc_default_options = {
'members': True,
'undoc-members': True
}

# Mapping for correctly linking other module documentations.
intersphinx_mapping = {
'python': ('https://docs.python.org/3', None),
'aas_core3': ('https://aas-core30-python.readthedocs.io/en/latest/', None)
}

# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# Fix white-space wrapping in tables. css files specified here will be applied on top of the selected theme.
# See https://github.com/readthedocs/sphinx_rtd_theme/issues/1505
# Once fixed, this can be removed and '_static' can be removed from html_static_path.
html_css_files = ["custom.css"]

# Configuration of the 'Edit on GitHub' button at the top right.
html_context = {
'display_github': True,
'github_user': 'eclipse-basyx',
'github_repo': 'basyx-python-framework',
'github_version': 'docs',
'conf_py_path': '/docs/source/'
}
21 changes: 21 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.. basyx-python-framework documentation master file, created by
sphinx-quickstart on Fri Aug 16 23:03:59 2024
Welcome to the Eclipse-BaSyx Python Framework's documentation!
==============================================================

.. toctree::
:numbered:
:maxdepth: 2
:caption: Contents:

sdk/index
client/index
server/index


Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
6 changes: 6 additions & 0 deletions docs/source/sdk/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.. basyx-python-framework documentation sub file, regarding the sdk module
sdk
===

.. automodule:: sdk.basyx.object_store
5 changes: 5 additions & 0 deletions docs/source/server/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.. basyx-python-framework documentation sub file, regarding the server module
server
======
.. FUTURE: automodule:: sdk.basyx.
49 changes: 29 additions & 20 deletions sdk/basyx/object_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,47 +6,56 @@
# SPDX-License-Identifier: MIT
"""
This module implements Registries for the AAS, in order to enable resolving global
`Identifiers`; and mapping `Identifiers` to `Identifiable` objects.
:any:`Identifier <aas_core3.verification.verify_identifier>` and mapping
:any:`Identifiers <aas_core3.verification.verify_identifier>` to :class:`~aas_core3.types.Identifiable` objects.
"""

import abc
from typing import MutableSet, Iterator, Generic, TypeVar, Dict, List, Optional, Iterable

from aas_core3.types import Identifiable, Referable, Class

# We define types for :class:`aas_core3.types.Identifier` and :class:`aas_core3.types.Referable` for easier referencing.
_IdentifiableType = TypeVar('_IdentifiableType', bound=Identifiable)
_ReferableType = TypeVar('_ReferableType', bound=Referable)


class AbstractObjectProvider(metaclass=abc.ABCMeta):
"""
Abstract baseclass for all objects, that allow to retrieve `Identifiable` objects
(resp. proxy objects for remote `Identifiable` objects) by their `Identifier`.
Abstract baseclass for all objects, that allows to retrieve :class:`~aas_core3.types.Identifiable` objects
(resp. proxy objects for remote :class:`~aas_core3.types.Identifiable` objects) by their
:any:`Identifier <aas_core3.verification.verify_identifier>`
This includes local object stores, database clients and AAS API clients.
"""

@abc.abstractmethod
def get_identifiable(self, identifier: str) -> Identifiable:
"""
Find an `Identifiable` by its `Identifier`
Find an :class:`~aas_core3.types.Identifiable` by its
:any:`Identifier <aas_core3.verification.verify_identifier>`
This may include looking up the object's endpoint in a registry and fetching it from an HTTP server or a
database.
:param identifier: `Identifier` of the object to return
:return: The `Identifiable` object (or a proxy object for a remote `Identifiable` object)
:raises KeyError: If no such `Identifiable` can be found
:param identifier: :any:`Identifier <aas_core3.verification.verify_identifier>` of the object to return
:return: The :class:`~aas_core3.types.Identifiable` object (or a proxy object for a remote
:class:`~aas_core3.types.Identifiable` object)
:raises KeyError: If no such :class:`~aas_core3.types.Identifiable` can be found
"""
pass

def get(self, identifier: str, default: Optional[Identifiable] = None) -> Optional[Identifiable]:
"""
Find an object in this set by its `Identifier`, with fallback parameter
Find an object in this set by its :any:`Identifier <aas_core3.verification.verify_identifier>`,
with fallback parameter
:param identifier: `Identifier` of the object to return
:param default: An object to be returned, if no object with the given `Identifier` is found
:return: The `Identifiable` object with the given `Identifier` in the provider. Otherwise, the ``default``
object or None, if none is given.
:param identifier: :any:`Identifier <aas_core3.verification.verify_identifier>` of the object to return
:param default: An object to be returned, if no object with the given
:any:`Identifier <aas_core3.verification.verify_identifier>` is found
:return: The :class:`~aas_core3.types.Identifiable` object with the given
:any:`Identifier <aas_core3.verification.verify_identifier>` in the provider.
Otherwise, the ``default`` object or None, if none is given.
"""
try:
return self.get_identifiable(identifier)
Expand All @@ -57,11 +66,11 @@ def get(self, identifier: str, default: Optional[Identifiable] = None) -> Option
class AbstractObjectStore(AbstractObjectProvider, MutableSet[_IdentifiableType], Generic[_IdentifiableType],
metaclass=abc.ABCMeta):
"""
Abstract baseclass of for container-like objects for storage of `Identifiable` objects.
Abstract baseclass of for container-like objects for storage of :class:`~aas_core3.types.Identifiable` objects.
ObjectStores are special ObjectProvides that – in addition to retrieving objects by
`Identifier` – allow to add and delete objects (i.e. behave like a Python set).
This includes local object stores (like :class:`~.DictObjectStore`) and database `Backends`.
:any:`Identifier <aas_core3.verification.verify_identifier>` – allow to add and delete objects (i.e. behave like a
Python set).
The AbstractObjectStore inherits from the :class:`~collections.abc.MutableSet` abstract collections class and
therefore implements all the functions of this class.
Expand All @@ -78,8 +87,8 @@ def update(self, other: Iterable[_IdentifiableType]) -> None:

class ObjectStore(AbstractObjectStore[_IdentifiableType], Generic[_IdentifiableType]):
"""
A local in-memory object store for `Identifiable` objects, backed by a dict, mapping
`Identifier` → `Identifiable`
A local in-memory object store for :class:`~aas_core3.types.Identifiable` objects, backed by a dict, mapping
:any:`Identifier <aas_core3.verification.verify_identifier>` → :class:`~aas_core3.types.Identifiable`
"""

def __init__(self, objects: Iterable[_IdentifiableType] = ()) -> None:
Expand Down Expand Up @@ -197,10 +206,10 @@ def __iter__(self) -> Iterator[_IdentifiableType]:

class ObjectProviderMultiplexer(AbstractObjectProvider):
"""
A multiplexer for Providers of `Identifiable` objects.
A multiplexer for Providers of :class:`~aas_core3.types.Identifiable` objects.
This class combines multiple registries of `Identifiable` objects into a single one
to allow retrieving `Identifiable` objects from different sources.
This class combines multiple registries of :class:`~aas_core3.types.Identifiable` objects into a single one
to allow retrieving :class:`~aas_core3.types.Identifiable` objects from different sources.
It implements the :class:`~.AbstractObjectProvider` interface to be used as registry itself.
:ivar providers: A list of :class:`AbstractObjectProviders <.AbstractObjectProvider>` to query when looking up an
Expand Down

0 comments on commit 5e5c5e8

Please sign in to comment.