Skip to content

Commit 9108ddd

Browse files
committed
Working on version contraints for the plugin system. Moved the load_plugin function and corresponding tests. Add a way to skip loading a plugin if it failed to load
1 parent f7f369a commit 9108ddd

File tree

21 files changed

+408
-67
lines changed

21 files changed

+408
-67
lines changed

docs/plugin_system.rst

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,44 @@ tmuxp expects all plugins to be class within a python submodule named
3939
python environment. A plugin interface is provided by tmuxp to inherit.
4040

4141
`poetry`_ is the chosen python package manager for tmuxp. It is highly
42-
suggested to use it when developing tmuxp plugins; however, ``pip`` will work
43-
just as well. Only one of the configuration files is needed for the packaging tool that the
44-
package developer desides to use.
42+
suggested to use it when developing plugins; however, ``pip`` will work
43+
just as well. Only one of the configuration files is needed for the packaging
44+
tool that the package developer desides to use.
4545

4646
.. code-block:: bash
4747
4848
python_module
49-
├── my_plugin_module
49+
├── tmuxp_plugin_my_plugin_module
5050
│   ├── __init__.py
5151
│   └── plugin.py
5252
├── pyproject.toml # Poetry's module configuration file
5353
└── setup.py # pip's module configuration file
5454
5555
56+
When publishing plugins to pypi, tmuxp advocates for standardized naming:
57+
``tmuxp-plugin-{your-plugin-name}`` to allow for easier searching. To create a
58+
module configuration file with poetry, run ``poetry init`` in the module
59+
directory. The resulting file looks something like this:
60+
61+
.. code-block:: toml
62+
63+
[tool.poetry]
64+
name = "tmuxp-plugin-my-tmuxp-plugin"
65+
version = "0.0.2"
66+
description = "An example tmuxp plugin."
67+
authors = ["Author Name <author.name@<domain>.com>"]
68+
69+
[tool.poetry.dependencies]
70+
python = "~2.7 || ^3.5"
71+
tmuxp = "^1.6.0"
72+
73+
[tool.poetry.dev-dependencies]
74+
75+
[build-system]
76+
requires = ["poetry>=0.12"]
77+
build-backend = "poetry.masonry.api"
78+
79+
5680
The `plugin.py` file could contain something like the following:
5781

5882
.. code-block:: python
@@ -63,13 +87,18 @@ The `plugin.py` file could contain something like the following:
6387
class MyTmuxpPlugin(TmuxpPluginInterface):
6488
def __init__(self):
6589
"""
66-
Currently optional.
67-
68-
In the current version of the plugin interface, the __init__
69-
isn't being utilized. However, it does provide a space for
70-
later additions to the interface.
90+
Initialize my custom plugin.
7191
"""
72-
super.__init__(self)
92+
super().__init__()
93+
94+
# Optional version dependency configurations:
95+
self.plugin_name = 'tmuxp-plugin-my-tmuxp-plugin'
96+
self.tmux_min_version = '1.8'
97+
self.tmux_max_version = '2.4'
98+
self.tmux_version_incompatible = ['2.3']
99+
self.tmuxp_min_version = '1.6.0'
100+
self.tmuxp_max_version = '1.6.2'
101+
self.tmuxp_version_incompatible = ['1.6.1']
73102
74103
def before_workspace_builder(self, session):
75104
session.rename_session('my-new-session-name')
@@ -78,6 +107,7 @@ The `plugin.py` file could contain something like the following:
78107
now = datetime.datetime.now().strftime('%Y-%m-%d')
79108
session.rename_session('session_{}'.format(now))
80109
110+
81111
Once this plugin is installed in the local python environment, it can be used
82112
in a configuration file like the following:
83113

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ skip-string-normalization = true
33

44
[tool.poetry]
55
name = "tmuxp"
6-
version = "1.5.6"
6+
version = "1.6.0"
77
description = "tmux session manager"
88
license = "MIT"
99
authors = ["Tony Narlock <tony@git-pull.com>"]

tests/fixtures/pluginsystem/__init__.py

Whitespace-only changes.

tests/fixtures/pluginsystem/partials/__init__.py

