mirror of
https://github.com/ModOrganizer2/pystubs-generation.git
synced 2026-07-27 14:07:13 -07:00
Add Sphinx documentation.
This commit is contained in:
@@ -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)
|
||||
@@ -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
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%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.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
||||
@@ -0,0 +1,70 @@
|
||||
# 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
|
||||
|
||||
sys.path.insert(0, os.path.abspath("../../stubs/2.3rc1/"))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = "MO2 Python Plugin API"
|
||||
copyright = "2020, Holt59"
|
||||
author = "Holt59"
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = "2.3rc1"
|
||||
|
||||
|
||||
# -- 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_autodoc_typehints",
|
||||
"sphinx.ext.napoleon",
|
||||
# "sphinx.ext.autosummary",
|
||||
"sphinx_automodapi.automodapi",
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ["_templates"]
|
||||
|
||||
# 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 = []
|
||||
|
||||
|
||||
# -- 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"
|
||||
|
||||
html_theme_options = {
|
||||
# Toc options
|
||||
"collapse_navigation": False,
|
||||
"titles_only": False,
|
||||
}
|
||||
html_sidebars = {
|
||||
"**": ["globaltoc.html", "relations.html", "sourcelink.html", "searchbox.html"]
|
||||
}
|
||||
|
||||
# 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"]
|
||||
@@ -0,0 +1,53 @@
|
||||
FAQ
|
||||
===
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
1. Why is MO2 throwing an exception when I try to create a type inheriting one of MO2 class?
|
||||
............................................................................................
|
||||
|
||||
This often happens if you forget to call ``super().__init__()`` with the right arguments.
|
||||
Even if the list of arguments is empty (as in the example), it must be called due to a "bug"
|
||||
in ``boost::python``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MySaveGame(mobase.ISaveGame):
|
||||
def __init__(self):
|
||||
super().__init__() # Mandatory!
|
||||
|
||||
2. How can I be sure to implement all the required methods when creating a plugin?
|
||||
..................................................................................
|
||||
|
||||
It is kind of annoying to create a MO2 python plugin that kind of works and have it crash
|
||||
at some point simply because there plugin is missing a function implementation.
|
||||
You can have ``mypy`` warn you for such issue by typing the ``createPlugin`` or ``createPlugins``
|
||||
functions:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import typing
|
||||
|
||||
# A plugin that is missing something (e.g. display()).
|
||||
class MyPlugin(mobase.IPluginTool): ...
|
||||
|
||||
# If you type-hint `createPlugin` by adding `-> mobase.IPlugin`,
|
||||
# `mypy` will warn you that `MyPlugin` is an incomplete class.
|
||||
def createPlugin() -> mobase.IPlugin:
|
||||
return MyPlugin()
|
||||
|
||||
# You can also type-hint `createPlugins`:
|
||||
def createPlugins() -> typing.List[mobase.IPlugin]:
|
||||
return [MyPlugin()]
|
||||
|
||||
|
||||
3. Why are my ``isinstance(x, QObject)`` and ``isinstance(y, QWidget)`` not working?
|
||||
....................................................................................
|
||||
|
||||
Some classes from ``mobase`` are said to inherit ``QObject`` and ``QWidget`` in the
|
||||
stubs, such as :class:`mobase.IDownloadManager`.
|
||||
This is not the case in practice due to limitation in Python and ``boost::python``.
|
||||
The bases are in the stubs to allow for auto-completion and typing, which is then
|
||||
simulated in the real ``mobase`` by overriding ``__getattr__`` and delegating to
|
||||
the underlying ``QObject`` or ``QWidget``.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,29 @@
|
||||
.. MO2 Python Plugin API documentation master file, created by
|
||||
sphinx-quickstart on Wed Jul 15 19:36:59 2020.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to MO2 Python Plugin API's documentation!
|
||||
=================================================
|
||||
|
||||
Plugins are a way to extend Mod Organizers functionality. You can write plugins in either C++ or Python,
|
||||
theoretically with either being as powerful as the other.
|
||||
This documentation is dedicated to writting MO2 **Python** plugins.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
setup-tools
|
||||
plugin-types
|
||||
faq
|
||||
mobase
|
||||
|
||||
|
||||
|
||||
.. Indices and tables
|
||||
.. ==================
|
||||
|
||||
.. * :ref:`genindex`
|
||||
.. * :ref:`modindex`
|
||||
.. * :ref:`search`
|
||||
@@ -0,0 +1,11 @@
|
||||
``mobase`` API
|
||||
==============
|
||||
|
||||
.. currentmodule:: mobase
|
||||
|
||||
.. automodapi:: mobase
|
||||
:no-inheritance-diagram:
|
||||
:no-heading:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
@@ -0,0 +1,7 @@
|
||||
mobase
|
||||
======
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
mobase
|
||||
@@ -0,0 +1,147 @@
|
||||
Type of Plugins
|
||||
===============
|
||||
|
||||
Plugins are passive, that is: they react to events emitted by the core application or extend
|
||||
an existing functionality (like adding support for additional types of installers). Plugins
|
||||
should integrate with Mod Organizers concepts. While you could write an INI editor that works with
|
||||
the global INI files instead of the profile specific ones, what would be the point?
|
||||
|
||||
Depending on where/how the plugin integrates with MO you need to write a different type of plugin,
|
||||
in practice this means you need to implement a different interface (inherit from a different base
|
||||
plugin class from ``mobase``). As mentioned above, plugins are passive: the plugin type decides how/when
|
||||
MO makes requests to/invokes your plugin.
|
||||
All plugins however gain access to MOs own plugin interface so all plugins get to make the same requests
|
||||
to MO.
|
||||
|
||||
Installers
|
||||
----------
|
||||
|
||||
**Interface:** :class:`IPluginInstaller`, :class:`IPluginInstallerSimple`, `IPluginInstallerCustom`
|
||||
**Examples:** ``installer_bain``, ``installer_bundle``, ``installer_fomod``, ``installer_ncc``, ``installer_quick``, ``installer_manual``.
|
||||
|
||||
An installer is invoked when the user tries to install a mod, either by double clicking in the download
|
||||
view or through the "Install Mod.." button or "Reinstall mod" item from the mod lists context menu.
|
||||
There are actually two ways to write an installer, *simple* or *complex*:
|
||||
|
||||
- With *simple* installers, MO does the unpacking of the file but this works only with standard archive formats.
|
||||
The plugin can then select the files and folders that requires extraction, and where to extract them.
|
||||
- *Complex* installers are more flexible but require a bit more work.
|
||||
|
||||
Previewers
|
||||
----------
|
||||
|
||||
**Interface:** :class:`IPluginPreview`
|
||||
**Examples:** ``preview_base``
|
||||
|
||||
These plugins add support for previewing files in the data pane.
|
||||
Right now all image formats supported by Qt are implemented (including `.dds`) but no audio files and
|
||||
no 3d mesh formats.
|
||||
|
||||
Mod Page
|
||||
--------
|
||||
|
||||
*WIP*
|
||||
|
||||
**Interface:** :class:`IPluginModPage`
|
||||
**Examples:** ``page_tesalliance``
|
||||
|
||||
Mod Page plugins implement interfaces to modding communities where mods can be downloaded, checked
|
||||
for updates and so on.
|
||||
This interface is not finished and some of the bits that are do not actually get used. The goal is
|
||||
that the whole Nexus integration can be implemented through this interface and can then be removed
|
||||
from the core application. This is a task for the distant future, unless someone wants to volunteer.
|
||||
|
||||
Game
|
||||
----
|
||||
|
||||
**Interface:** :class:`IPluginGame`
|
||||
**Examples:** ``game_oblivion``, ``game_fallout3``, ``game_falloutnv``, ``game_skyrim``, ...
|
||||
|
||||
These plugins (shall eventually) implement all the game specific features and further game plugins are
|
||||
able to add support for further games.
|
||||
The plugin is also responsible to help MO determine if (and where) the game is installed in the first place.
|
||||
Since supporting a game properly requires extensions in many places of the UI.
|
||||
To allow this without creating one huge plugin interface that involves every aspect of MO, game plugins
|
||||
expose a *feature list*.
|
||||
The list of possible features can be found in the "game_features" project and each feature can itself be
|
||||
considered a plugin interface.
|
||||
|
||||
As an example for a game feature take BSA invalidation: If the game requires BSA invalidation it will implement
|
||||
this feature.
|
||||
Wherever the core can support BSA invalidation it will query whether the current game has this feature and if so
|
||||
query the implementation on specifics (like "How should the invalidation BSA be called" and "what's the right bsa version").
|
||||
Of course, the goal is for feature interfaces to be as generic as possible without limiting usefulness.
|
||||
|
||||
**Note:** The :class:`IPluginGame` interface is complex, and mostly designed for Gamebryo games. If you plan
|
||||
on writting a game plugin for another type of games, you might be interested by a simpler interface and might
|
||||
want to check out the `Basic Games <https://github.com/Holt59/modorganizer-basic_games>`_ meta-plugin.
|
||||
|
||||
|
||||
Tool
|
||||
----
|
||||
|
||||
**Interface:** :class:`IPluginTool`
|
||||
**Examples:** ``tool_configurator``, ``tool_inieditor``, ``fnistool``
|
||||
|
||||
This is the simplest of plugin interfaces. Such plugins simply place an icon inside the tools submenu and get
|
||||
invoked when the user clicks it. They are expected to have a user interface of some sort.
|
||||
These are almost like independent applications except they can access all Mod Organizer interfaces like querying
|
||||
and modifying the current profile, mod list, load order, use MO to install mods and so on.
|
||||
A tool plugin can (and should!) integrate its UI as a window inside MO and thus doesn't have to initialize a
|
||||
windows application itself.
|
||||
|
||||
Proxies
|
||||
-------
|
||||
|
||||
**Interface:** :class:`IPluginProxy`
|
||||
**Examples:** ``plugin_python``
|
||||
|
||||
Proxy Plugins expose the plugin api to foreign languages. This is what allows you to write plugins using python
|
||||
in the first place.
|
||||
The python proxy is easily the most complicated plugin and requires constant updating so if you're considering
|
||||
writing a Haskell plugin because that is your programming language of choice, I am fairly certain learning python
|
||||
is easier than writing the haskell proxy. Just saying.
|
||||
And no, you can not write a proxy for a third language in Python, do not be silly.
|
||||
|
||||
*Free Plugins*
|
||||
--------------
|
||||
|
||||
**Interface:** :class:`IPlugin`
|
||||
**Examples:** ``check_fnis``, ``bsa_extractor``, ``diagnose_basic``, ``tool_inibakery``
|
||||
|
||||
"Free" plugins implement none of the interfaces and thus initially do not integrate with MO at all.
|
||||
They are initialized by MO and get access to the MO interface.
|
||||
This makes sense if you only want to implement one of the extension interfaces (see below) or register handlers
|
||||
for events.
|
||||
|
||||
Extension Interfaces
|
||||
--------------------
|
||||
|
||||
In Python, these interfaces are similar to other plugin types, but in C++, those do not inherit ``IPlugin`` so
|
||||
that plugins can implement one or more of these interfaces, in addition to a normal plugin type.
|
||||
|
||||
Diagnose
|
||||
........
|
||||
|
||||
**Interface:** :class:`IPluginDiagnose`
|
||||
**Examples:** ``diagnose_basic``, ``installer_ncc``, ``plugin_python``, ``script_extender_plugin_checker``
|
||||
|
||||
This interface lets the plugin report issues that are then listed in the "Problems" icon in the main window.
|
||||
If possible the plugin can also provide an automatic or guided fix to the problem.
|
||||
The ``diagnose_basic`` plugin does nothing but analyze the MO installation and report problems it discovers (like
|
||||
*"There are files in your overwrite directory."*) but usually a plugin will want to report issues relevant for its
|
||||
own operation.
|
||||
For instance, ``installer_ncc`` requires a specific version of .NET and will report a problem if it is not installed.
|
||||
This should always be the prefered way to communicate problems the user has to fix but should never be used for problems
|
||||
he cannot fix (i.e. *"This plugin does not work with this game."*).
|
||||
An empty problem list should always be achievable.
|
||||
|
||||
File Mappings
|
||||
.............
|
||||
|
||||
**Interface:** :class:`IPluginFileMapper`
|
||||
**Examples:** ``tool_inibakery``, ``game_gamebryo``
|
||||
|
||||
This interface allows plugins to add virtual file (or directory) links to the virtual file system in addition to the
|
||||
mod files.
|
||||
Profile-local save games, ini-files and load-orders are all implemented this way in MO2.
|
||||
@@ -0,0 +1,135 @@
|
||||
Setting up the environment
|
||||
==========================
|
||||
|
||||
This page will get you started for creating a MO2 python plugin. If you have troubles getting
|
||||
everything setup, feel free to come by our discord server at `https://discord.gg/cYwdcxj <https://discord.gg/cYwdcxj>`_.
|
||||
|
||||
Required tools
|
||||
--------------
|
||||
|
||||
It is possible to write a MO2 python plugin using any text editor, but this guide will focus on
|
||||
`Visual Studio Code <https://code.visualstudio.com/>`_.
|
||||
This guide assumes that:
|
||||
|
||||
- You have `Visual Studio Code <https://code.visualstudio.com/>`_ installed with the
|
||||
`Python extension <https://marketplace.visualstudio.com/items?itemName=ms-python.python>`_.
|
||||
- You have Python installed: https://www.python.org/downloads/.
|
||||
|
||||
- It is recommended but not mandatory to use the Python version that is used by MO2.
|
||||
You can check the ``pythonXX.dll`` in the MO2 installation folder to find the Python version used by MO2 (``python38.dll`` means Python 3.8).
|
||||
- You obviously need a valid MO2 installation: https://github.com/modorganizer2/modorganizer/releases
|
||||
|
||||
- This guide is written for MO2 ≥ 2.3.0.
|
||||
|
||||
**Note:** In the following, I will refer to the MO2 installation directory as ``$MO2DIR``. So if you
|
||||
installed MO2 at `C:\MO2` and you are asked to copy a file to ``$MO2DIR/plugins``, it refers to ``C:\MO2\plugins``.
|
||||
|
||||
Preparation
|
||||
-----------
|
||||
|
||||
**Note:** This part is optional but highly recommended if you want a proper environment to work with.
|
||||
Everything here is written to be as simple as possible but you can of course adapt it to your preferences: use a python virtual
|
||||
environment, use workspace settings instead of global ones, etc.
|
||||
|
||||
1. Get the ``mobase`` stubs
|
||||
...........................
|
||||
|
||||
``mobase`` is the MO2 Python module. The module is written in C++ and thus cannot be read directly by tools such
|
||||
as ``flake8`` or ``mypy``.
|
||||
Instead, we provide `stubs <https://stackoverflow.com/questions/59051631/what-is-the-use-of-stub-files-pyi-in-python>`_
|
||||
which can be used for auto-completion or type-checking.
|
||||
|
||||
The stubs for ``mobase`` are available at https://github.com/ModOrganizer2/pystubs-generation/tree/master/stubs.
|
||||
You want to download the ``mobase.pyi`` file in the folder corresponding to your MO2 version and put it under ``$MO2DIR/plugins/data``.
|
||||
|
||||
**Note:** It is possible to put the stubs in a different location, but we are going to use ``$MO2DIR/plugins/data`` for PyQt5,
|
||||
so we might as well use it for the stubs.
|
||||
|
||||
2. Configure Visual Studio Code for ``mobase``
|
||||
..............................................
|
||||
|
||||
We are going to configure Visual Studio Code to have auto-completion and linting (error and type checking)
|
||||
for the MO2 Python module.
|
||||
Open ``settings.json`` (Ctrl+Shift+P, then "Open Settings (JSON)"), and add the following entries:
|
||||
|
||||
.. code-block:: json-object
|
||||
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.mypyEnabled": true,
|
||||
"python.linting.flake8Enabled": true,
|
||||
"python.autoComplete.extraPaths": [
|
||||
"$MO2DIR\\plugins\\data",
|
||||
]
|
||||
|
||||
3. Configure ``mypy`` to find the ``mobase`` stubs
|
||||
..................................................
|
||||
|
||||
There are multiply way to configure ``mypy``:
|
||||
|
||||
1. You can create a ``mypy.ini`` file somewhere containing:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[mypy]
|
||||
mypy_path = $MO2DIR\plugins\data
|
||||
|
||||
And then add the following to ``settings.json`` (with the correct path):
|
||||
|
||||
.. code-block:: json-object
|
||||
|
||||
"python.linting.mypyArgs": [
|
||||
"--config-file=path-to-mypy.ini",
|
||||
]
|
||||
|
||||
2. You can set the ``MYPYPATH`` environment variable to ``$MO2DIR\plugins\data`` (this requires
|
||||
restarting VS code).
|
||||
|
||||
Testing the setup
|
||||
-----------------
|
||||
|
||||
Create a new Python file in Visual Studio Code, and paste the following content (if you create
|
||||
the file in ``$MO2DIR\plugins``, do not forget to delete it after since it is not a valid
|
||||
plugin):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import mobase
|
||||
|
||||
|
||||
class MyPlugin(mobase.IPluginTool):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer) -> bool:
|
||||
organizer.dow # (1)
|
||||
return True
|
||||
|
||||
def name(self) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def createPlugin() -> mobase.IPlugin:
|
||||
return MyPlugin()
|
||||
|
||||
|
||||
If your setup is valid, here is what you should have.
|
||||
|
||||
- You should see the following (with the errors underlined):
|
||||
|
||||
.. image:: images/check-setup-1.png
|
||||
|
||||
- If you press ``Ctrl+Space`` after ``.down`` (1), you should see the following
|
||||
meaning that auto-complete is working:
|
||||
|
||||
.. image:: images/check-setup-2.png
|
||||
|
||||
- In the error window (click |error-window| on the status bar), you should see
|
||||
the 3 following errors:
|
||||
|
||||
.. image:: images/check-setup-3.png
|
||||
|
||||
If everything is as above, you can delete the test file and move on to writting
|
||||
your own plugin!
|
||||
|
||||
|
||||
.. |error-window| image:: images/error-window.png
|
||||
Reference in New Issue
Block a user