From 05c93cc776312a5c943d98d43880f6ed3d63d539 Mon Sep 17 00:00:00 2001
From: Eric Froemling
Date: Fri, 16 Oct 2020 11:28:01 -0700
Subject: [PATCH] Cleaned up plugin subsystem
---
CHANGELOG.md | 2 +
assets/src/ba_data/python/ba/__init__.py | 2 +-
assets/src/ba_data/python/ba/_app.py | 52 +++---------------
assets/src/ba_data/python/ba/_meta.py | 7 +--
assets/src/ba_data/python/ba/_plugin.py | 54 +++++++++++++++++++
.../python/bastd/ui/settings/plugins.py | 4 +-
docs/ba_module.md | 26 +++++++++
7 files changed, 95 insertions(+), 52 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f73dd01d..60ef4789 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
### 1.5.27 (20218)
- Language functionality has been consolidated into a LanguageSubsystem object at ba.app.lang
- ba.get_valid_languages() is now an attr: ba.app.lang.available_languages
+- Achievement functionality has been consolidated into an AchievementSubsystem object at ba.app.ach
+- Plugin functionality has been consolidated into a PluginSubsystem obj at ba.app.plugins
### 1.5.26 (20217)
- Simplified licensing header on python scripts.
diff --git a/assets/src/ba_data/python/ba/__init__.py b/assets/src/ba_data/python/ba/__init__.py
index 642f4643..79c80daf 100644
--- a/assets/src/ba_data/python/ba/__init__.py
+++ b/assets/src/ba_data/python/ba/__init__.py
@@ -20,7 +20,7 @@ from _ba import (CollideModel, Context, ContextCall, Data, InputDevice,
set_analytics_screen, charstr, textwidget, time, timer,
open_url, widget)
from ba._activity import Activity
-from ba._plugin import PotentialPlugin, Plugin
+from ba._plugin import PotentialPlugin, Plugin, PluginSubsystem
from ba._actor import Actor
from ba._player import PlayerInfo, Player, EmptyPlayer, StandLocation
from ba._nodeactor import NodeActor
diff --git a/assets/src/ba_data/python/ba/_app.py b/assets/src/ba_data/python/ba/_app.py
index e5b84b90..636de193 100644
--- a/assets/src/ba_data/python/ba/_app.py
+++ b/assets/src/ba_data/python/ba/_app.py
@@ -175,6 +175,7 @@ class App:
from ba._language import LanguageSubsystem
from ba._ui import UISubsystem
from ba._achievement import AchievementSubsystem
+ from ba._plugin import PluginSubsystem
# Config.
self.config_file_healthy = False
@@ -198,10 +199,6 @@ class App:
self.iircade_mode: bool = self._env['iircade_mode']
assert isinstance(self.headless_mode, bool)
- # Plugins.
- self.potential_plugins: List[ba.PotentialPlugin] = []
- self.active_plugins: Dict[str, ba.Plugin] = {}
-
# Misc.
self.metascan: Optional[_meta.ScanResults] = None
self.tips: List[str] = []
@@ -241,6 +238,7 @@ class App:
self.last_ad_purpose = 'invalid'
self.attempted_first_ad = False
+ self.plugins = PluginSubsystem()
self.music = MusicSubsystem()
self.lang = LanguageSubsystem()
self.ach = AchievementSubsystem()
@@ -291,7 +289,6 @@ class App:
(internal)"""
# pylint: disable=too-many-locals
# pylint: disable=cyclic-import
- # pylint: disable=too-many-statements
from ba import _apputils
from ba import _appconfig
from ba import _achievement
@@ -401,49 +398,13 @@ class App:
_ba.pushcall(do_auto_sign_in)
- # Load up our plugins and go ahead and call their on_app_launch calls.
- self.load_plugins()
- for plugin in self.active_plugins.values():
- try:
- plugin.on_app_launch()
- except Exception:
- from ba import _error
- _error.print_exception('Error in plugin on_app_launch()')
+ self.plugins.on_app_launch()
self.ran_on_app_launch = True
# from ba._dependency import test_depset
# test_depset()
- def load_plugins(self) -> None:
- """(internal)"""
- from ba._general import getclass
- from ba._plugin import Plugin
-
- # Note: the plugins we load is purely based on what's enabled
- # in the app config. Our meta-scan gives us a list of available
- # plugins, but that is only used to give the user a list of plugins
- # that they can enable. (we wouldn't want to look at meta-scan here
- # anyway because it may not be done yet at this point in the launch)
- plugstates: Dict[str, Dict] = self.config.get('Plugins', {})
- assert isinstance(plugstates, dict)
- plugkeys: List[str] = sorted(key for key, val in plugstates.items()
- if val.get('enabled', False))
- for plugkey in plugkeys:
- try:
- cls = getclass(plugkey, Plugin)
- except Exception as exc:
- _ba.log(f"Error loading plugin class '{plugkey}': {exc}",
- to_server=False)
- continue
- try:
- plugin = cls()
- assert plugkey not in self.active_plugins
- self.active_plugins[plugkey] = plugin
- except Exception:
- from ba import _error
- _error.print_exception(f'Error loading plugin: {plugkey}')
-
def read_config(self) -> None:
"""(internal)"""
from ba import _appconfig
@@ -558,7 +519,6 @@ class App:
"""Run when the app resumes from a suspended state."""
self.music.on_app_resume()
-
self.fg_state += 1
# Mark our cached tourneys as invalid so anyone using them knows
@@ -657,13 +617,13 @@ class App:
# If we're still not signed in and have pending codes,
# inform the user that they need to sign in to use them.
- if _ba.app.pending_promo_codes:
+ if self.pending_promo_codes:
_ba.screenmessage(
Lstr(resource='signInForPromoCodeText'),
color=(1, 0, 0))
_ba.playsound(_ba.getsound('error'))
- _ba.app.pending_promo_codes.append(code)
+ self.pending_promo_codes.append(code)
_ba.timer(6.0, check_pending_codes, timetype=TimeType.REAL)
return
_ba.screenmessage(Lstr(resource='submittingPromoCodeText'),
@@ -681,7 +641,7 @@ class App:
def _test_https(self) -> None:
"""Testing https support.
- (would be nice to get this working on our custom python builds; need
+ (would be nice to get this working on our custom Python builds; need
to wrangle certificates somehow).
"""
import urllib.request
diff --git a/assets/src/ba_data/python/ba/_meta.py b/assets/src/ba_data/python/ba/_meta.py
index 7b314d68..28b88aff 100644
--- a/assets/src/ba_data/python/ba/_meta.py
+++ b/assets/src/ba_data/python/ba/_meta.py
@@ -68,6 +68,7 @@ def handle_scan_results(results: ScanResults) -> None:
_ba.log(textwrap.indent(results.errors, 'Error (meta-scan): '))
# Handle plugins.
+ plugs = _ba.app.plugins
config_changed = False
found_new = False
plugstates: Dict[str, Dict] = _ba.app.config.setdefault('Plugins', {})
@@ -75,7 +76,7 @@ def handle_scan_results(results: ScanResults) -> None:
# Create a potential-plugin for each class we found in the scan.
for class_path in results.plugins:
- _ba.app.potential_plugins.append(
+ plugs.potential_plugins.append(
PotentialPlugin(display_name=Lstr(value=class_path),
class_path=class_path,
available=True))
@@ -90,12 +91,12 @@ def handle_scan_results(results: ScanResults) -> None:
enabled = plugstate.get('enabled', False)
assert isinstance(enabled, bool)
if enabled and class_path not in results.plugins:
- _ba.app.potential_plugins.append(
+ plugs.potential_plugins.append(
PotentialPlugin(display_name=Lstr(value=class_path),
class_path=class_path,
available=False))
- _ba.app.potential_plugins.sort(key=lambda p: p.class_path)
+ plugs.potential_plugins.sort(key=lambda p: p.class_path)
if found_new:
_ba.screenmessage(Lstr(resource='pluginsDetectedText'),
diff --git a/assets/src/ba_data/python/ba/_plugin.py b/assets/src/ba_data/python/ba/_plugin.py
index 84658219..cf4251cf 100644
--- a/assets/src/ba_data/python/ba/_plugin.py
+++ b/assets/src/ba_data/python/ba/_plugin.py
@@ -6,11 +6,65 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from dataclasses import dataclass
+import _ba
if TYPE_CHECKING:
+ from typing import List, Dict
import ba
+class PluginSubsystem:
+ """Subsystem for plugin handling in the app.
+
+ Category: App Classes
+
+ Access the single shared instance of this class at 'ba.app.plugins'.
+ """
+
+ def __init__(self) -> None:
+ self.potential_plugins: List[ba.PotentialPlugin] = []
+ self.active_plugins: Dict[str, ba.Plugin] = {}
+
+ def on_app_launch(self) -> None:
+ """Should be called at app launch time."""
+ # Load up our plugins and go ahead and call their on_app_launch calls.
+ self.load_plugins()
+ for plugin in self.active_plugins.values():
+ try:
+ plugin.on_app_launch()
+ except Exception:
+ from ba import _error
+ _error.print_exception('Error in plugin on_app_launch()')
+
+ def load_plugins(self) -> None:
+ """(internal)"""
+ from ba._general import getclass
+
+ # Note: the plugins we load is purely based on what's enabled
+ # in the app config. Our meta-scan gives us a list of available
+ # plugins, but that is only used to give the user a list of plugins
+ # that they can enable. (we wouldn't want to look at meta-scan here
+ # anyway because it may not be done yet at this point in the launch)
+ plugstates: Dict[str, Dict] = _ba.app.config.get('Plugins', {})
+ assert isinstance(plugstates, dict)
+ plugkeys: List[str] = sorted(key for key, val in plugstates.items()
+ if val.get('enabled', False))
+ for plugkey in plugkeys:
+ try:
+ cls = getclass(plugkey, Plugin)
+ except Exception as exc:
+ _ba.log(f"Error loading plugin class '{plugkey}': {exc}",
+ to_server=False)
+ continue
+ try:
+ plugin = cls()
+ assert plugkey not in self.active_plugins
+ self.active_plugins[plugkey] = plugin
+ except Exception:
+ from ba import _error
+ _error.print_exception(f'Error loading plugin: {plugkey}')
+
+
@dataclass
class PotentialPlugin:
"""Represents a ba.Plugin which can potentially be loaded.
diff --git a/assets/src/ba_data/python/bastd/ui/settings/plugins.py b/assets/src/ba_data/python/bastd/ui/settings/plugins.py
index 457eded9..c9d347eb 100644
--- a/assets/src/ba_data/python/bastd/ui/settings/plugins.py
+++ b/assets/src/ba_data/python/bastd/ui/settings/plugins.py
@@ -97,11 +97,11 @@ class PluginSettingsWindow(ba.Window):
ba.screenmessage('Still scanning plugins; please try again.',
color=(1, 0, 0))
ba.playsound(ba.getsound('error'))
- pluglist = ba.app.potential_plugins
+ pluglist = ba.app.plugins.potential_plugins
plugstates: Dict[str, Dict] = ba.app.config.setdefault('Plugins', {})
assert isinstance(plugstates, dict)
for i, availplug in enumerate(pluglist):
- active = availplug.class_path in ba.app.active_plugins
+ active = availplug.class_path in ba.app.plugins.active_plugins
plugstate = plugstates.setdefault(availplug.class_path, {})
checked = plugstate.get('enabled', False)
diff --git a/docs/ba_module.md b/docs/ba_module.md
index 3fb8309b..f504997b 100644
--- a/docs/ba_module.md
+++ b/docs/ba_module.md
@@ -158,6 +158,7 @@
ba.MusicPlayer
ba.MusicSubsystem
ba.Plugin
+ ba.PluginSubsystem
ba.PotentialPlugin
ba.ServerController
ba.UISubsystem
@@ -4734,6 +4735,31 @@ the type-checker properly identifies the returned value as one.
Called when the app is being launched.
+
+
+
+<top level class>
+
+Subsystem for plugin handling in the app.
+
+Category: App Classes
+
+ Access the single shared instance of this class at 'ba.app.plugins'.
+
+
+Methods:
+
+
+-
+
ba.PluginSubsystem()
+
+
+-
+
on_app_launch(self) -> None
+
+Should be called at app launch time.
+