Whitespace-only changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from .test_plugin_helpers import TestTmuxpPluginInterface
2+
3+
4+
class AllVersionPassPlugin(TestTmuxpPluginInterface):
5+
def __init__(self):
6+
config = {
7+
'plugin_name': 'tmuxp-plugin-my-tmuxp-plugin',
8+
'tmux_min_version': '1.8',
9+
'tmux_max_version': '100.0',
10+
'tmux_version_incompatible': ['2.3'],
11+
'tmuxp_min_version': '1.6.0',
12+
'tmuxp_max_version': '100.0.0',
13+
'tmuxp_version_incompatible': ['1.5.6'],
14+
'tmux_version': '3.0',
15+
'tmuxp_version': '1.6.0',
16+
}
17+
TestTmuxpPluginInterface.__init__(self, config)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from tmuxp.plugin import TmuxpPluginInterface
2+
3+
class TestTmuxpPluginInterface(TmuxpPluginInterface):
4+
def __init__(self, config):
5+
tmux_version = config.pop('tmux_version', None)
6+
tmuxp_version = config.pop('tmuxp_version', None)
7+
8+
TmuxpPluginInterface.__init__(self, **config)
9+
10+
# WARNING! This should not be done in anything but a test
11+
if tmux_version:
12+
self.version_constraints['tmux']['version'] = tmux_version
13+
if tmuxp_version:
14+
self.version_constraints['tmuxp']['version'] = tmuxp_version
15+
16+
self._version_check()
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from .test_plugin_helpers import TestTmuxpPluginInterface
2+
3+
4+
class TmuxVersionFailMinPlugin(TestTmuxpPluginInterface):
5+
def __init__(self):
6+
config = {
7+
'plugin_name': 'tmux-min-version-fail',
8+
'tmux_min_version': '1.8',
9+
'tmux_version': '1.7'
10+
}
11+
TestTmuxpPluginInterface.__init__(self, config)
12+
13+
14+
class TmuxVersionFailMaxPlugin(TestTmuxpPluginInterface):
15+
def __init__(self):
16+
config = {
17+
'plugin_name': 'tmux-max-version-fail',
18+
'tmux_min_version': '3.0',
19+
'tmux_version': '3.5'
20+
}
21+
TestTmuxpPluginInterface.__init__(self, config)
22+
23+
24+
class TmuxVersionFailIncompatiblePlugin(TestTmuxpPluginInterface):
25+
def __init__(self):
26+
config = {
27+
'plugin_name': 'tmux-incompatible-version-fail',
28+
'tmux_version_incompatible': ['2.3'],
29+
'tmux_version': '2.3'
30+
}
31+
32+
TestTmuxpPluginInterface.__init__(self, config)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from .test_plugin_helpers import TestTmuxpPluginInterface
2+
3+
4+
class TmuxpVersionFailMinPlugin(TestTmuxpPluginInterface):
5+
def __init__(self):
6+
config = {
7+
'plugin_name': 'tmuxp-min-verion-fail',
8+
'tmuxp_min_version': '1.6.0',
9+
'tmuxp_version': '1.5.6'
10+
}
11+
TestTmuxpPluginInterface.__init__(self, config)
12+
13+
14+
class TmuxpVersionFailMaxPlugin(TestTmuxpPluginInterface):
15+
def __init__(self):
16+
config = {
17+
'plugin_name': 'tmuxp-max-verion-fail',
18+
'tmuxp_min_version': '2.0.0',
19+
'tmuxp_version': '2.5'
20+
}
21+
TestTmuxpPluginInterface.__init__(self, config)
22+
23+
24+
class TmuxpVersionFailIncompatiblePlugin(TestTmuxpPluginInterface):
25+
def __init__(self):
26+
config = {
27+
'plugin_name': 'tmuxp-incompatible-verion-fail',
28+
'tmuxp_version_incompatible': ['1.5.0'],
29+
'tmuxp_version': '1.5.0'
30+
}
31+
TestTmuxpPluginInterface.__init__(self, config)

tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_awf/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ authors = ["Joseph Flinn <joseph.s.flinn@gmail.com>"]
66

77
[tool.poetry.dependencies]
88
python = "~2.7 || ^3.5"
9-
tmuxp = "^1.5.6"
9+
tmuxp = "^1.6.0"
1010

1111
[tool.poetry.dev-dependencies]
1212

tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bs/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ authors = ["Joseph Flinn <joseph.s.flinn@gmail.com>"]
66

77
[tool.poetry.dependencies]
88
python = "~2.7 || ^3.5"
9-
tmuxp = "^1.5.6"
9+
tmuxp = "^1.6.0."
1010

1111
[tool.poetry.dev-dependencies]
1212

tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bwb/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ authors = ["Joseph Flinn <joseph.s.flinn@gmail.com>"]
66

77
[tool.poetry.dependencies]
88
python = "~2.7 || ^3.5"
9-
tmuxp = "^1.5.6"
9+
tmuxp = "^1.6.0"
1010

1111
[tool.poetry.dev-dependencies]
1212

tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_owc/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ authors = ["Joseph Flinn <joseph.s.flinn@gmail.com>"]
66

77
[tool.poetry.dependencies]
88
python = "~2.7 || ^3.5"
9-
tmuxp = "^1.5.6"
9+
tmuxp = "^1.6.0"
1010

1111
[tool.poetry.dev-dependencies]
1212

tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_r/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ authors = ["Joseph Flinn <joseph.s.flinn@gmail.com>"]
66

