Преглед изворни кода

Merge pull request #31 from kalliope-project/singleton_brain_settings

Singleton brain settings configchecker
Monf пре 8 година
родитељ
комит
4f54bc44d8

+ 32 - 25
core/ConfigurationManager/BrainLoader.py

@@ -34,31 +34,38 @@ class BrainLoader(object):
         :return: Brain object
         :rtype: Brain
         """
-        # get the brain with dict
-        dict_brain = cls.get_yaml_config(file_path)
-        # create a new brain
-        brain = Brain()
-        brain.brain_yaml = dict_brain
-        # create list of Synapse
-        synapses = list()
-        for synapses_dict in dict_brain:
-            if "includes" not in synapses_dict: # we don't need to check includes as it's not a synapse
-                if ConfigurationChecker().check_synape_dict(synapses_dict):
-                    # print "synapses_dict ok"
-                    name = synapses_dict["name"]
-                    neurons = cls._get_neurons(synapses_dict["neurons"])
-                    signals = cls._get_signals(synapses_dict["signals"])
-                    new_synapse = Synapse(name=name, neurons=neurons, signals=signals)
-                    synapses.append(new_synapse)
-        brain.synapses = synapses
-        if file_path is None:
-            brain.brain_file = cls._get_root_brain_path()
-        else:
-            brain.brain_file = file_path
-        # check that no synapse have the same name than another
-        if ConfigurationChecker().check_synapes(synapses):
-            return brain
-        return None
+
+        # Instantiate a brain
+        brain = Brain.Instance()
+        logger.debug("Is brain already loaded ? %r" % brain.is_loaded)
+        if brain.is_loaded is False:
+            # get the brain with dict
+            dict_brain = cls.get_yaml_config(file_path)
+
+            brain.brain_yaml = dict_brain
+            # create list of Synapse
+            synapses = list()
+            for synapses_dict in dict_brain:
+                if "includes" not in synapses_dict: # we don't need to check includes as it's not a synapse
+                    if ConfigurationChecker().check_synape_dict(synapses_dict):
+                        # print "synapses_dict ok"
+                        name = synapses_dict["name"]
+                        neurons = cls._get_neurons(synapses_dict["neurons"])
+                        signals = cls._get_signals(synapses_dict["signals"])
+                        new_synapse = Synapse(name=name, neurons=neurons, signals=signals)
+                        synapses.append(new_synapse)
+            brain.synapses = synapses
+            if file_path is None:
+                brain.brain_file = cls._get_root_brain_path()
+            else:
+                brain.brain_file = file_path
+            # check that no synapse have the same name than another
+            if not ConfigurationChecker().check_synapes(synapses):
+                brain = None
+
+            # The Brain Singleton is loaded
+            brain.is_loaded = True
+        return brain
 
     @staticmethod
     def _get_neurons(neurons_dict):

+ 22 - 1
core/ConfigurationManager/ConfigurationChecker.py

@@ -1,5 +1,7 @@
 import re
 
+from core.Utils import Utils, ModuleNotFoundError
+
 
 class InvalidSynapeName(Exception):
     pass
@@ -67,7 +69,26 @@ class ConfigurationChecker:
 
     @staticmethod
     def check_neuron_dict(neuron_dict):
-        # TODO check that the Neuron plugin exist
+        """
+        Check received neuron dict is valid:
+        - neuron exist
+        :param neuron_dict:
+        :return:
+        """
+        def check_neuron_exist(neuron_name):
+            package_name = "neurons"
+            mod = __import__(package_name, fromlist=[neuron_name])
+            try:
+                getattr(mod, neuron_name)
+            except AttributeError:
+                raise ModuleNotFoundError("The module %s does not exist in package %s" % (neuron_name, package_name))
+            return True
+
+        if isinstance(neuron_dict, dict):
+            for neuron_name in neuron_dict:
+                check_neuron_exist(neuron_name)
+        else:
+            check_neuron_exist(neuron_dict)
         return True
 
     @staticmethod

+ 35 - 24
core/ConfigurationManager/SettingLoader.py

@@ -1,7 +1,8 @@
-from YAMLLoader import YAMLLoader
 import logging
 
+from YAMLLoader import YAMLLoader
 from core.FileManager import FileManager
+from core.Models import Singleton
 from core.Models.RestAPI import RestAPI
 from core.Models.Settings import Settings
 from core.Models.Stt import Stt
@@ -43,29 +44,39 @@ class SettingLoader(object):
         Return a Settings object from settings.yml file
         :return:
         """
