mirror of
https://github.com/gsi-upm/senpy
synced 2024-11-22 08:12:27 +00:00
Merge branch '39-plugin-tests-missing'
This commit is contained in:
commit
9a2932b569
7
Makefile
7
Makefile
@ -70,8 +70,13 @@ dev: dev-$(PYMAIN)
|
|||||||
|
|
||||||
test-all: $(addprefix test-,$(PYVERSIONS))
|
test-all: $(addprefix test-,$(PYVERSIONS))
|
||||||
|
|
||||||
|
# Run setup.py from in an isolated container, built from the base image.
|
||||||
|
# This speeds tests up because the image has most (if not all) of the dependencies already.
|
||||||
test-%:
|
test-%:
|
||||||
docker run --rm --entrypoint /usr/local/bin/python -w /usr/src/app $(IMAGENAME):python$* setup.py test
|
docker rm $(NAME)-test-$* || true
|
||||||
|
docker create -ti --name $(NAME)-test-$* --entrypoint="" -w /usr/src/app/ $(IMAGENAME):python$* python setup.py test
|
||||||
|
docker cp . $(NAME)-test-$*:/usr/src/app
|
||||||
|
docker start -a $(NAME)-test-$*
|
||||||
|
|
||||||
test: test-$(PYMAIN)
|
test: test-$(PYMAIN)
|
||||||
|
|
||||||
|
@ -15,25 +15,12 @@ from threading import Thread
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import copy
|
import copy
|
||||||
import fnmatch
|
|
||||||
import inspect
|
|
||||||
import sys
|
|
||||||
import importlib
|
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
import yaml
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def log_subprocess_output(process):
|
|
||||||
for line in iter(process.stdout.readline, b''):
|
|
||||||
logger.info('%r', line)
|
|
||||||
for line in iter(process.stderr.readline, b''):
|
|
||||||
logger.error('%r', line)
|
|
||||||
|
|
||||||
|
|
||||||
class Senpy(object):
|
class Senpy(object):
|
||||||
""" Default Senpy extension for Flask """
|
""" Default Senpy extension for Flask """
|
||||||
|
|
||||||
@ -330,84 +317,6 @@ class Senpy(object):
|
|||||||
th.start()
|
th.start()
|
||||||
return th
|
return th
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def validate_info(cls, info):
|
|
||||||
return all(x in info for x in ('name', 'module', 'description', 'version'))
|
|
||||||
|
|
||||||
def install_deps(self):
|
|
||||||
for i in self.plugins.values():
|
|
||||||
self._install_deps(i)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _install_deps(cls, info=None):
|
|
||||||
requirements = info.get('requirements', [])
|
|
||||||
if requirements:
|
|
||||||
pip_args = ['pip']
|
|
||||||
pip_args.append('install')
|
|
||||||
pip_args.append('--use-wheel')
|
|
||||||
for req in requirements:
|
|
||||||
pip_args.append(req)
|
|
||||||
logger.info('Installing requirements: ' + str(requirements))
|
|
||||||
process = subprocess.Popen(pip_args,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE)
|
|
||||||
log_subprocess_output(process)
|
|
||||||
exitcode = process.wait()
|
|
||||||
if exitcode != 0:
|
|
||||||
raise Error("Dependencies not properly installed")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _load_module(cls, name, root):
|
|
||||||
sys.path.append(root)
|
|
||||||
tmp = importlib.import_module(name)
|
|
||||||
sys.path.remove(root)
|
|
||||||
return tmp
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _load_plugin_from_info(cls, info, root):
|
|
||||||
if not cls.validate_info(info):
|
|
||||||
logger.warn('The module info is not valid.\n\t{}'.format(info))
|
|
||||||
return None, None
|
|
||||||
module = info["module"]
|
|
||||||
name = info["name"]
|
|
||||||
|
|
||||||
cls._install_deps(info)
|
|
||||||
tmp = cls._load_module(module, root)
|
|
||||||
|
|
||||||
candidate = None
|
|
||||||
for _, obj in inspect.getmembers(tmp):
|
|
||||||
if inspect.isclass(obj) and inspect.getmodule(obj) == tmp:
|
|
||||||
logger.debug(("Found plugin class:"
|
|
||||||
" {}@{}").format(obj, inspect.getmodule(obj)))
|
|
||||||
candidate = obj
|
|
||||||
break
|
|
||||||
if not candidate:
|
|
||||||
logger.debug("No valid plugin for: {}".format(module))
|
|
||||||
return
|
|
||||||
module = candidate(info=info)
|
|
||||||
return name, module
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _load_plugin(cls, root, filename):
|
|
||||||
fpath = os.path.join(root, filename)
|
|
||||||
logger.debug("Loading plugin: {}".format(fpath))
|
|
||||||
with open(fpath, 'r') as f:
|
|
||||||
info = yaml.load(f)
|
|
||||||
logger.debug("Info: {}".format(info))
|
|
||||||
return cls._load_plugin_from_info(info, root)
|
|
||||||
|
|
||||||
def _load_plugins(self):
|
|
||||||
plugins = {}
|
|
||||||
for search_folder in self._search_folders:
|
|
||||||
for root, dirnames, filenames in os.walk(search_folder):
|
|
||||||
for filename in fnmatch.filter(filenames, '*.senpy'):
|
|
||||||
name, plugin = self._load_plugin(root, filename)
|
|
||||||
if plugin and name:
|
|
||||||
plugins[name] = plugin
|
|
||||||
|
|
||||||
self._outdated = False
|
|
||||||
return plugins
|
|
||||||
|
|
||||||
def teardown(self, exception):
|
def teardown(self, exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -415,7 +324,8 @@ class Senpy(object):
|
|||||||
def plugins(self):
|
def plugins(self):
|
||||||
""" Return the plugins registered for a given application. """
|
""" Return the plugins registered for a given application. """
|
||||||
if self._outdated:
|
if self._outdated:
|
||||||
self._plugin_list = self._load_plugins()
|
self._plugin_list = plugins.load_plugins(self._search_folders)
|
||||||
|
self._outdated = False
|
||||||
return self._plugin_list
|
return self._plugin_list
|
||||||
|
|
||||||
def filter_plugins(self, **kwargs):
|
def filter_plugins(self, **kwargs):
|
||||||
|
@ -218,12 +218,11 @@ class BaseModel(SenpyMixin, dict):
|
|||||||
super(BaseModel, self).__init__(temp)
|
super(BaseModel, self).__init__(temp)
|
||||||
|
|
||||||
def _get_key(self, key):
|
def _get_key(self, key):
|
||||||
|
if key is 'id':
|
||||||
|
key = '@id'
|
||||||
key = key.replace("__", ":", 1)
|
key = key.replace("__", ":", 1)
|
||||||
return key
|
return key
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
dict.__setitem__(self, key, value)
|
|
||||||
|
|
||||||
def __delitem__(self, key):
|
def __delitem__(self, key):
|
||||||
dict.__delitem__(self, key)
|
dict.__delitem__(self, key)
|
||||||
|
|
||||||
@ -244,8 +243,6 @@ class BaseModel(SenpyMixin, dict):
|
|||||||
|
|
||||||
def _plain_dict(self):
|
def _plain_dict(self):
|
||||||
d = {k: v for (k, v) in self.items() if k[0] != "_"}
|
d = {k: v for (k, v) in self.items() if k[0] != "_"}
|
||||||
if 'id' in d:
|
|
||||||
d["@id"] = d.pop('id')
|
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,14 +1,21 @@
|
|||||||
from future import standard_library
|
from future import standard_library
|
||||||
standard_library.install_aliases()
|
standard_library.install_aliases()
|
||||||
|
|
||||||
import inspect
|
|
||||||
import os.path
|
import os.path
|
||||||
import os
|
import os
|
||||||
import pickle
|
import pickle
|
||||||
import logging
|
import logging
|
||||||
import tempfile
|
import tempfile
|
||||||
import copy
|
import copy
|
||||||
from .. import models
|
|
||||||
|
import fnmatch
|
||||||
|
import inspect
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import importlib
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from .. import models, utils
|
||||||
from ..api import API_PARAMS
|
from ..api import API_PARAMS
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -37,6 +44,21 @@ class Plugin(models.Plugin):
|
|||||||
def deactivate(self):
|
def deactivate(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
if not hasattr(self, 'test_cases'):
|
||||||
|
import inspect
|
||||||
|
raise AttributeError(('Plugin {} [{}] does not have any defined '
|
||||||
|
'test cases').format(self.id, inspect.getfile(self.__class__)))
|
||||||
|
for case in self.test_cases:
|
||||||
|
res = list(self.analyse_entry(models.Entry(case['entry']),
|
||||||
|
case['params']))
|
||||||
|
exp = case['expected']
|
||||||
|
if not isinstance(exp, list):
|
||||||
|
exp = [exp]
|
||||||
|
utils.check_template(res, exp)
|
||||||
|
for r in res:
|
||||||
|
r.validate()
|
||||||
|
|
||||||
|
|
||||||
SenpyPlugin = Plugin
|
SenpyPlugin = Plugin
|
||||||
|
|
||||||
@ -160,3 +182,86 @@ def pfilter(plugins, **kwargs):
|
|||||||
if kwargs:
|
if kwargs:
|
||||||
candidates = filter(matches, candidates)
|
candidates = filter(matches, candidates)
|
||||||
return {p.name: p for p in candidates}
|
return {p.name: p for p in candidates}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_info(info):
|
||||||
|
return all(x in info for x in ('name', 'module', 'description', 'version'))
|
||||||
|
|
||||||
|
|
||||||
|
def load_module(name, root):
|
||||||
|
sys.path.append(root)
|
||||||
|
tmp = importlib.import_module(name)
|
||||||
|
sys.path.remove(root)
|
||||||
|
return tmp
|
||||||
|
|
||||||
|
|
||||||
|
def log_subprocess_output(process):
|
||||||
|
for line in iter(process.stdout.readline, b''):
|
||||||
|
logger.info('%r', line)
|
||||||
|
for line in iter(process.stderr.readline, b''):
|
||||||
|
logger.error('%r', line)
|
||||||
|
|
||||||
|
|
||||||
|
def install_deps(*plugins):
|
||||||
|
for info in plugins:
|
||||||
|
requirements = info.get('requirements', [])
|
||||||
|
if requirements:
|
||||||
|
pip_args = ['pip']
|
||||||
|
pip_args.append('install')
|
||||||
|
pip_args.append('--use-wheel')
|
||||||
|
for req in requirements:
|
||||||
|
pip_args.append(req)
|
||||||
|
logger.info('Installing requirements: ' + str(requirements))
|
||||||
|
process = subprocess.Popen(pip_args,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE)
|
||||||
|
log_subprocess_output(process)
|
||||||
|
exitcode = process.wait()
|
||||||
|
if exitcode != 0:
|
||||||
|
raise models.Error("Dependencies not properly installed")
|
||||||
|
|
||||||
|
|
||||||
|
def load_plugin_from_info(info, root, validator=validate_info):
|
||||||
|
if not validator(info):
|
||||||
|
logger.warn('The module info is not valid.\n\t{}'.format(info))
|
||||||
|
return None, None
|
||||||
|
module = info["module"]
|
||||||
|
name = info["name"]
|
||||||
|
|
||||||
|
install_deps(info)
|
||||||
|
tmp = load_module(module, root)
|
||||||
|
|
||||||
|
candidate = None
|
||||||
|
for _, obj in inspect.getmembers(tmp):
|
||||||
|
if inspect.isclass(obj) and inspect.getmodule(obj) == tmp:
|
||||||
|
logger.debug(("Found plugin class:"
|
||||||
|
" {}@{}").format(obj, inspect.getmodule(obj)))
|
||||||
|
candidate = obj
|
||||||
|
break
|
||||||
|
if not candidate:
|
||||||
|
logger.debug("No valid plugin for: {}".format(module))
|
||||||
|
return
|
||||||
|
module = candidate(info=info)
|
||||||
|
return name, module
|
||||||
|
|
||||||
|
|
||||||
|
def load_plugin(root, filename):
|
||||||
|
fpath = os.path.join(root, filename)
|
||||||
|
logger.debug("Loading plugin: {}".format(fpath))
|
||||||
|
with open(fpath, 'r') as f:
|
||||||
|
info = yaml.load(f)
|
||||||
|
logger.debug("Info: {}".format(info))
|
||||||
|
return load_plugin_from_info(info, root)
|
||||||
|
|
||||||
|
|
||||||
|
def load_plugins(folders, loader=load_plugin):
|
||||||
|
plugins = {}
|
||||||
|
for search_folder in folders:
|
||||||
|
for root, dirnames, filenames in os.walk(search_folder):
|
||||||
|
# Do not look for plugins in hidden or special folders
|
||||||
|
dirnames[:] = [d for d in dirnames if d[0] not in ['.', '_']]
|
||||||
|
for filename in fnmatch.filter(filenames, '*.senpy'):
|
||||||
|
name, plugin = loader(root, filename)
|
||||||
|
if plugin and name:
|
||||||
|
plugins[name] = plugin
|
||||||
|
return plugins
|
||||||
|
@ -100,3 +100,54 @@ class CentroidConversion(EmotionConversionPlugin):
|
|||||||
else:
|
else:
|
||||||
raise Error('EMOTION MODEL NOT KNOWN')
|
raise Error('EMOTION MODEL NOT KNOWN')
|
||||||
yield e
|
yield e
|
||||||
|
|
||||||
|
def test(self, info=None):
|
||||||
|
if not info:
|
||||||
|
info = {
|
||||||
|
"name": "CentroidTest",
|
||||||
|
"description": "Centroid test",
|
||||||
|
"version": 0,
|
||||||
|
"centroids": {
|
||||||
|
"c1": {"V1": 0.5,
|
||||||
|
"V2": 0.5},
|
||||||
|
"c2": {"V1": -0.5,
|
||||||
|
"V2": 0.5},
|
||||||
|
"c3": {"V1": -0.5,
|
||||||
|
"V2": -0.5},
|
||||||
|
"c4": {"V1": 0.5,
|
||||||
|
"V2": -0.5}},
|
||||||
|
"aliases": {
|
||||||
|
"V1": "X-dimension",
|
||||||
|
"V2": "Y-dimension"
|
||||||
|
},
|
||||||
|
"centroids_direction": ["emoml:big6", "emoml:fsre-dimensions"]
|
||||||
|
}
|
||||||
|
|
||||||
|
c = CentroidConversion(info)
|
||||||
|
|
||||||
|
es1 = EmotionSet()
|
||||||
|
e1 = Emotion()
|
||||||
|
e1.onyx__hasEmotionCategory = "c1"
|
||||||
|
es1.onyx__hasEmotion.append(e1)
|
||||||
|
res = c._forward_conversion(es1)
|
||||||
|
assert res["X-dimension"] == 0.5
|
||||||
|
assert res["Y-dimension"] == 0.5
|
||||||
|
|
||||||
|
e2 = Emotion()
|
||||||
|
e2.onyx__hasEmotionCategory = "c2"
|
||||||
|
es1.onyx__hasEmotion.append(e2)
|
||||||
|
res = c._forward_conversion(es1)
|
||||||
|
assert res["X-dimension"] == 0
|
||||||
|
assert res["Y-dimension"] == 1
|
||||||
|
|
||||||
|
e = Emotion()
|
||||||
|
e["X-dimension"] = -0.2
|
||||||
|
e["Y-dimension"] = -0.3
|
||||||
|
res = c._backwards_conversion(e)
|
||||||
|
assert res["onyx:hasEmotionCategory"] == "c3"
|
||||||
|
|
||||||
|
e = Emotion()
|
||||||
|
e["X-dimension"] = -0.2
|
||||||
|
e["Y-dimension"] = 0.3
|
||||||
|
res = c._backwards_conversion(e)
|
||||||
|
assert res["onyx:hasEmotionCategory"] == "c2"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
from senpy.plugins import EmotionPlugin
|
from senpy.plugins import EmotionPlugin
|
||||||
from senpy.models import EmotionSet, Emotion
|
from senpy.models import EmotionSet, Emotion, Entry
|
||||||
|
|
||||||
|
|
||||||
class RmoRandPlugin(EmotionPlugin):
|
class RmoRandPlugin(EmotionPlugin):
|
||||||
@ -16,3 +16,11 @@ class RmoRandPlugin(EmotionPlugin):
|
|||||||
emotionSet.prov__wasGeneratedBy = self.id
|
emotionSet.prov__wasGeneratedBy = self.id
|
||||||
entry.emotions.append(emotionSet)
|
entry.emotions.append(emotionSet)
|
||||||
yield entry
|
yield entry
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
params = dict()
|
||||||
|
results = list()
|
||||||
|
for i in range(100):
|
||||||
|
res = next(self.analyse_entry(Entry(nif__isString="Hello"), params))
|
||||||
|
res.validate()
|
||||||
|
results.append(res.emotions[0]['onyx:hasEmotion'][0]['onyx:hasEmotionCategory'])
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
from senpy.plugins import SentimentPlugin
|
from senpy.plugins import SentimentPlugin
|
||||||
from senpy.models import Sentiment
|
from senpy.models import Sentiment, Entry
|
||||||
|
|
||||||
|
|
||||||
class RandPlugin(SentimentPlugin):
|
class RandPlugin(SentimentPlugin):
|
||||||
@ -22,3 +22,13 @@ class RandPlugin(SentimentPlugin):
|
|||||||
entry.sentiments.append(sentiment)
|
entry.sentiments.append(sentiment)
|
||||||
entry.language = lang
|
entry.language = lang
|
||||||
yield entry
|
yield entry
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
params = dict()
|
||||||
|
results = list()
|
||||||
|
for i in range(100):
|
||||||
|
res = next(self.analyse_entry(Entry(nif__isString="Hello"), params))
|
||||||
|
res.validate()
|
||||||
|
results.append(res.sentiments[0]['marl:hasPolarity'])
|
||||||
|
assert 'marl:Positive' in results
|
||||||
|
assert 'marl:Negative' in results
|
||||||
|
@ -12,7 +12,7 @@ class Sentiment140Plugin(SentimentPlugin):
|
|||||||
json.dumps({
|
json.dumps({
|
||||||
"language": lang,
|
"language": lang,
|
||||||
"data": [{
|
"data": [{
|
||||||
"text": entry.text
|
"text": entry.nif__isString
|
||||||
}]
|
}]
|
||||||
}))
|
}))
|
||||||
p = params.get("prefix", None)
|
p = params.get("prefix", None)
|
||||||
@ -34,3 +34,20 @@ class Sentiment140Plugin(SentimentPlugin):
|
|||||||
entry.sentiments.append(sentiment)
|
entry.sentiments.append(sentiment)
|
||||||
entry.language = lang
|
entry.language = lang
|
||||||
yield entry
|
yield entry
|
||||||
|
|
||||||
|
test_cases = [
|
||||||
|
{
|
||||||
|
'entry': {
|
||||||
|
'nif:isString': 'I love Titanic'
|
||||||
|
},
|
||||||
|
'params': {},
|
||||||
|
'expected': {
|
||||||
|
"nif:isString": "I love Titanic",
|
||||||
|
'sentiments': [
|
||||||
|
{
|
||||||
|
'marl:hasPolarity': 'marl:Positive',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
@ -20,6 +20,9 @@
|
|||||||
"@id": "me:hasSuggestions",
|
"@id": "me:hasSuggestions",
|
||||||
"@container": "@set"
|
"@container": "@set"
|
||||||
},
|
},
|
||||||
|
"onyx:hasEmotion": {
|
||||||
|
"@container": "@set"
|
||||||
|
},
|
||||||
"emotions": {
|
"emotions": {
|
||||||
"@id": "onyx:hasEmotionSet",
|
"@id": "onyx:hasEmotionSet",
|
||||||
"@container": "@set"
|
"@container": "@set"
|
||||||
|
25
senpy/utils.py
Normal file
25
senpy/utils.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
|
def check_template(indict, template):
|
||||||
|
if isinstance(template, dict) and isinstance(indict, dict):
|
||||||
|
for k, v in template.items():
|
||||||
|
if k not in indict:
|
||||||
|
return '{} not in {}'.format(k, indict)
|
||||||
|
check_template(indict[k], v)
|
||||||
|
elif isinstance(template, list) and isinstance(indict, list):
|
||||||
|
if len(indict) != len(template):
|
||||||
|
raise models.Error('Different size for {} and {}'.format(indict, template))
|
||||||
|
for e in template:
|
||||||
|
found = False
|
||||||
|
for i in indict:
|
||||||
|
try:
|
||||||
|
check_template(i, e)
|
||||||
|
found = True
|
||||||
|
except models.Error as ex:
|
||||||
|
continue
|
||||||
|
if not found:
|
||||||
|
raise models.Error('{} not found in {}'.format(e, indict))
|
||||||
|
else:
|
||||||
|
if indict != template:
|
||||||
|
raise models.Error('{} and {} are different'.format(indict, template))
|
@ -21,3 +21,6 @@ class AsyncPlugin(AnalysisPlugin):
|
|||||||
values = self._do_async(2)
|
values = self._do_async(2)
|
||||||
entry.async_values = values
|
entry.async_values = values
|
||||||
yield entry
|
yield entry
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
pass
|
||||||
|
@ -6,3 +6,6 @@ class DummyPlugin(SentimentPlugin):
|
|||||||
entry.text = entry.text[::-1]
|
entry.text = entry.text[::-1]
|
||||||
entry.reversed = entry.get('reversed', 0) + 1
|
entry.reversed = entry.get('reversed', 0) + 1
|
||||||
yield entry
|
yield entry
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
pass
|
||||||
|
@ -9,3 +9,6 @@ class SleepPlugin(AnalysisPlugin):
|
|||||||
def analyse_entry(self, entry, params):
|
def analyse_entry(self, entry, params):
|
||||||
sleep(float(params.get("timeout", self.timeout)))
|
sleep(float(params.get("timeout", self.timeout)))
|
||||||
yield entry
|
yield entry
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
pass
|
||||||
|
@ -17,17 +17,19 @@ def parse_resp(resp):
|
|||||||
|
|
||||||
|
|
||||||
class BlueprintsTest(TestCase):
|
class BlueprintsTest(TestCase):
|
||||||
def setUp(self):
|
@classmethod
|
||||||
self.app = Flask("test_extensions")
|
def setUpClass(cls):
|
||||||
self.app.debug = False
|
"""Set up only once, and re-use in every individual test"""
|
||||||
self.client = self.app.test_client()
|
cls.app = Flask("test_extensions")
|
||||||
self.senpy = Senpy()
|
cls.app.debug = False
|
||||||
self.senpy.init_app(self.app)
|
cls.client = cls.app.test_client()
|
||||||
self.dir = os.path.join(os.path.dirname(__file__), "..")
|
cls.senpy = Senpy()
|
||||||
self.senpy.add_folder(self.dir)
|
cls.senpy.init_app(cls.app)
|
||||||
self.senpy.activate_plugin("Dummy", sync=True)
|
cls.dir = os.path.join(os.path.dirname(__file__), "..")
|
||||||
self.senpy.activate_plugin("DummyRequired", sync=True)
|
cls.senpy.add_folder(cls.dir)
|
||||||
self.senpy.default_plugin = 'Dummy'
|
cls.senpy.activate_plugin("Dummy", sync=True)
|
||||||
|
cls.senpy.activate_plugin("DummyRequired", sync=True)
|
||||||
|
cls.senpy.default_plugin = 'Dummy'
|
||||||
|
|
||||||
def assertCode(self, resp, code):
|
def assertCode(self, resp, code):
|
||||||
self.assertEqual(resp.status_code, code)
|
self.assertEqual(resp.status_code, code)
|
||||||
|
@ -10,6 +10,7 @@ except ImportError:
|
|||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from senpy.extensions import Senpy
|
from senpy.extensions import Senpy
|
||||||
|
from senpy import plugins
|
||||||
from senpy.models import Error, Results, Entry, EmotionSet, Emotion, Plugin
|
from senpy.models import Error, Results, Entry, EmotionSet, Emotion, Plugin
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
@ -18,7 +19,7 @@ from unittest import TestCase
|
|||||||
class ExtensionsTest(TestCase):
|
class ExtensionsTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.app = Flask('test_extensions')
|
self.app = Flask('test_extensions')
|
||||||
self.dir = os.path.join(os.path.dirname(__file__))
|
self.dir = os.path.dirname(__file__)
|
||||||
self.senpy = Senpy(plugin_folder=self.dir,
|
self.senpy = Senpy(plugin_folder=self.dir,
|
||||||
app=self.app,
|
app=self.app,
|
||||||
default_plugins=False)
|
default_plugins=False)
|
||||||
@ -38,8 +39,8 @@ class ExtensionsTest(TestCase):
|
|||||||
print(self.senpy.plugins)
|
print(self.senpy.plugins)
|
||||||
assert "Dummy" in self.senpy.plugins
|
assert "Dummy" in self.senpy.plugins
|
||||||
|
|
||||||
def test_enabling(self):
|
def test_installing(self):
|
||||||
""" Enabling a plugin """
|
""" Installing a plugin """
|
||||||
info = {
|
info = {
|
||||||
'name': 'TestPip',
|
'name': 'TestPip',
|
||||||
'module': 'dummy',
|
'module': 'dummy',
|
||||||
@ -48,14 +49,13 @@ class ExtensionsTest(TestCase):
|
|||||||
'version': 0
|
'version': 0
|
||||||
}
|
}
|
||||||
root = os.path.join(self.dir, 'plugins', 'dummy_plugin')
|
root = os.path.join(self.dir, 'plugins', 'dummy_plugin')
|
||||||
name, module = self.senpy._load_plugin_from_info(info, root=root)
|
name, module = plugins.load_plugin_from_info(info, root=root)
|
||||||
assert name == 'TestPip'
|
assert name == 'TestPip'
|
||||||
assert module
|
assert module
|
||||||
import noop
|
import noop
|
||||||
dir(noop)
|
dir(noop)
|
||||||
self.senpy.install_deps()
|
|
||||||
|
|
||||||
def test_installing(self):
|
def test_enabling(self):
|
||||||
""" Enabling a plugin """
|
""" Enabling a plugin """
|
||||||
self.senpy.activate_all(sync=True)
|
self.senpy.activate_all(sync=True)
|
||||||
assert len(self.senpy.plugins) >= 3
|
assert len(self.senpy.plugins) >= 3
|
||||||
@ -72,7 +72,7 @@ class ExtensionsTest(TestCase):
|
|||||||
}
|
}
|
||||||
root = os.path.join(self.dir, 'plugins', 'dummy_plugin')
|
root = os.path.join(self.dir, 'plugins', 'dummy_plugin')
|
||||||
with self.assertRaises(Error):
|
with self.assertRaises(Error):
|
||||||
name, module = self.senpy._load_plugin_from_info(info, root=root)
|
name, module = plugins.load_plugin_from_info(info, root=root)
|
||||||
|
|
||||||
def test_disabling(self):
|
def test_disabling(self):
|
||||||
""" Disabling a plugin """
|
""" Disabling a plugin """
|
||||||
@ -173,7 +173,7 @@ class ExtensionsTest(TestCase):
|
|||||||
'onyx:usesEmotionModel': 'emoml:fsre-dimensions'
|
'onyx:usesEmotionModel': 'emoml:fsre-dimensions'
|
||||||
})
|
})
|
||||||
eSet1 = EmotionSet()
|
eSet1 = EmotionSet()
|
||||||
eSet1.prov__wasGeneratedBy = plugin['id']
|
eSet1.prov__wasGeneratedBy = plugin['@id']
|
||||||
eSet1['onyx:hasEmotion'].append(Emotion({
|
eSet1['onyx:hasEmotion'].append(Emotion({
|
||||||
'emoml:arousal': 1,
|
'emoml:arousal': 1,
|
||||||
'emoml:potency': 0,
|
'emoml:potency': 0,
|
||||||
|
@ -7,11 +7,11 @@ import tempfile
|
|||||||
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from senpy.models import Results, Entry, EmotionSet, Emotion
|
from senpy.models import Results, Entry, EmotionSet, Emotion
|
||||||
from senpy.plugins import SentimentPlugin, ShelfMixin
|
from senpy import plugins
|
||||||
from senpy.plugins.conversion.emotion.centroids import CentroidConversion
|
from senpy.plugins.conversion.emotion.centroids import CentroidConversion
|
||||||
|
|
||||||
|
|
||||||
class ShelfDummyPlugin(SentimentPlugin, ShelfMixin):
|
class ShelfDummyPlugin(plugins.SentimentPlugin, plugins.ShelfMixin):
|
||||||
def activate(self, *args, **kwargs):
|
def activate(self, *args, **kwargs):
|
||||||
if 'counter' not in self.sh:
|
if 'counter' not in self.sh:
|
||||||
self.sh['counter'] = 0
|
self.sh['counter'] = 0
|
||||||
@ -33,7 +33,6 @@ class PluginsTest(TestCase):
|
|||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
if os.path.exists(self.shelf_dir):
|
if os.path.exists(self.shelf_dir):
|
||||||
shutil.rmtree(self.shelf_dir)
|
shutil.rmtree(self.shelf_dir)
|
||||||
|
|
||||||
if os.path.isfile(self.shelf_file):
|
if os.path.isfile(self.shelf_file):
|
||||||
os.remove(self.shelf_file)
|
os.remove(self.shelf_file)
|
||||||
|
|
||||||
@ -51,26 +50,29 @@ class PluginsTest(TestCase):
|
|||||||
|
|
||||||
def test_shelf(self):
|
def test_shelf(self):
|
||||||
''' A shelf is created and the value is stored '''
|
''' A shelf is created and the value is stored '''
|
||||||
|
newfile = self.shelf_file + "new"
|
||||||
a = ShelfDummyPlugin(info={
|
a = ShelfDummyPlugin(info={
|
||||||
'name': 'shelve',
|
'name': 'shelve',
|
||||||
'version': 'test',
|
'version': 'test',
|
||||||
'shelf_file': self.shelf_file
|
'shelf_file': newfile
|
||||||
})
|
})
|
||||||
assert a.sh == {}
|
assert a.sh == {}
|
||||||
a.activate()
|
a.activate()
|
||||||
assert a.sh == {'counter': 0}
|
assert a.sh == {'counter': 0}
|
||||||
assert a.shelf_file == self.shelf_file
|
assert a.shelf_file == newfile
|
||||||
|
|
||||||
a.sh['a'] = 'fromA'
|
a.sh['a'] = 'fromA'
|
||||||
assert a.sh['a'] == 'fromA'
|
assert a.sh['a'] == 'fromA'
|
||||||
|
|
||||||
a.save()
|
a.save()
|
||||||
|
|
||||||
sh = pickle.load(open(self.shelf_file, 'rb'))
|
sh = pickle.load(open(newfile, 'rb'))
|
||||||
|
|
||||||
assert sh['a'] == 'fromA'
|
assert sh['a'] == 'fromA'
|
||||||
|
|
||||||
def test_dummy_shelf(self):
|
def test_dummy_shelf(self):
|
||||||
|
with open(self.shelf_file, 'wb') as f:
|
||||||
|
pickle.dump({'counter': 99}, f)
|
||||||
a = ShelfDummyPlugin(info={
|
a = ShelfDummyPlugin(info={
|
||||||
'name': 'DummyShelf',
|
'name': 'DummyShelf',
|
||||||
'shelf_file': self.shelf_file,
|
'shelf_file': self.shelf_file,
|
||||||
@ -80,9 +82,13 @@ class PluginsTest(TestCase):
|
|||||||
|
|
||||||
assert a.shelf_file == self.shelf_file
|
assert a.shelf_file == self.shelf_file
|
||||||
res1 = a.analyse(input=1)
|
res1 = a.analyse(input=1)
|
||||||
assert res1.entries[0].nif__isString == 1
|
assert res1.entries[0].nif__isString == 100
|
||||||
res2 = a.analyse(input=1)
|
a.deactivate()
|
||||||
assert res2.entries[0].nif__isString == 2
|
del a
|
||||||
|
|
||||||
|
with open(self.shelf_file, 'rb') as f:
|
||||||
|
sh = pickle.load(f)
|
||||||
|
assert sh['counter'] == 100
|
||||||
|
|
||||||
def test_corrupt_shelf(self):
|
def test_corrupt_shelf(self):
|
||||||
''' Reusing the values of a previous shelf '''
|
''' Reusing the values of a previous shelf '''
|
||||||
@ -202,3 +208,22 @@ class PluginsTest(TestCase):
|
|||||||
e["Y-dimension"] = 0.3
|
e["Y-dimension"] = 0.3
|
||||||
res = c._backwards_conversion(e)
|
res = c._backwards_conversion(e)
|
||||||
assert res["onyx:hasEmotionCategory"] == "c2"
|
assert res["onyx:hasEmotionCategory"] == "c2"
|
||||||
|
|
||||||
|
|
||||||
|
def make_mini_test(plugin):
|
||||||
|
def mini_test(self):
|
||||||
|
plugin.test()
|
||||||
|
return mini_test
|
||||||
|
|
||||||
|
|
||||||
|
def add_tests():
|
||||||
|
root = os.path.dirname(__file__)
|
||||||
|
plugs = plugins.load_plugins(os.path.join(root, ".."))
|
||||||
|
for k, v in plugs.items():
|
||||||
|
t_method = make_mini_test(v)
|
||||||
|
t_method.__name__ = 'test_plugin_{}'.format(k)
|
||||||
|
setattr(PluginsTest, t_method.__name__, t_method)
|
||||||
|
del t_method
|
||||||
|
|
||||||
|
|
||||||
|
add_tests()
|
||||||
|
Loading…
Reference in New Issue
Block a user