Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/mapclient/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ def windows_main(workflow, execute_now):

splash.showMessage('Ready ...', 100)
splash.finish(window)

return app.exec()


Expand Down
44 changes: 18 additions & 26 deletions src/mapclient/core/managers/pluginmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
import types

from mapclient.application import get_app_path
from mapclient.core.pluginframework import discover_plugins, add_plugins
from mapclient.core.utils import which, FileTypeObject, grep, is_frozen, determine_step_name, determine_step_class_name, \
stable_hash
from mapclient.mountpoints.workflowstep import WorkflowStepMountPoint
from mapclient.settings.definitions import VIRTUAL_ENV_PATH, \
PLUGINS_PACKAGE_NAME, PLUGINS_PTH
from mapclient.core.checks import getPipExecutable
Expand Down Expand Up @@ -173,7 +175,7 @@ def getPluginDatabase(self):

def _addPluginDir(self, directory):
added = False
if isMapClientPluginsDir(directory):
if is_map_client_plugins_dir(directory):
if directory not in sys.path:
sys.path.append(directory)
added = True
Expand All @@ -192,7 +194,6 @@ def installPackage(self, uri):

if not is_frozen():
subprocess.Popen([python_executable, "-m", "pip", "install", str(uri)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=my_env)
# importlib.reload(pkg_resources)

def extractPluginDependencies(self, path):
setup_dir, step_dir = os.path.split(path)
Expand All @@ -218,7 +219,7 @@ def extractPluginDependencies(self, path):

return []

def load(self):
def load(self, initialise=True):
self._reload_plugins = False

len_package_modules_prior = len(sys.modules[PLUGINS_PACKAGE_NAME].__path__) if PLUGINS_PACKAGE_NAME in sys.modules else 0
Expand All @@ -237,15 +238,10 @@ def load(self):
new_plugin_directories.append(os.path.join(directory, name))

if len_package_modules_prior == 0:
try:
package = import_module(PLUGINS_PACKAGE_NAME)
except ModuleNotFoundError:
return
else:
for d in new_plugin_directories:
sys.modules[PLUGINS_PACKAGE_NAME].__path__.append(os.path.join(d, PLUGINS_PACKAGE_NAME))
discover_plugins(new_plugin_directories)

package = sys.modules[PLUGINS_PACKAGE_NAME]
add_plugins(new_plugin_directories)
package = sys.modules[PLUGINS_PACKAGE_NAME]

self._import_errors = []
self._type_errors = []
Expand All @@ -254,10 +250,6 @@ def load(self):
self._plugin_error_directories = {}
self._plugin_error_names = []

# a(pkg_resources.working_set)
# installed = [pkg.key for pkg in pkg_resources.working_set]
# print(installed)

for module_finder, modname, ispkg in pkgutil.iter_modules(package.__path__):
if ispkg:
try:
Expand Down Expand Up @@ -314,8 +306,8 @@ def load(self):
traceback.print_exception(exc_type, exc_value, exc_traceback, file=redirect_output)
logger.warning(''.join(redirect_output.messages))

# installed = [pkg.key for pkg in pkg_resources.working_set]
# print(installed)
if initialise:
WorkflowStepMountPoint.initialise_plugin_map()

def get_plugin_error_names(self):
return self._plugin_error_names
Expand Down Expand Up @@ -444,20 +436,20 @@ def writeSettings(self, settings):
settings.endGroup()


def isMapClientPluginsDir(plugin_dir):
result = False
def is_map_client_plugins_dir(plugin_dir):
try:
names = os.listdir(plugin_dir)
except:
return result
except OSError:
return False

if PLUGINS_PACKAGE_NAME in names:
files = grep(os.path.join(plugin_dir, PLUGINS_PACKAGE_NAME),
r'(from|import) mapclient.mountpoints.workflowstep', one_only=True, file_endswith='.py')
if files:
result = True
return True
# files = grep(os.path.join(plugin_dir, PLUGINS_PACKAGE_NAME),
# r'(from|import) mapclient.mountpoints.workflowstep', one_only=True, file_endswith='.py')
# if files:
# result = True

return result
return False


class PluginSiteManager(object):
Expand Down
73 changes: 66 additions & 7 deletions src/mapclient/core/pluginframework.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,19 @@
along with MAP Client. If not, see <http://www.gnu.org/licenses/>..
"""
import importlib
import importlib.util
import logging
import os
import pkgutil
import sys
import types

from importlib.metadata import distributions
from pathlib import Path

from PySide6.QtCore import QObject

from mapclient.settings.definitions import MAIN_MODULE
from mapclient.settings.definitions import MAIN_MODULE, PLUGINS_PACKAGE_NAME

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -74,22 +81,44 @@ class MetaPluginMountPoint(type):
classes like so:
MyPlugin = MetaPluginMountPoint('MyPlugin', (object, ), {})
"""

def __init__(cls, name, bases, attrs):
if not hasattr(cls, 'plugins'):
super(MetaPluginMountPoint, cls).__init__(name, bases, attrs)
if not hasattr(cls, '_plugins'):
# This branch only executes when processing the mount point itself.
# So, since this is a new plugin type, not an implementation, this
# class shouldn't be registered as a plugin. Instead, it sets up a
# list where plugins can be registered later.
cls.plugins = []
cls._plugins = []
cls._map = {}
else:
# This must be a plugin implementation, which should be registered.
# Simply appending it to the list is all that's needed to keep
# track of it later.
cls.plugins.append(cls)
cls._plugins.append(cls)

def initialise_plugin_map(self):
plugins = [p("") for p in self._plugins]
self._map = {plugin.getName(): index for index, plugin in enumerate(plugins)}
return plugins

def get_all_plugins(self, *args, **kwargs):
return self.initialise_plugin_map()

def getPlugins(self, *args, **kwargs):
return [p(*args, **kwargs) for p in self.plugins]
def get_plugin(self, name, *args, **kwargs):
index = self._map.get(name, -1)
if indexj >= 0:
return self._plugins[index](*args, **kwargs)

return None

def remove_plugin(self, step_module):
for cls in self._plugins:
if cls and step_module in cls.__module__:
print(cls.getName(), step_module)
index = self._plugins.index(cls)
self._plugins.pop(index)
if cls.getName() in self._map:
self._map.pop(cls.getName())


# Plugin mount points are defined below.
Expand Down Expand Up @@ -143,3 +172,33 @@ def __init__(self, name, bases, attrs):
ToolMountPoint = MetaPluginMountPoint('ToolMountPoint', (object,), {})


def add_plugins(plugin_directories):
for d in plugin_directories:
sys.modules[PLUGINS_PACKAGE_NAME].__path__.append(os.path.join(d, PLUGINS_PACKAGE_NAME))

def discover_plugins(plugin_directories):
# setuptools stopped supporting pkg_resources,
# but we cannot know if every plugin has removed
# this old import device. So, we will create a shim
# if it is no longer available and protect against
# plugins still using it.
mock_pkg_resources = False
try:
import pkg_resources
if not hasattr(pkg_resources, 'declare_namespace'):
mock_pkg_resources = True
except ModuleNotFoundError:
mock_pkg_resources = True

if mock_pkg_resources:
def declare_namespace(name):
pass

pkg_resources = types.ModuleType('pkg_resources')
pkg_resources.declare_namespace = declare_namespace
sys.modules['pkg_resources'] = pkg_resources

# bootstrap namespace
m = types.ModuleType(PLUGINS_PACKAGE_NAME)
m.__path__ = []
sys.modules[PLUGINS_PACKAGE_NAME] = m
2 changes: 1 addition & 1 deletion src/mapclient/core/workflow/workflowsteps.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,5 @@ def __init__(self, manager, parent=None):
def reload(self):
self.clear()
self.setColumnCount(1)
for step in WorkflowStepMountPoint.getPlugins(''):
for step in WorkflowStepMountPoint.get_all_plugins(''):
addStep(self, step)
13 changes: 5 additions & 8 deletions src/mapclient/mountpoints/workflowstep.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,11 +328,11 @@ def _workflow_step_relocate_configuration(self, to_location):


def workflowStepFactory(step_name, location):
for step in WorkflowStepMountPoint.getPlugins(location):
if step_name == step.getName():
return step
step = WorkflowStepMountPoint.get_plugin(step_name, location)
if step is None:
raise ValueError('Failed to find/create a step named: ' + step_name)

raise ValueError('Failed to find/create a step named: ' + step_name)
return step


def removeWorkflowStep(step_module):
Expand All @@ -345,7 +345,4 @@ def removeWorkflowStep(step_module):
if step_module in key:
del sys.modules[key]

for cls in WorkflowStepMountPoint.plugins[:]:
if cls and step_module in cls.__module__:
index = WorkflowStepMountPoint.plugins.index(cls)
WorkflowStepMountPoint.plugins.pop(index)
WorkflowStepMountPoint.remove_plugin(step_module)
1 change: 0 additions & 1 deletion src/mapclient/tools/pluginwizard/skeletonstrings.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,6 @@ def validate(self):


NAMESPACE_INIT = """\
__import__('pkg_resources').declare_namespace(__name__)
"""


Expand Down
4 changes: 2 additions & 2 deletions src/mapclient/tools/renameplugin/renamedialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from PySide6 import QtCore, QtWidgets

from mapclient.core.managers.pluginmanager import isMapClientPluginsDir
from mapclient.core.managers.pluginmanager import is_map_client_plugins_dir
from mapclient.core.utils import grep, determine_package_name, determine_step_class_name, determine_step_name, \
convert_name_to_python_package, find_file, qt_tool_wrapper
from mapclient.settings.definitions import PLUGINS_PACKAGE_NAME
Expand Down Expand Up @@ -125,7 +125,7 @@ def _chooseStepClicked(self):

def _interrogateTarget(self):
target = self._ui.lineEditStepLocation.text()
is_plugin_dir = isMapClientPluginsDir(target)
is_plugin_dir = is_map_client_plugins_dir(target)
if is_plugin_dir:
step_dir = os.path.join(target, PLUGINS_PACKAGE_NAME)
files = grep(step_dir, r'(from|import) mapclient.mountpoints.workflowstep',
Expand Down
2 changes: 1 addition & 1 deletion src/mapclient/view/mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ def _plugin_manager_load_plugins(self):
pm.set_directories(self._plugin_manager_dlg.directories())

if pm.reloadPlugins():
pm.load()
pm.load(initialise=False)
wm = self._model.workflowManager()
wm.updateAvailableSteps()
self._workflowWidget.updateStepTree()
Expand Down