summaryrefslogtreecommitdiff
path: root/pelican/tests/test_plugins.py
blob: 348c3e94b1b660f300b42eadbef8eeea5f8f8b9c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
import os
from contextlib import contextmanager

import pelican.tests.dummy_plugins.normal_plugin.normal_plugin as normal_plugin
from pelican.plugins._utils import (get_namespace_plugins, get_plugin_name,
                                    load_plugins)
from pelican.tests.support import unittest


@contextmanager
def tmp_namespace_path(path):
    '''Context manager for temporarily appending namespace plugin packages

    path: path containing the `pelican` folder

    This modifies the `pelican.__path__` and lets the `pelican.plugins`
    namespace package resolve it from that.
    '''
    # This avoids calls to internal `pelican.plugins.__path__._recalculate()`
    # as it should not be necessary
    import pelican

    old_path = pelican.__path__[:]
    try:
        pelican.__path__.append(os.path.join(path, 'pelican'))
        yield
    finally:
        pelican.__path__ = old_path


class PluginTest(unittest.TestCase):
    _PLUGIN_FOLDER = os.path.join(
        os.path.abspath(os.path.dirname(__file__)),
        'dummy_plugins')
    _NS_PLUGIN_FOLDER = os.path.join(_PLUGIN_FOLDER, 'namespace_plugin')
    _NORMAL_PLUGIN_FOLDER = os.path.join(_PLUGIN_FOLDER, 'normal_plugin')

    def test_namespace_path_modification(self):
        import pelican
        import pelican.plugins
        old_path = pelican.__path__[:]

        # not existing path
        path = os.path.join(self._PLUGIN_FOLDER, 'foo')
        with tmp_namespace_path(path):
            self.assertIn(
                os.path.join(path, 'pelican'),
                pelican.__path__)
            # foo/pelican does not exist, so it won't propagate
            self.assertNotIn(
                os.path.join(path, 'pelican', 'plugins'),
                pelican.plugins.__path__)
        # verify that we restored path back
        self.assertEqual(pelican.__path__, old_path)

        # existing path
        with tmp_namespace_path(self._NS_PLUGIN_FOLDER):
            self.assertIn(
                os.path.join(self._NS_PLUGIN_FOLDER, 'pelican'),
                pelican.__path__)
            # /namespace_plugin/pelican exists, so it should be in
            self.assertIn(
                os.path.join(self._NS_PLUGIN_FOLDER, 'pelican', 'plugins'),
                pelican.plugins.__path__)
        self.assertEqual(pelican.__path__, old_path)

    def test_get_namespace_plugins(self):
        # existing namespace plugins
        existing_ns_plugins = get_namespace_plugins()

        # with plugin
        with tmp_namespace_path(self._NS_PLUGIN_FOLDER):
            ns_plugins = get_namespace_plugins()
            self.assertEqual(len(ns_plugins), len(existing_ns_plugins)+1)
            self.assertIn('pelican.plugins.ns_plugin', ns_plugins)
            self.assertEqual(
                ns_plugins['pelican.plugins.ns_plugin'].NAME,
                'namespace plugin')

        # should be back to existing namespace plugins outside `with`
        ns_plugins = get_namespace_plugins()
        self.assertEqual(ns_plugins, existing_ns_plugins)

    def test_load_plugins(self):
        def get_plugin_names(plugins):
            return {get_plugin_name(p) for p in plugins}

        # existing namespace plugins
        existing_ns_plugins = load_plugins({})

        with tmp_namespace_path(self._NS_PLUGIN_FOLDER):
            # with no `PLUGINS` setting, load namespace plugins
            plugins = load_plugins({})
            self.assertEqual(len(plugins), len(existing_ns_plugins)+1, plugins)
            self.assertEqual(
                {'pelican.plugins.ns_plugin'} | get_plugin_names(existing_ns_plugins),
                get_plugin_names(plugins))

            # disable namespace plugins with `PLUGINS = []`
            SETTINGS = {
                'PLUGINS': []
            }
            plugins = load_plugins(SETTINGS)
            self.assertEqual(len(plugins), 0, plugins)

            # with `PLUGINS`, load only specified plugins

            # normal plugin
            SETTINGS = {
                'PLUGINS': ['normal_plugin'],
                'PLUGIN_PATHS': [self._NORMAL_PLUGIN_FOLDER]
            }
            plugins = load_plugins(SETTINGS)
            self.assertEqual(len(plugins), 1, plugins)
            self.assertEqual(
                {'normal_plugin'},
                get_plugin_names(plugins))

            # normal submodule/subpackage plugins
            SETTINGS = {
                'PLUGINS': [
                    'normal_submodule_plugin.subplugin',
                    'normal_submodule_plugin.subpackage.subpackage',
                ],
                'PLUGIN_PATHS': [self._NORMAL_PLUGIN_FOLDER]
            }
            plugins = load_plugins(SETTINGS)
            self.assertEqual(len(plugins), 2, plugins)
            self.assertEqual(
                {'normal_submodule_plugin.subplugin',
                 'normal_submodule_plugin.subpackage.subpackage'},
                get_plugin_names(plugins))

            # ensure normal plugins are loaded only once
            SETTINGS = {
                'PLUGINS': ['normal_plugin'],
                'PLUGIN_PATHS': [self._NORMAL_PLUGIN_FOLDER],
            }
            plugins = load_plugins(SETTINGS)
            for plugin in load_plugins(SETTINGS):
                # The second load_plugins() should return the same plugin
                # objects as the first one
                self.assertIn(plugin, plugins)

            # namespace plugin short
            SETTINGS = {
                'PLUGINS': ['ns_plugin']
            }
            plugins = load_plugins(SETTINGS)
            self.assertEqual(len(plugins), 1, plugins)
            self.assertEqual(
                {'pelican.plugins.ns_plugin'},
                get_plugin_names(plugins))

            # namespace plugin long
            SETTINGS = {
                'PLUGINS': ['pelican.plugins.ns_plugin']
            }
            plugins = load_plugins(SETTINGS)
            self.assertEqual(len(plugins), 1, plugins)
            self.assertEqual(
                {'pelican.plugins.ns_plugin'},
                get_plugin_names(plugins))

            # normal and namespace plugin
            SETTINGS = {
                'PLUGINS': ['normal_plugin', 'ns_plugin'],
                'PLUGIN_PATHS': [self._NORMAL_PLUGIN_FOLDER]
            }
            plugins = load_plugins(SETTINGS)
            self.assertEqual(len(plugins), 2, plugins)
            self.assertEqual(
                {'normal_plugin', 'pelican.plugins.ns_plugin'},
                get_plugin_names(plugins))

    def test_get_plugin_name(self):
        self.assertEqual(
            get_plugin_name(normal_plugin),
            'pelican.tests.dummy_plugins.normal_plugin.normal_plugin',
        )

        class NoopPlugin:
            def register(self):
                pass

        self.assertEqual(
            get_plugin_name(NoopPlugin),
            'PluginTest.test_get_plugin_name.<locals>.NoopPlugin')
        self.assertEqual(
            get_plugin_name(NoopPlugin()),
            'PluginTest.test_get_plugin_name.<locals>.NoopPlugin')