mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 03:56:43 +00:00
Optimisations for editable installs of plugins (#3634)
* out-of-scope: add function to check if a package is editable * out-of-scope: move to included meta toolset for metadata discovery * out-of-scope: make lookup safe for editable installs
This commit is contained in:
parent
2601cf0279
commit
abf133384b
@ -7,9 +7,7 @@ import pkgutil
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sysconfig
|
import sysconfig
|
||||||
import traceback
|
import traceback
|
||||||
from importlib.metadata import (PackageNotFoundError, distributions,
|
from importlib.metadata import entry_points
|
||||||
entry_points)
|
|
||||||
from importlib.util import find_spec
|
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -71,18 +69,21 @@ def handle_error(error, do_raise: bool = True, do_log: bool = True, log_name: st
|
|||||||
package_name = pathlib.Path(package_path).relative_to(install_path).parts[0]
|
package_name = pathlib.Path(package_path).relative_to(install_path).parts[0]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# is file - loaded -> form a name for that
|
# is file - loaded -> form a name for that
|
||||||
path_obj = pathlib.Path(package_path).relative_to(settings.BASE_DIR)
|
try:
|
||||||
path_parts = [*path_obj.parts]
|
path_obj = pathlib.Path(package_path).relative_to(settings.BASE_DIR)
|
||||||
path_parts[-1] = path_parts[-1].replace(path_obj.suffix, '') # remove suffix
|
path_parts = [*path_obj.parts]
|
||||||
|
path_parts[-1] = path_parts[-1].replace(path_obj.suffix, '') # remove suffix
|
||||||
|
|
||||||
# remove path prefixes
|
# remove path prefixes
|
||||||
if path_parts[0] == 'plugin':
|
if path_parts[0] == 'plugin':
|
||||||
path_parts.remove('plugin')
|
path_parts.remove('plugin')
|
||||||
path_parts.pop(0)
|
path_parts.pop(0)
|
||||||
else:
|
else:
|
||||||
path_parts.remove('plugins') # pragma: no cover
|
path_parts.remove('plugins') # pragma: no cover
|
||||||
|
|
||||||
package_name = '.'.join(path_parts)
|
package_name = '.'.join(path_parts)
|
||||||
|
except Exception:
|
||||||
|
package_name = package_path
|
||||||
|
|
||||||
if do_log:
|
if do_log:
|
||||||
log_kwargs = {}
|
log_kwargs = {}
|
||||||
@ -231,37 +232,6 @@ def get_plugins(pkg, baseclass, path=None):
|
|||||||
plugins.append(plugin)
|
plugins.append(plugin)
|
||||||
|
|
||||||
return plugins
|
return plugins
|
||||||
|
|
||||||
|
|
||||||
def get_module_meta(mdl_name):
|
|
||||||
"""Return distribution for module.
|
|
||||||
|
|
||||||
Modified form source: https://stackoverflow.com/a/60975978/17860466
|
|
||||||
"""
|
|
||||||
# Get spec for module
|
|
||||||
spec = find_spec(mdl_name)
|
|
||||||
|
|
||||||
if not spec: # pragma: no cover
|
|
||||||
raise PackageNotFoundError(mdl_name)
|
|
||||||
|
|
||||||
# Try to get specific package for the module
|
|
||||||
result = None
|
|
||||||
for dist in distributions():
|
|
||||||
try:
|
|
||||||
relative = pathlib.Path(spec.origin).relative_to(dist.locate_file(''))
|
|
||||||
except ValueError: # pragma: no cover
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
if relative in dist.files:
|
|
||||||
result = dist
|
|
||||||
|
|
||||||
# Check if a distribution was found
|
|
||||||
# A no should not be possible here as a call can only be made on a discovered module but better save then sorry
|
|
||||||
if not result: # pragma: no cover
|
|
||||||
raise PackageNotFoundError(mdl_name)
|
|
||||||
|
|
||||||
# Return metadata
|
|
||||||
return result.metadata
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import inspect
|
|||||||
import logging
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from distutils.sysconfig import get_python_lib
|
||||||
from importlib.metadata import PackageNotFoundError, metadata
|
from importlib.metadata import PackageNotFoundError, metadata
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ from django.urls.base import reverse
|
|||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from plugin.helpers import GitStatus, get_git_log, get_module_meta
|
from plugin.helpers import GitStatus, get_git_log
|
||||||
|
|
||||||
logger = logging.getLogger("inventree")
|
logger = logging.getLogger("inventree")
|
||||||
|
|
||||||
@ -325,6 +326,13 @@ class InvenTreePlugin(VersionMixin, MixinBase, MetaBase):
|
|||||||
"""Get last git commit for the plugin."""
|
"""Get last git commit for the plugin."""
|
||||||
return get_git_log(str(self.file()))
|
return get_git_log(str(self.file()))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_editable(cls):
|
||||||
|
"""Returns if the current part is editable."""
|
||||||
|
pkg_name = cls.__name__.split('.')[0]
|
||||||
|
dist_info = list(Path(get_python_lib()).glob(f'{pkg_name}-*.dist-info'))
|
||||||
|
return bool(len(dist_info) == 1)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_package_metadata(cls):
|
def _get_package_metadata(cls):
|
||||||
"""Get package metadata for plugin."""
|
"""Get package metadata for plugin."""
|
||||||
@ -334,7 +342,7 @@ class InvenTreePlugin(VersionMixin, MixinBase, MetaBase):
|
|||||||
meta = metadata(cls.__name__)
|
meta = metadata(cls.__name__)
|
||||||
# Simpel lookup did not work - get data from module
|
# Simpel lookup did not work - get data from module
|
||||||
except PackageNotFoundError:
|
except PackageNotFoundError:
|
||||||
meta = get_module_meta(cls.__module__)
|
meta = metadata(cls.__module__.split('.')[0])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'author': meta['Author-email'],
|
'author': meta['Author-email'],
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from .helpers import get_module_meta, render_template
|
from .helpers import render_template
|
||||||
|
|
||||||
|
|
||||||
class HelperTests(TestCase):
|
class HelperTests(TestCase):
|
||||||
@ -21,15 +21,3 @@ class HelperTests(TestCase):
|
|||||||
response = render_template(ErrorSource(), 'sample/wrongsample.html', {'abc': 123})
|
response = render_template(ErrorSource(), 'sample/wrongsample.html', {'abc': 123})
|
||||||
self.assertTrue('lert alert-block alert-danger' in response)
|
self.assertTrue('lert alert-block alert-danger' in response)
|
||||||
self.assertTrue('Template file <em>sample/wrongsample.html</em>' in response)
|
self.assertTrue('Template file <em>sample/wrongsample.html</em>' in response)
|
||||||
|
|
||||||
def test_get_module_meta(self):
|
|
||||||
"""Test for get_module_meta."""
|
|
||||||
|
|
||||||
# We need a stable, known good that will be in enviroment for sure
|
|
||||||
# and it can't be stdlib because does might differ depending on the abstraction layer
|
|
||||||
# and version
|
|
||||||
meta = get_module_meta('django')
|
|
||||||
|
|
||||||
# Lets just hope they do not change the name or author
|
|
||||||
self.assertEqual(meta['Name'], 'Django')
|
|
||||||
self.assertEqual(meta['Author'], 'Django Software Foundation')
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user