diff options
author | Fabien Castan <fabcastan@gmail.com> | 2022-10-21 17:49:28 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-21 17:49:28 +0300 |
commit | a0068c3da4c4f120e849416d90a5683c35cdf994 (patch) | |
tree | 6dc8cc894219fd6447fb125f1db55c072adb6db0 | |
parent | ad69c7085e4f7bf225670bead4697794aeb47279 (diff) | |
parent | f3bed081bda887d2c9df953fbdbe30d01829940e (diff) |
Merge pull request #1794 from alicevision/dev/lv/sphinxDoc
[docs] Python documentation generation using Sphinx
-rw-r--r-- | .readthedocs.yaml | 18 | ||||
-rw-r--r-- | docs/.gitignore | 3 | ||||
-rw-r--r-- | docs/Makefile | 20 | ||||
-rw-r--r-- | docs/README.md | 24 | ||||
-rw-r--r-- | docs/make.bat | 35 | ||||
-rw-r--r-- | docs/requirements.txt | 1 | ||||
-rw-r--r-- | docs/source/_ext/__init__.py | 0 | ||||
-rw-r--r-- | docs/source/_ext/fetch_md.py | 70 | ||||
-rw-r--r-- | docs/source/_ext/meshroom_doc.py | 67 | ||||
-rw-r--r-- | docs/source/_ext/utils.py | 25 | ||||
-rw-r--r-- | docs/source/_templates/autosummary/class.rst | 38 | ||||
-rw-r--r-- | docs/source/_templates/autosummary/module.rst | 63 | ||||
-rw-r--r-- | docs/source/api.rst | 10 | ||||
-rw-r--r-- | docs/source/changes.rst | 5 | ||||
-rw-r--r-- | docs/source/conf.py | 43 | ||||
-rw-r--r-- | docs/source/index.rst | 14 | ||||
-rw-r--r-- | docs/source/install.rst | 5 | ||||
-rw-r--r-- | meshroom/__init__.py | 19 | ||||
-rw-r--r-- | meshroom/common/__init__.py | 73 | ||||
-rw-r--r-- | meshroom/ui/__init__.py | 2 | ||||
-rw-r--r-- | meshroom/ui/__main__.py | 13 |
21 files changed, 474 insertions, 74 deletions
diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..5aebe693 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,18 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Build HTML documentation with Sphinx +sphinx: + builder: html + configuration: docs/source/conf.py + +# Python requirements +python: + install: + - requirements: requirements.txt + - requirements: dev_requirements.txt + - requirements: docs/requirements.txt diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..50e5506a --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,3 @@ +# Sphinx +build/ +source/generated/ diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..d0c3cbf1 --- /dev/null +++ b/docs/Makefile @@ -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) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..6680e8c8 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,24 @@ +# Documentation + +We use [Sphinx](https://www.sphinx-doc.org) to generate Meshroom's documentation. + +## Requirements + +To install all the requirements for building the documentation, simply run: +```bash +pip install sphinx sphinx-rtd-theme myst-parser +``` + +You also need to have [Graphviz](https://graphviz.org/) installed. + +> Note: since Sphinx will import the entire `meshroom` package, all requirements for Meshroom must also be installed + +## Build + +To generate the documentation, go to the `docs` folder and run the Sphinx makefile: +```bash +cd meshroom/docs +make html +``` + +To access the documentation, simply go to `meshroom/docs/build/html` and open `index.html` in a browser. diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..dc1312ab --- /dev/null +++ b/docs/make.bat @@ -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 diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..f0694bdc --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1 @@ +myst-parser diff --git a/docs/source/_ext/__init__.py b/docs/source/_ext/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/docs/source/_ext/__init__.py diff --git a/docs/source/_ext/fetch_md.py b/docs/source/_ext/fetch_md.py new file mode 100644 index 00000000..fa85fcc3 --- /dev/null +++ b/docs/source/_ext/fetch_md.py @@ -0,0 +1,70 @@ +# Sphinx extension defining the fetch_md directive +# +# Goal: +# include the content of a given markdown file +# +# Usage: +# .. fetch_md:: path/to/file.md +# the filepath is relative to the project base directory +# +# Note: +# some markdown files contain links to other files that belong to the project +# since those links are relative to the file location, they are no longer valid in the new context +# therefore we try to update these links but it is not always possible + +import os +from docutils.nodes import SparseNodeVisitor +from docutils.parsers.rst import Directive +from utils import md_to_docutils, get_link_key + +# Python2 compatibility +try: + FileNotFoundError +except NameError: + FileNotFoundError = IOError + + +class Relinker(SparseNodeVisitor): + + def relink(self, node, base_dir): + key = get_link_key(node) + if key is None: + return + link = node.attributes[key] + if link.startswith('http') or link.startswith('mailto'): + return + if link.startswith('/'): + link = link[1:] + node.attributes[key] = base_dir+'/'+link + + def visit_image(self, node): + self.relink(node, os.getenv('PROJECT_DIR')) + + +class FetchMd(Directive): + + required_arguments = 1 + + def run(self): + path = os.path.abspath(os.getenv('PROJECT_DIR')+'/'+self.arguments[0]) + result = [] + try: + with open(path) as file: + text = file.read() + doc = md_to_docutils(text) + relinker = Relinker(doc) + doc.walk(relinker) + result.append(doc[0]) + except FileNotFoundError: + pass + return result + + +def setup(app): + app.add_directive('fetch_md', FetchMd) + + return { + 'version': '0.1', + 'parallel_read_safe': True, + 'parallel_write_safe': True + } diff --git a/docs/source/_ext/meshroom_doc.py b/docs/source/_ext/meshroom_doc.py new file mode 100644 index 00000000..d5151ef7 --- /dev/null +++ b/docs/source/_ext/meshroom_doc.py @@ -0,0 +1,67 @@ +# Sphinx extension defining the meshroom_doc directive +# +# Goal: +# create specific documentation content for meshroom objects +# +# Usage: +# .. meshroom_doc:: +# :module: module_name +# :class: class_name +# +# Note: +# for now this tool focuses only on meshroom nodes + +from docutils import nodes +from docutils.parsers.rst import Directive +from utils import md_to_docutils + +import importlib +from meshroom.core import desc + + +class MeshroomDoc(Directive): + + required_arguments = 4 + + def parse_args(self): + module_name = self.arguments[self.arguments.index(':module:')+1] + class_name = self.arguments[self.arguments.index(':class:')+1] + return (module_name, class_name) + + def run(self): + result = [] + # Import module and class + module_name, class_name = self.parse_args() + module = importlib.import_module(module_name) + node_class = getattr(module, class_name) + # Class inherits desc.Node + if issubclass(node_class, desc.Node): + node = node_class() + # Category + doc = md_to_docutils('**Category**: {}'.format(node.category)) + result.extend(doc.children) + # Documentation + doc = md_to_docutils(node.documentation) + result.extend(doc.children) + # Inputs + text_inputs = '**Inputs**: \n' + for attr in node.inputs: + text_inputs += '- {} ({})\n'.format(attr._name, attr.__class__.__name__) + doc = md_to_docutils(text_inputs) + result.extend(doc.children) + # Outputs + text_outputs = '**Outputs**: \n' + for attr in node.outputs: + text_outputs += '- {} ({})\n'.format(attr._name, attr.__class__.__name__) + doc = md_to_docutils(text_outputs) + result.extend(doc.children) + return result + + +def setup(app): + app.add_directive("meshroom_doc", MeshroomDoc) + return { + 'version': '0.1', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } diff --git a/docs/source/_ext/utils.py b/docs/source/_ext/utils.py new file mode 100644 index 00000000..cbfbb6b9 --- /dev/null +++ b/docs/source/_ext/utils.py @@ -0,0 +1,25 @@ +# Utility functions for custom Sphinx extensions + +from myst_parser.docutils_ import Parser +from myst_parser.mdit_to_docutils.base import make_document + + +# Given a string written in markdown +# parse its content +# and return the corresponding docutils document +def md_to_docutils(text): + parser = Parser() + doc = make_document(parser_cls=Parser) + parser.parse(text, doc) + return doc + + +# Given a docutils node +# find an attribute that corresponds to a link (if it exists) +# and return its key +def get_link_key(node): + link_keys = ['uri', 'refuri', 'refname'] + for key in link_keys: + if key in node.attributes.keys(): + return key + return None diff --git a/docs/source/_templates/autosummary/class.rst b/docs/source/_templates/autosummary/class.rst new file mode 100644 index 00000000..958658a3 --- /dev/null +++ b/docs/source/_templates/autosummary/class.rst @@ -0,0 +1,38 @@ +{{ fullname | escape | underline}} + + +.. inheritance-diagram:: {{ fullname }} + + +.. meshroom_doc:: + :module: {{ module }} + :class: {{ objname }} + + +.. currentmodule:: {{ module }} + +.. autoclass:: {{ objname }} + + {% block methods %} + .. automethod:: __init__ + + {% if methods %} + .. rubric:: {{ _('Methods') }} + + .. autosummary:: + {% for item in methods %} + ~{{ name }}.{{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block attributes %} + {% if attributes %} + .. rubric:: {{ _('Attributes') }} + + .. autosummary:: + {% for item in attributes %} + ~{{ name }}.{{ item }} + {%- endfor %} + {% endif %} + {% endblock %} diff --git a/docs/source/_templates/autosummary/module.rst b/docs/source/_templates/autosummary/module.rst new file mode 100644 index 00000000..635912f7 --- /dev/null +++ b/docs/source/_templates/autosummary/module.rst @@ -0,0 +1,63 @@ +{{ fullname | escape | underline}} + + +.. automodule:: {{ fullname }} + + {% block attributes %} + {% if attributes %} + .. rubric:: {{ _('Module Attributes') }} + + .. autosummary:: + {% for item in attributes %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block functions %} + {% if functions %} + .. rubric:: {{ _('Functions') }} + + .. autosummary:: + {% for item in functions %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block classes %} + {% if classes %} + .. rubric:: {{ _('Classes') }} + + .. autosummary:: + :toctree: + :recursive: + {% for item in classes %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block exceptions %} + {% if exceptions %} + .. rubric:: {{ _('Exceptions') }} + + .. autosummary:: + {% for item in exceptions %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + +{% block modules %} +{% if modules %} +.. rubric:: Modules + +.. autosummary:: + :toctree: + :recursive: +{% for item in modules %} + {{ item }} +{%- endfor %} +{% endif %} +{% endblock %} diff --git a/docs/source/api.rst b/docs/source/api.rst new file mode 100644 index 00000000..a47d7527 --- /dev/null +++ b/docs/source/api.rst @@ -0,0 +1,10 @@ +Python API Reference +==================== + + +.. autosummary:: + :recursive: + :toctree: generated + + meshroom + tests diff --git a/docs/source/changes.rst b/docs/source/changes.rst new file mode 100644 index 00000000..6f46995b --- /dev/null +++ b/docs/source/changes.rst @@ -0,0 +1,5 @@ +Release Notes +============= + + +.. fetch_md:: CHANGES.md diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 00000000..33c201d9 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,43 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +import os +from pathlib import Path +import sys + +os.environ['PROJECT_DIR'] = Path('../..').resolve().as_posix() + +sys.path.append(os.path.abspath(os.getenv('PROJECT_DIR'))) +sys.path.append(os.path.abspath('./_ext')) + +project = 'Meshroom' +copyright = '2022, AliceVision Association' +author = 'AliceVision Association' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.autosummary', + 'fetch_md', + 'meshroom_doc', + 'sphinx.ext.graphviz', + 'sphinx.ext.inheritance_diagram' +] + +templates_path = ['_templates'] +exclude_patterns = [] + + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'sphinx_rtd_theme' +html_static_path = ['_static'] diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 00000000..e1c45124 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,14 @@ +Welcome to meshroom's documentation! +==================================== + + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + api + install + changes + + +.. fetch_md:: README.md diff --git a/docs/source/install.rst b/docs/source/install.rst new file mode 100644 index 00000000..a6500ed1 --- /dev/null +++ b/docs/source/install.rst @@ -0,0 +1,5 @@ +Install +======= + + +.. fetch_md:: INSTALL.md diff --git a/meshroom/__init__.py b/meshroom/__init__.py index 9b091cfe..c29a3450 100644 --- a/meshroom/__init__.py +++ b/meshroom/__init__.py @@ -2,10 +2,10 @@ __version__ = "2021.1.0" __version_name__ = __version__ from distutils import util -from enum import Enum import logging import os import sys +from .common import init, Backend # sys.frozen is initialized by cx_Freeze and identifies a release package isFrozen = getattr(sys, "frozen", False) @@ -25,20 +25,7 @@ __version_name__ = os.environ.get("REZ_MESHROOM_VERSION", __version_name__) useMultiChunks = util.strtobool(os.environ.get("MESHROOM_USE_MULTI_CHUNKS", "True")) -class Backend(Enum): - STANDALONE = 1 - PYSIDE = 2 - - -backend = Backend.STANDALONE - - -def useUI(): - global backend - backend = Backend.PYSIDE - - -def setupEnvironment(): +def setupEnvironment(backend=Backend.STANDALONE): """ Setup environment for Meshroom to work in a prebuilt, standalone configuration. @@ -61,6 +48,8 @@ def setupEnvironment(): COPYING.md # Meshroom COPYING file """ + init(backend) + def addToEnvPath(var, val, index=-1): """ Add paths to the given environment variable. diff --git a/meshroom/common/__init__.py b/meshroom/common/__init__.py index 3331e9c6..db73fe05 100644 --- a/meshroom/common/__init__.py +++ b/meshroom/common/__init__.py @@ -1,4 +1,15 @@ -import meshroom +""" +This module provides an abstraction around standard non-gui Qt notions (like Signal/Slot), +so it can be used in python-only without the dependency to Qt. + +Warning: A call to `init(Backend.XXX)` is required to choose the backend before using this module. +""" + +from enum import Enum + +class Backend(Enum): + STANDALONE = 1 + PYSIDE = 2 DictModel = None ListModel = None @@ -10,52 +21,14 @@ Variant = None VariantList = None JSValue = None -if meshroom.backend == meshroom.Backend.PYSIDE: - # PySide types - from .qt import DictModel, ListModel, Slot, Signal, Property, BaseObject, Variant, VariantList, JSValue -elif meshroom.backend == meshroom.Backend.STANDALONE: - # Core types - from .core import DictModel, ListModel, Slot, Signal, Property, BaseObject, Variant, VariantList, JSValue - - -class _BaseModel: - """ Common API for model implementation """ - - def __init__(self, keyAttrName="name", **kwargs): - """ - :param keyAttrName: name of the attribute containing the unique key for an object - """ - pass - - @property - def objects(self): - """ Return a dictionary with {unique_key: object} mapping""" - return None - - def get(self, key): - """ Get the object with the unique key 'key' """ - pass - - def add(self, obj): - """ Add given object using its 'keyAttrName' as unique key """ - pass - - def pop(self, key): - """ Remove 'item' with unique key 'key' from the model """ - pass - - def remove(self, obj): - """ Remove 'obj' from the model """ - pass - - def clear(self): - """ Remove all internal objects """ - pass - - def update(self, d): - """ Combine dict 'd' with self """ - pass - - def reset(self, d): - """ Reset model with given values """ - pass +def init(backend): + global DictModel, ListModel, Slot, Signal, Property, BaseObject, Variant, VariantList, JSValue + if backend == Backend.PYSIDE: + # PySide types + from .qt import DictModel, ListModel, Slot, Signal, Property, BaseObject, Variant, VariantList, JSValue + elif backend == Backend.STANDALONE: + # Core types + from .core import DictModel, ListModel, Slot, Signal, Property, BaseObject, Variant, VariantList, JSValue + +# default initialization +init(Backend.STANDALONE) diff --git a/meshroom/ui/__init__.py b/meshroom/ui/__init__.py index 58c3fb21..e69de29b 100644 --- a/meshroom/ui/__init__.py +++ b/meshroom/ui/__init__.py @@ -1,2 +0,0 @@ -import meshroom -meshroom.useUI() diff --git a/meshroom/ui/__main__.py b/meshroom/ui/__main__.py index 5914f1ad..7cede123 100644 --- a/meshroom/ui/__main__.py +++ b/meshroom/ui/__main__.py @@ -1,12 +1,11 @@ import signal import sys import meshroom +from meshroom.common import Backend +meshroom.setupEnvironment(backend=Backend.PYSIDE) -if __name__ == "__main__": - meshroom.setupEnvironment() - - signal.signal(signal.SIGINT, signal.SIG_DFL) - from meshroom.ui.app import MeshroomApp - app = MeshroomApp(sys.argv) - app.exec_() +signal.signal(signal.SIGINT, signal.SIG_DFL) +from meshroom.ui.app import MeshroomApp +app = MeshroomApp(sys.argv) +app.exec_() |