-        settings = cls.get_yaml_config(file_path)
-        default_stt_name = cls._get_default_speech_to_text(settings)
-        default_tts_name = cls._get_default_text_to_speech(settings)
-        default_trigger_name = cls._get_default_trigger(settings)
-        stts = cls._get_stts(settings)
-        ttss = cls._get_ttss(settings)
-        triggers = cls._get_triggers(settings)
-        random_wake_up_answers = cls._get_random_wake_up_answers(settings)
-        random_wake_up_sounds = cls._get_random_wake_up_sounds(settings)
-        rest_api = cls._get_rest_api(settings)
-        cache_path = cls._get_cache_path(settings)
-
-        # create a setting object
-        setting_object = Settings(default_stt_name=default_stt_name,
-                                  default_tts_name=default_tts_name,
-                                  default_trigger_name=default_trigger_name,
-                                  stts=stts,
-                                  ttss=ttss,
-                                  triggers=triggers,
-                                  random_wake_up_answers=random_wake_up_answers,
-                                  random_wake_up_sounds=random_wake_up_sounds,
-                                  rest_api=rest_api,
-                                  cache_path=cache_path)
+
+        # create a new setting
+        setting_object = Settings.Instance()
+        logger.debug("Is Settings already loaded ? %r" % setting_object.is_loaded)
+        if setting_object.is_loaded is False:
+
+            # Get the setting parameters
+            settings = cls.get_yaml_config(file_path)
+            default_stt_name = cls._get_default_speech_to_text(settings)
+            default_tts_name = cls._get_default_text_to_speech(settings)
+            default_trigger_name = cls._get_default_trigger(settings)
+            stts = cls._get_stts(settings)
+            ttss = cls._get_ttss(settings)
+            triggers = cls._get_triggers(settings)
+            random_wake_up_answers = cls._get_random_wake_up_answers(settings)
+            random_wake_up_sounds = cls._get_random_wake_up_sounds(settings)
+            rest_api = cls._get_rest_api(settings)
+            cache_path = cls._get_cache_path(settings)
+
+            # Load the setting singleton with the parameters
+            setting_object.default_tts_name=default_tts_name
+            setting_object.default_stt_name=default_stt_name
+            setting_object.default_trigger_name=default_trigger_name
+            setting_object.stts=stts
+            setting_object.ttss=ttss
+            setting_object.triggers=triggers
+            setting_object.random_wake_up_answers=random_wake_up_answers
+            setting_object.random_wake_up_sounds=random_wake_up_sounds
+            setting_object.rest_api=rest_api
+            setting_object.cache_path=cache_path
+            # The Settings Singleton is loaded
+            setting_object.is_loaded = True
+
         return setting_object
 
     @staticmethod

+ 2 - 1
core/ConfigurationManager/__init__.py

@@ -1,2 +1,3 @@
 from YAMLLoader import YAMLLoader
-from .SettingLoader import SettingLoader
+from SettingLoader import SettingLoader
+from BrainLoader import BrainLoader

+ 6 - 1
core/Models/Brain.py

@@ -1,6 +1,11 @@
+from core.Models import Singleton
+
+
+@Singleton
+class Brain:
 
-class Brain(object):
     def __init__(self, synapses=None, brain_file=None, brain_yaml=None):
         self.synapses = synapses
         self.brain_file = brain_file
         self.brain_yaml = brain_yaml
+        self.is_loaded = False

+ 15 - 3
core/Models/Settings.py

@@ -1,9 +1,20 @@
+from core.Models import Singleton
 
 
+@Singleton
 class Settings(object):
-    def __init__(self, default_tts_name=None, default_stt_name=None,
-                 default_trigger_name=None, ttss=None, stts=None,
-                 random_wake_up_answers=None, random_wake_up_sounds=None, triggers=None, rest_api=None, cache_path=None):
+    def __init__(self,
+                 default_tts_name=None,
+                 default_stt_name=None,
+                 default_trigger_name=None,
+                 ttss=None,
+                 stts=None,
+                 random_wake_up_answers=None,
+                 random_wake_up_sounds=None,
+                 triggers=None,
+                 rest_api=None,
+                 cache_path=None):
+
         self.default_tts_name = default_tts_name
         self.default_stt_name = default_stt_name
         self.default_trigger_name = default_trigger_name
@@ -14,3 +25,4 @@ class Settings(object):
         self.triggers = triggers
         self.rest_api = rest_api
         self.cache_path = cache_path
+        self.is_loaded = False

+ 38 - 0
core/Models/Singleton.py

