-
Notifications
You must be signed in to change notification settings - Fork 365
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
299 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
In one terminal, run: | ||
|
||
./manage.py run_huey | ||
|
||
In another terminal: | ||
|
||
./manage.py shell | ||
|
||
Commands to try out: | ||
|
||
from test_app.commands import count_beans | ||
res = count_beans(500) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import logging | ||
|
||
INSTALLED_APPS = [ | ||
'huey.contrib.djhuey', | ||
'djangoex.test_app', | ||
] | ||
|
||
HUEY = { | ||
'name': 'test-django', | ||
'consumer': { | ||
'loglevel': logging.DEBUG, | ||
'workers': 2, | ||
'scheduler_interval': 5, | ||
}, | ||
} | ||
|
||
SECRET_KEY = 'foo' |
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import random | ||
from huey.contrib.djhuey import task, periodic_task, crontab, db_task | ||
|
||
|
||
@task() | ||
def count_beans(number): | ||
print('-- counted %s beans --' % number) | ||
return 'Counted %s beans' % number | ||
|
||
@periodic_task(crontab(minute='*/5')) | ||
def every_five_mins(): | ||
print('Every five minutes this will be printed by the consumer') | ||
|
||
@task(retries=3, retry_delay=10) | ||
def try_thrice(): | ||
if random.randint(1, 3) == 1: | ||
print('OK') | ||
else: | ||
print('About to fail, will retry in 10 seconds') | ||
raise Exception('Crap something went wrong') | ||
|
||
@db_task() | ||
def foo(number): | ||
print('foo(%s)' % number) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from django.conf.urls import patterns | ||
|
||
urlpatterns = patterns('', | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#!/usr/bin/env python | ||
import os | ||
import sys | ||
|
||
if __name__ == "__main__": | ||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoex.settings") | ||
|
||
from django.core.management import execute_from_command_line | ||
|
||
execute_from_command_line(sys.argv) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
from functools import wraps | ||
import sys | ||
|
||
from django.conf import settings | ||
from django.db import connection | ||
|
||
from huey import crontab | ||
from huey import RedisHuey | ||
from huey.utils import load_class | ||
|
||
|
||
configuration_message = """ | ||
Configuring Huey for use with Django | ||
==================================== | ||
Huey was designed to be simple to configure in the general case. For that | ||
reason, huey will "just work" with no configuration at all provided you have | ||
Redis installed and running locally. | ||
On the other hand, you can configure huey manually using the following | ||
setting structure. | ||
The following example uses Redis on localhost, and will run four worker | ||
processes: | ||
HUEY = { | ||
'name': 'my-app', | ||
'connection': {'host': 'localhost', 'port': 6379}, | ||
'consumer': { | ||
'workers': 4, | ||
'worker_type': 'process', # "thread" or "greenlet" are other options | ||
}, | ||
} | ||
If you would like to configure Huey's logger using Django's integrated logging | ||
settings, the logger used by consumer is named "huey.consumer". | ||
Alternatively you can simply assign `settings.HUEY` to an actual `Huey` | ||
object instance: | ||
from huey import RedisHuey | ||
HUEY = RedisHuey('my-app') | ||
""" | ||
|
||
def default_queue_name(): | ||
try: | ||
return settings.DATABASE_NAME | ||
except AttributeError: | ||
try: | ||
return settings.DATABASES['default']['NAME'] | ||
except KeyError: | ||
return 'huey' | ||
|
||
def config_error(msg): | ||
print(configuration_message) | ||
print('\n\n') | ||
print(msg) | ||
sys.exit(1) | ||
|
||
HUEY = getattr(settings, 'HUEY', None) | ||
if HUEY is None: | ||
try: | ||
from huey import RedisHuey | ||
except ImportError: | ||
config_error('Error: Huey could not import the redis backend. ' | ||
'Install `redis-py`.') | ||
else: | ||
HUEY = RedisHuey(default_queue_name()) | ||
|
||
if isinstance(HUEY, dict): | ||
huey_config = HUEY.copy() # Operate on a copy. | ||
name = huey_config.pop('name', default_queue_name()) | ||
conn_kwargs = huey_config.pop('connection', {}) | ||
try: | ||
del huey_config['consumer'] # Don't need consumer opts here. | ||
except KeyError: | ||
pass | ||
if 'always_eager' not in huey_config: | ||
huey_config['always_eager'] = settings.DEBUG | ||
huey_config.update(conn_kwargs) | ||
HUEY = RedisHuey(name, **huey_config) | ||
|
||
task = HUEY.task | ||
periodic_task = HUEY.periodic_task | ||
|
||
def close_db(fn): | ||
"""Decorator to be used with tasks that may operate on the database.""" | ||
@wraps(fn) | ||
def inner(*args, **kwargs): | ||
try: | ||
return fn(*args, **kwargs) | ||
finally: | ||
connection.close() | ||
return inner | ||
|
||
def db_task(*args, **kwargs): | ||
def decorator(fn): | ||
return task(*args, **kwargs)(close_db(fn)) | ||
return decorator | ||
|
||
def db_periodic_task(*args, **kwargs): | ||
def decorator(fn): | ||
return periodic_task(*args, **kwargs)(close_db(fn)) | ||
return decorator |
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import imp | ||
import sys | ||
from importlib import import_module | ||
from optparse import make_option | ||
|
||
from django.conf import settings | ||
from django.core.management.base import BaseCommand | ||
|
||
try: | ||
from django.apps import apps as django_apps | ||
HAS_DJANGO_APPS = True | ||
except ImportError: | ||
# Django 1.6 | ||
HAS_DJANGO_APPS = False | ||
|
||
from huey.consumer import Consumer | ||
from huey.bin.huey_consumer import get_loglevel | ||
from huey.bin.huey_consumer import setup_logger | ||
|
||
|
||
class Command(BaseCommand): | ||
""" | ||
Queue consumer. Example usage:: | ||
To start the consumer (note you must export the settings module): | ||
django-admin.py run_huey | ||
""" | ||
help = "Run the queue consumer" | ||
|
||
option_list = BaseCommand.option_list + ( | ||
make_option('--workers', '-w', | ||
dest='workers', | ||
type='int', | ||
help='Number of worker threads/processes/greenlets' | ||
), | ||
make_option('--worker-type', '-k', | ||
dest='worker_type', | ||
help='worker execution model (thread, greenlet, process).', | ||
default='thread', | ||
choices=['greenlet', 'thread', 'process', 'gevent'], | ||
), | ||
make_option('--delay', '-d', | ||
dest='initial_delay', | ||
type='float', | ||
help='Delay between polling requests' | ||
), | ||
make_option('--max_delay', '-m', | ||
dest='max_delay', | ||
type='float', | ||
help='Maximum delay between polling requests' | ||
), | ||
make_option('--no-periodic', '-n', | ||
default=True, | ||
dest='periodic', | ||
action='store_false', | ||
help='Do not enqueue periodic commands' | ||
), | ||
) | ||
|
||
def autodiscover_appconfigs(self): | ||
"""Use Django app registry to pull out potential apps with tasks.py module.""" | ||
module_name = 'tasks' | ||
for config in django_apps.get_app_configs(): | ||
app_path = config.module.__path__ | ||
try: | ||
fp, path, description = imp.find_module(module_name, app_path) | ||
except ImportError: | ||
continue | ||
else: | ||
import_path = '%s.%s' % (config.name, module_name) | ||
imp.load_module(import_path, fp, path, description) | ||
|
||
def autodiscover_old(self): | ||
# this is to find modules named <commands.py> in a django project's | ||
# installed apps directories | ||
module_name = 'tasks' | ||
|
||
for app in settings.INSTALLED_APPS: | ||
try: | ||
import_module(app) | ||
app_path = sys.modules[app].__path__ | ||
except AttributeError: | ||
continue | ||
try: | ||
imp.find_module(module_name, app_path) | ||
except ImportError: | ||
continue | ||
import_module('%s.%s' % (app, module_name)) | ||
app_path = sys.modules['%s.%s' % (app, module_name)] | ||
|
||
def autodiscover(self): | ||
"""Switch between Django 1.7 style and old style app importing.""" | ||
if HAS_DJANGO_APPS: | ||
self.autodiscover_appconfigs() | ||
else: | ||
self.autodiscover_old() | ||
|
||
def handle(self, *args, **options): | ||
from huey.contrib.djhuey import HUEY | ||
|
||
consumer_options = {} | ||
if isinstance(settings.HUEY, dict): | ||
consumer_options.update(settings.HUEY.get('consumer', {})) | ||
|
||
if options['workers'] is not None: | ||
consumer_options['workers'] = options['workers'] | ||
|
||
if options['worker_type'] is not None: | ||
consumer_options['worker_type'] = options['worker_type'] | ||
|
||
if options['periodic'] is not None: | ||
consumer_options['periodic'] = options['periodic'] | ||
|
||
if options['initial_delay'] is not None: | ||
consumer_options['initial_delay'] = options['initial_delay'] | ||
|
||
if options['max_delay'] is not None: | ||
consumer_options['max_delay'] = options['max_delay'] | ||
|
||
self.autodiscover() | ||
|
||
loglevel = get_loglevel(consumer_options.pop('loglevel', None)) | ||
logfile = consumer_options.pop('logfile', None) | ||
setup_logger(loglevel, logfile, consumer_options['worker_type']) | ||
|
||
consumer = Consumer(HUEY, **consumer_options) | ||
consumer.run() |
Empty file.