Phase 3 (part 1): manifest-first loader, shopdb.api namespace, auto-register
Hardens the plugin framework so sister-site adoption is safe. Loader rewrite (shopdb/plugins/loader.py): - Reads manifest.json directly. Dependency sort and version checks no longer instantiate plugin classes (avoids __init__ side effects). - Fail-loud policy: in dev/test (DEBUG or TESTING true), plugin errors re-raise. In production, errors log with full context and the plugin is excluded from registration. Framework keeps booting. - Contract-version range check via packaging.SpecifierSet. Plugin's manifest.core_version must include the framework's __contract_version__ or load fails per the policy above. - Manifest validation: required fields (name, version, description), name matches directory, JSON parseable. Exceptions (shopdb/exceptions.py): - PluginNotFoundError, PluginContractError, PluginVersionError, PluginDependencyError. Specific types replace generic Exception swallowing. Auto-register core blueprints (shopdb/__init__.py): - CORE_BLUEPRINT_NAMES tuple drives registration. Adding a core resource is one entry, not three lines (import + register call). - Replaces 27 hand-coded register_blueprint calls. - Asserts each blueprint is exported by shopdb.core.api at boot. Public API namespace (shopdb/api/__init__.py): - audit_log: thin wrapper over AuditLog.log() with stable signature. - resolve_asset_position: implements ADR-001 position resolution (asset > related > location). Asset.mapx/mapy and AssetRelationship.inheritsposition columns are part of the locked contract surface but not yet in models; helper degrades gracefully to location-only fallback until the migration lands. BasePlugin helpers (shopdb/plugins/base.py): - get_setting(key, default), set_setting(key, value, ...). Settings namespaced as plugin.<pluginname>.<key> so two plugins can use the same key without colliding. Manifest version compatibility (plugins/*/manifest.json): - Bumped core_version from ">=1.0.0" to ">=0.1.0,<1.0.0" so all bundled plugins satisfy the new range check. Contract version bump (shopdb/__init__.py): - 0.1.0 -> 0.2.0. Additive surface change (Setting helpers, shopdb.api namespace) per ADR-002 minor-bump rules. Tests (tests/test_plugin_loader.py, tests/test_api_namespace.py): - 13 loader tests: manifest validation failures, version range checks, plugin.py import errors, strict-vs-isolate behavior under TESTING vs production-like config, manifest-first dependency sort. - 8 api-namespace tests: audit_log roundtrip, resolve position fallback chain, plugin.get_setting/set_setting roundtrip with per-plugin namespacing. Test count: 66 -> 87 passing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -73,6 +73,36 @@ class BasePlugin(ABC):
|
||||
"""Return dict of service name -> service class."""
|
||||
return {}
|
||||
|
||||
def get_setting(self, key: str, default=None):
|
||||
"""Read a plugin-scoped setting from the core Setting store.
|
||||
|
||||
Settings are namespaced by plugin name to avoid collisions
|
||||
across plugins. The on-disk key is `plugin.<pluginname>.<key>`.
|
||||
|
||||
Returns the typed value (string, integer, boolean, etc.) or
|
||||
the default if not set.
|
||||
"""
|
||||
from shopdb.core.models import Setting
|
||||
namespaced_key = f'plugin.{self.meta.name}.{key}'
|
||||
return Setting.get(namespaced_key, default)
|
||||
|
||||
def set_setting(self, key: str, value, valuetype: str = 'string',
|
||||
description: str = None) -> None:
|
||||
"""Write a plugin-scoped setting to the core Setting store.
|
||||
|
||||
Settings are namespaced by plugin name. Persists immediately and
|
||||
survives restarts.
|
||||
"""
|
||||
from shopdb.core.models import Setting
|
||||
namespaced_key = f'plugin.{self.meta.name}.{key}'
|
||||
Setting.set(
|
||||
namespaced_key,
|
||||
value,
|
||||
valuetype=valuetype,
|
||||
category=f'plugin.{self.meta.name}',
|
||||
description=description,
|
||||
)
|
||||
|
||||
def get_collector_schema(self) -> Optional[Dict]:
|
||||
"""Return JSON Schema describing the collector payload for this plugin.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user