Replaces async occurrences with alternatives

* async is now a reserved word in python3.7
 * Rename async function to enqueue
 * Rename all async_ functions to enqueue_
 * Rename Async class to AsyncTask
 * Updates the docs.
This commit is contained in:
Pierre-Elliott Bécue
2018-07-07 01:58:03 +02:00
parent 2040771e80
commit ae75a84f7f
16 changed files with 190 additions and 190 deletions

View File

@@ -2,17 +2,17 @@
Chains
======
Sometimes you want to run tasks sequentially. For that you can use the :func:`async_chain` function:
Sometimes you want to run tasks sequentially. For that you can use the :func:`enqueue_chain` function:
.. code-block:: python
# Async a chain of tasks
from django_q.tasks import async_chain, result_group
# enqueue a chain of tasks
from django_q.tasks import enqueue_chain, result_group
# the chain must be in the format
# [(func,(args),{kwargs}),(func,(args),{kwargs}),..]
group_id = async_chain([('math.copysign', (1, -1)),
('math.floor', (1,))])
group_id = enqueue_chain([('math.copysign', (1, -1)),
('math.floor', (1,))])
# get group result
result_group(group_id, count=2)
@@ -21,7 +21,7 @@ A slightly more convenient way is to use a :class:`Chain` instance:
.. code-block:: python
# Chain async
# Chain enqueue
from django_q.tasks import Chain
# create a chain that uses the cache backend
@@ -41,9 +41,9 @@ A slightly more convenient way is to use a :class:`Chain` instance:
Reference
---------
.. py:function:: async_chain(chain, group=None, cached=Conf.CACHED, sync=Conf.SYNC, broker=None)
.. py:function:: enqueue_chain(chain, group=None, cached=Conf.CACHED, sync=Conf.SYNC, broker=None)
Async a chain of tasks. See also the :class:`Chain` class.
enqueue a chain of tasks. See also the :class:`Chain` class.
:param list chain: a list of tasks in the format [(func,(args),{kwargs}), (func,(args),{kwargs})]
:param str group: an optional group name.
@@ -52,7 +52,7 @@ Reference
.. py:class:: Chain(chain=None, group=None, cached=Conf.CACHED, sync=Conf.SYNC)
A sequential chain of tasks. Acts as a convenient wrapper for :func:`async_chain`
A sequential chain of tasks. Acts as a convenient wrapper for :func:`enqueue_chain`
You can pass the task chain at construction or you can append individual tasks before running them.
:param list chain: a list of task in the format [(func,(args),{kwargs}), (func,(args),{kwargs})]
@@ -63,7 +63,7 @@ Reference
.. py:method:: append(func, *args, **kwargs)
Append a task to the chain. Takes the same arguments as :func:`async`
Append a task to the chain. Takes the same arguments as :func:`enqueue`
:return: the current number of tasks in the chain
:rtype: int
@@ -102,4 +102,4 @@ Reference
get the length of the chain
:return int: length of the chain
:return int: length of the chain

View File

@@ -64,7 +64,7 @@ Set this to something that makes sense for your project. Can be overridden for i
ack_failures
~~~~~~~~~~~~
When set to ``True``, also acknowledge unsuccessful tasks. This causes failed tasks to be considered as successful deliveries, thereby removing them from the task queue. Can also be set per-task by passing the ``ack_failure`` option to :func:`async`. Defaults to ``False``.
When set to ``True``, also acknowledge unsuccessful tasks. This causes failed tasks to be considered as successful deliveries, thereby removing them from the task queue. Can also be set per-task by passing the ``ack_failure`` option to :func:`enqueue`. Defaults to ``False``.
.. _retry:
@@ -101,7 +101,7 @@ Guard loop sleep in seconds, must be greater than 0 and less than 60.
sync
~~~~
When set to ``True`` this configuration option forces all :func:`async` calls to be run with ``sync=True``.
When set to ``True`` this configuration option forces all :func:`enqueue` calls to be run with ``sync=True``.
Effectively making everything synchronous. Useful for testing. Defaults to ``False``.
.. _queue_limit:

