mirror of
				https://github.com/gsi-upm/senpy
				synced 2025-10-31 07:28:16 +00:00 
			
		
		
		
	Pre-0.8.6
* Improved debugging (back to using Flask's built-in mechanisms) * Recursive model loading from json * Added DEVPORT to Makefile * Accept json-ld input. Closes #16 * Improved Exception handling in client * Modified default plugin selection to only include analysis plugins * More tests
This commit is contained in:
		
							
								
								
									
										5
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								Makefile
									
									
									
									
									
								
							| @@ -6,6 +6,7 @@ VERSION=$(shell git describe --tags --dirty 2>/dev/null) | |||||||
| TARNAME=$(NAME)-$(VERSION).tar.gz  | TARNAME=$(NAME)-$(VERSION).tar.gz  | ||||||
| IMAGENAME=$(REPO)/$(NAME) | IMAGENAME=$(REPO)/$(NAME) | ||||||
| IMAGEWTAG=$(IMAGENAME):$(VERSION) | IMAGEWTAG=$(IMAGENAME):$(VERSION) | ||||||
|  | DEVPORT=5000 | ||||||
| action="test-${PYMAIN}" | action="test-${PYMAIN}" | ||||||
|  |  | ||||||
| all: build run | all: build run | ||||||
| @@ -43,7 +44,7 @@ quick_test: $(addprefix test-,$(PYMAIN)) | |||||||
| dev-%: | dev-%: | ||||||
| 	@docker start $(NAME)-dev$* || (\ | 	@docker start $(NAME)-dev$* || (\ | ||||||
| 		$(MAKE) build-$*; \ | 		$(MAKE) build-$*; \ | ||||||
| 		docker run -d -w /usr/src/app/ -v $$PWD:/usr/src/app --entrypoint=/bin/bash -ti --name $(NAME)-dev$* '$(IMAGEWTAG)-python$*'; \ | 		docker run -d -w /usr/src/app/ -p $(DEVPORT):5000 -v $$PWD:/usr/src/app --entrypoint=/bin/bash -ti --name $(NAME)-dev$* '$(IMAGEWTAG)-python$*'; \ | ||||||
| 	)\ | 	)\ | ||||||
|  |  | ||||||
| 	docker exec -ti $(NAME)-dev$* bash | 	docker exec -ti $(NAME)-dev$* bash | ||||||
| @@ -86,7 +87,7 @@ pip_upload: | |||||||
| 	python setup.py sdist upload ; | 	python setup.py sdist upload ; | ||||||
|  |  | ||||||
| run-%: build-% | run-%: build-% | ||||||
| 	docker run --rm -p 5000:5000 -ti '$(IMAGEWTAG)-python$(PYMAIN)' --default-plugins | 	docker run --rm -p $(DEVPORT):5000 -ti '$(IMAGEWTAG)-python$(PYMAIN)' --default-plugins | ||||||
|  |  | ||||||
| run: run-$(PYMAIN) | run: run-$(PYMAIN) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -26,7 +26,6 @@ from gevent.wsgi import WSGIServer | |||||||
| from gevent.monkey import patch_all | from gevent.monkey import patch_all | ||||||
| import logging | import logging | ||||||
| import os | import os | ||||||
| import sys |  | ||||||
| import argparse | import argparse | ||||||
| import senpy | import senpy | ||||||
|  |  | ||||||
| @@ -35,22 +34,6 @@ patch_all(thread=False) | |||||||
| SERVER_PORT = os.environ.get("PORT", 5000) | SERVER_PORT = os.environ.get("PORT", 5000) | ||||||
|  |  | ||||||
|  |  | ||||||
| def info(type, value, tb): |  | ||||||
|     if hasattr(sys, 'ps1') or not sys.stderr.isatty(): |  | ||||||
|         #  we are in interactive mode or we don't have a tty-like |  | ||||||
|         #  device, so we call the default hook |  | ||||||
|         sys.__excepthook__(type, value, tb) |  | ||||||
|     else: |  | ||||||
|         import traceback |  | ||||||
|         import pdb |  | ||||||
|         # we are NOT in interactive mode, print the exception... |  | ||||||
|         traceback.print_exception(type, value, tb) |  | ||||||
|         print |  | ||||||
|         # ...then start the debugger in post-mortem mode. |  | ||||||
|         # pdb.pm() # deprecated |  | ||||||
|         pdb.post_mortem(tb)  # more "modern" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def main(): | def main(): | ||||||
|     parser = argparse.ArgumentParser(description='Run a Senpy server') |     parser = argparse.ArgumentParser(description='Run a Senpy server') | ||||||
|     parser.add_argument( |     parser.add_argument( | ||||||
| @@ -100,22 +83,25 @@ def main(): | |||||||
|     rl.setLevel(getattr(logging, args.level)) |     rl.setLevel(getattr(logging, args.level)) | ||||||
|     app = Flask(__name__) |     app = Flask(__name__) | ||||||
|     app.debug = args.debug |     app.debug = args.debug | ||||||
|     if args.debug: |  | ||||||
|         sys.excepthook = info |  | ||||||
|     sp = Senpy(app, args.plugins_folder, default_plugins=args.default_plugins) |     sp = Senpy(app, args.plugins_folder, default_plugins=args.default_plugins) | ||||||
|     if args.only_install: |     if args.only_install: | ||||||
|         sp.install_deps() |         sp.install_deps() | ||||||
|         return |         return | ||||||
|     sp.activate_all() |     sp.activate_all() | ||||||
|     http_server = WSGIServer((args.host, args.port), app) |     print('Senpy version {}'.format(senpy.__version__)) | ||||||
|     try: |     print('Server running on port %s:%d. Ctrl+C to quit' % (args.host, | ||||||
|         print('Senpy version {}'.format(senpy.__version__)) |                                                             args.port)) | ||||||
|         print('Server running on port %s:%d. Ctrl+C to quit' % (args.host, |     if not app.debug: | ||||||
|                                                                 args.port)) |         http_server = WSGIServer((args.host, args.port), app) | ||||||
|         http_server.serve_forever() |         try: | ||||||
|     except KeyboardInterrupt: |             http_server.serve_forever() | ||||||
|         print('Bye!') |         except KeyboardInterrupt: | ||||||
|     http_server.stop() |             print('Bye!') | ||||||
|  |         http_server.stop() | ||||||
|  |     else: | ||||||
|  |         app.run(args.host, | ||||||
|  |                 args.port, | ||||||
|  |                 debug=True) | ||||||
|     sp.deactivate_all() |     sp.deactivate_all() | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -70,7 +70,7 @@ NIF_PARAMS = { | |||||||
|         "aliases": ["f", "informat"], |         "aliases": ["f", "informat"], | ||||||
|         "required": False, |         "required": False, | ||||||
|         "default": "text", |         "default": "text", | ||||||
|         "options": ["turtle", "text"], |         "options": ["turtle", "text", "json-ld"], | ||||||
|     }, |     }, | ||||||
|     "intype": { |     "intype": { | ||||||
|         "@id": "intype", |         "@id": "intype", | ||||||
|   | |||||||
| @@ -92,6 +92,9 @@ def basic_api(f): | |||||||
|             response = f(*args, **kwargs) |             response = f(*args, **kwargs) | ||||||
|         except Error as ex: |         except Error as ex: | ||||||
|             response = ex |             response = ex | ||||||
|  |             logger.error(ex) | ||||||
|  |             if current_app.debug: | ||||||
|  |                 raise | ||||||
|  |  | ||||||
|         in_headers = web_params['inHeaders'] != "0" |         in_headers = web_params['inHeaders'] != "0" | ||||||
|         expanded = api_params['expanded-jsonld'] |         expanded = api_params['expanded-jsonld'] | ||||||
| @@ -113,11 +116,8 @@ def basic_api(f): | |||||||
| @api_blueprint.route('/', methods=['POST', 'GET']) | @api_blueprint.route('/', methods=['POST', 'GET']) | ||||||
| @basic_api | @basic_api | ||||||
| def api(): | def api(): | ||||||
|     try: |     response = current_app.senpy.analyse(**request.params) | ||||||
|         response = current_app.senpy.analyse(**request.params) |     return response | ||||||
|         return response |  | ||||||
|     except Error as ex: |  | ||||||
|         return ex |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @api_blueprint.route('/plugins/', methods=['POST', 'GET']) | @api_blueprint.route('/plugins/', methods=['POST', 'GET']) | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ standard_library.install_aliases() | |||||||
|  |  | ||||||
| from . import plugins | from . import plugins | ||||||
| from .plugins import SenpyPlugin | from .plugins import SenpyPlugin | ||||||
| from .models import Error, Entry, Results, from_dict | from .models import Error, Entry, Results, from_string | ||||||
| from .blueprints import api_blueprint, demo_blueprint, ns_blueprint | from .blueprints import api_blueprint, demo_blueprint, ns_blueprint | ||||||
| from .api import API_PARAMS, NIF_PARAMS, parse_params | from .api import API_PARAMS, NIF_PARAMS, parse_params | ||||||
|  |  | ||||||
| @@ -128,7 +128,7 @@ class Senpy(object): | |||||||
|             entry = Entry(text=params['input']) |             entry = Entry(text=params['input']) | ||||||
|             results.entries.append(entry) |             results.entries.append(entry) | ||||||
|         elif params['informat'] == 'json-ld': |         elif params['informat'] == 'json-ld': | ||||||
|             results = from_dict(params['input']) |             results = from_string(params['input'], cls=Results) | ||||||
|         else: |         else: | ||||||
|             raise NotImplemented('Informat {} is not implemented'.format(params['informat'])) |             raise NotImplemented('Informat {} is not implemented'.format(params['informat'])) | ||||||
|         return results |         return results | ||||||
| @@ -171,7 +171,7 @@ class Senpy(object): | |||||||
|         except (Error, Exception) as ex: |         except (Error, Exception) as ex: | ||||||
|             if not isinstance(ex, Error): |             if not isinstance(ex, Error): | ||||||
|                 ex = Error(message=str(ex), status=500) |                 ex = Error(message=str(ex), status=500) | ||||||
|             logger.exception('Error returning analysis result') |             logger.error('Error returning analysis result') | ||||||
|             raise ex |             raise ex | ||||||
|         return resp |         return resp | ||||||
|  |  | ||||||
| @@ -236,7 +236,8 @@ class Senpy(object): | |||||||
|     def default_plugin(self): |     def default_plugin(self): | ||||||
|         candidate = self._default |         candidate = self._default | ||||||
|         if not candidate: |         if not candidate: | ||||||
|             candidates = self.filter_plugins(is_activated=True) |             candidates = self.filter_plugins(plugin_type='analysisPlugin', | ||||||
|  |                                              is_activated=True) | ||||||
|             if len(candidates) > 0: |             if len(candidates) > 0: | ||||||
|                 candidate = list(candidates.values())[0] |                 candidate = list(candidates.values())[0] | ||||||
|         logger.debug("Default: {}".format(candidate)) |         logger.debug("Default: {}".format(candidate)) | ||||||
|   | |||||||
| @@ -214,6 +214,7 @@ class BaseModel(SenpyMixin, dict): | |||||||
|             temp['@type'] = getattr(self, '@type') |             temp['@type'] = getattr(self, '@type') | ||||||
|         except AttributeError: |         except AttributeError: | ||||||
|             logger.warn('Creating an instance of an unknown model') |             logger.warn('Creating an instance of an unknown model') | ||||||
|  |  | ||||||
|         super(BaseModel, self).__init__(temp) |         super(BaseModel, self).__init__(temp) | ||||||
|  |  | ||||||
|     def _get_key(self, key): |     def _get_key(self, key): | ||||||
| @@ -252,13 +253,32 @@ def register(rsubclass, rtype=None): | |||||||
|     _subtypes[rtype or rsubclass.__name__] = rsubclass |     _subtypes[rtype or rsubclass.__name__] = rsubclass | ||||||
|  |  | ||||||
|  |  | ||||||
| def from_dict(indict): | def from_dict(indict, cls=None): | ||||||
|     target = indict.get('@type', None) |     if not cls: | ||||||
|     if target and target in _subtypes: |         target = indict.get('@type', None) | ||||||
|         cls = _subtypes[target] |         try: | ||||||
|     else: |             if target and target in _subtypes: | ||||||
|         cls = BaseModel |                 cls = _subtypes[target] | ||||||
|     return cls(**indict) |             else: | ||||||
|  |                 cls = BaseModel | ||||||
|  |         except Exception: | ||||||
|  |             cls = BaseModel | ||||||
|  |     outdict = dict() | ||||||
|  |     for k, v in indict.items(): | ||||||
|  |         if k == '@context': | ||||||
|  |             pass | ||||||
|  |         elif isinstance(v, dict): | ||||||
|  |             v = from_dict(indict[k]) | ||||||
|  |         elif isinstance(v, list): | ||||||
|  |             for ix, v2 in enumerate(v): | ||||||
|  |                 if isinstance(v2, dict): | ||||||
|  |                     v[ix] = from_dict(v2) | ||||||
|  |         outdict[k] = v | ||||||
|  |     return cls(**outdict) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def from_string(string, **kwargs): | ||||||
|  |     return from_dict(json.loads(string), **kwargs) | ||||||
|  |  | ||||||
|  |  | ||||||
| def from_json(injson): | def from_json(injson): | ||||||
| @@ -308,7 +328,7 @@ for i in [ | |||||||
| _ErrorModel = from_schema('error') | _ErrorModel = from_schema('error') | ||||||
|  |  | ||||||
|  |  | ||||||
| class Error(SenpyMixin, BaseException): | class Error(SenpyMixin, Exception): | ||||||
|     def __init__(self, message, *args, **kwargs): |     def __init__(self, message, *args, **kwargs): | ||||||
|         super(Error, self).__init__(self, message, message) |         super(Error, self).__init__(self, message, message) | ||||||
|         self._error = _ErrorModel(message=message, *args, **kwargs) |         self._error = _ErrorModel(message=message, *args, **kwargs) | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ def parse_resp(resp): | |||||||
| class BlueprintsTest(TestCase): | class BlueprintsTest(TestCase): | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|         self.app = Flask("test_extensions") |         self.app = Flask("test_extensions") | ||||||
|  |         self.app.debug = False | ||||||
|         self.client = self.app.test_client() |         self.client = self.app.test_client() | ||||||
|         self.senpy = Senpy() |         self.senpy = Senpy() | ||||||
|         self.senpy.init_app(self.app) |         self.senpy.init_app(self.app) | ||||||
|   | |||||||
| @@ -96,6 +96,27 @@ class ExtensionsTest(TestCase): | |||||||
|         assert r2.analysis[0] == "plugins/Dummy_0.1" |         assert r2.analysis[0] == "plugins/Dummy_0.1" | ||||||
|         assert r1.entries[0].text == 'input' |         assert r1.entries[0].text == 'input' | ||||||
|  |  | ||||||
|  |     def test_analyse_jsonld(self): | ||||||
|  |         """ Using a plugin with JSON-LD input""" | ||||||
|  |         js_input = '''{ | ||||||
|  |         "@id": "prueba", | ||||||
|  |         "@type": "results", | ||||||
|  |         "entries": [ | ||||||
|  |           {"@id": "entry1", | ||||||
|  |            "text": "tupni", | ||||||
|  |            "@type": "entry" | ||||||
|  |           } | ||||||
|  |         ] | ||||||
|  |         }''' | ||||||
|  |         r1 = self.senpy.analyse(algorithm="Dummy", | ||||||
|  |                                 input=js_input, | ||||||
|  |                                 informat="json-ld", | ||||||
|  |                                 output="tuptuo") | ||||||
|  |         r2 = self.senpy.analyse(input="tupni", output="tuptuo") | ||||||
|  |         assert r1.analysis[0] == "plugins/Dummy_0.1" | ||||||
|  |         assert r2.analysis[0] == "plugins/Dummy_0.1" | ||||||
|  |         assert r1.entries[0].text == 'input' | ||||||
|  |  | ||||||
|     def test_analyse_error(self): |     def test_analyse_error(self): | ||||||
|         mm = mock.MagicMock() |         mm = mock.MagicMock() | ||||||
|         mm.id = 'magic_mock' |         mm.id = 'magic_mock' | ||||||
|   | |||||||
| @@ -13,7 +13,9 @@ from senpy.models import (Emotion, | |||||||
|                           Results, |                           Results, | ||||||
|                           Sentiment, |                           Sentiment, | ||||||
|                           Plugins, |                           Plugins, | ||||||
|                           Plugin) |                           Plugin, | ||||||
|  |                           from_string, | ||||||
|  |                           from_dict) | ||||||
| from senpy import plugins | from senpy import plugins | ||||||
| from pprint import pprint | from pprint import pprint | ||||||
|  |  | ||||||
| @@ -167,3 +169,22 @@ class ModelsTest(TestCase): | |||||||
|         assert isinstance(plugs.plugins, list) |         assert isinstance(plugs.plugins, list) | ||||||
|         js = plugs.jsonld() |         js = plugs.jsonld() | ||||||
|         assert isinstance(js['plugins'], list) |         assert isinstance(js['plugins'], list) | ||||||
|  |  | ||||||
|  |     def test_from_string(self): | ||||||
|  |         results = { | ||||||
|  |             '@type': 'results', | ||||||
|  |             '@id': 'prueba', | ||||||
|  |             'entries': [{ | ||||||
|  |                 '@id': 'entry1', | ||||||
|  |                 '@type': 'entry', | ||||||
|  |                 'text': 'TEST' | ||||||
|  |             }] | ||||||
|  |         } | ||||||
|  |         recovered = from_dict(results) | ||||||
|  |         assert isinstance(recovered, Results) | ||||||
|  |         assert isinstance(recovered.entries[0], Entry) | ||||||
|  |  | ||||||
|  |         string = json.dumps(results) | ||||||
|  |         recovered = from_string(string) | ||||||
|  |         assert isinstance(recovered, Results) | ||||||
|  |         assert isinstance(recovered.entries[0], Entry) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user