porringer 0.2.1.dev0__tar.gz → 0.2.1.dev6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/PKG-INFO +1 -1
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/api.py +1 -1
- porringer-0.2.1.dev6/porringer/backend/backend.py +153 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/backend/builder.py +70 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/backend/command/sync.py +456 -87
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/backend/resolver.py +4 -1
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/console/command/plugin.py +3 -1
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/console/command/self.py +1 -1
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/console/command/sync.py +23 -2
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/core/plugin_schema/environment.py +151 -29
- porringer-0.2.1.dev6/porringer/core/plugin_schema/project_environment.py +195 -0
- porringer-0.2.1.dev6/porringer/core/plugin_schema/runtime.py +85 -0
- porringer-0.2.1.dev6/porringer/core/schema.py +298 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/apt/plugin.py +18 -5
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/brew/plugin.py +20 -5
- porringer-0.2.1.dev6/porringer/plugin/bun/__init__.py +1 -0
- porringer-0.2.1.dev6/porringer/plugin/bun/plugin.py +139 -0
- porringer-0.2.1.dev6/porringer/plugin/bun_project/__init__.py +1 -0
- porringer-0.2.1.dev6/porringer/plugin/bun_project/plugin.py +41 -0
- porringer-0.2.1.dev6/porringer/plugin/deno/__init__.py +1 -0
- porringer-0.2.1.dev6/porringer/plugin/deno/plugin.py +156 -0
- porringer-0.2.1.dev6/porringer/plugin/deno_project/__init__.py +1 -0
- porringer-0.2.1.dev6/porringer/plugin/deno_project/plugin.py +69 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/npm/plugin.py +44 -10
- porringer-0.2.1.dev6/porringer/plugin/npm_project/__init__.py +1 -0
- porringer-0.2.1.dev6/porringer/plugin/npm_project/plugin.py +39 -0
- porringer-0.2.1.dev6/porringer/plugin/pdm/__init__.py +1 -0
- porringer-0.2.1.dev6/porringer/plugin/pdm/plugin.py +37 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/pim/plugin.py +75 -9
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/pip/plugin.py +137 -38
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/pipx/plugin.py +37 -8
- porringer-0.2.1.dev6/porringer/plugin/pnpm/__init__.py +1 -0
- porringer-0.2.1.dev6/porringer/plugin/pnpm/plugin.py +167 -0
- porringer-0.2.1.dev6/porringer/plugin/pnpm_project/__init__.py +1 -0
- porringer-0.2.1.dev6/porringer/plugin/pnpm_project/plugin.py +66 -0
- porringer-0.2.1.dev6/porringer/plugin/poetry/__init__.py +1 -0
- porringer-0.2.1.dev6/porringer/plugin/poetry/plugin.py +78 -0
- porringer-0.2.1.dev6/porringer/plugin/pyenv/__init__.py +1 -0
- porringer-0.2.1.dev6/porringer/plugin/pyenv/plugin.py +309 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/uv/plugin.py +43 -23
- porringer-0.2.1.dev6/porringer/plugin/uv_project/__init__.py +1 -0
- porringer-0.2.1.dev6/porringer/plugin/uv_project/plugin.py +39 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/winget/plugin.py +15 -13
- porringer-0.2.1.dev6/porringer/plugin/yarn_project/__init__.py +1 -0
- porringer-0.2.1.dev6/porringer/plugin/yarn_project/plugin.py +71 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/schema.py +88 -21
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/test/mock/environment.py +2 -4
- porringer-0.2.1.dev6/porringer/test/mock/project_environment.py +29 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/test/pytest/shared.py +29 -13
- porringer-0.2.1.dev6/porringer/test/pytest/tests.py +100 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/test/pytest/variants.py +17 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/pyproject.toml +20 -1
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/integration/test_example_presence.py +2 -2
- porringer-0.2.1.dev6/tests/unit/plugins/apt/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/apt/test_environment.py +20 -0
- porringer-0.2.1.dev6/tests/unit/plugins/brew/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/brew/test_environment.py +20 -0
- porringer-0.2.1.dev6/tests/unit/plugins/bun/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/bun/test_environment.py +20 -0
- porringer-0.2.1.dev6/tests/unit/plugins/bun_project/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/bun_project/test_environment.py +16 -0
- porringer-0.2.1.dev6/tests/unit/plugins/deno/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/deno/test_environment.py +20 -0
- porringer-0.2.1.dev6/tests/unit/plugins/deno_project/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/deno_project/test_environment.py +16 -0
- porringer-0.2.1.dev6/tests/unit/plugins/npm/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/npm/test_environment.py +20 -0
- porringer-0.2.1.dev6/tests/unit/plugins/npm_project/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/npm_project/test_environment.py +16 -0
- porringer-0.2.1.dev6/tests/unit/plugins/pdm/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/pdm/test_environment.py +16 -0
- porringer-0.2.1.dev6/tests/unit/plugins/pim/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/pim/test_environment.py +97 -0
- porringer-0.2.1.dev6/tests/unit/plugins/pip/test_environment.py +234 -0
- porringer-0.2.1.dev6/tests/unit/plugins/pipx/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/pipx/test_environment.py +20 -0
- porringer-0.2.1.dev6/tests/unit/plugins/pnpm/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/pnpm/test_environment.py +20 -0
- porringer-0.2.1.dev6/tests/unit/plugins/pnpm_project/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/pnpm_project/test_environment.py +16 -0
- porringer-0.2.1.dev6/tests/unit/plugins/poetry/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/poetry/test_environment.py +16 -0
- porringer-0.2.1.dev6/tests/unit/plugins/pyenv/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/pyenv/test_environment.py +135 -0
- porringer-0.2.1.dev6/tests/unit/plugins/uv/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/uv/test_environment.py +20 -0
- porringer-0.2.1.dev6/tests/unit/plugins/uv_project/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/uv_project/test_environment.py +16 -0
- porringer-0.2.1.dev6/tests/unit/plugins/yarn_project/__init__.py +1 -0
- porringer-0.2.1.dev6/tests/unit/plugins/yarn_project/test_environment.py +16 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/unit/test_command_plugin.py +6 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/unit/test_command_self.py +3 -3
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/unit/test_manifest.py +85 -73
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/unit/test_package_ref.py +76 -4
- porringer-0.2.1.dev6/tests/unit/test_project_directory.py +240 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/unit/test_sub_action_progress.py +4 -1
- porringer-0.2.1.dev0/porringer/backend/backend.py +0 -142
- porringer-0.2.1.dev0/porringer/core/schema.py +0 -150
- porringer-0.2.1.dev0/porringer/test/pytest/tests.py +0 -31
- porringer-0.2.1.dev0/tests/unit/plugins/pip/test_environment.py +0 -182
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/LICENSE.md +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/README.md +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/backend/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/backend/cache.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/backend/command/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/backend/command/plugin.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/backend/command/self.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/backend/schema.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/console/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/console/command/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/console/command/cache.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/console/command/check.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/console/command/download.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/console/entry.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/console/schema.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/core/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/core/plugin_schema/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/apt/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/brew/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/npm/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/pim/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/pip/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/pipx/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/uv/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/plugin/winget/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/py.typed +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/test/mock/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/test/pytest/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/test/pytest/plugin.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/utility/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/utility/download.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/utility/exception.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/utility/py.typed +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/porringer/utility/utility.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/conftest.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/integration/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/integration/plugins/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/integration/plugins/pip/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/integration/plugins/pip/test_environment.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/integration/plugins/pipx/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/integration/plugins/pipx/test_environment.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/integration/plugins/winget/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/integration/plugins/winget/test_environment.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/unit/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/unit/plugins/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/unit/plugins/pip/__init__.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/unit/test_cache.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/unit/test_check.py +0 -0
- {porringer-0.2.1.dev0 → porringer-0.2.1.dev6}/tests/unit/test_cli.py +0 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Backend resolution for mapping (kind, ecosystem) pairs to installer plugins.
|
|
2
|
+
|
|
3
|
+
The ``BackendResolver`` determines which plugin should handle each
|
|
4
|
+
``(PluginKind, ecosystem)`` pair declared in a manifest. For example,
|
|
5
|
+
``(PACKAGE, "python")`` might resolve to ``uv`` or ``pip`` depending
|
|
6
|
+
on availability and user preferences.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
from collections.abc import Mapping
|
|
13
|
+
|
|
14
|
+
from porringer.core.plugin_schema.environment import Environment
|
|
15
|
+
from porringer.core.plugin_schema.project_environment import ProjectEnvironment
|
|
16
|
+
from porringer.core.schema import PluginKind
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
# Type alias for any plugin that participates in backend resolution.
|
|
21
|
+
BackendPlugin = Environment | ProjectEnvironment
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class BackendResolver:
|
|
25
|
+
"""Maps ``(PluginKind, ecosystem)`` pairs to the best available plugin.
|
|
26
|
+
|
|
27
|
+
Construction requires two inputs:
|
|
28
|
+
|
|
29
|
+
* *environments* – all instantiated :class:`Environment` plugins
|
|
30
|
+
keyed by their canonical name.
|
|
31
|
+
* *preferences* – an optional dict coming from the manifest's
|
|
32
|
+
``preferences`` field (e.g. ``{"python": "uv"}``).
|
|
33
|
+
|
|
34
|
+
Optionally accepts *project_environments* for project-scoped plugins.
|
|
35
|
+
|
|
36
|
+
Resolution algorithm per ``(kind, ecosystem)`` pair:
|
|
37
|
+
|
|
38
|
+
1. If the user gave an explicit **preference** for the ecosystem and
|
|
39
|
+
the named plugin is available, use it.
|
|
40
|
+
2. Otherwise sort all registered candidates by
|
|
41
|
+
:meth:`~Plugin.default_priority` ascending and pick the first one
|
|
42
|
+
whose ``is_available()`` returns ``True``.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
environments: Mapping[str, Environment],
|
|
48
|
+
preferences: Mapping[str, str] | None = None,
|
|
49
|
+
project_environments: Mapping[str, ProjectEnvironment] | None = None,
|
|
50
|
+
) -> None:
|
|
51
|
+
self._environments = environments
|
|
52
|
+
self._project_environments: Mapping[str, ProjectEnvironment] = project_environments or {}
|
|
53
|
+
self._preferences = preferences or {}
|
|
54
|
+
|
|
55
|
+
# Merged view for indexing and availability checks
|
|
56
|
+
self._all_plugins: dict[str, BackendPlugin] = dict(environments)
|
|
57
|
+
self._all_plugins.update(self._project_environments)
|
|
58
|
+
|
|
59
|
+
# Index: (kind, ecosystem) -> [plugin_name, ...]
|
|
60
|
+
self._backend_plugins: dict[tuple[PluginKind, str], list[str]] = {}
|
|
61
|
+
for name, plugin in self._all_plugins.items():
|
|
62
|
+
ecosystem = type(plugin).ecosystem()
|
|
63
|
+
if ecosystem is not None:
|
|
64
|
+
kind = type(plugin).plugin_kind()
|
|
65
|
+
key = (kind, ecosystem)
|
|
66
|
+
self._backend_plugins.setdefault(key, []).append(name)
|
|
67
|
+
|
|
68
|
+
# Resolve once and cache
|
|
69
|
+
self._resolved: dict[tuple[PluginKind, str], str | None] = {}
|
|
70
|
+
for key in self._backend_plugins:
|
|
71
|
+
self._resolved[key] = self._resolve(key)
|
|
72
|
+
|
|
73
|
+
# ------------------------------------------------------------------
|
|
74
|
+
# Public API
|
|
75
|
+
# ------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
def resolve(self, kind: PluginKind, ecosystem: str) -> str | None:
|
|
78
|
+
"""Return the chosen plugin name for *(kind, ecosystem)*, or ``None``."""
|
|
79
|
+
key = (kind, ecosystem)
|
|
80
|
+
if key not in self._resolved:
|
|
81
|
+
logger.warning("No plugins registered for (%s, '%s')", kind.value, ecosystem)
|
|
82
|
+
return None
|
|
83
|
+
return self._resolved[key]
|
|
84
|
+
|
|
85
|
+
def validator_for(self, kind: PluginKind, ecosystem: str) -> str | None:
|
|
86
|
+
"""Return the ``package_name_validator()`` tag for the resolved plugin.
|
|
87
|
+
|
|
88
|
+
Returns ``None`` when no plugin is resolved or the plugin
|
|
89
|
+
declares no validator.
|
|
90
|
+
"""
|
|
91
|
+
name = self.resolve(kind, ecosystem)
|
|
92
|
+
if name is None:
|
|
93
|
+
return None
|
|
94
|
+
plugin = self._all_plugins.get(name)
|
|
95
|
+
if plugin is None:
|
|
96
|
+
return None
|
|
97
|
+
return type(plugin).package_name_validator()
|
|
98
|
+
|
|
99
|
+
# ------------------------------------------------------------------
|
|
100
|
+
# Internal
|
|
101
|
+
# ------------------------------------------------------------------
|
|
102
|
+
|
|
103
|
+
def _resolve(self, key: tuple[PluginKind, str]) -> str | None:
|
|
104
|
+
"""Pick the best plugin for *(kind, ecosystem)*.
|
|
105
|
+
|
|
106
|
+
1. Explicit preference (if available).
|
|
107
|
+
2. Sort candidates by ``default_priority()`` ascending, pick first available.
|
|
108
|
+
"""
|
|
109
|
+
kind, ecosystem = key
|
|
110
|
+
candidates = self._backend_plugins.get(key, [])
|
|
111
|
+
if not candidates:
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
# 1. Explicit preference
|
|
115
|
+
if ecosystem in self._preferences:
|
|
116
|
+
preferred = self._preferences[ecosystem]
|
|
117
|
+
if preferred in candidates and self._is_available(preferred):
|
|
118
|
+
return preferred
|
|
119
|
+
logger.warning(
|
|
120
|
+
"Preferred plugin '%s' for (%s, '%s') is not available; falling back",
|
|
121
|
+
preferred,
|
|
122
|
+
kind.value,
|
|
123
|
+
ecosystem,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# 2. Sort by default_priority ascending
|
|
127
|
+
def _priority(name: str) -> int:
|
|
128
|
+
plugin = self._all_plugins.get(name)
|
|
129
|
+
if plugin is None:
|
|
130
|
+
return 9999
|
|
131
|
+
try:
|
|
132
|
+
return type(plugin).default_priority()
|
|
133
|
+
except Exception:
|
|
134
|
+
return 9999
|
|
135
|
+
|
|
136
|
+
sorted_candidates = sorted(candidates, key=_priority)
|
|
137
|
+
for name in sorted_candidates:
|
|
138
|
+
if self._is_available(name):
|
|
139
|
+
return name
|
|
140
|
+
|
|
141
|
+
logger.warning("No available plugin for (%s, '%s')", kind.value, ecosystem)
|
|
142
|
+
return None
|
|
143
|
+
|
|
144
|
+
def _is_available(self, plugin_name: str) -> bool:
|
|
145
|
+
"""Check if *plugin_name* reports itself as available."""
|
|
146
|
+
plugin = self._all_plugins.get(plugin_name)
|
|
147
|
+
if plugin is None:
|
|
148
|
+
return False
|
|
149
|
+
try:
|
|
150
|
+
return type(plugin).is_available()
|
|
151
|
+
except Exception:
|
|
152
|
+
logger.debug("is_available() failed for plugin '%s'", plugin_name, exc_info=True)
|
|
153
|
+
return False
|
|
@@ -6,6 +6,7 @@ from importlib import metadata
|
|
|
6
6
|
from packaging.version import Version
|
|
7
7
|
|
|
8
8
|
from porringer.core.plugin_schema.environment import Environment
|
|
9
|
+
from porringer.core.plugin_schema.project_environment import ProjectEnvironment
|
|
9
10
|
from porringer.core.schema import Distribution, PluginDependency, PluginParameters
|
|
10
11
|
from porringer.schema import PluginInformation
|
|
11
12
|
from porringer.utility.exception import PluginDependencyError
|
|
@@ -163,3 +164,72 @@ class Builder:
|
|
|
163
164
|
environments.append(Builder.build_environment(environment_type))
|
|
164
165
|
|
|
165
166
|
return environments
|
|
167
|
+
|
|
168
|
+
@staticmethod
|
|
169
|
+
def find_project_environments() -> list[PluginInformation[ProjectEnvironment]]:
|
|
170
|
+
"""Searches for registered project environment plugins.
|
|
171
|
+
|
|
172
|
+
Scans the ``porringer.project_environment`` entry-point group for
|
|
173
|
+
classes that subclass :class:`ProjectEnvironment`.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
A list of loaded project-environment plugins.
|
|
177
|
+
"""
|
|
178
|
+
group_name = 'project_environment'
|
|
179
|
+
plugin_types: list[PluginInformation[ProjectEnvironment]] = []
|
|
180
|
+
|
|
181
|
+
for entry_point in list(metadata.entry_points(group=f'porringer.{group_name}')):
|
|
182
|
+
try:
|
|
183
|
+
loaded_type = entry_point.load()
|
|
184
|
+
except ModuleNotFoundError as e:
|
|
185
|
+
logger.warning(f"Plugin '{entry_point.name}' could not be loaded: {e}. Skipping")
|
|
186
|
+
continue
|
|
187
|
+
|
|
188
|
+
canonicalized = canonicalize_type(loaded_type)
|
|
189
|
+
|
|
190
|
+
if entry_point.dist is None:
|
|
191
|
+
logger.error(f"Plugin '{canonicalized.name}' is not installed. Skipping")
|
|
192
|
+
continue
|
|
193
|
+
|
|
194
|
+
if not issubclass(loaded_type, ProjectEnvironment):
|
|
195
|
+
logger.warning(
|
|
196
|
+
f"Found incompatible plugin. The '{canonicalized.name}' plugin must be an instance"
|
|
197
|
+
f" of '{group_name}'"
|
|
198
|
+
)
|
|
199
|
+
else:
|
|
200
|
+
logger.debug(f'{group_name} plugin found: {canonicalized.name}')
|
|
201
|
+
plugin_types.append(PluginInformation(loaded_type, entry_point.dist))
|
|
202
|
+
|
|
203
|
+
return plugin_types
|
|
204
|
+
|
|
205
|
+
@staticmethod
|
|
206
|
+
def build_project_environment(
|
|
207
|
+
project_environment_type: PluginInformation[ProjectEnvironment],
|
|
208
|
+
) -> ProjectEnvironment:
|
|
209
|
+
"""Constructs a single project environment from input type.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
project_environment_type: The type to construct.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
The instantiated project environment.
|
|
216
|
+
"""
|
|
217
|
+
plugin_version = Version(project_environment_type.distribution.version)
|
|
218
|
+
plugin_distribution = Distribution(version=plugin_version)
|
|
219
|
+
parameters = PluginParameters(distribution=plugin_distribution)
|
|
220
|
+
|
|
221
|
+
return project_environment_type.type(parameters)
|
|
222
|
+
|
|
223
|
+
@staticmethod
|
|
224
|
+
def build_project_environments(
|
|
225
|
+
project_environment_types: list[PluginInformation[ProjectEnvironment]],
|
|
226
|
+
) -> list[ProjectEnvironment]:
|
|
227
|
+
"""Constructs project environments from input types.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
project_environment_types: The types to construct.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
The instantiated project environments.
|
|
234
|
+
"""
|
|
235
|
+
return [Builder.build_project_environment(t) for t in project_environment_types]
|