@@ -0,0 +1,38 @@
+class Singleton:
+    """
+    A non-thread-safe helper class to ease implementing singletons.
+    This should be used as a decorator -- not a metaclass -- to the
+    class that should be a singleton.
+
+    The decorated class can define one `__init__` function that
+    takes only the `self` argument. Other than that, there are
+    no restrictions that apply to the decorated class.
+
+    To get the singleton instance, use the `Instance` method. Trying
+    to use `__call__` will result in a `TypeError` being raised.
+
+    Limitations: The decorated class cannot be inherited from.
+
+    """
+
+    def __init__(self, decorated):
+        self._decorated = decorated
+
+    def Instance(self):
+        """
+        Returns the singleton instance. Upon its first call, it creates a
+        new instance of the decorated class and calls its `__init__` method.
+        On all subsequent calls, the already created instance is returned.
+
+        """
+        try:
+            return self._instance
+        except AttributeError:
+            self._instance = self._decorated()
+            return self._instance
+
+    def __call__(self):
+        raise TypeError('Singletons must be accessed through `Instance()`.')
+
+    def __instancecheck__(self, inst):
+        return isinstance(inst, self._decorated)

+ 3 - 1
core/Models/__init__.py

@@ -1,5 +1,7 @@
+from Singleton import Singleton
 from Event import Event
 from Brain import Brain
 from Order import Order
 from Synapse import Synapse
-from Neuron import Neuron
+from Neuron import Neuron
+

+ 6 - 5
core/NeuronModule.py

@@ -9,7 +9,7 @@ from jinja2 import Template
 from core import OrderListener
 from core.SynapseLauncher import SynapseLauncher
 from core.Utils import Utils
-from core.ConfigurationManager import SettingLoader
+from core.ConfigurationManager import SettingLoader, BrainLoader
 
 logging.basicConfig()
 logger = logging.getLogger("kalliope")
@@ -55,6 +55,7 @@ class NeuronModule(object):
         logger.debug("NeuronModule called from class %s with parameters: %s" % (child_name, str(kwargs)))
 
         self.settings = SettingLoader.get_settings()
+        self.brain = BrainLoader.get_brain()
 
         # check if the user has overrider the TTS
         tts = kwargs.get('tts', None)
@@ -156,6 +157,9 @@ class NeuronModule(object):
         # else:
         #     raise NoTemplateException("You must specify a say_template or a file_template")
 
+    def run_synapse_ny_name(self, name):
+        SynapseLauncher.start_synapse(name=name, brain=self.brain)
+
     @staticmethod
     def _get_content_of_file(real_file_template_path):
         with open(real_file_template_path, 'r') as content_file:
@@ -179,7 +183,4 @@ class NeuronModule(object):
         oa = OrderListener(callback=callback)
         oa.start()
 
-    @staticmethod
-    def run_synapse_ny_name(name):
-        # TODO find a way to get the current brain file. NeuronModule doesn't have any ref about it
-        SynapseLauncher.start_synapse(name=name)
+

+ 1 - 0
core/__init__.py

@@ -3,3 +3,4 @@ from core.OrderListener import OrderListener
 from core.ShellGui import ShellGui
 from core.FileManager import FileManager
 from core.Utils import Utils
+

+ 1 - 2
install/files/python_requirements.txt

@@ -1,7 +1,6 @@
 SpeechRecognition==3.4.6
 pyaudio==0.2.9
 ansible==2.1.1.0
-pygame
 python2-pythondialog==3.4.0
 jinja==1.2
 python-crontab==2.1.1
@@ -14,4 +13,4 @@ pyowm==2.5.0
 python-twitter==3.1
 flask==0.11.1
 Flask-Restful==0.3.5
-wikipedia==1.4.0
+wikipedia==1.4.0

+ 16 - 15
test.py

@@ -1,14 +1,8 @@
 # coding: utf8
 import logging
 
-from flask import Flask
-
-from core import OrderAnalyser
-from core import Utils
-from core.ConfigurationManager import SettingLoader
 from core.ConfigurationManager.BrainLoader import BrainLoader
-from core.Players import Mplayer
-from core.RestAPI.FlaskAPI import FlaskAPI
+from core.ConfigurationManager.SettingLoader import SettingLoader
 
 logging.basicConfig()
 logger = logging.getLogger("kalliope")
@@ -16,17 +10,24 @@ logger.setLevel(logging.DEBUG)
 
 
 brain = BrainLoader.get_brain()
-#
-# order = "bonjour"
-# oa = OrderAnalyser(order=order, brain=brain)
-# oa.start()
-
 
+brain2 = BrainLoader.get_brain()
+brain3 = BrainLoader.get_brain()
+brain4 = BrainLoader.get_brain()
 
-app = Flask(__name__)
-flask_api = FlaskAPI(app, port=5000, brain=brain)
-flask_api.start()
 
+print brain is brain2
+print brain is brain3
+print brain is brain4
+print brain4 is brain2
 
+set = SettingLoader.get_settings()
+set2 = SettingLoader.get_settings()
+set3 = SettingLoader.get_settings()
+set4 = SettingLoader.get_settings()
 
+print set is set2
+print set is set3
+print set is set4
+print set3 is set2