mirror of
				https://github.com/gsi-upm/senpy
				synced 2025-10-31 15:38:17 +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 | import config | ||||||
| from flask import Flask | from flask import Flask | ||||||
| from senpy import Senpy | from senpy.extensions import Senpy | ||||||
|  |  | ||||||
| app = Flask(__name__) | app = Flask(__name__) | ||||||
|  |  | ||||||
| @@ -30,4 +30,4 @@ sp.init_app(app) | |||||||
|  |  | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     app.debug = config.DEBUG |     app.debug = config.DEBUG | ||||||
|     app.run() |     app.run(use_reloader=False) | ||||||
|   | |||||||
| @@ -18,6 +18,8 @@ | |||||||
| Sentiment analysis server in Python | Sentiment analysis server in Python | ||||||
| ''' | ''' | ||||||
|  |  | ||||||
|  | VERSION = "0.2.4" | ||||||
|  |  | ||||||
| import extensions | import extensions | ||||||
| import blueprints | import blueprints | ||||||
| import plugins | import plugins | ||||||
|   | |||||||
| @@ -22,43 +22,13 @@ import json | |||||||
|  |  | ||||||
| nif_blueprint = Blueprint("NIF Sentiment Analysis Server", __name__) | nif_blueprint = Blueprint("NIF Sentiment Analysis Server", __name__) | ||||||
|  |  | ||||||
| PARAMS = {"input": {"aliases": ["i", "input"], | BASIC_PARAMS = { | ||||||
|                     "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"], |  | ||||||
|                         }, |  | ||||||
|     "algorithm": {"aliases": ["algorithm", "a", "algo"], |     "algorithm": {"aliases": ["algorithm", "a", "algo"], | ||||||
|                   "required": False, |                   "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): | def get_params(req, params=BASIC_PARAMS): | ||||||
|     return get_params(req, params={"algorithm": PARAMS["algorithm"]}) |  | ||||||
|  |  | ||||||
| def get_params(req, params=PARAMS): |  | ||||||
|     indict = None |     indict = None | ||||||
|     if req.method == 'POST': |     if req.method == 'POST': | ||||||
|         indict = req.form |         indict = req.form | ||||||
| @@ -113,35 +83,44 @@ def basic_analysis(params): | |||||||
| @nif_blueprint.route('/', methods=['POST', 'GET']) | @nif_blueprint.route('/', methods=['POST', 'GET']) | ||||||
| def home(entries=None): | def home(entries=None): | ||||||
|     try: |     try: | ||||||
|         algo = get_algorithm(request)["algorithm"] |         algo = get_params(request).get("algorithm", None) | ||||||
|         specific_params = PARAMS.copy() |         specific_params = current_app.senpy.parameters(algo) | ||||||
|         specific_params.update(current_app.senpy.parameters(algo)) |  | ||||||
|         params = get_params(request, specific_params) |         params = get_params(request, specific_params) | ||||||
|     except ValueError as ex: |     except ValueError as ex: | ||||||
|         return ex.message |         return ex.message | ||||||
|     response = current_app.senpy.analyse(**params) |     response = current_app.senpy.analyse(**params) | ||||||
|     return jsonify(response) |     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/', methods=['POST', 'GET']) | ||||||
| @nif_blueprint.route('/plugins/<plugin>', methods=['POST', 'GET']) | @nif_blueprint.route('/plugins/<plugin>', methods=['POST', 'GET']) | ||||||
| @nif_blueprint.route('/plugins/<plugin>/<action>', methods=['POST', 'GET']) | @nif_blueprint.route('/plugins/<plugin>/<action>', methods=['POST', 'GET']) | ||||||
| def plugins(plugin=None, action="list"): | def plugins(plugin=None, action="list"): | ||||||
|     print current_app.senpy.plugins.keys() |     filt = {} | ||||||
|     if plugin: |     if plugin: | ||||||
|         plugs = {plugin:current_app.senpy.plugins[plugin]} |         filt["name"] = plugin | ||||||
|     else: |     plugs = current_app.senpy.filter_plugins(**filt) | ||||||
|         plugs = current_app.senpy.plugins |     if plugin and not plugs: | ||||||
|  |         return "Plugin not found", 400 | ||||||
|     if action == "list": |     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) |         return jsonify(dic) | ||||||
|     elif action == "disable": |     if action == "disable": | ||||||
|         plugs[plugin].enabled = False |         current_app.senpy.disable_plugin(plugin) | ||||||
|         return "Ok" |         return "Ok" | ||||||
|     elif action == "enable": |     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" |         return "Ok" | ||||||
|     else: |     else: | ||||||
|         return "action '{}' not allowed".format(action), 404 |         return "action '{}' not allowed".format(action), 400 | ||||||
|  |  | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     import config |     import config | ||||||
|   | |||||||
| @@ -1,8 +1,10 @@ | |||||||
| import os | import os | ||||||
| import sys | import sys | ||||||
| import importlib | import imp | ||||||
|  |  | ||||||
| from flask import current_app | from flask import current_app | ||||||
|  | from collections import defaultdict | ||||||
|  | from .plugins import SentimentPlugin, EmotionPlugin | ||||||
|  |  | ||||||
| try: | try: | ||||||
|     from flask import _app_ctx_stack as stack |     from flask import _app_ctx_stack as stack | ||||||
| @@ -18,7 +20,7 @@ class Senpy(object): | |||||||
|         self.app = app |         self.app = app | ||||||
|         base_folder = os.path.join(os.path.dirname(__file__), "plugins") |         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 folder and os.path.isdir(folder)) | ||||||
|  |  | ||||||
|         if app is not None: |         if app is not None: | ||||||
| @@ -41,24 +43,49 @@ class Senpy(object): | |||||||
|         app.register_blueprint(nif_blueprint) |         app.register_blueprint(nif_blueprint) | ||||||
|  |  | ||||||
|     def analyse(self, **params): |     def analyse(self, **params): | ||||||
|  |         algo = None | ||||||
|  |         print("analysing with params: {}".format(params)) | ||||||
|         if "algorithm" in params: |         if "algorithm" in params: | ||||||
|             algo = params["algorithm"] |             algo = params["algorithm"] | ||||||
|  |         elif self.plugins: | ||||||
|  |             algo = self.default_plugin | ||||||
|         if algo in self.plugins and self.plugins[algo].enabled: |         if algo in self.plugins and self.plugins[algo].enabled: | ||||||
|             plug = self.plugins[algo] |             plug = self.plugins[algo] | ||||||
|             resp = plug.analyse(**params) |             resp = plug.analyse(**params) | ||||||
|             resp.analysis.append(plug.jsonable()) |             resp.analysis.append(plug.jsonable()) | ||||||
|             return resp |             return resp | ||||||
|  |         else: | ||||||
|             return {"status": 500, "message": "No valid algorithm"} |             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): |     def parameters(self, algo): | ||||||
|         if algo in self.plugins: |         return getattr(self.plugins.get(algo or self.default_plugin), "params", {}) | ||||||
|             if hasattr(self.plugins[algo], "parameters"): |  | ||||||
|                 return self.plugins[algo].parameters |     def enable_plugin(self, plugin): | ||||||
|         return {} |         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): |     def _load_plugin(self, plugin, search_folder, enabled=True): | ||||||
|         sys.path.append(search_folder) |         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) |         sys.path.remove(search_folder) | ||||||
|         tmp.path = search_folder |         tmp.path = search_folder | ||||||
|         try: |         try: | ||||||
| @@ -68,19 +95,19 @@ class Senpy(object): | |||||||
|             tmp.repo = None |             tmp.repo = None | ||||||
|         if not hasattr(tmp, "enabled"): |         if not hasattr(tmp, "enabled"): | ||||||
|             tmp.enabled = enabled |             tmp.enabled = enabled | ||||||
|  |         tmp.module = plugin | ||||||
|         return tmp |         return tmp | ||||||
|  |  | ||||||
|  |  | ||||||
|     def _load_plugins(self): |     def _load_plugins(self): | ||||||
|         #print(sys.path) |  | ||||||
|         #print(search_folder) |  | ||||||
|         plugins = {} |         plugins = {} | ||||||
|         for search_folder in self.search_folders: |         for search_folder in self.search_folders: | ||||||
|             for item in os.listdir(search_folder): |             for item in os.listdir(search_folder): | ||||||
|                 if os.path.isdir(os.path.join(search_folder, item)) \ |                 if os.path.isdir(os.path.join(search_folder, item)) \ | ||||||
|                         and os.path.exists( |                         and os.path.exists( | ||||||
|                             os.path.join(search_folder, item, "__init__.py")): |                             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 |         return plugins | ||||||
|  |  | ||||||
| @@ -105,6 +132,20 @@ class Senpy(object): | |||||||
|                 self._plugins = self._load_plugins() |                 self._plugins = self._load_plugins() | ||||||
|             return self._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__': | if __name__ == '__main__': | ||||||
|     from flask import Flask |     from flask import Flask | ||||||
|     app = Flask(__name__) |     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): | 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.name = name | ||||||
|         self.version = version |         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): |     def analyse(self, *args, **kwargs): | ||||||
|         pass |         pass | ||||||
|  |  | ||||||
|     def activate(self): |     def enable(self): | ||||||
|         pass |         self.enabled = True | ||||||
|  |  | ||||||
|     def deactivate(self): |     def disable(self): | ||||||
|         pass |         self.enabled = False | ||||||
|  |  | ||||||
|     def jsonable(self, parameters=False): |     def jsonable(self, parameters=False): | ||||||
|         resp =  { |         resp =  { | ||||||
|             "@id": "{}_{}".format(self.name, self.version), |             "@id": "{}_{}".format(self.name, self.version), | ||||||
|  |             "enabled": self.enabled, | ||||||
|         } |         } | ||||||
|  |         if self.repo: | ||||||
|  |             resp["repo"] = self.repo.remotes[0].url | ||||||
|         if parameters: |         if parameters: | ||||||
|             resp["parameters"] = self.params, |             resp["parameters"] = self.params | ||||||
|  |         elif self.extraparams: | ||||||
|  |             resp["extra_parameters"] = self.extraparams | ||||||
|         return resp |         return resp | ||||||
|  |  | ||||||
| class SentimentPlugin(SenpyPlugin): | class SentimentPlugin(SenpyPlugin): | ||||||
|   | |||||||
| @@ -3,20 +3,20 @@ import json | |||||||
|  |  | ||||||
| import sys | import sys | ||||||
|  |  | ||||||
| print(sys.path) |  | ||||||
| from senpy.plugins import SentimentPlugin | from senpy.plugins import SentimentPlugin | ||||||
| from senpy.models import Response, Opinion, Entry | from senpy.models import Response, Opinion, Entry | ||||||
|  |  | ||||||
| class Sentiment140Plugin(SentimentPlugin): | class Sentiment140Plugin(SentimentPlugin): | ||||||
|     parameters = { |     EXTRA_PARAMS = { | ||||||
|         "language": {"aliases": ["language", "l"], |         "language": {"aliases": ["language", "l"], | ||||||
|                      "required": False, |                      "required": False, | ||||||
|                      "options": ["es", "en", "auto"], |                      "options": ["es", "en", "auto"], | ||||||
|                      } |                      } | ||||||
|     } |     } | ||||||
|     def __init__(self, **kwargs): |     def __init__(self, **kwargs): | ||||||
|         super(Sentiment140Plugin, self).__init__(name="Sentiment140", |         super(Sentiment140Plugin, self).__init__(name="sentiment140", | ||||||
|                                                  version="1.0", |                                                  version="2.0", | ||||||
|  |                                                  extraparams=self.EXTRA_PARAMS, | ||||||
|                                                  **kwargs) |                                                  **kwargs) | ||||||
|  |  | ||||||
|     def analyse(self, **params): |     def analyse(self, **params): | ||||||
| @@ -44,5 +44,4 @@ class Sentiment140Plugin(SentimentPlugin): | |||||||
|         response.entries.append(entry) |         response.entries.append(entry) | ||||||
|         return response |         return response | ||||||
|  |  | ||||||
|  |  | ||||||
| plugin = Sentiment140Plugin() | plugin = Sentiment140Plugin() | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								setup.py
									
									
									
									
									
								
							| @@ -1,9 +1,10 @@ | |||||||
| from setuptools import setup | from setuptools import setup | ||||||
|  | import senpy | ||||||
|  |  | ||||||
| setup( | setup( | ||||||
|   name = 'senpy', |   name = 'senpy', | ||||||
|   packages = ['senpy'], # this must be the same as the name above |   packages = ['senpy'], # this must be the same as the name above | ||||||
|   version = '0.2.3', |   version = senpy.VERSION, | ||||||
|   description = ''' |   description = ''' | ||||||
|     A sentiment analysis server implementation. Designed to be \ |     A sentiment analysis server implementation. Designed to be \ | ||||||
| extendable, so new algorithms and sources can be used. | extendable, so new algorithms and sources can be used. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user