mirror of
				https://github.com/gsi-upm/senpy
				synced 2025-10-31 07:28:16 +00:00 
			
		
		
		
	Improved plugins (reload, imp)
This commit is contained in:
		
							
								
								
									
										4
									
								
								app.py
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								app.py
									
									
									
									
									
								
							| @@ -21,7 +21,7 @@ This class shows how to use the nif_server module to create custom services. | ||||
| ''' | ||||
| import config | ||||
| from flask import Flask | ||||
| from senpy import Senpy | ||||
| from senpy.extensions import Senpy | ||||
|  | ||||
| app = Flask(__name__) | ||||
|  | ||||
| @@ -30,4 +30,4 @@ sp.init_app(app) | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     app.debug = config.DEBUG | ||||
|     app.run() | ||||
|     app.run(use_reloader=False) | ||||
|   | ||||
| @@ -18,6 +18,8 @@ | ||||
| Sentiment analysis server in Python | ||||
| ''' | ||||
|  | ||||
| VERSION = "0.2.4" | ||||
|  | ||||
| import extensions | ||||
| import blueprints | ||||
| import plugins | ||||
|   | ||||
| @@ -22,43 +22,13 @@ import json | ||||
|  | ||||
| nif_blueprint = Blueprint("NIF Sentiment Analysis Server", __name__) | ||||
|  | ||||
| PARAMS = {"input": {"aliases": ["i", "input"], | ||||
|                     "required": True, | ||||
|                     "help": "Input text" | ||||
|                     }, | ||||
|           "informat": {"aliases": ["f", "informat"], | ||||
|                        "required": False, | ||||
|                        "default": "text", | ||||
|                        "options": ["turtle", "text"], | ||||
|                        }, | ||||
|           "intype": {"aliases": ["intype", "t"], | ||||
|                      "required": False, | ||||
|                      "default": "direct", | ||||
|                      "options": ["direct", "url", "file"], | ||||
|                      }, | ||||
|           "outformat": {"aliases": ["outformat", "o"], | ||||
|                         "default": "json-ld", | ||||
|                         "required": False, | ||||
|                         "options": ["json-ld"], | ||||
|                         }, | ||||
| BASIC_PARAMS = { | ||||
|     "algorithm": {"aliases": ["algorithm", "a", "algo"], | ||||
|                   "required": False, | ||||
|                   }, | ||||
|           "language": {"aliases": ["language", "l"], | ||||
|                        "required": False, | ||||
|                        "options": ["es", "en"], | ||||
|                        }, | ||||
|           "urischeme": {"aliases": ["urischeme", "u"], | ||||
|                         "required": False, | ||||
|                         "default": "RFC5147String", | ||||
|                         "options": "RFC5147String" | ||||
|                         }, | ||||
| } | ||||
|  | ||||
| def get_algorithm(req): | ||||
|     return get_params(req, params={"algorithm": PARAMS["algorithm"]}) | ||||
|  | ||||
| def get_params(req, params=PARAMS): | ||||
| def get_params(req, params=BASIC_PARAMS): | ||||
|     indict = None | ||||
|     if req.method == 'POST': | ||||
|         indict = req.form | ||||
| @@ -113,35 +83,44 @@ def basic_analysis(params): | ||||
| @nif_blueprint.route('/', methods=['POST', 'GET']) | ||||
| def home(entries=None): | ||||
|     try: | ||||
|         algo = get_algorithm(request)["algorithm"] | ||||
|         specific_params = PARAMS.copy() | ||||
|         specific_params.update(current_app.senpy.parameters(algo)) | ||||
|         algo = get_params(request).get("algorithm", None) | ||||
|         specific_params = current_app.senpy.parameters(algo) | ||||
|         params = get_params(request, specific_params) | ||||
|     except ValueError as ex: | ||||
|         return ex.message | ||||
|     response = current_app.senpy.analyse(**params) | ||||
|     return jsonify(response) | ||||
|  | ||||
| @nif_blueprint.route("/default") | ||||
| def default(): | ||||
|     return current_app.senpy.default_plugin | ||||
|     #return plugins(action="list", plugin=current_app.senpy.default_algorithm) | ||||
|  | ||||
| @nif_blueprint.route('/plugins/', methods=['POST', 'GET']) | ||||
| @nif_blueprint.route('/plugins/<plugin>', methods=['POST', 'GET']) | ||||
| @nif_blueprint.route('/plugins/<plugin>/<action>', methods=['POST', 'GET']) | ||||
| def plugins(plugin=None, action="list"): | ||||
|     print current_app.senpy.plugins.keys() | ||||
|     filt = {} | ||||
|     if plugin: | ||||
|         plugs = {plugin:current_app.senpy.plugins[plugin]} | ||||
|     else: | ||||
|         plugs = current_app.senpy.plugins | ||||
|         filt["name"] = plugin | ||||
|     plugs = current_app.senpy.filter_plugins(**filt) | ||||
|     if plugin and not plugs: | ||||
|         return "Plugin not found", 400 | ||||
|     if action == "list": | ||||
|         dic = {plug:plugs[plug].jsonable(True) for plug in plugs} | ||||
|         with_params = request.args.get("params", "") == "1" | ||||
|         dic = {plug:plugs[plug].jsonable(with_params) for plug in plugs} | ||||
|         return jsonify(dic) | ||||
|     elif action == "disable": | ||||
|         plugs[plugin].enabled = False | ||||
|     if action == "disable": | ||||
|         current_app.senpy.disable_plugin(plugin) | ||||
|         return "Ok" | ||||
|     elif action == "enable": | ||||
|         plugs[plugin].enabled = True | ||||
|         current_app.senpy.enable_plugin(plugin) | ||||
|         return "Ok" | ||||
|     elif action == "reload": | ||||
|         current_app.senpy.reload_plugin(plugin) | ||||
|         return "Ok" | ||||
|     else: | ||||
|         return "action '{}' not allowed".format(action), 404 | ||||
|         return "action '{}' not allowed".format(action), 400 | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     import config | ||||
|   | ||||
| @@ -1,8 +1,10 @@ | ||||
| import os | ||||
| import sys | ||||
| import importlib | ||||
| import imp | ||||
|  | ||||
| from flask import current_app | ||||
| from collections import defaultdict | ||||
| from .plugins import SentimentPlugin, EmotionPlugin | ||||
|  | ||||
| try: | ||||
|     from flask import _app_ctx_stack as stack | ||||
| @@ -18,7 +20,7 @@ class Senpy(object): | ||||
|         self.app = app | ||||
|         base_folder = os.path.join(os.path.dirname(__file__), "plugins") | ||||
|  | ||||
|         self.search_folders = (folder for folder in (base_folder, plugin_folder) | ||||
|         self.search_folders = (folder for folder in (base_folder, plugin_folder, '/tmp/plugins') | ||||
|                                 if folder and os.path.isdir(folder)) | ||||
|  | ||||
|         if app is not None: | ||||
| @@ -41,24 +43,49 @@ class Senpy(object): | ||||
|         app.register_blueprint(nif_blueprint) | ||||
|  | ||||
|     def analyse(self, **params): | ||||
|         algo = None | ||||
|         print("analysing with params: {}".format(params)) | ||||
|         if "algorithm" in params: | ||||
|             algo = params["algorithm"] | ||||
|         elif self.plugins: | ||||
|             algo = self.default_plugin | ||||
|         if algo in self.plugins and self.plugins[algo].enabled: | ||||
|             plug = self.plugins[algo] | ||||
|             resp = plug.analyse(**params) | ||||
|             resp.analysis.append(plug.jsonable()) | ||||
|             return resp | ||||
|         else: | ||||
|             return {"status": 500, "message": "No valid algorithm"} | ||||
|  | ||||
|     @property | ||||
|     def default_plugin(self): | ||||
|         if self.plugins: | ||||
|             candidate = self.filter_plugins(enabled=True).keys()[0] | ||||
|             print("Default: {}".format(candidate)) | ||||
|             return candidate | ||||
|         else: | ||||
|             return Exception("No algorithm") | ||||
|  | ||||
|     def parameters(self, algo): | ||||
|         if algo in self.plugins: | ||||
|             if hasattr(self.plugins[algo], "parameters"): | ||||
|                 return self.plugins[algo].parameters | ||||
|         return {} | ||||
|         return getattr(self.plugins.get(algo or self.default_plugin), "params", {}) | ||||
|  | ||||
|     def enable_plugin(self, plugin): | ||||
|         self.plugins[plugin].disable() | ||||
|  | ||||
|     def disable_plugin(self, plugin): | ||||
|         self.plugins[plugin].disable() | ||||
|  | ||||
|     def reload_plugin(self, plugin): | ||||
|         print("Reloading {}".format(plugin)) | ||||
|         plug = self.plugins[plugin] | ||||
|         nplug = self._load_plugin(plug.module, plug.path) | ||||
|         del self.plugins[plugin] | ||||
|         self.plugins[nplug.name] = nplug | ||||
|  | ||||
|     def _load_plugin(self, plugin, search_folder, enabled=True): | ||||
|         sys.path.append(search_folder) | ||||
|         tmp = importlib.import_module(plugin).plugin | ||||
|         (fp, pathname, desc) = imp.find_module(plugin) | ||||
|         tmp = imp.load_module(plugin, fp, pathname, desc).plugin | ||||
|         sys.path.remove(search_folder) | ||||
|         tmp.path = search_folder | ||||
|         try: | ||||
| @@ -68,19 +95,19 @@ class Senpy(object): | ||||
|             tmp.repo = None | ||||
|         if not hasattr(tmp, "enabled"): | ||||
|             tmp.enabled = enabled | ||||
|         tmp.module = plugin | ||||
|         return tmp | ||||
|  | ||||
|  | ||||
|     def _load_plugins(self): | ||||
|         #print(sys.path) | ||||
|         #print(search_folder) | ||||
|         plugins = {} | ||||
|         for search_folder in self.search_folders: | ||||
|             for item in os.listdir(search_folder): | ||||
|                 if os.path.isdir(os.path.join(search_folder, item)) \ | ||||
|                         and os.path.exists( | ||||
|                             os.path.join(search_folder, item, "__init__.py")): | ||||
|                     plugins[item] = self._load_plugin(item, search_folder) | ||||
|                     plugin = self._load_plugin(item, search_folder) | ||||
|                     plugins[plugin.name] = plugin | ||||
|  | ||||
|         return plugins | ||||
|  | ||||
| @@ -105,6 +132,20 @@ class Senpy(object): | ||||
|                 self._plugins = self._load_plugins() | ||||
|             return self._plugins | ||||
|  | ||||
|     def filter_plugins(self, **kwargs): | ||||
|         def matches(plug): | ||||
|             res = all(getattr(plug, k, None)==v for (k,v) in kwargs.items()) | ||||
|             print("matching {} with {}: {}".format(plug.name, kwargs, res)) | ||||
|             return res | ||||
|         if not kwargs: | ||||
|             return self.plugins | ||||
|         else: | ||||
|             return {n:p for n,p in self.plugins.items() if matches(p)} | ||||
|  | ||||
|     def sentiment_plugins(self): | ||||
|         return (plugin for plugin in self.plugins if | ||||
|                 isinstance(plugin, SentimentPlugin)) | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     from flask import Flask | ||||
|     app = Flask(__name__) | ||||
|   | ||||
| @@ -1,24 +1,67 @@ | ||||
| PARAMS = {"input": {"aliases": ["i", "input"], | ||||
|                     "required": True, | ||||
|                     "help": "Input text" | ||||
|                     }, | ||||
|           "informat": {"aliases": ["f", "informat"], | ||||
|                        "required": False, | ||||
|                        "default": "text", | ||||
|                        "options": ["turtle", "text"], | ||||
|                        }, | ||||
|           "intype": {"aliases": ["intype", "t"], | ||||
|                      "required": False, | ||||
|                      "default": "direct", | ||||
|                      "options": ["direct", "url", "file"], | ||||
|                      }, | ||||
|           "outformat": {"aliases": ["outformat", "o"], | ||||
|                         "default": "json-ld", | ||||
|                         "required": False, | ||||
|                         "options": ["json-ld"], | ||||
|                         }, | ||||
|           "language": {"aliases": ["language", "l"], | ||||
|                        "required": False, | ||||
|                        "options": ["es", "en"], | ||||
|                        }, | ||||
|           "urischeme": {"aliases": ["urischeme", "u"], | ||||
|                         "required": False, | ||||
|                         "default": "RFC5147String", | ||||
|                         "options": "RFC5147String" | ||||
|                         }, | ||||
|           } | ||||
|  | ||||
| class SenpyPlugin(object): | ||||
|     def __init__(self, name=None, version=None, params=None): | ||||
|     def __init__(self, name=None, version=None, extraparams=None, params=None): | ||||
|         print("Initing {}".format(name)) | ||||
|         self.name = name | ||||
|         self.version = version | ||||
|         self.params = params or [] | ||||
|         if params: | ||||
|             self.params = params | ||||
|         else: | ||||
|             self.params = PARAMS.copy() | ||||
|             if extraparams: | ||||
|                 self.params.update(extraparams) | ||||
|         self.extraparams = extraparams or {} | ||||
|         self.enabled = True | ||||
|  | ||||
|     def analyse(self, *args, **kwargs): | ||||
|         pass | ||||
|  | ||||
|     def activate(self): | ||||
|         pass | ||||
|     def enable(self): | ||||
|         self.enabled = True | ||||
|  | ||||
|     def deactivate(self): | ||||
|         pass | ||||
|     def disable(self): | ||||
|         self.enabled = False | ||||
|  | ||||
|     def jsonable(self, parameters=False): | ||||
|         resp =  { | ||||
|             "@id": "{}_{}".format(self.name, self.version), | ||||
|             "enabled": self.enabled, | ||||
|         } | ||||
|         if self.repo: | ||||
|             resp["repo"] = self.repo.remotes[0].url | ||||
|         if parameters: | ||||
|             resp["parameters"] = self.params, | ||||
|             resp["parameters"] = self.params | ||||
|         elif self.extraparams: | ||||
|             resp["extra_parameters"] = self.extraparams | ||||
|         return resp | ||||
|  | ||||
| class SentimentPlugin(SenpyPlugin): | ||||
|   | ||||
| @@ -3,20 +3,20 @@ import json | ||||
|  | ||||
| import sys | ||||
|  | ||||
| print(sys.path) | ||||
| from senpy.plugins import SentimentPlugin | ||||
| from senpy.models import Response, Opinion, Entry | ||||
|  | ||||
| class Sentiment140Plugin(SentimentPlugin): | ||||
|     parameters = { | ||||
|     EXTRA_PARAMS = { | ||||
|         "language": {"aliases": ["language", "l"], | ||||
|                      "required": False, | ||||
|                      "options": ["es", "en", "auto"], | ||||
|                      } | ||||
|     } | ||||
|     def __init__(self, **kwargs): | ||||
|         super(Sentiment140Plugin, self).__init__(name="Sentiment140", | ||||
|                                                  version="1.0", | ||||
|         super(Sentiment140Plugin, self).__init__(name="sentiment140", | ||||
|                                                  version="2.0", | ||||
|                                                  extraparams=self.EXTRA_PARAMS, | ||||
|                                                  **kwargs) | ||||
|  | ||||
|     def analyse(self, **params): | ||||
| @@ -44,5 +44,4 @@ class Sentiment140Plugin(SentimentPlugin): | ||||
|         response.entries.append(entry) | ||||
|         return response | ||||
|  | ||||
|  | ||||
| plugin = Sentiment140Plugin() | ||||
|   | ||||
							
								
								
									
										3
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								setup.py
									
									
									
									
									
								
							| @@ -1,9 +1,10 @@ | ||||
| from setuptools import setup | ||||
| import senpy | ||||
|  | ||||
| setup( | ||||
|   name = 'senpy', | ||||
|   packages = ['senpy'], # this must be the same as the name above | ||||
|   version = '0.2.3', | ||||
|   version = senpy.VERSION, | ||||
|   description = ''' | ||||
|     A sentiment analysis server implementation. Designed to be \ | ||||
| extendable, so new algorithms and sources can be used. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user