77
[tool.poetry.dependencies]
88
python = "~2.7 || ^3.5"
9-
tmuxp = "^1.5.6"
9+
tmuxp = "^1.6.0"
1010

1111
[tool.poetry.dev-dependencies]
1212

tests/test_cli.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
load_workspace,
2323
scan_config,
2424
_reattach,
25+
load_plugins,
2526
)
2627
from tmuxp.workspacebuilder import WorkspaceBuilder
28+
from tmuxp_test_plugin_bwb.plugin import PluginBeforeWorkspaceBuilder
2729

2830
from .fixtures._util import curjoin, loadfixture
2931

@@ -681,6 +683,24 @@ def test_ls_cli(monkeypatch, tmpdir):
681683
assert cli_output == '\n'.join(stems) + '\n'
682684

683685

686+
def test_load_plugins():
687+
plugins_config = loadfixture("workspacebuilder/plugin_bwb.yaml")
688+
689+
sconfig = kaptan.Kaptan(handler='yaml')
690+
sconfig = sconfig.import_config(plugins_config).get()
691+
sconfig = config.expand(sconfig)
692+
693+
plugins = load_plugins(sconfig)
694+
695+
assert len(plugins) == 1
696+
697+
test_plugin_class_types = [
698+
PluginBeforeWorkspaceBuilder().__class__,
699+
]
700+
for plugin in plugins:
701+
assert plugin.__class__ in test_plugin_class_types
702+
703+
684704
def test_plugin_system_before_script(server, monkeypatch):
685705
# this is an implementation test. Since this testsuite may be ran within
686706
# a tmux session by the developer himself, delete the TMUX variable
@@ -705,7 +725,11 @@ def test_reattach_plugins(server):
705725
sconfig = config.expand(sconfig)
706726

707727
# open it detached
708-
builder = WorkspaceBuilder(sconf=sconfig, server=server)
728+
builder = WorkspaceBuilder(
729+
sconf=sconfig,
730+
plugins=load_plugins(sconfig),
731+
server=server
732+
)
709733
builder.build()
710734

711735
try:

tests/test_plugin.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# -*- coding: utf-8 -*-
2+
"""Test for tmuxp plugin api."""
3+
from __future__ import absolute_import
4+
5+
import json
6+
import os
7+
8+
import pytest
9+
10+
import libtmux
11+
from libtmux.common import has_lt_version
12+
from tmuxp.plugin import TmuxpPluginInterface
13+
from tmuxp.exc import TmuxpPluginException
14+
15+
from .fixtures.pluginsystem.partials.all_pass import AllVersionPassPlugin
16+
from .fixtures.pluginsystem.partials.tmux_version_fail import (
17+
TmuxVersionFailMinPlugin,
18+
TmuxVersionFailMaxPlugin,
19+
TmuxVersionFailIncompatiblePlugin
20+
)
21+
from .fixtures.pluginsystem.partials.tmuxp_version_fail import (
22+
TmuxpVersionFailMinPlugin,
23+
TmuxpVersionFailMaxPlugin,
24+
TmuxpVersionFailIncompatiblePlugin
25+
)
26+
27+
28+
def test_all_pass():
29+
try:
30+
plugin = AllVersionPassPlugin()
31+
assert(True)
32+
except TmuxpPluginException as error:
33+
assert(False)
34+
35+
36+
def test_tmux_version_fail_min():
37+
try:
38+
plugin = TmuxVersionFailMinPlugin()
39+
assert(False)
40+
except TmuxpPluginException as error:
41+
assert('Incompatible' in error.__str__())
42+
43+
44+
def test_tmux_version_fail_max():
45+
try:
46+
plugin = TmuxVersionFailMaxPlugin()
47+
assert(False)
48+
except TmuxpPluginException as error:
49+
assert('Incompatible' in error.__str__())
50+
51+
52+
def test_tmux_version_fail_incompatible():
53+
try:
54+
plugin = TmuxVersionFailIncompatiblePlugin()
55+
assert(False)
56+
except TmuxpPluginException as error:
57+
assert('Incompatible' in error.__str__())
58+
59+
60+
def test_tmuxp_version_fail_min():
61+
try:
62+
plugin = TmuxpVersionFailMinPlugin()
63+
assert(False)
64+
except TmuxpPluginException as error:
65+
assert('Incompatible' in error.__str__())
66+
67+
68+
def test_tmux_version_fail_max():
69+
try:
70+
plugin = TmuxpVersionFailMaxPlugin()
71+
assert(False)
72+
except TmuxpPluginException as error:
73+
assert('Incompatible' in error.__str__())
74+
75+
76+
def test_tmux_version_fail_incompatible():
77+
try:
78+
plugin = TmuxpVersionFailIncompatiblePlugin()
79+
assert(False)
80+
except TmuxpPluginException as error:
81+
assert('Incompatible' in error.__str__())

0 commit comments

Comments
 (0)