1
0
mirror of https://github.com/gsi-upm/senpy synced 2025-09-17 03:52:22 +00:00

Compare commits

..

20 Commits
0.6.1 ... nacho

Author SHA1 Message Date
NachoCP
0c54370e28 Docs updated 2016-03-18 13:05:51 +01:00
NachoCP
818b4599a9 Update demo.rst 2016-03-17 11:42:49 +01:00
NachoCP
25ac0220e4 Update usage.rst 2016-03-17 10:45:14 +01:00
NachoCP
c22f825fef Update demo.rst 2016-03-17 10:27:46 +01:00
NachoCP
38a3ce4cae Added files via upload 2016-03-17 10:27:17 +01:00
NachoCP
61739a1903 Delete senpy-demo.png 2016-03-17 10:26:47 +01:00
NachoCP
10ed836f2f Added files via upload 2016-03-17 10:26:36 +01:00
NachoCP
e861054cbc Delete senpy-demo.png 2016-03-17 10:26:12 +01:00
NachoCP
d93a7f7973 Update demo.rst 2016-03-17 10:25:26 +01:00
NachoCP
14459847ac Added files via upload 2016-03-17 10:24:50 +01:00
NachoCP
ce275d6c4f Delete senpy-demo.png 2016-03-17 10:24:42 +01:00
NachoCP
b34e0e65a6 Added files via upload 2016-03-17 10:21:44 +01:00
NachoCP
a8ab5bd908 Update index.rst 2016-03-17 09:52:35 +01:00
NachoCP
f8e8ec19e8 Create demo.rst 2016-03-17 09:52:23 +01:00
NachoCP
e83a397588 Update plugins.rst 2016-03-17 09:43:25 +01:00
NachoCP
382b8b0a50 Update plugins.rst 2016-03-17 09:39:33 +01:00
NachoCP
7f0d76665a Update plugins.rst 2016-03-16 19:44:01 +01:00
NachoCP
fe4ee87813 Update usage.rst 2016-03-16 18:03:32 +01:00
NachoCP
23796b81af Update usage.rst 2016-03-16 17:48:19 +01:00
NachoCP
69296a21cb Update usage.rst 2016-03-16 17:47:18 +01:00
21 changed files with 77 additions and 225 deletions

3
.gitignore vendored
View File

@@ -4,5 +4,4 @@
dist
build
README.html
__pycache__
Dockerfile-*
__pycache__

View File

@@ -1,8 +1,6 @@
language: python
python:
- "2.7"
- "3.4"
- "3.5"
install: "pip install -r requirements.txt"
# run nosetests - Tests
script: nosetests

View File

@@ -1,5 +0,0 @@
from python:{{PYVERSION}}-onbuild
RUN pip install .
ENTRYPOINT ["python", "-m", "senpy", "-f", ".", "--host", "0.0.0.0"]

View File

@@ -2,7 +2,6 @@ include requirements.txt
include test-requirements.txt
include README.md
include senpy/context.jsonld
include senpy/VERSION
graft senpy/plugins
graft senpy/schemas
graft senpy/templates

View File

