fsl.utils.idle
This module provides functions and classes for running tasks asynchronously, either in an idle loop, or on a separate thread.
Note
The IdleLoop
functionality in this module is intended to be
run from within a wx
application. However, it will still work
without wx
, albeit with slightly modified behaviour.
Idle tasks
This class contains logic for running tasks via |
|
Equivalent to calling |
|
Equivalent to calling |
|
Blocks for the specified number of seconds, yielding to the main |
The IdleLoop
class provides a simple way to run a task on an wx
EVT_IDLE
event handler. A single IdleLoop
instance is created when
this module is imported; it can be accessed via the idleLoop
attribute,
and via the module-level idle()
and idleWhen()
functions.
The IdleLoop.idle()
method effectively performs the same job as the
run()
function (described below), but is more suitable for short tasks
which do not warrant running in a separate thread.
Thread tasks
Run the given |
|
Creates and starts a new |
|
The |
The run()
function simply runs a task in a separate thread. This
doesn’t seem like a worthy task to have a function of its own, but the
run()
function additionally provides the ability to schedule another
function to run on the wx.MainLoop
when the original function has
completed (via idle()
). This therefore gives us a simple way to run a
computationally intensitve task off the main GUI thread (preventing the GUI
from locking up), and to perform some clean up/refresh/notification
afterwards.
The wait()
function is given one or more Thread
instances, and a
task to run. It waits until all the threads have finished, and then runs
the task (via idle()
).
The TaskThread
class is a simple thread which runs a queue of tasks.
Other facilities
The idle
module also defines the mutex()
decorator, which is
intended to be used to mark the methods of a class as being mutually exclusive.
The mutex
decorator uses the MutexFactory
class to do its work.
- fsl.utils.idle._canHaveGui()[source]
Return
True
if wxPython is installed, and a display is available,False
otherwise.
- fsl.utils.idle._haveGui()[source]
Return
True
if wxPython is installed, a display is available, and awx.App
exists,False
otherwise.
- class fsl.utils.idle.IdleTask(name, task, schedtime, after, timeout, args, kwargs)[source]
Bases:
object
Container object used by the
IdleLoop
class. Used to encapsulate information about a queued task.- __dict__ = mappingproxy({'__module__': 'fsl.utils.idle', '__doc__': 'Container object used by the :class:`IdleLoop` class.\n Used to encapsulate information about a queued task.\n ', '__init__': <function IdleTask.__init__>, '__dict__': <attribute '__dict__' of 'IdleTask' objects>, '__weakref__': <attribute '__weakref__' of 'IdleTask' objects>, '__annotations__': {}})
- __module__ = 'fsl.utils.idle'
- __weakref__
list of weak references to the object (if defined)
- class fsl.utils.idle.IdleLoop[source]
Bases:
object
This class contains logic for running tasks via
wx.EVT_IDLE
events.A single
IdleLoop
instance is created when this module is first imported - it is accessed via the module-levelidleLoop
attribute.In normal circumstances, this
idleLoop
instance should be treated as a singleton, although this is not enforced in any way.The
EVT_IDLE
event is generated automatically bywx
during periods of inactivity. However, there are some circumstances in whichEVT_IDLE
will not be generated, and pending events may be left on the queue. For this reason, theIdleLoop
will occasionally use awx.Timer
to ensure that it continues to be called. The time-out used by thisTimer
can be queried and set via thecallRate()
property.- __init__()[source]
Create an
IdleLoop
.This method does not do much - the real initialisation takes place on the first call to
idle()
.
- property registered
Boolean flag indicating whether a handler has been registered on
wx.EVT_IDLE
events. Checked and set in theidle()
method.
- property queue
A
Queue
of functions which are to be run on thewx.EVT_IDLE
loop.
- property queueDict
A
dict
containing the names of all named tasks which are currently queued on the idle loop (see thename
parameter to theidle()
method).
- property timer
A
wx.Timer
instance which is used to periodically trigger the_wxIdleLoop()
in circumstances wherewx.EVT_IDLE
events may not be generated. This is created in the first call toidle()
.
- property callRate
Minimum time (in milliseconds) between consecutive calls to the idle loop (
__idleLoop()
). Ifwx.EVT_IDLE
events are not being fired, thetimer()
is used to maintain the idle loop at this rate.
- property allowErrors
Used for testing/debugging. If
True
, and a function called on the idle loop raises an error, that error will not be caught, and the idle loop will stop.
- property neverQueue
If
True
, tasks passed toidle()
will never be queued, and instead will always be executed directly/synchonously. See also thesynchronous()
context manager.
- synchronous()[source]
Context manager which can be used to tenporarily set
neverQueue()
toTrue
, restoring its previous value afterwards.
- reset()[source]
Reset the internal idle loop state.
In a normal execution environment, this method will never need to be called. However, in an execution environment where multiple
wx.App
instances are created, run, and destroyed sequentially, this function will need to be called after eachwx.App
has been destroyed. Otherwise theidle
function will not work during exeution of subsequentwx.App
instances.
- inIdle(taskName)[source]
Returns
True
if a task with the given name is queued on the idle loop (or is currently running),False
otherwise.
- cancelIdle(taskName)[source]
If a task with the given
taskName
is in the idle queue, it is cancelled. If the task is already running, it cannot be cancelled.A
KeyError
is raised if no task calledtaskName
exists.
- idle(task, *args, **kwargs)[source]
Run the given task on a
wx.EVT_IDLE
event.- Parameters:
task – The task to run.
name – Optional. If provided, must be provided as a keyword argument. Specifies a name that can be used to query the state of this task via
inIdle()
.after – Optional. If provided, must be provided as a keyword argument. A time, in seconds, which specifies the amount of time to wait before running this task after it has been scheduled.
timeout – Optional. If provided, must be provided as a keyword argument. Specifies a time out, in seconds. If this amount of time passes before the function gets scheduled to be called on the idle loop, the function is not called, and is dropped from the queue.
dropIfQueued – Optional. If provided, must be provided as a keyword argument. If
True
, and a task with the givenname
is already enqueud, that function is dropped from the queue, and the new task is enqueued. Defaults toFalse
. This argument takes precedence over theskipIfQueued
argument.skipIfQueued – Optional. If provided, must be provided as a keyword argument. If
True
, and a task with the givenname
is already enqueud, (or is running), the function is not called. Defaults toFalse
.alwaysQueue – Optional. If provided, must be provided as a keyword argument. If
True
, and awx.MainLoop
is not running, the task is enqueued anyway, under the assumption that awx.MainLoop
will be started in the future. Note that, ifwx.App
has not yet been created, another call toidle
must be made after the app has been created for the original task to be executed. Ifwx
is not available, this parameter will be ignored, and the task executed directly.
All other arguments are passed through to the task function.
If a
wx.App
is not running, orneverQueue()
has been set toTrue
, thetimeout
,name
,dropIfQueued
,skipIfQueued
, andalwaysQueue
arguments are ignored. Instead, the call will sleep forafter
seconds, and then thetask
will be called directly.Note
If the
after
argument is used, there is no guarantee that the task will be executed in the order that it is scheduled. This is because, if the required time has not elapsed when the task is popped from the queue, it will be re-queued.Note
If you schedule multiple tasks with the same
name
, and you do not use theskipIfQueued
ordropIfQueued
arguments, all of those tasks will be executed, but you will only be able to query/cancel the most recently enqueued task.Note
You will run into difficulties if you schedule a function that expects/accepts its own keyword arguments called
name
,skipIfQueued
,dropIfQueued
,after
,timeout
, oralwaysQueue
.
- idleWhen(func, condition, *args, **kwargs)[source]
Poll the
condition
function periodically, and schedulefunc
onidle()
when it returnsTrue
.- Parameters:
func – Function to call.
condition – Function which returns
True
orFalse
. Thefunc
function is only called when thecondition
function returnsTrue
.pollTime – Must be passed as a keyword argument. Time (in seconds) to wait between successive calls to
when
. Defaults to0.2
.
- __idleLoop(ev)
This method is called on
wx.EVT_IDLE
events, and occasionally onwx.EVT_TIMER
events via thetimer()
. If there is a function on thequeue()
, it is popped and called.
- __dict__ = mappingproxy({'__module__': 'fsl.utils.idle', '__doc__': 'This class contains logic for running tasks via ``wx.EVT_IDLE`` events.\n\n A single ``IdleLoop`` instance is created when this module is first\n imported - it is accessed via the module-level :attr:`idleLoop` attribute.\n\n In normal circumstances, this ``idleLoop`` instance should be treated as a\n singleton, although this is not enforced in any way.\n\n The ``EVT_IDLE`` event is generated automatically by ``wx`` during periods\n of inactivity. However, there are some circumstances in which ``EVT_IDLE``\n will not be generated, and pending events may be left on the queue. For\n this reason, the ``IdleLoop`` will occasionally use a ``wx.Timer`` to\n ensure that it continues to be called. The time-out used by this ``Timer``\n can be queried and set via the :meth:`callRate` property.\n ', '__init__': <function IdleLoop.__init__>, 'registered': <property object>, 'queue': <property object>, 'queueDict': <property object>, 'timer': <property object>, 'callRate': <property object>, 'allowErrors': <property object>, 'neverQueue': <property object>, 'synchronous': <function IdleLoop.synchronous>, 'reset': <function IdleLoop.reset>, 'inIdle': <function IdleLoop.inIdle>, 'cancelIdle': <function IdleLoop.cancelIdle>, 'idle': <function IdleLoop.idle>, 'idleWhen': <function IdleLoop.idleWhen>, '_IdleLoop__idleLoop': <function IdleLoop.__idleLoop>, '__dict__': <attribute '__dict__' of 'IdleLoop' objects>, '__weakref__': <attribute '__weakref__' of 'IdleLoop' objects>, '__annotations__': {}})
- __module__ = 'fsl.utils.idle'
- __weakref__
list of weak references to the object (if defined)
- fsl.utils.idle.idleLoop = <fsl.utils.idle.IdleLoop object>
A singleton
IdleLoop
instance, created when this module is imported.
- fsl.utils.idle.idle(*args, **kwargs)[source]
Equivalent to calling
IdleLoop.idle()
on theidleLoop
singleton.
- fsl.utils.idle.idleWhen(*args, **kwargs)[source]
Equivalent to calling
IdleLoop.idleWhen()
on theidleLoop
singleton.
- fsl.utils.idle.block(secs, delta=0.01, until=None)[source]
Blocks for the specified number of seconds, yielding to the main
wx
loop.If
wx
is not available, or awx
application is not running, this function is equivalent totime.sleep(secs)
.If
until
is provided, this function will block untiluntil
returnsTrue
, orsecs
have elapsed, whichever comes first.- Parameters:
secs – Time in seconds to block
delta – Time in seconds to sleep between successive yields to
wx
.until – Function which returns
True
orFalse
, and which determins when calls toblock
will return.
- fsl.utils.idle.run(task, onFinish=None, onError=None, name=None)[source]
Run the given
task
in a separate thread.- Parameters:
task – The function to run. Must accept no arguments.
onFinish – An optional function to schedule (on the
wx.MainLoop
, viaidle()
) once thetask
has finished.onError – An optional function to be called (on the
wx.MainLoop
, viaidle()
) if thetask
raises an error. Passed theException
that was raised.name – An optional name to use for this task in log statements.
- Returns:
A reference to the
Thread
that was created.
Note
If a
wx
application is not running, thetask
andonFinish
functions will simply be called directly, and the return value will beNone
.
- fsl.utils.idle.wait(threads, task, *args, **kwargs)[source]
Creates and starts a new
Thread
which waits for all of theThread
instances to finish (byjoin``ing them), and then runs the given ``task
viaidle()
.If the
direct
parameter isTrue
, or awx.App
is not running, this functionjoin``s the threads directly instead of creating a new ``Thread
to do so.- Parameters:
threads – A
Thread
, or a sequence ofThread
instances to join. Elements in the sequence may beNone
.task – The task to run once all
threads
have completed.wait_direct – Must be passed as a keyword argument. If
True
, this function call willjoin
all of thethreads
, and then call thetask
. Otherwise (the default), this function will create a new thread tojoin
thethreads
, and will return immediately.
All other arguments are passed to the
task
function.Note
This function will not support
task
functions which expect a keyword argument calledwait_direct
.
- class fsl.utils.idle.Task(name, func, onFinish, onError, args, kwargs)[source]
Bases:
object
Container object which encapsulates a task that is run by a
TaskThread
.- __dict__ = mappingproxy({'__module__': 'fsl.utils.idle', '__doc__': 'Container object which encapsulates a task that is run by a\n :class:`TaskThread`.\n ', '__init__': <function Task.__init__>, '__dict__': <attribute '__dict__' of 'Task' objects>, '__weakref__': <attribute '__weakref__' of 'Task' objects>, '__annotations__': {}})
- __module__ = 'fsl.utils.idle'
- __weakref__
list of weak references to the object (if defined)
- exception fsl.utils.idle.TaskThreadVeto[source]
Bases:
Exception
Task functions which are added to a
TaskThread
may raise aTaskThreadVeto
error to skip processing of the task’sonFinish
handler (if one has been specified). See theTaskThread.enqueue()
method for more details.- __module__ = 'fsl.utils.idle'
- __weakref__
list of weak references to the object (if defined)
- class fsl.utils.idle.TaskThread(*args, **kwargs)[source]
Bases:
Thread
The
TaskThread
is a simple thread which runs tasks. Tasks may be enqueued and dequeued.- enqueue(func, *args, **kwargs)[source]
Enqueue a task to be executed.
- Parameters:
func – The task function.
taskName – Task name. Must be specified as a keyword argument. Does not necessarily have to be a string, but must be hashable. If you wish to use the
dequeue()
orisQueued()
methods, you must provide a task name.onFinish – An optional function to be called (via
idle()
) when the task funtion has finished. Must be provided as a keyword argument, and must itself accept no arguments. If thefunc
raises a :class`TaskThreadVeto` error, this function will not be called.onError – An optional function to be called (via
idle()
) if the task funtion raises anException
. Must be provided as a keyword argument, and must itself accept the raisedException
object as a single argument. If thefunc
raises a :class`TaskThreadVeto` error, this function will not be called.
All other arguments are passed through to the task function when it is executed.
Note
If the specified
taskName
is not unique (i.e. another task with the same name may already be enqueued), theisQueued()
method will probably return invalid results.Warning
Make sure that your task function is not expecting keyword arguments called
taskName
,onFinish
, oronError
!
- __module__ = 'fsl.utils.idle'
- fsl.utils.idle.mutex(*args, **kwargs)[source]
Decorator for use on methods of a class, which makes the method call mutually exclusive.
If you define a class which has one or more methods that must only be accessed by one thread at a time, you can use the
mutex
decorator to enforce this restriction. As a contrived example:class Example(object): def __init__(self): self.__sharedData = [] @mutex def dangerousMethod1(self, message): sefl.__sharedData.append(message) @mutex def dangerousMethod2(self): return sefl.__sharedData.pop()
The
@mutex
decorator will ensure that, at any point in time, only one thread is running either of thedangerousMethod1
ordangerousMethod2
methods.See the
MutexFactory`
- class fsl.utils.idle.MutexFactory(function)[source]
Bases:
object
The
MutexFactory
is a placeholder for methods which have been decorated with themutex()
decorator. When the method of a class is decorated with@mutex
, aMutexFactory
is created.Later on, when the method is accessed on an instance, the
__get__()
method creates the true decorator function, and replaces the instance method with that decorator.Note
The
MutexFactory
adds an attribute called_async_mutex_lock
to all instances that have@mutex
-decorated methods.- __dict__ = mappingproxy({'__module__': 'fsl.utils.idle', '__doc__': 'The ``MutexFactory`` is a placeholder for methods which have been\n decorated with the :func:`mutex` decorator. When the method of a class\n is decorated with ``@mutex``, a ``MutexFactory`` is created.\n\n Later on, when the method is accessed on an instance, the :meth:`__get__`\n method creates the true decorator function, and replaces the instance\n method with that decorator.\n\n .. note:: The ``MutexFactory`` adds an attribute called\n ``_async_mutex_lock`` to all instances that have\n ``@mutex``-decorated methods.\n ', 'createLock': <unlocked _thread.lock object>, '__init__': <function MutexFactory.__init__>, '__get__': <function MutexFactory.__get__>, '__dict__': <attribute '__dict__' of 'MutexFactory' objects>, '__weakref__': <attribute '__weakref__' of 'MutexFactory' objects>, '__annotations__': {}})
- __module__ = 'fsl.utils.idle'
- __weakref__
list of weak references to the object (if defined)
- createLock = <unlocked _thread.lock object>
This lock is used by all
MutexFactory
instances when a decorated instance method is accessed for the first time.The first time that a mutexed method is accessed on an instance, a new
threading.Lock
is created, to be shared by all mutexed methods of that instance. ThecreateLock
is used to ensure that this can only occur once for each instance.
- __get__(instance, cls)[source]
When this
MutexFactory
is accessed through an instance, a decorator function is created which enforces mutually exclusive access to the decorated method. A singlethreading.Lock
object is shared between all@mutex
-decorated methods on a single instance.If this
MutexFactory
is accessed through a class, the decorated function is returned.