mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 13:05:42 +00:00
Implement removal of plugin from plugins dir
This commit is contained in:
@ -3,6 +3,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@ -21,8 +22,10 @@ logger = logging.getLogger('inventree')
|
|||||||
def pip_command(*args):
|
def pip_command(*args):
|
||||||
"""Build and run a pip command using using the current python executable.
|
"""Build and run a pip command using using the current python executable.
|
||||||
|
|
||||||
returns: subprocess.check_output
|
Returns: The output of the pip command
|
||||||
throws: subprocess.CalledProcessError
|
|
||||||
|
Raises:
|
||||||
|
subprocess.CalledProcessError: If the pip command fails
|
||||||
"""
|
"""
|
||||||
python = sys.executable
|
python = sys.executable
|
||||||
|
|
||||||
@ -88,14 +91,18 @@ def check_plugins_path(packagename: str) -> bool:
|
|||||||
return any(re.match(f'^{packagename}==', line.strip()) for line in output)
|
return any(re.match(f'^{packagename}==', line.strip()) for line in output)
|
||||||
|
|
||||||
|
|
||||||
def check_package_path(packagename: str):
|
def check_package_path(packagename: str) -> str:
|
||||||
"""Determine the install path of a particular package.
|
"""Determine the install path of a particular package.
|
||||||
|
|
||||||
- If installed, return the installation path
|
- If installed, return the installation path
|
||||||
- If not installed, return False
|
- If not installed, return an empty string
|
||||||
"""
|
"""
|
||||||
logger.debug('check_package_path: %s', packagename)
|
logger.debug('check_package_path: %s', packagename)
|
||||||
|
|
||||||
|
# First check if the package is installed in the plugins directory
|
||||||
|
if check_plugins_path(packagename):
|
||||||
|
return f'plugins/{packagename}'
|
||||||
|
|
||||||
# Remove version information
|
# Remove version information
|
||||||
for c in '<>=! ':
|
for c in '<>=! ':
|
||||||
packagename = packagename.split(c)[0]
|
packagename = packagename.split(c)[0]
|
||||||
@ -120,7 +127,7 @@ def check_package_path(packagename: str):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# If we get here, the package is not installed
|
# If we get here, the package is not installed
|
||||||
return False
|
return ''
|
||||||
|
|
||||||
|
|
||||||
def plugins_dir():
|
def plugins_dir():
|
||||||
@ -257,13 +264,6 @@ def install_plugin(url=None, packagename=None, user=None, version=None):
|
|||||||
|
|
||||||
logger.info('install_plugin: %s, %s', url, packagename)
|
logger.info('install_plugin: %s, %s', url, packagename)
|
||||||
|
|
||||||
# Check if we are running in a virtual environment
|
|
||||||
# For now, just log a warning
|
|
||||||
in_venv = sys.prefix != sys.base_prefix
|
|
||||||
|
|
||||||
if not in_venv:
|
|
||||||
logger.warning('InvenTree is not running in a virtual environment')
|
|
||||||
|
|
||||||
plugin_dir = plugins_dir()
|
plugin_dir = plugins_dir()
|
||||||
|
|
||||||
# build up the command
|
# build up the command
|
||||||
@ -359,23 +359,22 @@ def uninstall_plugin(cfg: plugin.models.PluginConfig, user=None, delete_config=T
|
|||||||
_('Plugin cannot be uninstalled as it is currently active')
|
_('Plugin cannot be uninstalled as it is currently active')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not cfg.is_installed():
|
||||||
|
raise ValidationError(_('Plugin is not installed'))
|
||||||
|
|
||||||
validate_package_plugin(cfg, user)
|
validate_package_plugin(cfg, user)
|
||||||
package_name = cfg.package_name
|
package_name = cfg.package_name
|
||||||
logger.info('Uninstalling plugin: %s', package_name)
|
logger.info('Uninstalling plugin: %s', package_name)
|
||||||
|
|
||||||
cmd = ['uninstall', '-y', package_name]
|
if check_plugins_path(package_name):
|
||||||
|
# Uninstall the plugin from the plugins directory
|
||||||
try:
|
uninstall_from_plugins_dir(cfg)
|
||||||
result = pip_command(*cmd)
|
elif check_package_path(package_name):
|
||||||
|
# Uninstall the plugin using pip
|
||||||
ret = {
|
uninstall_from_pip(cfg)
|
||||||
'result': _('Uninstalled plugin successfully'),
|
else:
|
||||||
'success': True,
|
# No matching install target found
|
||||||
'output': str(result, 'utf-8'),
|
raise ValidationError(_('Plugin installation not found'))
|
||||||
}
|
|
||||||
|
|
||||||
except subprocess.CalledProcessError as error:
|
|
||||||
handle_pip_error(error, 'plugin_uninstall')
|
|
||||||
|
|
||||||
# Update the plugins file
|
# Update the plugins file
|
||||||
update_plugins_file(package_name, remove=True)
|
update_plugins_file(package_name, remove=True)
|
||||||
@ -390,4 +389,36 @@ def uninstall_plugin(cfg: plugin.models.PluginConfig, user=None, delete_config=T
|
|||||||
# Reload the plugin registry
|
# Reload the plugin registry
|
||||||
registry.reload_plugins(full_reload=True, force_reload=True, collect=True)
|
registry.reload_plugins(full_reload=True, force_reload=True, collect=True)
|
||||||
|
|
||||||
return ret
|
return {'result': _('Uninstalled plugin successfully'), 'success': True}
|
||||||
|
|
||||||
|
|
||||||
|
def uninstall_from_plugins_dir(cfg: plugin.models.PluginConfig):
|
||||||
|
"""Uninstall a plugin from the plugins directory."""
|
||||||
|
package_name = cfg.package_name
|
||||||
|
logger.debug('Uninstalling plugin from plugins directory: %s', package_name)
|
||||||
|
|
||||||
|
plugin_install_dir = plugins_dir()
|
||||||
|
plugin_dir = cfg.plugin.path()
|
||||||
|
|
||||||
|
if plugin_dir.is_relative_to(plugin_install_dir):
|
||||||
|
# Find the top-most relative path
|
||||||
|
while plugin_dir.parent and plugin_dir.parent != plugin_install_dir:
|
||||||
|
plugin_dir = plugin_dir.parent
|
||||||
|
|
||||||
|
if plugin_dir and plugin_dir.is_relative_to(plugin_install_dir):
|
||||||
|
shutil.rmtree(plugin_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def uninstall_from_pip(cfg: plugin.models.PluginConfig):
|
||||||
|
"""Uninstall a plugin using pip."""
|
||||||
|
package_name = cfg.package_name
|
||||||
|
|
||||||
|
logger.debug('Uninstalling plugin via PIP: %s', package_name)
|
||||||
|
|
||||||
|
cmd = ['uninstall', '-y', package_name]
|
||||||
|
|
||||||
|
try:
|
||||||
|
pip_command(*cmd)
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as error:
|
||||||
|
handle_pip_error(error, 'plugin_uninstall')
|
||||||
|
@ -239,9 +239,10 @@ class InvenTreePlugin(VersionMixin, MixinBase, MetaBase):
|
|||||||
"""File that contains plugin definition."""
|
"""File that contains plugin definition."""
|
||||||
return Path(inspect.getfile(cls))
|
return Path(inspect.getfile(cls))
|
||||||
|
|
||||||
def path(self) -> Path:
|
@classmethod
|
||||||
|
def path(cls) -> Path:
|
||||||
"""Path to plugins base folder."""
|
"""Path to plugins base folder."""
|
||||||
return self.file().parent
|
return cls.file().parent
|
||||||
|
|
||||||
def _get_value(self, meta_name: str, package_name: str) -> str:
|
def _get_value(self, meta_name: str, package_name: str) -> str:
|
||||||
"""Extract values from class meta or package info.
|
"""Extract values from class meta or package info.
|
||||||
|
Reference in New Issue
Block a user