change ComponentManager to use instance methods rather than class methods

-add get_component method to ComponentManager

-add override_components kwargs to ComponentManager

-add skip_components to ComponentManager

-change component_manager attribute to exist on the AuthJSONRPCServer instance instead of the class
This commit is contained in:
Jack Robison 2018-07-24 12:03:43 -04:00
parent b808d08eb3
commit 7e8ca842a2
No known key found for this signature in database
GPG key ID: DF25C68FE0239BB2
4 changed files with 90 additions and 41 deletions

View file

@ -155,13 +155,23 @@ class InvalidAuthenticationToken(Exception):
class NegotiationError(Exception): class NegotiationError(Exception):
pass pass
class InvalidCurrencyError(Exception): class InvalidCurrencyError(Exception):
def __init__(self, currency): def __init__(self, currency):
self.currency = currency self.currency = currency
Exception.__init__( Exception.__init__(
self, 'Invalid currency: {} is not a supported currency.'.format(currency)) self, 'Invalid currency: {} is not a supported currency.'.format(currency))
class NoSuchDirectoryError(Exception): class NoSuchDirectoryError(Exception):
def __init__(self, directory): def __init__(self, directory):
self.directory = directory self.directory = directory
Exception.__init__(self, 'No such directory {}'.format(directory)) Exception.__init__(self, 'No such directory {}'.format(directory))
class ComponentStartConditionNotMet(Exception):
pass
class ComponentsNotStarted(Exception):
pass

View file

@ -9,7 +9,7 @@ class ComponentType(type):
def __new__(mcs, name, bases, newattrs): def __new__(mcs, name, bases, newattrs):
klass = type.__new__(mcs, name, bases, newattrs) klass = type.__new__(mcs, name, bases, newattrs)
if name != "Component": if name != "Component":
ComponentManager.components.add(klass) ComponentManager.default_component_classes[klass.component_name] = klass
return klass return klass
@ -24,34 +24,43 @@ class Component(object):
__metaclass__ = ComponentType __metaclass__ = ComponentType
depends_on = [] depends_on = []
component_name = None component_name = None
running = False
@classmethod def __init__(self, component_manager):
def setup(cls): self.component_manager = component_manager
raise NotImplementedError() # override self._running = False
@classmethod def __lt__(self, other):
def stop(cls): return self.component_name < other.component_name
raise NotImplementedError() # override
@property
def running(self):
return self._running
def start(self):
raise NotImplementedError()
def stop(self):
raise NotImplementedError()
def component(self):
raise NotImplementedError()
@classmethod
@defer.inlineCallbacks @defer.inlineCallbacks
def _setup(cls): def _setup(self):
try: try:
result = yield defer.maybeDeferred(cls.setup) result = yield defer.maybeDeferred(self.start)
cls.running = True self._running = True
defer.returnValue(result) defer.returnValue(result)
except Exception as err: except Exception as err:
log.exception("Error setting up %s", cls.component_name or cls.__name__) log.exception("Error setting up %s", self.component_name or self.__class__.__name__)
raise err raise err
@classmethod
@defer.inlineCallbacks @defer.inlineCallbacks
def _stop(cls): def _stop(self):
try: try:
result = yield defer.maybeDeferred(cls.stop) result = yield defer.maybeDeferred(self.stop)
cls.running = False self._running = False
defer.returnValue(result) defer.returnValue(result)
except Exception as err: except Exception as err:
log.exception("Error stopping %s", cls.__name__) log.exception("Error stopping %s", self.__class__.__name__)
raise err raise err

View file

