Skip to content

Commit

Permalink
Merge branch 'master' of github.com:colab/colab
Browse files Browse the repository at this point in the history
  • Loading branch information
seocam committed Aug 18, 2015
2 parents fe35cec + 60aef4a commit fd74767
Show file tree
Hide file tree
Showing 22 changed files with 349 additions and 45 deletions.
5 changes: 5 additions & 0 deletions colab/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from __future__ import absolute_import

# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app # noqa
7 changes: 0 additions & 7 deletions colab/celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,6 @@
app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

app.conf.update(
CELERY_RESULT_BACKEND='djcelery.backends.database:DatabaseBackend',
)
app.conf.update(
CELERY_RESULT_BACKEND='djcelery.backends.cache:CacheBackend',
)


@app.task(bind=True)
def debug_task(self):
Expand Down
3 changes: 3 additions & 0 deletions colab/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

class ColabException(Exception):
"""Base class for all exceptions raised by Colab"""
3 changes: 0 additions & 3 deletions colab/home/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,3 @@

class HomeConfig(AppConfig):
name = 'colab.home'

def ready(self):
from ..celery import app # noqa
2 changes: 2 additions & 0 deletions colab/plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

default_app_config = 'colab.plugins.apps.PluginAppConfig'
12 changes: 12 additions & 0 deletions colab/plugins/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

from django.apps import AppConfig

from .utils.signals import connect_signal, register_signal


class PluginAppConfig(AppConfig):
name = 'colab.plugins'

def ready(self):
register_signal()
connect_signal()
11 changes: 11 additions & 0 deletions colab/plugins/gitlab/apps.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@

from ..utils.apps import ColabProxiedAppConfig
from colab.plugins.gitlab.tasks import handling_method
from colab.signals.signals import register_signal, connect_signal


class ProxyGitlabAppConfig(ColabProxiedAppConfig):
name = 'colab.plugins.gitlab'
verbose_name = 'Gitlab Plugin'
short_name = 'gitlab'

signals_list = ['gitlab_create_project']

def register_signal(self):
register_signal(self.short_name, self.signals_list)

def connect_signal(self):
connect_signal(self.signals_list[0], self.short_name, handling_method)
10 changes: 10 additions & 0 deletions colab/plugins/gitlab/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

from colab.celery import app


@app.task(bind=True)
def handling_method(self, **kwargs):
f = open('/vagrant/test_plugin', 'wb')
f.write(str(kwargs))
f.close()
return 5
6 changes: 6 additions & 0 deletions colab/plugins/utils/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@

class ColabProxiedAppConfig(AppConfig):
colab_proxied_app = True

def register_signals(self):
pass

def connect_signals(self):
pass
22 changes: 22 additions & 0 deletions colab/plugins/utils/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

from django.apps import apps


def _init_signals(method_name):
for app in apps.get_app_configs():
# Try to get the method with `method_name`.
# If it exists call it using `app` as the first parameter.
# This is required because methods take `self` as first
# parameter and as we are calling it as a function python
# won't send it explicitly.
# If the method doesn't exist we return a dummy function that
# won't do anything.
getattr(app, method_name, lambda: None)()


def register_signal():
_init_signals('register_signal')


def connect_signal():
_init_signals('connect_signal')
Empty file.
20 changes: 20 additions & 0 deletions colab/plugins/utils/tests/test_signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from django.test import TestCase
from mock import patch, MagicMock
from colab.plugins.utils import signals


class SignalsTest(TestCase):
@patch("colab.plugins.utils.signals.apps.get_app_configs")
def test_init_signals(self, mock_app):
method_name = 'test'

app_mock = MagicMock()

apps_list = ['a', 'b', app_mock]

mock_app.return_value = apps_list
signals._init_signals(method_name)

app_mock.test.assert_called_with()
self.assertEqual(1, app_mock.test.call_count)
self.assertTrue(app_mock.test.called)
4 changes: 2 additions & 2 deletions colab/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
'haystack',
'hitcounter',
'taggit',
'djcelery',

# Own apps
'colab.home',
Expand All @@ -57,6 +56,7 @@
'colab.search',
'colab.tz',
'colab.utils',
'colab.signals',
)

ROOT_URLCONF = 'colab.urls'
Expand Down Expand Up @@ -253,8 +253,8 @@ def get_env_setting(setting):

