2017-03-17 16:35:53 +03:00
#
# libvirtaio -- asyncio adapter for libvirt
# Copyright (C) 2017 Wojtek Porczyk <woju@invisiblethingslab.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, see
# <http://www.gnu.org/licenses/>.
#
''' Libvirt event loop implementation using asyncio
Register the implementation of default loop :
2022-05-17 17:36:01 +03:00
import asyncio
import libvirtaio
async def myapp ( ) :
libvirtaio . virEventRegisterAsyncIOImpl ( )
conn = libvirt . open ( " test:///default " )
For compatibility with Python < 3.7 :
loop = asyncio . new_event_loop ( )
asyncio . set_event_loop ( loop )
loop . run_until_complete ( myapp ( ) )
asyncio . set_event_loop ( None )
loop . close ( )
If Python > = 3.7 can be required then
asyncio . run ( myapp ( ) )
2017-03-17 16:35:53 +03:00
. . seealso : :
https : / / libvirt . org / html / libvirt - libvirt - event . html
'''
2018-11-16 15:55:56 +03:00
import asyncio
import itertools
import logging
import warnings
import libvirt
2018-11-20 10:59:35 +03:00
from typing import Any , Callable , Dict , Generator , Optional , TypeVar # noqa F401
_T = TypeVar ( ' _T ' )
2017-03-17 16:35:53 +03:00
__author__ = ' Wojtek Porczyk <woju@invisiblethingslab.com> '
__license__ = ' LGPL-2.1+ '
2017-09-14 03:29:29 +03:00
__all__ = [
' getCurrentImpl ' ,
' virEventAsyncIOImpl ' ,
' virEventRegisterAsyncIOImpl ' ,
]
2017-03-17 16:35:53 +03:00
class Callback ( object ) :
''' Base class for holding callback
: param virEventAsyncIOImpl impl : the implementation in which we run
: param cb : the callback itself
: param opaque : the opaque tuple passed by libvirt
'''
# pylint: disable=too-few-public-methods
_iden_counter = itertools . count ( )
Revert "libvirtaio: Drop object(*args, **kwargs)"
This reverts commit f4be03b330125ab1e5a2bb10b4f12674aeff4691.
While object.__init__() does not expect any additional arguments, this
construct is required for Pythons multiple inheritance implementation.
The original author Wojtek Porczyk <woju@invisiblethingslab.com>
explained is this way:
> I'm sorry I didn't notice this earlier, but the commit f4be03b3 dated
> 2020-04-20 [0] is wrong. The super().__init__(*args, **kwargs) in
> Callback.__init__ was there on purpose, because of how Python's inheritance in
> new-style classes works.
>
> Let me explain this a bit, because it is not obvious.
>
> Suppose you had diamond inheritance like this:
>
> class A(object): pass
> class B(A): pass
> class C(A): pass
> class D(B,C): pass
>
> And those classes needed a common function with varying arguments:
>
> class A(object):
> def spam(self, a): print(f'A: {a}')
> class B(A):
> def spam(self, b): print(f'B: {b}')
> class C(A):
> def spam(self, c): print(f'C: {c}')
> class D(B,C):
> def spam(self, d): print(f'D: {d}')
>
> The way to call all parent's functions exactly once (as per MRO) and accept
> all arguments and also forbid unknown arguments is to accept **kwargs
> everywhere and pass them to super().spam():
>
> class A:
> def spam(self, a):
> print(f'A: {a}')
> class B(A):
> def spam(self, b, **kwargs):
> print(f'B: {b}')
> super().spam(**kwargs)
> class C(A):
> def spam(self, c, **kwargs):
> print(f'C: {c}')
> super().spam(**kwargs)
> class D(B, C):
> def spam(self, d, **kwargs):
> print(f'D: {d}')
> super().spam(**kwargs)
>
> Let's run this:
>
> >>> B().spam(a=1, b=2)
> B: 2
> A: 1
> >>> D().spam(a=1, b=2, c=3, d=4)
> D: 4
> B: 2
> C: 3
> A: 1
>
> You may notice that super() in B.spam refers to two different classes, either
> A or C, depending on inheritance order in yet undefined classes (as of B's
> definition).
>
> That's why the conclusion that super() in Callback.__init__ refers to object
> is wrong. In this example, spam=__init__, A=object, B=Callback and C and D are
> not yet written, but theoretically possible classes that could be written by
> someone else. Why would they be needed, I don't know, but if someone writes
> them, s/he would be out of options to invent new arguments to C.__init__.
>
> Note that super().__init__(*args, **kwargs) when super() refers to object
> isn't harmful, and just ensures that args and kwargs are empty (i.e. no
> unknown arguments were passed). In fact, this is exactly why object.__init__()
> takes no arguments since Python 2.6 [1][2], as you correctly point out in the
> commit message.
>
> I don't think this breaks anything (I very much doubt anyone would need to
> write code that would trigger this), nevertheless, as the commit is both
> pointless and wrong, and as the original author of libvirtaio I'd like to ask
> for this commit to be reverted. If this breaks some static analysis tool,
> could you just suppress it for this particular line?
>
>
> [0] https://gitlab.com/libvirt/libvirt-python/-/commit/f4be03b330125ab1e5a2bb10b4f12674aeff4691
> [1] https://bugs.python.org/issue1683368
> [2] https://docs.python.org/3/whatsnew/2.6.html#porting-to-python-2-6
> (fourth point)
>
Signed-off-by: Philipp Hahn <hahn@univention.de>
2020-08-28 19:34:10 +03:00
def __init__ ( self , impl : " virEventAsyncIOImpl " , cb : Callable [ [ int , _T ] , None ] , opaque : _T , * args : Any , * * kwargs : Any ) - > None :
super ( ) . __init__ ( * args , * * kwargs ) # type: ignore
2017-03-17 16:35:53 +03:00
self . iden = next ( self . _iden_counter )
self . impl = impl
self . cb = cb
self . opaque = opaque
2018-11-20 10:59:35 +03:00
def __repr__ ( self ) - > str :
2017-03-17 16:35:53 +03:00
return ' < {} iden= {} > ' . format ( self . __class__ . __name__ , self . iden )
2018-11-20 10:59:35 +03:00
def close ( self ) - > None :
2017-03-17 16:35:53 +03:00
''' Schedule *ff* callback '''
self . impl . log . debug ( ' callback %d close(), scheduling ff ' , self . iden )
2017-08-24 22:22:49 +03:00
self . impl . schedule_ff_callback ( self . iden , self . opaque )
2017-03-17 16:35:53 +03:00
2018-11-20 10:34:48 +03:00
2017-03-17 16:35:53 +03:00
#
# file descriptors
#
class Descriptor ( object ) :
''' Manager of one file descriptor
: param virEventAsyncIOImpl impl : the implementation in which we run
: param int fd : the file descriptor
'''
2018-11-20 10:59:35 +03:00
def __init__ ( self , impl : " virEventAsyncIOImpl " , fd : int ) - > None :
2017-03-17 16:35:53 +03:00
self . impl = impl
self . fd = fd
2018-11-20 10:59:35 +03:00
self . callbacks = { } # type: Dict
2017-03-17 16:35:53 +03:00
2018-11-20 10:59:35 +03:00
def _handle ( self , event : int ) - > None :
2017-03-17 16:35:53 +03:00
''' Dispatch the event to the descriptors
: param int event : The event ( from libvirt ' s constants) being dispatched
'''
2017-08-31 22:20:40 +03:00
for callback in list ( self . callbacks . values ( ) ) :
2017-03-17 16:35:53 +03:00
if callback . event is not None and callback . event & event :
callback . cb ( callback . iden , self . fd , event , callback . opaque )
2018-11-20 10:59:35 +03:00
def update ( self ) - > None :
2017-03-17 16:35:53 +03:00
''' Register or unregister callbacks at event loop
This should be called after change of any ` ` . event ` ` in callbacks .
'''
# It seems like loop.add_{reader,writer} can be run multiple times
# and will still register the callback only once. Likewise,
# remove_{reader,writer} may be run even if the reader/writer
# is not registered (and will just return False).
# For the edge case of empty callbacks, any() returns False.
if any ( callback . event & ~ (
2018-11-20 10:34:48 +03:00
libvirt . VIR_EVENT_HANDLE_READABLE |
libvirt . VIR_EVENT_HANDLE_WRITABLE )
2017-03-17 16:35:53 +03:00
for callback in self . callbacks . values ( ) ) :
warnings . warn (
' The only event supported are VIR_EVENT_HANDLE_READABLE '
' and VIR_EVENT_HANDLE_WRITABLE ' ,
UserWarning )
if any ( callback . event & libvirt . VIR_EVENT_HANDLE_READABLE
for callback in self . callbacks . values ( ) ) :
self . impl . loop . add_reader (
self . fd , self . _handle , libvirt . VIR_EVENT_HANDLE_READABLE )
else :
self . impl . loop . remove_reader ( self . fd )
if any ( callback . event & libvirt . VIR_EVENT_HANDLE_WRITABLE
for callback in self . callbacks . values ( ) ) :
self . impl . loop . add_writer (
self . fd , self . _handle , libvirt . VIR_EVENT_HANDLE_WRITABLE )
else :
self . impl . loop . remove_writer ( self . fd )
2018-11-20 10:59:35 +03:00
def add_handle ( self , callback : " FDCallback " ) - > None :
2017-03-17 16:35:53 +03:00
''' Add a callback to the descriptor
: param FDCallback callback : the callback to add
: rtype : None
After adding the callback , it is immediately watched .
'''
self . callbacks [ callback . iden ] = callback
self . update ( )
2018-11-20 10:59:35 +03:00
def remove_handle ( self , iden : int ) - > None :
2017-03-17 16:35:53 +03:00
''' Remove a callback from the descriptor
: param int iden : the identifier of the callback
: returns : the callback
: rtype : FDCallback
After removing the callback , the descriptor may be unwatched , if there
are no more handles for it .
'''
callback = self . callbacks . pop ( iden )
self . update ( )
return callback
2018-11-20 10:34:48 +03:00
2017-03-17 16:35:53 +03:00
class DescriptorDict ( dict ) :
''' Descriptors collection
This is used internally by virEventAsyncIOImpl to hold descriptors .
'''
2018-11-20 10:59:35 +03:00
def __init__ ( self , impl : " virEventAsyncIOImpl " ) - > None :
2017-03-17 16:35:53 +03:00
super ( ) . __init__ ( )
self . impl = impl
2018-11-20 10:59:35 +03:00
def __missing__ ( self , fd : int ) - > Descriptor :
2017-03-17 16:35:53 +03:00
descriptor = Descriptor ( self . impl , fd )
self [ fd ] = descriptor
return descriptor
2018-11-20 10:34:48 +03:00
2017-03-17 16:35:53 +03:00
class FDCallback ( Callback ) :
''' Callback for file descriptor (watcher)
: param Descriptor descriptor : the descriptor manager
: param int event : bitset of events on which to fire the callback
'''
# pylint: disable=too-few-public-methods
2018-11-20 10:59:35 +03:00
def __init__ ( self , * args : Any , descriptor : Descriptor , event : int , * * kwargs : Any ) - > None :
2017-03-17 16:35:53 +03:00
super ( ) . __init__ ( * args , * * kwargs )
self . descriptor = descriptor
self . event = event
2018-11-20 10:59:35 +03:00
def __repr__ ( self ) - > str :
2017-03-17 16:35:53 +03:00
return ' < {} iden= {} fd= {} event= {} > ' . format (
self . __class__ . __name__ , self . iden , self . descriptor . fd , self . event )
2018-11-20 10:59:35 +03:00
def update ( self , event : int ) - > None :
2017-03-17 16:35:53 +03:00
''' Update the callback and fix descriptor ' s watchers '''
self . event = event
self . descriptor . update ( )
2018-11-20 10:34:48 +03:00
2017-03-17 16:35:53 +03:00
#
# timeouts
#
class TimeoutCallback ( Callback ) :
''' Callback for timer '''
2018-11-20 10:59:35 +03:00
def __init__ ( self , * args : Any , * * kwargs : Any ) - > None :
2017-03-17 16:35:53 +03:00
super ( ) . __init__ ( * args , * * kwargs )
self . timeout = - 1
self . _task = None
2018-11-20 10:59:35 +03:00
def __repr__ ( self ) - > str :
2017-03-17 16:35:53 +03:00
return ' < {} iden= {} timeout= {} > ' . format (
self . __class__ . __name__ , self . iden , self . timeout )
2022-05-16 19:56:50 +03:00
async def _timer ( self ) - > Generator [ Any , None , None ] :
2017-03-17 16:35:53 +03:00
''' An actual timer running on the event loop.
This is a coroutine .
'''
while True :
try :
if self . timeout > 0 :
timeout = self . timeout * 1e-3
self . impl . log . debug ( ' sleeping %r ' , timeout )
2022-05-16 19:56:50 +03:00
await asyncio . sleep ( timeout )
2017-03-17 16:35:53 +03:00
else :
# scheduling timeout for next loop iteration
2022-05-16 19:56:50 +03:00
await asyncio . sleep ( 0 )
2017-03-17 16:35:53 +03:00
except asyncio . CancelledError :
self . impl . log . debug ( ' timer %d cancelled ' , self . iden )
break
self . cb ( self . iden , self . opaque )
self . impl . log . debug ( ' timer %r callback ended ' , self . iden )
2018-11-20 10:59:35 +03:00
def update ( self , timeout : int ) - > None :
2017-03-17 16:35:53 +03:00
''' Start or the timer, possibly updating timeout '''
self . timeout = timeout
if self . timeout > = 0 and self . _task is None :
self . impl . log . debug ( ' timer %r start ' , self . iden )
2022-05-16 19:34:59 +03:00
self . _task = asyncio . ensure_future ( self . _timer ( ) ,
loop = self . impl . loop )
2017-03-17 16:35:53 +03:00
elif self . timeout < 0 and self . _task is not None :
self . impl . log . debug ( ' timer %r stop ' , self . iden )
self . _task . cancel ( ) # pylint: disable=no-member
self . _task = None
2018-11-20 10:59:35 +03:00
def close ( self ) - > None :
2017-03-17 16:35:53 +03:00
''' Stop the timer and call ff callback '''
self . update ( timeout = - 1 )
2017-09-14 03:26:53 +03:00
super ( TimeoutCallback , self ) . close ( )
2017-03-17 16:35:53 +03:00
2018-11-20 10:34:48 +03:00
2017-03-17 16:35:53 +03:00
#
# main implementation
#
class virEventAsyncIOImpl ( object ) :
''' Libvirt event adapter to asyncio.
: param loop : asyncio ' s event loop
If * loop * is not specified , the current ( or default ) event loop is used .
'''
2018-11-20 10:59:35 +03:00
def __init__ ( self , loop : asyncio . AbstractEventLoop = None ) - > None :
2017-03-17 16:35:53 +03:00
self . loop = loop or asyncio . get_event_loop ( )
2018-11-20 10:59:35 +03:00
self . callbacks = { } # type: Dict[int, Callback]
2017-03-17 16:35:53 +03:00
self . descriptors = DescriptorDict ( self )
self . log = logging . getLogger ( self . __class__ . __name__ )
2017-09-14 03:41:12 +03:00
self . _pending = 0
2022-05-17 16:11:55 +03:00
# Transient asyncio.Event instance dynamically created
# and destroyed by drain()
# NOTE invariant: _finished.is_set() iff _pending == 0
self . _finished = None
2017-09-14 03:41:12 +03:00
2018-11-20 10:59:35 +03:00
def __repr__ ( self ) - > str :
2017-08-24 22:22:49 +03:00
return ' < {} callbacks= {} descriptors= {} > ' . format (
type ( self ) . __name__ , self . callbacks , self . descriptors )
2018-11-20 10:59:35 +03:00
def _pending_inc ( self ) - > None :
2017-09-14 03:41:12 +03:00
''' Increase the count of pending affairs. Do not use directly. '''
self . _pending + = 1
2022-05-17 16:11:55 +03:00
if self . _finished is not None :
self . _finished . clear ( )
2017-09-14 03:41:12 +03:00
2018-11-20 10:59:35 +03:00
def _pending_dec ( self ) - > None :
2017-09-14 03:41:12 +03:00
''' Decrease the count of pending affairs. Do not use directly. '''
assert self . _pending > 0
self . _pending - = 1
2022-05-17 16:11:55 +03:00
if self . _pending == 0 and self . _finished is not None :
2017-09-14 03:41:12 +03:00
self . _finished . set ( )
2018-11-20 10:59:35 +03:00
def register ( self ) - > " virEventAsyncIOImpl " :
2017-03-17 16:35:53 +03:00
''' Register this instance as event loop implementation '''
# pylint: disable=bad-whitespace
self . log . debug ( ' register() ' )
libvirt . virEventRegisterImpl (
2018-11-20 10:34:48 +03:00
self . _add_handle , self . _update_handle , self . _remove_handle ,
2017-03-17 16:35:53 +03:00
self . _add_timeout , self . _update_timeout , self . _remove_timeout )
return self
2018-11-20 10:59:35 +03:00
def schedule_ff_callback ( self , iden : int , opaque : _T ) - > None :
2017-03-17 16:35:53 +03:00
''' Schedule a ff callback from one of the handles or timers '''
2022-05-16 19:34:59 +03:00
asyncio . ensure_future ( self . _ff_callback ( iden , opaque ) , loop = self . loop )
2017-08-24 22:22:49 +03:00
2022-05-16 19:56:50 +03:00
async def _ff_callback ( self , iden : int , opaque : _T ) - > None :
2017-08-24 22:22:49 +03:00
''' Directly free the opaque object
This is a coroutine .
'''
self . log . debug ( ' ff_callback(iden= %d , opaque=...) ' , iden )
2020-04-20 12:51:48 +03:00
libvirt . virEventInvokeFreeCallback ( opaque )
2017-09-14 03:41:12 +03:00
self . _pending_dec ( )
2022-05-16 19:56:50 +03:00
async def drain ( self ) - > None :
2017-09-14 03:41:12 +03:00
''' Wait for the implementation to become idle.
This is a coroutine .
'''
self . log . debug ( ' drain() ' )
if self . _pending :
2022-05-17 16:11:55 +03:00
assert self . _finished is None
self . _finished = asyncio . Event ( )
2022-05-16 19:56:50 +03:00
await self . _finished . wait ( )
2022-05-17 16:11:55 +03:00
self . _finished = None
assert self . _pending == 0
2017-09-14 03:41:12 +03:00
self . log . debug ( ' drain ended ' )
2017-03-17 16:35:53 +03:00
2018-11-20 10:59:35 +03:00
def is_idle ( self ) - > bool :
2017-03-17 16:35:53 +03:00
''' Returns False if there are leftovers from a connection
Those may happen if there are sematical problems while closing
a connection . For example , not deregistered events before . close ( ) .
'''
2017-09-14 03:41:12 +03:00
return not self . callbacks and not self . _pending
2017-03-17 16:35:53 +03:00
2018-11-20 10:59:35 +03:00
def _add_handle ( self , fd : int , event : int , cb : libvirt . _EventCB , opaque : _T ) - > int :
2017-03-17 16:35:53 +03:00
''' Register a callback for monitoring file handle events
: param int fd : file descriptor to listen on
: param int event : bitset of events on which to fire the callback
: param cb : the callback to be called when an event occurrs
: param opaque : user data to pass to the callback
: rtype : int
: returns : handle watch number to be used for updating and unregistering for events
. . seealso : :
https : / / libvirt . org / html / libvirt - libvirt - event . html #virEventAddHandleFuncFunc
'''
callback = FDCallback ( self , cb , opaque ,
2018-11-20 10:34:48 +03:00
descriptor = self . descriptors [ fd ] , event = event )
2017-09-14 03:24:29 +03:00
assert callback . iden not in self . callbacks
2017-08-24 22:22:49 +03:00
self . log . debug ( ' add_handle(fd= %d , event= %d , cb=..., opaque=...) = %d ' ,
2018-11-20 10:34:48 +03:00
fd , event , callback . iden )
2017-03-17 16:35:53 +03:00
self . callbacks [ callback . iden ] = callback
self . descriptors [ fd ] . add_handle ( callback )
2017-09-14 03:41:12 +03:00
self . _pending_inc ( )
2017-03-17 16:35:53 +03:00
return callback . iden
2018-11-20 10:59:35 +03:00
def _update_handle ( self , watch : int , event : int ) - > None :
2017-03-17 16:35:53 +03:00
''' Change event set for a monitored file handle
: param int watch : file descriptor watch to modify
: param int event : new events to listen on
. . seealso : :
https : / / libvirt . org / html / libvirt - libvirt - event . html #virEventUpdateHandleFunc
'''
self . log . debug ( ' update_handle(watch= %d , event= %d ) ' , watch , event )
2020-04-20 12:58:17 +03:00
callback = self . callbacks [ watch ]
assert isinstance ( callback , FDCallback )
callback . update ( event = event )
2017-03-17 16:35:53 +03:00
2018-11-20 10:59:35 +03:00
def _remove_handle ( self , watch : int ) - > int :
2017-03-17 16:35:53 +03:00
''' Unregister a callback from a file handle.
: param int watch : file descriptor watch to stop listening on
2020-04-27 10:44:57 +03:00
: returns : - 1 on error , 0 on success
2017-03-17 16:35:53 +03:00
. . seealso : :
https : / / libvirt . org / html / libvirt - libvirt - event . html #virEventRemoveHandleFunc
'''
self . log . debug ( ' remove_handle(watch= %d ) ' , watch )
2017-08-24 22:22:49 +03:00
try :
callback = self . callbacks . pop ( watch )
except KeyError as err :
self . log . warning ( ' remove_handle(): no such handle: %r ' , err . args [ 0 ] )
2020-04-27 10:44:57 +03:00
return - 1
2020-04-20 12:58:17 +03:00
assert isinstance ( callback , FDCallback )
2017-03-17 16:35:53 +03:00
fd = callback . descriptor . fd
assert callback is self . descriptors [ fd ] . remove_handle ( watch )
if len ( self . descriptors [ fd ] . callbacks ) == 0 :
del self . descriptors [ fd ]
callback . close ( )
2020-04-27 10:44:57 +03:00
return 0
2017-03-17 16:35:53 +03:00
2018-11-20 10:59:35 +03:00
def _add_timeout ( self , timeout : int , cb : libvirt . _TimerCB , opaque : _T ) - > int :
2017-03-17 16:35:53 +03:00
''' Register a callback for a timer event
: param int timeout : the timeout to monitor
: param cb : the callback to call when timeout has expired
: param opaque : user data to pass to the callback
: rtype : int
: returns : a timer value
. . seealso : :
https : / / libvirt . org / html / libvirt - libvirt - event . html #virEventAddTimeoutFunc
'''
callback = TimeoutCallback ( self , cb , opaque )
2017-09-14 03:24:29 +03:00
assert callback . iden not in self . callbacks
2017-08-24 22:22:49 +03:00
self . log . debug ( ' add_timeout(timeout= %d , cb=..., opaque=...) = %d ' ,
2018-11-20 10:34:48 +03:00
timeout , callback . iden )
2017-03-17 16:35:53 +03:00
self . callbacks [ callback . iden ] = callback
callback . update ( timeout = timeout )
2017-09-14 03:41:12 +03:00
self . _pending_inc ( )
2017-03-17 16:35:53 +03:00
return callback . iden
2018-11-20 10:59:35 +03:00
def _update_timeout ( self , timer : int , timeout : int ) - > None :
2017-03-17 16:35:53 +03:00
''' Change frequency for a timer
: param int timer : the timer to modify
: param int timeout : the new timeout value in ms
. . seealso : :
https : / / libvirt . org / html / libvirt - libvirt - event . html #virEventUpdateTimeoutFunc
'''
self . log . debug ( ' update_timeout(timer= %d , timeout= %d ) ' , timer , timeout )
2020-04-20 12:58:17 +03:00
callback = self . callbacks [ timer ]
assert isinstance ( callback , TimeoutCallback )
callback . update ( timeout = timeout )
2017-03-17 16:35:53 +03:00
2018-11-20 10:59:35 +03:00
def _remove_timeout ( self , timer : int ) - > int :
2017-03-17 16:35:53 +03:00
''' Unregister a callback for a timer
: param int timer : the timer to remove
2020-04-27 10:44:57 +03:00
: returns : - 1 on error , 0 on success
2017-03-17 16:35:53 +03:00
. . seealso : :
https : / / libvirt . org / html / libvirt - libvirt - event . html #virEventRemoveTimeoutFunc
'''
self . log . debug ( ' remove_timeout(timer= %d ) ' , timer )
2020-04-27 10:44:57 +03:00
try :
callback = self . callbacks . pop ( timer )
except KeyError as err :
self . log . warning ( ' remove_timeout(): no such timeout: %r ' , err . args [ 0 ] )
return - 1
2017-03-17 16:35:53 +03:00
callback . close ( )
2020-04-27 10:44:57 +03:00
return 0
2017-03-17 16:35:53 +03:00
2017-09-14 03:29:29 +03:00
2018-11-20 10:59:35 +03:00
_current_impl = None # type: Optional[virEventAsyncIOImpl]
2018-11-20 10:34:48 +03:00
2018-11-20 10:59:35 +03:00
def getCurrentImpl ( ) - > Optional [ virEventAsyncIOImpl ] :
2017-09-14 03:29:29 +03:00
''' Return the current implementation, or None if not yet registered '''
return _current_impl
2018-11-20 10:34:48 +03:00
2018-11-20 10:59:35 +03:00
def virEventRegisterAsyncIOImpl ( loop : asyncio . AbstractEventLoop = None ) - > virEventAsyncIOImpl :
2017-03-17 16:35:53 +03:00
''' Arrange for libvirt ' s callbacks to be dispatched via asyncio event loop
The implementation object is returned , but in normal usage it can safely be
discarded .
'''
2017-09-14 03:29:29 +03:00
global _current_impl
_current_impl = virEventAsyncIOImpl ( loop = loop ) . register ( )
return _current_impl