diff --git a/src/octoprint/plugin/core.py b/src/octoprint/plugin/core.py index d30e5c77..29f47584 100644 --- a/src/octoprint/plugin/core.py +++ b/src/octoprint/plugin/core.py @@ -614,7 +614,11 @@ class PluginManager(object): # of the virtual env, so this check is necessary plugin.managable = os.access(plugin.location, os.W_OK) \ and (not self._python_virtual_env - or is_sub_path_of(plugin.location, self._python_prefix)) + or is_sub_path_of(plugin.location, self._python_prefix) + or is_editable_install(self._python_install_dir, + package_name, + module_name, + plugin.location)) plugin.enabled = False result[key] = plugin @@ -648,7 +652,7 @@ class PluginManager(object): def _import_plugin(self, key, f, filename, description, name=None, version=None, summary=None, author=None, url=None, license=None): try: instance = imp.load_module(key, f, filename, description) - return PluginInfo(key, os.path.realpath(filename), instance, name=name, version=version, description=summary, author=author, url=url, license=license) + return PluginInfo(key, filename, instance, name=name, version=version, description=summary, author=author, url=url, license=license) except: self.logger.exception("Error loading plugin {key}".format(key=key)) return None @@ -1260,6 +1264,22 @@ def is_sub_path_of(path, parent): rel_path.startswith(os.pardir + os.sep)) +def is_editable_install(install_dir, package, module, location): + package_link = os.path.join(install_dir, "{}.egg-link".format(package)) + if os.path.isfile(package_link): + expected_target = os.path.normcase(os.path.realpath(location)) + try: + with open(package_link) as f: + contents = f.readlines() + for line in contents: + target = os.path.normcase(os.path.realpath(os.path.join(line.strip(), module))) + if target == expected_target: + return True + except: + pass + return False + + class InstalledEntryPoint(pkginfo.Installed): def __init__(self, entry_point, metadata_version=None):