SOCIAL_NETWORK_ENABLED = locals().get('SOCIAL_NETWORK_ENABLED') or False

locals().update(conf.load_colab_apps())
locals().update(conf.load_py_settings())
locals().update(conf.load_colab_apps())

COLAB_APPS = locals().get('COLAB_APPS') or {}
PROXIED_APPS = {}
Expand Down
Empty file added colab/signals/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions colab/signals/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

from ..exceptions import ColabException


class SignalDoesNotExist(ColabException):
"""Expcetion raised when signal does not exist"""
47 changes: 47 additions & 0 deletions colab/signals/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@

from django.dispatch import Signal

from .exceptions import SignalDoesNotExist

registered_signals = {}
signal_instances = {}


class ColabSignal(Signal):
def __reduce__(self):
"""
In order to send a signal to a celery task, it is necessary to pickle
the objects that will be used as parameters. However,
django.dispatch.Signal has an instance of threading.Lock, which is an
object that cannot be pickled. Therefore, this function changes the
pickle behaviour of Signal, making that only the providind_args of
Signal to be pickled."""

return (ColabSignal, (self.providing_args,))


def register_signal(plugin_name, list_signals):
for signal in list_signals:
if signal in registered_signals:
if plugin_name not in registered_signals[signal]:
registered_signals[signal].append(plugin_name)
else:
registered_signals[signal] = []
registered_signals[signal].append(plugin_name)
signal_instances[signal] = ColabSignal()


def connect_signal(signal_name, sender, handling_method):
if signal_name in signal_instances:
signal_instances[signal_name].connect(handling_method.delay,
sender=sender)
else:
raise SignalDoesNotExist


def send(signal_name, sender, **kwargs):
if signal_name in signal_instances:
signal_instances[signal_name].send(sender=sender, **kwargs)
else:
raise SignalDoesNotExist
Empty file added colab/signals/tests/__init__.py
Empty file.
76 changes: 76 additions & 0 deletions colab/signals/tests/test_signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""
Test Signals class.
Objective: Test parameters, and behavior.
"""

from django.test import TestCase

from mock import patch, MagicMock, PropertyMock

from ..signals import registered_signals, register_signal, connect_signal, send
from ..exceptions import SignalDoesNotExist


class SignalsTest(TestCase):

def setUp(self):
self.list_signal = ['a', 'b', 'c']
self.plugin_name = 'test_signal'

def test_register_signal_(self):
register_signal(self.plugin_name, self.list_signal)
signal_name = 'a'
signal_list = ['test_signal']
self.assertEqual(len(registered_signals[signal_name]), 1)
self.assertEqual(registered_signals[signal_name], signal_list)

def test_register_signal_already_registered(self):
signal_name = 'a'
signal_list = ['test_signal']

register_signal(self.plugin_name, self.list_signal)
self.assertEqual(len(registered_signals[signal_name]), 1)

register_signal(self.plugin_name, self.list_signal)
self.assertEqual(len(registered_signals[signal_name]), 1)
self.assertEqual(registered_signals[signal_name], signal_list)

def test_connect_non_registered_signal(self):
sender = 'Test'
handling_method = 'Test'
signal_name = 'Test'

self.assertRaises(SignalDoesNotExist, connect_signal, signal_name,
sender, handling_method)

@patch('colab.signals.signals.Signal.connect')
def test_connect_already_registered_signal(self, mock):
sender = 'Test'
handling_method = MagicMock()
type(handling_method).delay = PropertyMock(return_value='Test')
signal_name = 'a'

register_signal(self.plugin_name, self.list_signal)

connect_signal(signal_name, sender, handling_method)
args, kwargs = mock.call_args

self.assertEqual(args[0], handling_method.delay)
self.assertEqual(kwargs['sender'], sender)
self.assertTrue(mock.is_called)

@patch('colab.signals.signals.Signal.send')
def test_send_signal(self, mock):
sender = 'Test'
signal_name = 'a'

register_signal(self.plugin_name, self.list_signal)
send(signal_name, sender)

args, kwargs = mock.call_args

self.assertEqual(kwargs['sender'], sender)
self.assertTrue(mock.is_called)

def test_send_signal_not_registered(self):
self.assertRaises(SignalDoesNotExist, send, 'test_signal', 'test')

0 comments on commit fd74767

Please sign in to comment.