Skip to content

Commit 07b7d7c

Browse files
committed
Merge branch 'main' of github.com:openpathsampling/openpathsampling-cli into wizard
2 parents 3d92cd2 + ba3e668 commit 07b7d7c

18 files changed

Lines changed: 289 additions & 176 deletions

‎.github/workflows/test-suite.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ name: "Tests"
33
on:
44
pull_request:
55
branches:
6-
- master
6+
- main
77
- stable
88
push:
99
branches:
10-
- master
10+
- main
1111
tags:
1212
- "v*"
1313
schedule:

‎docs/plugins.rst‎

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,33 @@ There are two possible ways to distribute plugins (file plugins and
88
namespace plugins), but a given plugin script could be distributed either
99
way.
1010

11-
Writing a plugin script
12-
-----------------------
13-
14-
An OPS plugin is simply a Python module that follows a few rules.
15-
16-
* It must define a variable ``CLI`` that is the main CLI function is
17-
assigned to.
18-
* It must define a variable ``SECTION`` to determine where to show it in
19-
help (what kind of command it is). Valid values are ``"Simulation"``,
20-
``"Analysis"``, ``"Miscellaneous"``, or ``"Workflow"``. If ``SECTION`` is
21-
defined but doesn't have one of these values, it won't show in
22-
``openpathsampling --help``, but might still be usable. If your command
23-
doesn't show in the help, carefully check your spelling of the ``SECTION``
24-
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).
11+
Writing a command plugin
12+
------------------------
13+
14+
To write an OPS command plugin, you simply need to create an instance of
15+
:class:`paths_cli.OPSCommandPlugin` and to install the module in a location
16+
where the CLI knows to look for it. The input parameters to
17+
``OPSCommandPlugin`` are:
18+
19+
* ``command``: This is the main CLI function for the subcommand. It must be
20+
decorated as a ``click.command``.
21+
* ``section``: This is a string to determine where to show it in help (what
22+
kind of command it is). Valid values are ``"Simulation"``,
23+
``"Analysis"``, ``"Miscellaneous"``, or ``"Workflow"``. If ``section``
24+
doesn't have one of these values, it won't show in ``openpathsampling
25+
--help``, but might still be usable. If your command doesn't show in the
26+
help, carefully check your spelling of the ``section`` variable.
27+
* ``requires_ops`` (optional, default ``(1, 0)``): Minimum allowed version
28+
of OpenPathSampling. Note that this is currently informational only, and
29+
has no effect on functionality.
30+
* ``requires_cli`` (optional, default ``(0, 3)``): Minimum allowed version
31+
of the OpenPathSampling CLI. Note that this is currently informational
32+
only, and has no effect on functionality.
33+
34+
35+
If you distribute your plugin as a file-based plugin, be aware that it must
36+
be possible to ``exec`` it in an empty namespace (mainly, this can mean no
37+
relative imports).
2838

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

@@ -41,16 +51,15 @@ As a suggestion, I (DWHS) tend to structure my plugins as follows:
4151
...
4252
return final_status, simulation
4353
44-
CLI = plugin
45-
SECTION = "MySection"
54+
PLUGIN = OPSCommandPlugin(command=plugin, section="MySection")
4655
4756
The basic idea is that there's a ``plugin_main`` function that is based on
4857
pure OPS, using only inputs that OPS can immediately understand (no need to
4958
process the command line). This is easy to develop/test with OPS. Then
5059
there's a wrapper function whose sole purpose is to convert the command line
5160
parameters to something OPS can understand (using the ``get`` method). This
52-
wrapper is the ``CLI`` variable. Give it an allowed ``SECTION``, and the
53-
plugin is ready!
61+
wrapper is the ``command`` in you ``OPSCommandPlugin``. Also provide an
62+
allowed ``section``, and the plugin is ready!
5463

5564
The result is that plugins are astonishingly easy to develop, once you have
5665
the scientific code implemented in a library. This structure also makes it
@@ -89,10 +98,9 @@ If the plugin is part of a larger Python package, or if it is important to
8998
track version numbers or to be able to change which plugins are installed
9099
in particular Python environments, the namespace distribution mechanism is a
91100
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``
101+
of making plugins in Python. Plugins should be in the ``paths_cli_plugins``
93102
namespace.
94103

95104
.. _native namespace packages:
96105
https://packaging.python.org/guides/packaging-namespace-packages/#native-namespace-packages
97106

98-

‎example_plugins/one_pot_tps.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import click
2+
from paths_cli import OPSCommandPlugin
23
from paths_cli.parameters import (INPUT_FILE, OUTPUT_FILE, ENGINE, STATES,
34
N_STEPS_MC, INIT_SNAP)
45
from paths_cli.commands.visit_all import visit_all_main
@@ -44,6 +45,15 @@ def one_pot_tps_main(output_storage, states, engine, engine_hot,
4445
equil_multiplier, equil_extra)
4546
return pathsampling_main(output_storage, scheme, equil_set, nsteps)
4647

48+
# these lines enable this plugin to support OPS CLI < 0.3
4749
CLI = one_pot_tps
4850
SECTION = "Workflow"
4951
REQUIRES_OPS = (1, 2)
52+
53+
# these lines enable this plugin to support OPS CLI >= 0.3
54+
PLUGIN = OPSCommandPlugin(
55+
command=one_pot_tps,
56+
section="Workflow",
57+
requires_ops=(1, 2),
58+
requires_cli=(0, 3)
59+
)

