mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 05:05:42 +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:
		| @@ -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') |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user