Skip to content

Commit d979537

Browse files
authored
Merge pull request #28 from openpathsampling/docs
Docs updates
2 parents 1f1c52c + 2d71c69 commit d979537

File tree

7 files changed

+122
-190
lines changed

7 files changed

+122
-190
lines changed

docs/api/index.rst

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
.. _api:
2+
3+
API
4+
===
5+
6+
.. currentmodule:: paths_cli
7+
8+
CLI and Plugins
9+
---------------
10+
11+
.. autosummary::
12+
:toctree: generated
13+
14+
OpenPathSamplingCLI
15+
plugin_management.CLIPluginLoader
16+
plugin_management.FilePluginLoader
17+
plugin_management.NamespacePluginLoader
18+
19+
20+
Parameter Decorators
21+
--------------------
22+
23+
These are the functions used to create the reusable parameter decorators.
24+
Note that you will probably never need to use these; instead, use the
25+
existing parameter decorators.
26+
27+
.. autosummary::
28+
:toctree: generated
29+
30+
param_core.Option
31+
param_core.Argument
32+
param_core.AbstractLoader
33+
param_core.StorageLoader
34+
param_core.OPSStorageLoadNames
35+
param_core.OPSStorageLoadSingle
36+
37+
Search strategies
38+
-----------------
39+
40+
These are the various strategies for finding objects in a storage, in
41+
particular if we have to guess because the user didn't provide an explicit
42+
choice or didn't tag.
43+
44+
.. autosummary::
45+
:toctree: generated
46+
47+
param_core.Getter
48+
param_core.GetByName
49+
param_core.GetByNumber
50+
param_core.GetPredefinedName
51+
param_core.GetOnly
52+
param_core.GetOnlyNamed
53+
param_core.GetOnlySnapshot
54+
55+
56+
Commands
57+
--------
58+
59+
.. autosummary::
60+
:toctree: generated
61+
:recursive:
62+
63+
commands.visit_all
64+
commands.equilibrate
65+
commands.pathsampling
66+
commands.append
67+
commands.contents

docs/conf.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,13 @@
4141
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
4242
# ones.
4343
extensions = [
44+
'sphinx.ext.autodoc',
45+
'sphinx.ext.autosummary',
4446
'sphinx_click.ext',
4547
]
4648

49+
autosummary_generate = True
50+
4751
# Add any paths that contain templates here, relative to this directory.
4852
templates_path = ['_templates']
4953

docs/for_core/README.md

Lines changed: 0 additions & 1 deletion
This file was deleted.

docs/for_core/cli.rst

Lines changed: 0 additions & 174 deletions
This file was deleted.

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,5 @@ wrappers around well-tested OPS code.
6161
parameters
6262
workflows
6363
full_cli
64+
api/index
6465

docs/plugins.rst

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,15 @@ Plugin Infrastructure
44
=====================
55

66
All subcommands to the OpenPathSampling CLI use a plugin infrastructure.
7-
They simply need to be Python modules, following a few rules, that are
8-
placed into the user's ``~/.openpathsampling/cli-plugins/`` directory.
7+
There are two possible ways to distribute plugins (file plugins and
8+
namespace plugins), but a given plugin script could be distributed either
9+
way.
910

10-
Technically, the code searches two directories for plugins: first,
11-
``$DIRECTORY/commands``, where ``$DIRECTORY`` is the directory where the
12-
main OPS CLI script has been installed (i.e., the directory that corresponds
13-
to the Python package ``paths_cli``). This is where the default commands are
14-
kept. Then it searches the user directory. Duplicate commands will lead to
15-
errors when running the CLI, as you can't register the same name twice.
11+
Writing a plugin script
12+
-----------------------
1613

17-
Other than being in the right place, the script must do the following:
14+
An OPS plugin is simply a Python module that follows a few rules.
1815