@@ -1,57 +0,0 @@
PYVERSIONS=3.4 2.7
PYMAIN=$(firstword $(PYVERSIONS))
NAME=senpy
REPO=gsiupm
VERSION=$(shell cat $(NAME)/VERSION)
all: build run
dockerfiles: $(addprefix Dockerfile-,$(PYVERSIONS))
Dockerfile-%: Dockerfile.template
sed "s/{{PYVERSION}}/$*/" Dockerfile.template > Dockerfile-$*
build: $(addprefix build-, $(PYMAIN))
buildall: $(addprefix build-, $(PYVERSIONS))
build-%: Dockerfile-%
docker build -t '$(REPO)/$(NAME):$(VERSION)-python$*' -f Dockerfile-$* .;
test: $(addprefix test-,$(PYMAIN))
testall: $(addprefix test-,$(PYVERSIONS))
test-%: build-%
docker run --rm -w /usr/src/app/ --entrypoint=/usr/local/bin/python -ti '$(REPO)/$(NAME):$(VERSION)-python$*' setup.py test --addopts "-vvv -s --pdb" ;
pip_test-%:
docker run --rm -ti python:$* pip install senpy ;
upload-%: test-%
docker push '$(REPO)/$(NAME):$(VERSION)-python$(PYMAIN)'
upload: testall $(addprefix upload-,$(PYVERSIONS))
docker tag '$(REPO)/$(NAME):$(VERSION)-python$(PYMAIN)' '$(REPO)/$(NAME):$(VERSION)'
docker tag '$(REPO)/$(NAME):$(VERSION)-python$(PYMAIN)' '$(REPO)/$(NAME)'
docker push '$(REPO)/$(NAME):$(VERSION)'
clean:
@docker ps -a | awk '/$(REPO)\/$(NAME)/{ split($$2, vers, "-"); if(vers[1] != "${VERSION}"){ print $$1;}}' | xargs docker rm 2>/dev/null|| true
@docker images | awk '/$(REPO)\/$(NAME)/{ split($$2, vers, "-"); if(vers[1] != "${VERSION}"){ print $$1":"$$2;}}' | xargs docker rmi 2>/dev/null|| true
upload_git:
git commit -a
git tag ${VERSION}
git push --tags origin master
pip_upload:
python setup.py sdist upload ;
pip_test: $(addprefix pip_test-,$(PYVERSIONS))
run: build
docker run --rm -p 5000:5000 -ti '$(REPO)/$(NAME):$(VERSION)-python$(PYMAIN)'
.PHONY: test test-% build-% build test test_pip run

View File

@@ -30,7 +30,7 @@ Alternatively, you can use the development version:
.. code:: bash
git clone http://github.com/gsi-upm/senpy
git clone git@github.com:gsi-upm/senpy
cd senpy
pip install --user .
@@ -38,9 +38,9 @@ If you want to install senpy globally, use sudo instead of the ``--user`` flag.
Docker Image
************
Build the image or use the pre-built one: ``docker run -ti -p 5000:5000 gsiupm/senpy --host 0.0.0.0 --default-plugins``.
Build the image or use the pre-built one: ``docker run -ti -p 5000:5000 balkian/senpy --host 0.0.0.0 --default-plugins``.
To add custom plugins, add a volume and tell senpy where to find the plugins: ``docker run -ti -p 5000:5000 -v <PATH OF PLUGINS>:/plugins gsiupm/senpy --host 0.0.0.0 --default-plugins -f /plugins``
To add custom plugins, add a volume and tell senpy where to find the plugins: ``docker run -ti -p 5000:5000 -v <PATH OF PLUGINS>:/plugins balkian/senpy --host 0.0.0.0 --default-plugins -f /plugins``
Usage
-----

View File

@@ -1 +0,0 @@
../../senpy/schemas/

View File

@@ -52,17 +52,16 @@ master_doc = 'index'
# General information about the project.
project = u'Senpy'
copyright = u'2016, J. Fernando Sánchez'
copyright = u'2015, J. Fernando Sánchez'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
with open('../senpy/VERSION') as f:
version = f.read().strip()
version = '0.4'
# The full version, including alpha/beta/rc tags.
release = version
release = '0.4'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@@ -22,6 +22,6 @@ If you want to install senpy globally, use sudo instead of the ``--user`` flag.
Docker Image
************
Build the image or use the pre-built one: ``docker run -ti -p 5000:5000 gsiupm/senpy --host 0.0.0.0 --default-plugins``.
Build the image or use the pre-built one: ``docker run -ti -p 5000:5000 balkian/senpy --host 0.0.0.0 --default-plugins``.
To add custom plugins, add a volume and tell senpy where to find the plugins: ``docker run -ti -p 5000:5000 -v <PATH OF PLUGINS>:/plugins gsiupm/senpy --host 0.0.0.0 --default-plugins -f /plugins``
To add custom plugins, add a volume and tell senpy where to find the plugins: ``docker run -ti -p 5000:5000 -v <PATH OF PLUGINS>:/plugins balkian/senpy --host 0.0.0.0 --default-plugins -f /plugins``