‎example_plugins/tps.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import click
2+
from paths_cli import OPSCommandPlugin
23
from paths_cli.parameters import (
34
INPUT_FILE, OUTPUT_FILE, ENGINE, STATES, INIT_CONDS, N_STEPS_MC
45
)
@@ -40,8 +41,17 @@ def tps_main(engine, states, init_traj, output_storage, n_steps):
4041
return simulation.sample_set, simulation
4142

4243

44+
# these lines enable this plugin to support OPS CLI < 0.3
4345
CLI = tps
4446
SECTION = "Simulation"
4547

48+
# these lines enable this plugin to support OPS CLI >= 0.3
49+
PLUGIN = OPSCommandPlugin(
50+
command=tps,
51+
section="Simulation"
52+
)
53+
54+
55+
# this allows you to use this as a script, independently of the OPS CLI
4656
if __name__ == "__main__":
4757
tps()

‎paths_cli/__init__.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
from .plugin_management import (
2+
OPSCommandPlugin,
3+
)
14
from .cli import OpenPathSamplingCLI
25
from . import commands
36
from . import version

‎paths_cli/cli.py‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
# import click_completion
1414
# click_completion.init()
1515

16-
from .plugin_management import FilePluginLoader, NamespacePluginLoader
16+
from .plugin_management import (FilePluginLoader, NamespacePluginLoader,
17+
OPSCommandPlugin)
1718

1819
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
1920

@@ -31,10 +32,10 @@ def app_dir_plugins(posix):
3132
).resolve() / 'cli-plugins')
3233

3334
self.plugin_loaders = [
34-
FilePluginLoader(commands),
35-
FilePluginLoader(app_dir_plugins(posix=False)),
36-
FilePluginLoader(app_dir_plugins(posix=True)),
37-
NamespacePluginLoader('paths_cli.plugins')
35+
FilePluginLoader(commands, OPSCommandPlugin),
36+
FilePluginLoader(app_dir_plugins(posix=False), OPSCommandPlugin),
37+
FilePluginLoader(app_dir_plugins(posix=True), OPSCommandPlugin),
38+
NamespacePluginLoader('paths_cli_plugins', OPSCommandPlugin)
3839
]
3940

4041
plugins = sum([loader() for loader in self.plugin_loaders], [])

‎paths_cli/commands/append.py‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import click
2+
from paths_cli import OPSCommandPlugin
23
from paths_cli.parameters import (
34
INPUT_FILE, APPEND_FILE, MULTI_CV, MULTI_ENGINE, MULTI_VOLUME,
45
MULTI_NETWORK, MULTI_SCHEME, MULTI_TAG
@@ -55,6 +56,9 @@ def append(input_file, append_file, engine, cv, volume, network, scheme,
5556
storage.close()
5657

5758

58-
CLI = append
59-
SECTION = "Miscellaneous"
60-
REQUIRES_OPS = (1, 0)
59+
PLUGIN = OPSCommandPlugin(
60+
command=append,
61+
section="Miscellaneous",
62+
requires_ops=(1, 0),
63+
requires_cli=(0, 3)
64+
)

‎paths_cli/commands/contents.py‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import click
22
from paths_cli.parameters import INPUT_FILE
3+
from paths_cli import OPSCommandPlugin
34

45
UNNAMED_SECTIONS = ['steps', 'movechanges', 'samplesets', 'trajectories',
56
'snapshots']
@@ -112,6 +113,10 @@ def get_section_string_nameable(section, store, get_named):
112113
+ _item_or_items(n_unnamed))
113114
return out_str
114115

115-
CLI = contents
116-
SECTION = "Miscellaneous"
117-
REQUIRES_OPS = (1, 0)
116+
117+
PLUGIN = OPSCommandPlugin(
118+
command=contents,
119+
section="Miscellaneous",
120+
requires_ops=(1, 0),
121+
requires_cli=(0, 3)
122+
)

‎paths_cli/commands/equilibrate.py‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import click
22
# import openpathsampling as paths
33

4+
from paths_cli import OPSCommandPlugin
45
from paths_cli.parameters import (
56
INPUT_FILE, OUTPUT_FILE, INIT_CONDS, SCHEME
67
)
@@ -58,6 +59,10 @@ def equilibrate_main(output_storage, scheme, init_conds, multiplier,
5859
return simulation.sample_set, simulation
5960

6061

61-
CLI = equilibrate
62-
SECTION = "Simulation"
63-
REQUIRES_OPS = (1, 2)
62+
PLUGIN = OPSCommandPlugin(
63+
command=equilibrate,
64+
section="Simulation",
65+
requires_ops=(1, 2),
66+
requires_cli=(0, 3)
67+
)
68+

‎paths_cli/commands/md.py‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import click
22

33
import paths_cli.utils
4+
from paths_cli import OPSCommandPlugin
45
from paths_cli.parameters import (INPUT_FILE, OUTPUT_FILE, ENGINE,
56
MULTI_ENSEMBLE, INIT_SNAP)
67

@@ -197,7 +198,10 @@ def md_main(output_storage, engine, ensembles, nsteps, initial_frame):
197198
'final_conditions')
198199
return trajectory, None
199200

200-
CLI = md
201-
SECTION = "Simulation"
202-
REQUIRES_OPS = (1, 0)
203201

202+
PLUGIN = OPSCommandPlugin(
203+
command=md,
204+
section="Simulation",
205+
requires_ops=(1, 0),
206+
requires_cli=(0, 3)
207+
)

0 commit comments

Comments
 (0)