Make Dispatcher a singleton after first instantiation
This allows plugins to re-instantiate the Dispatcher (or, now, use the instance cached in `current_dispatcher`) without knowing what plugins have been loaded by dotbot. This fixes plugins that want to dispatch to other plugins. Fixes #339
This commit is contained in:
parent
840cd164d2
commit
25497721a3
3 changed files with 47 additions and 0 deletions
|
@ -5,8 +5,23 @@ from .context import Context
|
||||||
from .messenger import Messenger
|
from .messenger import Messenger
|
||||||
from .plugin import Plugin
|
from .plugin import Plugin
|
||||||
|
|
||||||
|
current_dispatcher = None
|
||||||
|
|
||||||
|
|
||||||
class Dispatcher:
|
class Dispatcher:
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
# After dotbot instantiates this class, the instance will be cached.
|
||||||
|
# Subsequent instantiations (say, by plugins) will return the same instance.
|
||||||
|
# This is needed because plugins don't have access to the entire configuration
|
||||||
|
# (for example, they won't know which plugins have been loaded).
|
||||||
|
# This ensures a consistent configuration is used.
|
||||||
|
global current_dispatcher
|
||||||
|
if current_dispatcher is None:
|
||||||
|
instance = object.__new__(cls)
|
||||||
|
instance.is_initialized = False
|
||||||
|
current_dispatcher = instance
|
||||||
|
return current_dispatcher
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
base_directory,
|
base_directory,
|
||||||
|
@ -16,6 +31,9 @@ class Dispatcher:
|
||||||
options=Namespace(),
|
options=Namespace(),
|
||||||
plugins=None,
|
plugins=None,
|
||||||
):
|
):
|
||||||
|
if self.is_initialized:
|
||||||
|
return
|
||||||
|
self.is_initialized = True
|
||||||
self._log = Messenger()
|
self._log = Messenger()
|
||||||
self._setup_context(base_directory, options)
|
self._setup_context(base_directory, options)
|
||||||
plugins = plugins or []
|
plugins = plugins or []
|
||||||
|
|
|
@ -12,6 +12,7 @@ import pytest
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
import dotbot.cli
|
import dotbot.cli
|
||||||
|
import dotbot.dispatcher
|
||||||
|
|
||||||
|
|
||||||
def get_long_path(path):
|
def get_long_path(path):
|
||||||
|
@ -312,3 +313,9 @@ def run_dotbot(dotfiles):
|
||||||
dotbot.cli.main()
|
dotbot.cli.main()
|
||||||
|
|
||||||
yield runner
|
yield runner
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def reset_current_dispatcher():
|
||||||
|
yield
|
||||||
|
dotbot.dispatcher.current_dispatcher = None
|
||||||
|
|
22
tests/test_dispatcher.py
Normal file
22
tests/test_dispatcher.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import dotbot.dispatcher
|
||||||
|
|
||||||
|
|
||||||
|
def test_dispatcher_instantiation(home, dotfiles, run_dotbot):
|
||||||
|
"""Verify that the dispatcher caches itself as a singleton."""
|
||||||
|
|
||||||
|
assert dotbot.dispatcher.current_dispatcher is None
|
||||||
|
|
||||||
|
dotfiles.write_config([])
|
||||||
|
run_dotbot()
|
||||||
|
|
||||||
|
# Verify the dispatcher has been cached.
|
||||||
|
assert dotbot.dispatcher.current_dispatcher is not None
|
||||||
|
|
||||||
|
existing_id = id(dotbot.dispatcher.current_dispatcher)
|
||||||
|
new_instance = dotbot.dispatcher.Dispatcher("bogus")
|
||||||
|
|
||||||
|
# Verify the new and existing instances are the same.
|
||||||
|
assert existing_id == id(new_instance)
|
||||||
|
|
||||||
|
# Verify the singleton was not overridden.
|
||||||
|
assert existing_id == id(dotbot.dispatcher.current_dispatcher)
|
Loading…
Reference in a new issue