mirror of
https://github.com/gsi-upm/senpy
synced 2024-11-22 08:12:27 +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:
parent
cc298742ec
commit
0c8f98d466
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)
|
|
||||||
try:
|
|
||||||
print('Senpy version {}'.format(senpy.__version__))
|
print('Senpy version {}'.format(senpy.__version__))
|
||||||
print('Server running on port %s:%d. Ctrl+C to quit' % (args.host,
|
print('Server running on port %s:%d. Ctrl+C to quit' % (args.host,
|
||||||
args.port))
|
args.port))
|
||||||
|
if not app.debug:
|
||||||
|
http_server = WSGIServer((args.host, args.port), app)
|
||||||
|
try:
|
||||||
http_server.serve_forever()
|
http_server.serve_forever()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print('Bye!')
|
print('Bye!')
|
||||||
http_server.stop()
|
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):
|
||||||
|
if not cls:
|
||||||
target = indict.get('@type', None)
|
target = indict.get('@type', None)
|
||||||
|
try:
|
||||||
if target and target in _subtypes:
|
if target and target in _subtypes:
|
||||||
cls = _subtypes[target]
|
cls = _subtypes[target]
|
||||||
else:
|
else:
|
||||||
cls = BaseModel
|
cls = BaseModel
|
||||||
return cls(**indict)
|
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user