View File

@@ -2,8 +2,6 @@ Developing new plugins
----------------------
Each plugin represents a different analysis process.There are two types of files that are needed by senpy for loading a plugin:
Plugins Interface
=======
- Definition file, has the ".senpy" extension.
- Code file, is a python file.
@@ -36,7 +34,7 @@ The basic methods in a plugin are:
* __init__
* activate: used to load memory-hungry resources
* deactivate: used to free up resources
* analyse: called in every user requests. It takes in the parameters supplied by a user and should return a senpy Response.
* analyse: called in every user requests. It takes in the parameters supplied by a user and should return a senpy Results.
Plugins are loaded asynchronously, so don't worry if the activate method takes too long. The plugin will be marked as activated once it is finished executing the method.

View File

@@ -1,6 +1,6 @@
Schema Examples
===============
All the examples in this page use the :download:`the main schema <_static/schemas/definitions.json>`.
All the examples in this page use the schema defined in :ref:`schema`.
Simple NIF annotation
---------------------

View File

@@ -4,8 +4,8 @@ requests>=2.4.1
GitPython>=0.3.2.RC1
gevent>=1.1rc4
PyLD>=0.6.5
Flask-Testing>=0.4.2
six
future
jsonschema
jsonref
PyYAML

View File

@@ -1 +0,0 @@
0.6.1

View File