View File

@@ -12,18 +12,18 @@ Sending an email can take a while so why not queue it:
# Welcome mail with follow up example
from datetime import timedelta
from django.utils import timezone
from django_q.tasks import async, schedule
from django_q.tasks import enqueue, schedule
from django_q.models import Schedule
def welcome_mail(user):
msg = 'Welcome to our website'
# send this message right away
async('django.core.mail.send_mail',
'Welcome',
msg,
'from@example.com',
[user.email])
enqueue('django.core.mail.send_mail',
'Welcome',
msg,
'from@example.com',
[user.email])
# and this follow up email in one hour
msg = 'Here are some tips to get you started...'
schedule('django.core.mail.send_mail',
@@ -51,7 +51,7 @@ A good place to use async tasks are Django's model signals. You don't want to de
from django.contrib.auth.models import User
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django_q.tasks import async
from django_q.tasks import enqueue
# set up the pre_save signal for our user
@receiver(pre_save, sender=User)
@@ -64,7 +64,7 @@ A good place to use async tasks are Django's model signals. You don't want to de
# has his email changed?
if not user.email == instance.email:
# tell everyone
async('tasks.inform_everyone', instance)
enqueue('tasks.inform_everyone', instance)
The task will send a message to everyone else informing them that the users email address has changed. Note that this adds almost no overhead to the save action:
@@ -87,8 +87,8 @@ The task will send a message to everyone else informing them that the users emai
for u in User.objects.exclude(pk=user.pk):
msg = 'Dear {}, {} has a new email address: {}'
msg = msg.format(u.username, user.username, user.email)
async('django.core.mail.send_mail',
'New email', msg, 'from@example.com', [u.email])
enqueue('django.core.mail.send_mail',
'New email', msg, 'from@example.com', [u.email])
Of course you can do other things beside sending emails. These are just generic examples. You can use signals with async to update fields in other objects too.
@@ -104,19 +104,19 @@ In this example the user requests a report and we let the cluster do the generat
.. code-block:: python
# Report generation with hook example
from django_q.tasks import async
from django_q.tasks import enqueue
# views.py
# user requests a report.
def create_report(request):
async('tasks.create_html_report',
request.user,
hook='tasks.email_report')
enqueue('tasks.create_html_report',
request.user,
hook='tasks.email_report')
.. code-block:: python
# tasks.py
from django_q.tasks import async
from django_q.tasks import enqueue
# report generator
def create_html_report(user):
@@ -127,16 +127,16 @@ In this example the user requests a report and we let the cluster do the generat
def email_report(task):
if task.success:
# Email the report
async('django.core.mail.send_mail',
'The report you requested',
task.result,
'from@example.com',
task.args[0].email)
enqueue('django.core.mail.send_mail',
'The report you requested',
task.result,
'from@example.com',
task.args[0].email)
else:
# Tell the admins something went wrong
async('django.core.mail.mail_admins',
'Report generation failed',
task.result)
enqueue('django.core.mail.mail_admins',
'Report generation failed',
task.result)
The hook is practical here, because it allows us to detach the sending task from the report generation function and to report on possible failures.
@@ -152,12 +152,12 @@ here's an example of how you can have Django Q take care of your indexes in real
from .models import Document
from django.db.models.signals import post_save
from django.dispatch import receiver
from django_q.tasks import async
from django_q.tasks import enqueue
# hook up the post save handler
@receiver(post_save, sender=Document)
def document_changed(sender, instance, **kwargs):
async('tasks.index_object', sender, instance, save=False)
enqueue('tasks.index_object', sender, instance, save=False)
# turn off result saving to not flood your database
.. code-block:: python
@@ -177,7 +177,7 @@ here's an example of how you can have Django Q take care of your indexes in real
index.update_object(instance, using=backend)
Now every time a Document is saved, your indexes will be updated without causing a delay in your save action.
You could expand this to dealing with deletes, by adding a ``post_delete`` signal and calling ``index.remove_object`` in the async function.
You could expand this to dealing with deletes, by adding a ``post_delete`` signal and calling ``index.remove_object`` in the enqueue function.
.. _shell:
@@ -187,13 +187,13 @@ You can execute or schedule shell commands using Pythons :mod:`subprocess` modul
.. code-block:: python
from django_q.tasks import async, result
from django_q.tasks import enqueue, result
# make a backup copy of setup.py
async('subprocess.call', ['cp', 'setup.py', 'setup.py.bak'])
enqueue('subprocess.call', ['cp', 'setup.py', 'setup.py.bak'])
# call ls -l and dump the output
task_id=async('subprocess.check_output', ['ls', '-l'])
task_id=enqueue('subprocess.check_output', ['ls', '-l'])
# get the result
dir_list = result(task_id)
@@ -202,10 +202,10 @@ In Python 3.5 the subprocess module has changed quite a bit and returns a :class
.. code-block:: python
from django_q.tasks import async, result
from django_q.tasks import enqueue, result
# make a backup copy of setup.py
tid = async('subprocess.run', ['cp', 'setup.py', 'setup.py.bak'])
tid = enqueue('subprocess.run', ['cp', 'setup.py', 'setup.py.bak'])
# get the result
r=result(tid, 500)
@@ -220,22 +220,22 @@ In Python 3.5 the subprocess module has changed quite a bit and returns a :class
from subprocess import PIPE
# call ls -l and pipe the output
tid = async('subprocess.run', ['ls', '-l'], stdout=PIPE)
tid = enqueue('subprocess.run', ['ls', '-l'], stdout=PIPE)
# get the result
res = result(tid, 500)
# print the output
print(res.stdout)
Instead of :func:`async` you can of course also use :func:`schedule` to schedule commands.
Instead of :func:`enqueue` you can of course also use :func:`schedule` to schedule commands.
For regular Django management commands, it is easier to call them directly:
.. code-block:: python
from django_q.tasks import async, schedule
from django_q.tasks import enqueue, schedule
async('django.core.management.call_command','clearsessions')
enqueue('django.core.management.call_command','clearsessions')
# or clear those sessions every hour
@@ -255,7 +255,7 @@ Adapted from `Sebastian Raschka's blog <http://sebastianraschka.com/Articles/201
# Group example with Parzen-window estimation
import numpy
from django_q.tasks import async, result_group, delete_group
from django_q.tasks import enqueue, result_group, delete_group
# the estimation function
def parzen_estimation(x_samples, point_x, h):
@@ -270,7 +270,7 @@ Adapted from `Sebastian Raschka's blog <http://sebastianraschka.com/Articles/201
return h, (k_n / len(x_samples)) / (h ** point_x.shape[1])
# create 100 calculations and return the collated result
def parzen_async():
def parzen_enqueue():
# clear the previous results
delete_group('parzen', cached=True)
mu_vec = numpy.array([0, 0])
@@ -279,10 +279,10 @@ Adapted from `Sebastian Raschka's blog <http://sebastianraschka.com/Articles/201
multivariate_normal(mu_vec, cov_mat, 10000)
widths = numpy.linspace(1.0, 1.2, 100)
x = numpy.array([[0], [0]])
# async them with a group label to the cache backend
# enqueue them with a group label to the cache backend
for w in widths:
async(parzen_estimation, sample, x, w,
group='parzen', cached=True)
enqueue(parzen_estimation, sample, x, w,
group='parzen', cached=True)
# return after 100 results
return result_group('parzen', count=100, cached=True)
@@ -290,21 +290,21 @@ Adapted from `Sebastian Raschka's blog <http://sebastianraschka.com/Articles/201
Django Q is not optimized for distributed computing, but this example will give you an idea of what you can do with task :doc:`group`.
Alternatively the ``parzen_async()`` function can also be written with :func:`async_iter`, which automatically utilizes the cache backend and groups to return a single result from an iterable:
Alternatively the ``parzen_enqueue()`` function can also be written with :func:`enqueue_iter`, which automatically utilizes the cache backend and groups to return a single result from an iterable:
.. code-block:: python
# create 100 calculations and return the collated result
def parzen_async():
def parzen_enqueue():
mu_vec = numpy.array([0, 0])
cov_mat = numpy.array([[1, 0], [0, 1]])
sample = numpy.random. \
multivariate_normal(mu_vec, cov_mat, 10000)
widths = numpy.linspace(1.0, 1.2, 100)
x = numpy.array([[0], [0]])
# async them with async iterable
# enqueue them with enqueue iterable
args = [(sample, x, w) for w in widths]
result_id = async_iter(parzen_estimation, args, cached=True)
result_id = enqueue_iter(parzen_estimation, args, cached=True)
# return the cached result or timeout after 10 seconds
return result(result_id, wait=10000, cached=True)

View File

@@ -2,15 +2,15 @@
Groups
======
You can group together results by passing :func:`async` the optional ``group`` keyword:
You can group together results by passing :func:`enqueue` the optional ``group`` keyword:
.. code-block:: python
# result group example
from django_q.tasks import async, result_group
from django_q.tasks import enqueue, result_group
for i in range(4):
async('math.modf', i, group='modf')
enqueue('math.modf', i, group='modf')
# wait until the group has 4 results
result = result_group('modf', count=4)
@@ -66,14 +66,14 @@ You can also access group functions from a task result instance:
task.group_delete()
print('Deleted group {}'.format(task.group))
or call them directly on :class:`Async` object:
or call them directly on :class:`AsyncTask` object:
.. code-block:: python
from django_q.tasks import Async
from django_q.tasks import enqueue
# add a task to the math group and run it cached
a = Async('math.floor', 2.5, group='math', cached=True)
a = enqueue('math.floor', 2.5, group='math', cached=True)
# wait until this tasks group has 10 results
result = a.result_group(count=10)
@@ -122,4 +122,4 @@ Reference
:param bool tasks: also deletes the associated tasks if ``True``
:param bool cached: run this against the cache backend.
:returns: the numbers of tasks affected
:rtype: int
:rtype: int

View File

@@ -2,16 +2,16 @@
Iterable
========
If you have an iterable object with arguments for a function, you can use :func:`async_iter` to async them with a single command::
If you have an iterable object with arguments for a function, you can use :func:`enqueue_iter` to async them with a single command::
# Async Iterable example
from django_q.tasks import async_iter, result
from django_q.tasks import enqueue_iter, result
# set up a list of arguments for math.floor
iter = [i for i in range(100)]
# async iter them
id=async_iter('math.floor',iter)
# enqueue iter them
id=enqueue_iter('math.floor',iter)
# wait for the collated result for 1 second
result_list = result(id, wait=1000)
@@ -45,10 +45,10 @@ You can also use an :class:`Iter` instance which can sometimes be more convenien
Reference
---------
.. py:function:: async_iter(func, args_iter,**kwargs)
.. py:function:: enqueue_iter(func, args_iter,**kwargs)
Runs iterable arguments against the cache backend and returns a single collated result.
Accepts the same options as :func:`async` except ``hook``. See also the :class:`Iter` class.
Accepts the same options as :func:`enqueue` except ``hook``. See also the :class:`Iter` class.
:param object func: The task function to execute
:param args: An iterable containing arguments for the task function
@@ -58,7 +58,7 @@ Reference
.. py:class:: Iter(func=None, args=None, kwargs=None, cached=Conf.CACHED, sync=Conf.SYNC, broker=None)
An async task with iterable arguments. Serves as a convenient wrapper for :func:`async_iter`
An async task with iterable arguments. Serves as a convenient wrapper for :func:`enqueue_iter`
You can pass the iterable arguments at construction or you can append individual argument tuples.
:param func: the function to execute

View File

@@ -27,7 +27,7 @@ You can manage them through the :ref:`admin_page` or directly from your code wit
schedule_type=Schedule.DAILY
)
# In case you want to use async options
# In case you want to use q_options
schedule('math.sqrt',
9,
hook='hooks.print_result',
@@ -103,7 +103,7 @@ Reference
:param int minutes: Number of minutes for the Minutes type.
:param int repeats: Number of times to repeat schedule. -1=Always, 0=Never, n =n.
:param datetime next_run: Next or first scheduled execution datetime.
:param dict q_options: async options to use for this schedule
:param dict q_options: options passed to enqueue for this schedule
:param kwargs: optional keyword arguments for the scheduled function.
.. class:: Schedule

View File

@@ -4,22 +4,22 @@ Tasks
.. _async:
async()
-------
enqueue()
---------
Use :func:`async` from your code to quickly offload tasks to the :class:`Cluster`:
Use :func:`enqueue` from your code to quickly offload tasks to the :class:`Cluster`:
.. code:: python
from django_q.tasks import async, result
from django_q.tasks import enqueue, result
# create the task
async('math.copysign', 2, -2)
enqueue('math.copysign', 2, -2)
# or with import and storing the id
import math.copysign
task_id = async(copysign, 2, -2)
task_id = enqueue(copysign, 2, -2)
# get the result
task_result = result(task_id)
@@ -30,13 +30,13 @@ Use :func:`async` from your code to quickly offload tasks to the :class:`Cluster
# but in most cases you will want to use a hook:
async('math.modf', 2.5, hook='hooks.print_result')
enqueue('math.modf', 2.5, hook='hooks.print_result')
# hooks.py
def print_result(task):
print(task.result)
:func:`async` can take the following optional keyword arguments:
:func:`enqueue` can take the following optional keyword arguments:
hook
""""
@@ -84,13 +84,13 @@ None of the option keywords get passed on to the task function.
As an alternative you can also put them in
a single keyword dict named ``q_options``. This enables you to use these keywords for your function call::
# Async options in a dict
# Enqueue options in a dict
opts = {'hook': 'hooks.print_result',
'group': 'math',
'timeout': 30}
async('math.modf', 2.5, q_options=opts)
enqueue('math.modf', 2.5, q_options=opts)
Please note that this will override any other option keywords.
@@ -99,18 +99,18 @@ Please note that this will override any other option keywords.
or you need to configure Django Q to run in synchronous mode for testing using the :ref:`sync` option.
Async
-----
AsyncTask
---------
Optionally you can use the :class:`Async` class to instantiate a task and keep everything in a single object.:
Optionally you can use the :class:`AsyncTask` class to instantiate a task and keep everything in a single object.:
.. code-block:: python
# Async class instance example
from django_q.tasks import Async
# AsyncTask class instance example
from django_q.tasks import AsyncTask
# instantiate an async task
a = Async('math.floor', 1.5, group='math')
a = AsyncTask('math.floor', 1.5, group='math')
# you can set or change keywords afterwards
a.cached = True
@@ -136,7 +136,7 @@ Optionally you can use the :class:`Async` class to instantiate a task and keep e
1
2
Once you change any of the parameters of the task after it has run, the result is invalidated and you will have to :func:`Async.run` it again to retrieve a new result.
Once you change any of the parameters of the task after it has run, the result is invalidated and you will have to :func:`AsyncTask.run` it again to retrieve a new result.
Cached operations
-----------------
@@ -150,10 +150,10 @@ You can also opt to set a manual timeout on the results, by setting e.g. ``cache
This works both globally or on individual async executions.::
# simple cached example
from django_q.tasks import async, result
from django_q.tasks import enqueue, result
# cache the result for 10 seconds
id = async('math.floor', 100, cached=10)
id = enqueue('math.floor', 100, cached=10)
# wait max 50ms for the result to appear in the cache
result(id, wait=50, cached=True)
@@ -169,35 +169,35 @@ As you can see you can easily turn a cached result into a permanent database res
This also works for group actions::
# cached group example
from django_q.tasks import async, result_group
from django_q.tasks import enqueue, result_group
from django_q.brokers import get_broker
# set up a broker instance for better performance
broker = get_broker()
# async a hundred functions under a group label
# enqueue a hundred functions under a group label
for i in range(100):
async('math.frexp',
i,
group='frexp',
cached=True,
broker=broker)
enqueue('math.frexp',
i,
group='frexp',
cached=True,
broker=broker)
# wait max 50ms for one hundred results to return
result_group('frexp', wait=50, count=100, cached=True)
If you don't need hooks, that exact same result can be achieved by using the more convenient :func:`async_iter`.
If you don't need hooks, that exact same result can be achieved by using the more convenient :func:`enqueue_iter`.
Synchronous testing
-------------------
:func:`async` can be instructed to execute a task immediately by setting the optional keyword ``sync=True``.
:func:`enqueue` can be instructed to execute a task immediately by setting the optional keyword ``sync=True``.
The task will then be injected straight into a worker and the result saved by a monitor instance::
from django_q.tasks import async, fetch
from django_q.tasks import enqueue, fetch
# create a synchronous task
task_id = async('my.buggy.code', sync=True)
task_id = enqueue('my.buggy.code', sync=True)
# the task will then be available immediately
task = fetch(task_id)
@@ -210,24 +210,24 @@ The task will then be injected straight into a worker and the result saved by a
An error occurred: ImportError("No module named 'my'",)
Note that :func:`async` will block until the task is executed and saved. This feature bypasses the broker and is intended for debugging and development.
Instead of setting ``sync`` on each individual ``async`` you can also configure :ref:`sync` as a global override.
Note that :func:`enqueue` will block until the task is executed and saved. This feature bypasses the broker and is intended for debugging and development.
Instead of setting ``sync`` on each individual ``enqueue`` you can also configure :ref:`sync` as a global override.
Connection pooling
------------------
Django Q tries to pass broker instances around its parts as much as possible to save you from running out of connections.
When you are making individual calls to :func:`async` a lot though, it can help to set up a broker to reuse for :func:`async`:
When you are making individual calls to :func:`enqueue` a lot though, it can help to set up a broker to reuse for :func:`enqueue`:
.. code:: python
# broker connection economy example
from django_q.tasks import async
from django_q.tasks import enqueue
from django_q.brokers import get_broker
broker = get_broker()
for i in range(50):
async('math.modf', 2.5, broker=broker)
enqueue('math.modf', 2.5, broker=broker)
.. tip::
@@ -237,7 +237,7 @@ When you are making individual calls to :func:`async` a lot though, it can help
Reference
---------
.. py:function:: async(func, *args, hook=None, group=None, timeout=None,\
.. py:function:: enqueue(func, *args, hook=None, group=None, timeout=None,\
save=None, sync=False, cached=False, broker=None, q_options=None, **kwargs)
Puts a task in the cluster queue
@@ -249,7 +249,7 @@ Reference
:param int timeout: Overrides global cluster :ref:`timeout`.
:param bool save: Overrides global save setting for this task.
:param bool ack_failure: Overrides the global :ref:`ack_failures` setting for this task.
:param bool sync: If set to True, async will simulate a task execution
:param bool sync: If set to True, enqueue will simulate a task execution
:param cached: Output the result to the cache backend. Bool or timeout in seconds
:param broker: Optional broker connection from :func:`brokers.get_broker`
:param dict q_options: Options dict, overrides option keywords
@@ -408,13 +408,13 @@ Reference
A proxy model of :class:`Task` with the queryset filtered on :attr:`Task.success` is ``False``.
.. py:class:: Async(func, *args, **kwargs)
.. py:class:: AsyncTask(func, *args, **kwargs)
A class wrapper for the :func:`async` function.
A class wrapper for the :func:`enqueue` function.
:param object func: The task function to execute
:param tuple args: The arguments for the task function
:param dict kwargs: Keyword arguments for the task function, including async options
:param dict kwargs: Keyword arguments for the task function, including enqueue options
.. py:attribute:: id
@@ -434,7 +434,7 @@ Reference
.. py:attribute:: kwargs
Keyword arguments for the function. Can include any of the optional async keyword attributes directly or in a `q_options` dictionary.
Keyword arguments for the function. Can include any of the optional enqueue keyword attributes directly or in a `q_options` dictionary.
.. py:attribute:: broker