@ -1,20 +1,41 @@
import logging import logging
from twisted.internet import defer from twisted.internet import defer
from lbrynet.core.Error import ComponentStartConditionNotMet
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class ComponentManager(object): class ComponentManager(object):
components = set() default_component_classes = {}
@classmethod def __init__(self, reactor=None, analytics_manager=None, skip_components=None, **override_components):
def sort_components(cls, reverse=False): self.skip_components = skip_components or []
self.reactor = reactor
self.component_classes = {}
self.components = set()
self.analytics_manager = analytics_manager
for component_name, component_class in self.default_component_classes.iteritems():
if component_name in override_components:
component_class = override_components.pop(component_name)
if component_name not in self.skip_components:
self.component_classes[component_name] = component_class
if override_components:
raise SyntaxError("unexpected components: %s" % override_components)
for component_class in self.component_classes.itervalues():
self.components.add(component_class(self))
def sort_components(self, reverse=False):
""" """
Sort components by requirements Sort components by requirements
""" """
steps = [] steps = []
staged = set() staged = set()
components = set(cls.components) components = set(self.components)
# components with no requirements # components with no requirements
step = [] step = []
@ -25,6 +46,7 @@ class ComponentManager(object):
components.remove(component) components.remove(component)
if step: if step:
step.sort()
steps.append(step) steps.append(step)
while components: while components:
@ -40,58 +62,58 @@ class ComponentManager(object):
to_stage.add(component.component_name) to_stage.add(component.component_name)
components.remove(component) components.remove(component)
if step: if step:
step.sort()
staged.update(to_stage) staged.update(to_stage)
steps.append(step) steps.append(step)
elif components: elif components:
raise SyntaxError("components cannot be started: %s" % components) raise ComponentStartConditionNotMet("Unresolved dependencies for: %s" % components)
if reverse: if reverse:
steps.reverse() steps.reverse()
return steps return steps
@classmethod
@defer.inlineCallbacks @defer.inlineCallbacks
def setup(cls, **callbacks): def setup(self, **callbacks):
""" """
Start Components in sequence sorted by requirements Start Components in sequence sorted by requirements
:return: (defer.Deferred) :return: (defer.Deferred)
""" """
for component_name, cb in callbacks.iteritems(): for component_name, cb in callbacks.iteritems():
if component_name not in self.component_classes:
raise NameError("unknown component: %s" % component_name)
if not callable(cb): if not callable(cb):
raise ValueError("%s is not callable" % cb) raise ValueError("%s is not callable" % cb)
cls.get_component(component_name)
def _setup(component): def _setup(component):
if component.component_name in callbacks: if component.component_name in callbacks:
d = component._setup() d = component._setup()
d.addCallback(callbacks[component.component_name]) d.addCallback(callbacks[component.component_name])
return d return d
return component.setup() return component._setup()
stages = cls.sort_components() stages = self.sort_components()
for stage in stages: for stage in stages:
yield defer.DeferredList([_setup(component) for component in stage]) yield defer.DeferredList([_setup(component) for component in stage])
@classmethod
@defer.inlineCallbacks @defer.inlineCallbacks
def stop(cls): def stop(self):
""" """
Stop Components in reversed startup order Stop Components in reversed startup order
:return: (defer.Deferred) :return: (defer.Deferred)
""" """
stages = cls.sort_components(reverse=True) stages = self.sort_components(reverse=True)
for stage in stages: for stage in stages:
yield defer.DeferredList([component._stop() for component in stage]) yield defer.DeferredList([component._stop() for component in stage if component.running])
@classmethod def all_components_running(self, *component_names):
def all_components_running(cls, *component_names):
""" """
Check if components are running Check if components are running
:return: (bool) True if all specified components are running :return: (bool) True if all specified components are running
""" """
components = {component.component_name: component for component in cls.components} components = {component.component_name: component for component in self.components}
for component in component_names: for component in component_names:
if component not in components: if component not in components:
raise NameError("%s is not a known Component" % component) raise NameError("%s is not a known Component" % component)
@ -99,9 +121,19 @@ class ComponentManager(object):
return False return False
return True return True
@classmethod def get_components_status(self):
def get_component(cls, component_name): """
for component in cls.components: List status of all the components, whether they are running or not
:return: (dict) {(str) component_name: (bool) True is running else False}
"""
return {
component.component_name: component.running
for component in self.components
}
def get_component(self, component_name):
for component in self.components:
if component.component_name == component_name: if component.component_name == component_name:
return component return component.component
raise NameError(component_name) raise NameError(component_name)

View file

@ -17,7 +17,6 @@ from lbrynet.core.Error import InvalidAuthenticationToken
from lbrynet.core import utils from lbrynet.core import utils
from lbrynet.daemon.auth.util import APIKey, get_auth_message from lbrynet.daemon.auth.util import APIKey, get_auth_message
from lbrynet.daemon.auth.client import LBRY_SECRET from lbrynet.daemon.auth.client import LBRY_SECRET
from lbrynet.daemon.Component import ComponentManager
from lbrynet.undecorated import undecorated from lbrynet.undecorated import undecorated
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -133,7 +132,6 @@ class JSONRPCServerType(type):
class AuthorizedBase(object): class AuthorizedBase(object):
__metaclass__ = JSONRPCServerType __metaclass__ = JSONRPCServerType
component_manager = ComponentManager
@staticmethod @staticmethod
def deprecated(new_command=None): def deprecated(new_command=None):