3043
+ − 1 #!/usr/bin/env python3
+ − 2
+ − 3 # jp: a SAT command line tool
3479
+ − 4 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org)
3043
+ − 5
+ − 6 # This program is free software: you can redistribute it and/or modify
+ − 7 # it under the terms of the GNU Affero General Public License as published by
+ − 8 # the Free Software Foundation, either version 3 of the License, or
+ − 9 # (at your option) any later version.
+ − 10
+ − 11 # This program is distributed in the hope that it will be useful,
+ − 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ − 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ − 14 # GNU Affero General Public License for more details.
+ − 15
+ − 16 # You should have received a copy of the GNU Affero General Public License
+ − 17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
+ − 18
+ − 19 import sys
+ − 20 import asyncio
+ − 21 import logging as log
+ − 22 from sat.core.i18n import _
+ − 23 from sat_frontends.jp.constants import Const as C
+ − 24
+ − 25 log . basicConfig ( level = log . WARNING ,
+ − 26 format = '[ %(name)s ] %(message)s ' )
+ − 27
+ − 28 USER_INTER_MSG = _ ( "User interruption: good bye" )
+ − 29
+ − 30
+ − 31 class QuitException ( BaseException ):
+ − 32 """Quitting is requested
+ − 33
+ − 34 This is used to stop execution when host.quit() is called
+ − 35 """
+ − 36
+ − 37
+ − 38 def getJPLoop ( bridge_name ):
+ − 39 if 'dbus' in bridge_name :
+ − 40 import signal
+ − 41 import threading
+ − 42 from gi.repository import GLib
+ − 43
+ − 44 class JPLoop :
+ − 45
+ − 46 def run ( self , jp , args , namespace ):
+ − 47 signal . signal ( signal . SIGINT , self . _on_sigint )
+ − 48 self . _glib_loop = GLib . MainLoop ()
+ − 49 threading . Thread ( target = self . _glib_loop . run ) . start ()
+ − 50 loop = asyncio . get_event_loop ()
+ − 51 loop . run_until_complete ( jp . main ( args = args , namespace = namespace ))
+ − 52 loop . run_forever ()
+ − 53
+ − 54 def quit ( self , exit_code ):
+ − 55 loop = asyncio . get_event_loop ()
+ − 56 loop . stop ()
+ − 57 self . _glib_loop . quit ()
+ − 58 sys . exit ( exit_code )
+ − 59
+ − 60 def call_later ( self , delay , callback , * args ):
+ − 61 """call a callback repeatedly
+ − 62
+ − 63 @param delay(int): delay between calls in s
+ − 64 @param callback(callable): method to call
+ − 65 if the callback return True, the call will continue
+ − 66 else the calls will stop
+ − 67 @param *args: args of the callbac
+ − 68 """
+ − 69 loop = asyncio . get_event_loop ()
+ − 70 loop . call_later ( delay , callback , * args )
+ − 71
+ − 72 def _on_sigint ( self , sig_number , stack_frame ):
+ − 73 """Called on keyboard interruption
+ − 74
+ − 75 Print user interruption message, set exit code and stop reactor
+ − 76 """
+ − 77 print ( " \r " + USER_INTER_MSG )
+ − 78 self . quit ( C . EXIT_USER_CANCELLED )
+ − 79 else :
+ − 80 import signal
+ − 81 from twisted.internet import asyncioreactor
+ − 82 asyncioreactor . install ()
+ − 83 from twisted.internet import reactor , defer
+ − 84
+ − 85 class JPLoop :
+ − 86
+ − 87 def __init__ ( self ):
+ − 88 # exit code must be set when using quit, so if it's not set
+ − 89 # something got wrong and we must report it
+ − 90 self . _exit_code = C . EXIT_INTERNAL_ERROR
+ − 91
+ − 92 def run ( self , jp , * args ):
+ − 93 self . jp = jp
+ − 94 signal . signal ( signal . SIGINT , self . _on_sigint )
+ − 95 defer . ensureDeferred ( self . _start ( jp , * args ))
+ − 96 try :
+ − 97 reactor . run ( installSignalHandlers = False )
+ − 98 except SystemExit as e :
+ − 99 self . _exit_code = e . code
+ − 100 sys . exit ( self . _exit_code )
+ − 101
+ − 102 async def _start ( self , jp , * args ):
+ − 103 fut = asyncio . ensure_future ( jp . main ( * args ))
+ − 104 try :
+ − 105 await defer . Deferred . fromFuture ( fut )
+ − 106 except BaseException :
+ − 107 import traceback
+ − 108 traceback . print_exc ()
+ − 109 jp . quit ( 1 )
+ − 110
+ − 111 def quit ( self , exit_code ):
+ − 112 self . _exit_code = exit_code
+ − 113 reactor . stop ()
+ − 114
+ − 115 def _timeout_cb ( self , args , callback , delay ):
+ − 116 try :
+ − 117 ret = callback ( * args )
+ − 118 # FIXME: temporary hack to avoid traceback when using XMLUI
+ − 119 # to be removed once create_task is not used anymore in
+ − 120 # xmlui_manager (i.e. once sat_frontends.tools.xmlui fully supports
+ − 121 # async syntax)
+ − 122 except QuitException :
+ − 123 return
+ − 124 if ret :
+ − 125 reactor . callLater ( delay , self . _timeout_cb , args , callback , delay )
+ − 126
+ − 127 def call_later ( self , delay , callback , * args ):
+ − 128 reactor . callLater ( delay , self . _timeout_cb , args , callback , delay )
+ − 129
+ − 130 def _on_sigint ( self , sig_number , stack_frame ):
+ − 131 """Called on keyboard interruption
+ − 132
+ − 133 Print user interruption message, set exit code and stop reactor
+ − 134 """
+ − 135 print ( " \r " + USER_INTER_MSG )
+ − 136 self . _exit_code = C . EXIT_USER_CANCELLED
+ − 137 reactor . callFromThread ( reactor . stop )
+ − 138
+ − 139
+ − 140 if bridge_name == "embedded" :
+ − 141 raise NotImplementedError
+ − 142 # from sat.core import sat_main
+ − 143 # sat = sat_main.SAT()
+ − 144
+ − 145 return JPLoop