@@ -18,8 +18,4 @@
Sentiment analysis server in Python
"""
import os
VFILE = os.path.join(os.path.dirname(__file__), "VERSION")
with open(VFILE, 'r') as f:
__version__ = f.read().strip()
__version__ = "0.5.5"

View File

@@ -65,11 +65,6 @@ def main():
type=str,
default='plugins',
help='Where to look for plugins.')
parser.add_argument('--only-install',
'-i',
action='store_true',
default=False,
help='Do not run a server, only install the dependencies of the plugins.')
args = parser.parse_args()
logging.basicConfig()
rl = logging.getLogger()
@@ -77,9 +72,6 @@ def main():
app = Flask(__name__)
app.debug = args.debug
sp = Senpy(app, args.plugins_folder, default_plugins=args.default_plugins)
if args.only_install:
sp.install_deps()
return
sp.activate_all()
http_server = WSGIServer((args.host, args.port), app)
try:

View File

@@ -22,8 +22,7 @@ import imp
import logging
import traceback
import gevent
import yaml
import pip
import json
logger = logging.getLogger(__name__)
@@ -199,38 +198,18 @@ class Senpy(object):
logger.error('Error reloading {}: {}'.format(name, ex))
self.plugins[name] = plugin
@classmethod
def validate_info(cls, info):
return all(x in info for x in ('name', 'module', 'version'))
def install_deps(self):
for i in self.plugins.values():
self._install_deps(i._info)
@classmethod
def _install_deps(cls, info=None):
requirements = info.get('requirements', [])
if requirements:
pip_args = []
pip_args.append('install')
for req in requirements:
pip_args.append( req )
logger.info('Installing requirements: ' + str(requirements))
pip.main(pip_args)
@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
@staticmethod
def _load_plugin(root, filename):
logger.debug("Loading plugin: {}".format(filename))
fpath = os.path.join(root, filename)
with open(fpath, 'r') as f:
info = json.load(f)
logger.debug("Info: {}".format(info))
sys.path.append(root)
module = info["module"]
name = info["name"]
requirements = info.get("requirements", [])
sys.path.append(root)
(fp, pathname, desc) = imp.find_module(module, [root, ])
try:
cls._install_deps(info)
tmp = imp.load_module(module, fp, pathname, desc)
sys.path.remove(root)
candidate = None
@@ -242,30 +221,20 @@ class Senpy(object):
candidate = obj
break
if not candidate:
logger.debug("No valid plugin for: {}".format(module))
logger.debug("No valid plugin for: {}".format(filename))
return
module = candidate(info=info)
repo_path = root
module._repo = Repo(repo_path)
except InvalidGitRepositoryError:
logger.debug("The plugin {} is not in a Git repository".format(module))
module._repo = None
try:
repo_path = root
module._repo = Repo(repo_path)
except InvalidGitRepositoryError:
module._repo = None
except Exception as ex:
logger.error("Exception importing {}: {}".format(module, ex))
logger.error("Exception importing {}: {}".format(filename, ex))
logger.error("Trace: {}".format(traceback.format_exc()))
return None, None
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:

View File

@@ -91,4 +91,4 @@ class ShelfMixin(object):
if hasattr(self, '_sh') and self._sh is not None:
with open(self.shelf_file, 'wb') as f:
pickle.dump(self._sh, f)
del(self.__dict__['_sh'])
del(self.__dict__['_sh'])

View File

@@ -15,8 +15,7 @@ except AttributeError:
install_reqs = [str(ir.req) for ir in install_reqs]
test_reqs = [str(ir.req) for ir in test_reqs]
with open('senpy/VERSION') as f:
__version__ = f.read().strip()
exec(open('senpy/__init__.py').read())
setup(
name='senpy',

View File

@@ -1,10 +1,9 @@
import os
import logging
import json
from senpy.extensions import Senpy
from flask import Flask
from unittest import TestCase
from flask.ext.testing import TestCase
from gevent import sleep
from itertools import product
@@ -12,38 +11,31 @@ from itertools import product
def check_dict(indic, template):
return all(item in indic.items() for item in template.items())
def parse_resp(resp):
return json.loads(resp.data.decode('utf-8'))
class BlueprintsTest(TestCase):
def setUp(self):
def create_app(self):
self.app = Flask("test_extensions")
self.client = self.app.test_client()
self.senpy = Senpy()
self.senpy.init_app(self.app)
self.dir = os.path.join(os.path.dirname(__file__), "..")
self.senpy.add_folder(self.dir)
self.senpy.activate_plugin("Dummy", sync=True)
return self.app
def assertCode(self, resp, code):
self.assertEqual(resp.status_code, code)
def test_home(self):
"""
Calling with no arguments should ask the user for more arguments
"""
resp = self.client.get("/api/")
self.assertCode(resp, 404)
js = parse_resp(resp)
logging.debug(js)
assert js["status"] == 404
self.assert404(resp)
logging.debug(resp.json)
assert resp.json["status"] == 404
atleast = {
"status": 404,
"message": "Missing or invalid parameters",
}
assert check_dict(js, atleast)
assert check_dict(resp.json, atleast)
def test_analysis(self):
"""
@@ -51,93 +43,81 @@ class BlueprintsTest(TestCase):
it should contain the context
"""
resp = self.client.get("/api/?i=My aloha mohame")
self.assertCode(resp, 200)
js = parse_resp(resp)
logging.debug("Got response: %s", js)
assert "@context" in js
assert "entries" in js
self.assert200(resp)
logging.debug("Got response: %s", resp.json)
assert "@context" in resp.json
assert "entries" in resp.json
def test_list(self):
""" List the plugins """
resp = self.client.get("/api/plugins/")
self.assertCode(resp, 200)
js = parse_resp(resp)
logging.debug(js)
assert 'plugins' in js
plugins = js['plugins']
self.assert200(resp)
logging.debug(resp.json)
assert 'plugins' in resp.json
plugins = resp.json['plugins']
assert len(plugins) > 1
assert list(p for p in plugins if p['name'] == "Dummy")
assert "@context" in js
assert "@context" in resp.json
def test_headers(self):
for i, j in product(["/api/plugins/?nothing=", "/api/?i=test&"],
["inHeaders"]):
resp = self.client.get("%s" % (i))
js = parse_resp(resp)
assert "@context" in js
assert "@context" in resp.json
resp = self.client.get("%s&%s=0" % (i, j))
js = parse_resp(resp)
assert "@context" in js
assert "@context" in resp.json
resp = self.client.get("%s&%s=1" % (i, j))
js = parse_resp(resp)
assert "@context" not in js
assert "@context" not in resp.json
resp = self.client.get("%s&%s=true" % (i, j))
js = parse_resp(resp)
assert "@context" not in js
assert "@context" not in resp.json
def test_detail(self):
""" Show only one plugin"""
resp = self.client.get("/api/plugins/Dummy/")
self.assertCode(resp, 200)
js = parse_resp(resp)
logging.debug(js)
assert "@id" in js
assert js["@id"] == "Dummy_0.1"
self.assert200(resp)
logging.debug(resp.json)
assert "@id" in resp.json
assert resp.json["@id"] == "Dummy_0.1"
def test_activate(self):
""" Activate and deactivate one plugin """
resp = self.client.get("/api/plugins/Dummy/deactivate")
self.assertCode(resp, 200)
self.assert200(resp)
sleep(0.5)
resp = self.client.get("/api/plugins/Dummy/")
self.assertCode(resp, 200)
js = parse_resp(resp)
assert "is_activated" in js
assert js["is_activated"] == False
self.assert200(resp)
assert "is_activated" in resp.json
assert resp.json["is_activated"] == False
resp = self.client.get("/api/plugins/Dummy/activate")
self.assertCode(resp, 200)
self.assert200(resp)
sleep(0.5)
resp = self.client.get("/api/plugins/Dummy/")
self.assertCode(resp, 200)
js = parse_resp(resp)
assert "is_activated" in js
assert js["is_activated"] == True
self.assert200(resp)
assert "is_activated" in resp.json
assert resp.json["is_activated"] == True
def test_default(self):
""" Show only one plugin"""
resp = self.client.get("/api/plugins/default/")
self.assertCode(resp, 200)
js = parse_resp(resp)
logging.debug(js)
assert "@id" in js
assert js["@id"] == "Dummy_0.1"
self.assert200(resp)
logging.debug(resp.json)
assert "@id" in resp.json
assert resp.json["@id"] == "Dummy_0.1"
resp = self.client.get("/api/plugins/Dummy/deactivate")
self.assertCode(resp, 200)
self.assert200(resp)
sleep(0.5)
resp = self.client.get("/api/plugins/default/")
self.assertCode(resp, 404)
self.assert404(resp)
def test_context(self):
resp = self.client.get("/api/contexts/context.jsonld")
self.assertCode(resp, 200)
js = parse_resp(resp)
assert "@context" in js
self.assert200(resp)
assert "@context" in resp.json
assert check_dict(
js["@context"],
resp.json["@context"],
{"marl": "http://www.gsi.dit.upm.es/ontologies/marl/ns#"})
def test_schema(self):
resp = self.client.get("/api/schemas/definitions.json")
self.assertCode(resp, 200)
js = parse_resp(resp)
assert "$schema" in js
self.assert200(resp)
assert "$schema" in resp.json

View File

@@ -6,17 +6,18 @@ from functools import partial
from senpy.extensions import Senpy
from senpy.models import Error
from flask import Flask
from unittest import TestCase
from flask.ext.testing import TestCase
class ExtensionsTest(TestCase):
def setUp(self):
def create_app(self):
self.app = Flask("test_extensions")
self.dir = os.path.join(os.path.dirname(__file__))
self.senpy = Senpy(plugin_folder=self.dir, default_plugins=False)
self.senpy.init_app(self.app)
self.senpy.activate_plugin("Dummy", sync=True)
return self.app
def test_init(self):
""" Initialising the app with the extension. """
@@ -33,20 +34,6 @@ class ExtensionsTest(TestCase):
assert "Dummy" in self.senpy.plugins
def test_enabling(self):
""" Enabling a plugin """
info = {
'name': 'TestPip',
'module': 'dummy',
'requirements': ['noop'],
'version': 0
}
root = os.path.join(self.dir, 'dummy_plugin')
name, module = self.senpy._load_plugin_from_info(info, root=root)
assert name == 'TestPip'
assert module
import noop
def test_installing(self):
""" Enabling a plugin """
self.senpy.activate_all(sync=True)
assert len(self.senpy.plugins) == 2