19-
* It must be possible to ``exec`` it in an empty namespace (mainly, this
20-
can mean no relative imports).
2116
* It must define a variable ``CLI`` that is the main CLI function is
2217
assigned to.
2318
* It must define a variable ``SECTION`` to determine where to show it in
@@ -27,11 +22,15 @@ Other than being in the right place, the script must do the following:
2722
``openpathsampling --help``, but might still be usable. If your command
2823
doesn't show in the help, carefully check your spelling of the ``SECTION``
2924
variable.
25+
* The main CLI function must be decorated as a ``click.command``.
26+
* (If distributed as a file plugin) It must be possible to ``exec`` it in an
27+
empty namespace (mainly, this can mean no relative imports).
3028

3129
As a suggestion, I (DWHS) tend to structure my plugins as follows:
3230

3331
.. code:: python
3432
33+
@click.command("plugin", short_help="brief description")
3534
@PARAMETER.clicked(required)
3635
def plugin(parameter):
3736
plugin_main(PARAMETER.get(parameter))
@@ -40,25 +39,60 @@ As a suggestion, I (DWHS) tend to structure my plugins as follows:
4039
import openpathsampling as paths
4140
# do the real stuff with OPS
4241
...
42+
return final_status, simulation
4343
4444
CLI = plugin
4545
SECTION = "MySection"
4646
4747
The basic idea is that there's a ``plugin_main`` function that is based on
4848
pure OPS, using only inputs that OPS can immediately understand (no need to
49-
go to storage, etc). This is easy to develop/test with OPS. Then there's a
50-
wrapper function whose sole purpose is to convert the command line
49+
process the command line). This is easy to develop/test with OPS. Then
50+
there's a wrapper function whose sole purpose is to convert the command line
5151
parameters to something OPS can understand (using the ``get`` method). This
5252
wrapper is the ``CLI`` variable. Give it an allowed ``SECTION``, and the
5353
plugin is ready!
5454

5555
The result is that plugins are astonishingly easy to develop, once you have
56-
the scientific code implemented in a library.
56+
the scientific code implemented in a library. This structure also makes it
57+
very easy to test the plugins: a mock replaces the ``plugin_main`` in
58+
``plugin`` to check that the integration works, and then a simple smoke test
59+
for the ``plugin_main`` is sufficient, since the core code should already be
60+
well-tested.
5761

5862
Note that we recommend that the import of OpenPathSampling only be done
5963
inside the ``plugin_main`` function. Although this is contrary to normal
6064
Python practice, we do this because tools like tab-autocomplete require
6165
that you run the program each time. The import of OPS is rather slow, so we
6266
delay it until it is needed, keeping the CLI interface fast and responsive.
6367

64-
.. TODO : look into having the plugin auto-installed using setuptools
68+
Finally, the ``plugin_main`` function returns some sort of final status and
69+
the simulation object that was created (or ``None`` if there wasn't one).
70+
This makes it very easy to chain multiple main functions to make a workflow.
71+
72+
73+
Distributing file plugins
74+
-------------------------
75+
76+
Once you have a plugin module written, the easiest way to install it is to
77+
put it in your ``~/.openpathsampling/cli_plugins/`` directory. This is the
78+
file-based plugin distribution mechanism -- you send the file to someone,
79+
and they put in that directory.
80+
81+
This is great for plugins shared in a single team, or for creating
82+
reproducible workflows that aren't intended for wide distribution.
83+
84+
85+
Distributing namespace plugins
86+
------------------------------
87+
88+
If the plugin is part of a larger Python package, or if it is important to
89+
track version numbers or to be able to change which plugins are installed
90+
in particular Python environments, the namespace distribution mechanism is a
91+
better choice. We use `native namespace packages`_, which is a standard way
92+
of making plugins in Python. Plugins should be in the ``paths_cli.plugins``
93+
namespace.
94+
95+
.. _native namespace packages:
96+
https://packaging.python.org/guides/packaging-namespace-packages/#native-namespace-packages
97+
98+

docs/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ numpy
33

44
packaging
55
sphinx-click
6+
sphinx >= 3.1 # will this work?

0 commit comments

Comments
 (0)