試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

PythonでUNIXシグナルのハンドラを扱う(後半)

PythonでUNIXシグナルのハンドラを扱う(前半)」の続き。

UNIXシグナルハンドラ処理に関する特殊な指定

signal.signal()の2番目の引数にsignal.SIG_DFLもしくはsignal.SIG_IGNという値を指定すると、ハンドラが指定される代わりに動作を変更する。
signal.SIG_DFL*1はシグナルに応じた「既定の動作」を行う。SIGINTシグナルを受け取った場合にはKeyboardInterrupt例外を出さずにプログラムを終了する。

# シグナルSIGINTを受け取った場合に
# 例外KeyboardInterruptを出さずに終了する
signal.signal(signal.SIGINT, signal.SIG_DFL)

signal.SIG_IGN*2ではそのシグナルを無視する(そのシグナルに対して無反応になる)。SIGINTに対してsignal.SIG_IGNを指定するとCtrl+Cを押しても終了ができなくなる。

# シグナルSIGINTを受け取っても無視するようにする場合
signal.signal(signal.SIGINT, signal.SIG_IGN)

KeyboardInterrupt例外の抑制例

前半の例では自作のハンドラの中で終了処理をしたが、下の例ではCtrl+Cを押したときにKeyboardInterruptに関するメッセージを出さずに終了する。

#! /usr/bin/python
# -*- encoding: utf-8 -*-

import signal
import sys
import os
try:
  import pygtk
  pygtk.require('2.0')
except:
  pass
try:
  import gtk
except:
  sys.exit(1)


class MainWindow(gtk.Window):
  """
  メインウィンドウ
  """
  def __init__(self, *args, **kwargs):
    gtk.Window.__init__(self, *args, **kwargs)
    self.set_title('test')
    self.set_size_request(320, 200)
    self.__accelgroup = gtk.AccelGroup()
    self.add_accel_group(self.__accelgroup)
    self.__item_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.__accelgroup)
    self.__menu_file = gtk.Menu()
    self.__menu_file.add(self.__item_quit)
    self.__item_file = gtk.MenuItem('_File')
    self.__item_file.set_submenu(self.__menu_file)
    self.__menubar = gtk.MenuBar()
    self.__menubar.append(self.__item_file)
    self.__button = gtk.Button()
    self.__button.set_label('Test')
    self.__statusbar = gtk.Statusbar()
    self.__vbox = gtk.VBox(False, 0)
    self.__vbox.pack_start(self.__menubar, expand=False, fill=False)
    self.__vbox.pack_start(self.__button)
    self.__vbox.pack_start(self.__statusbar, expand=False, fill=False)
    self.add(self.__vbox)
    self.__button.connect('clicked', self.__on_button_clicked)
    self.__item_quit.connect('activate', gtk.main_quit)
    self.connect('delete_event', gtk.main_quit)
  def __on_button_clicked(self, widget):
    """
    ボタンが押されたときの処理
    """
    print '__on_button_clicked()'

class PyGTKUnixSigHandlerTest2:
  """
  UNIXシグナルハンドラのテスト2
  """
  def main(self):
    """
    ウィンドウを作成してGTK+のメインループを呼ぶ
    """
    # シグナルSIGINTを受け取った場合に
    # 例外KeyboardInterruptを出さずに終了する
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    win = MainWindow()
    win.show_all()
    gtk.main()


if __name__ == '__main__':
  app = PyGTKUnixSigHandlerTest2()
  app.main()

KeyboardInterrupt例外はどこから?

signal.signal()によりUNIXシグナルハンドラの設定を行わない場合に発生するKeyboardInterruptの例外はどこで呼ばれているのかを調べるため、まずはsignal.getsignal()でSIGINTに対応する既定のハンドラを問い合わせてみると

>>> import signal
>>> signal.getsignal(signal.SIGINT)
<built-in function default_int_handler>

となった。これは関数なので呼び出してみると

>>> signal.getsignal(signal.SIGINT)()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyboardInterrupt

KeyboardInterrupt例外がここ(signal.default_int_handler()関数の中)で出てきた。文字通り、SIGINT用の既定のハンドラが標準で用意されており、この中で例外を出すようになっているようだ。

関連記事:

参考URL:

*1:「DeFauLt」より

*2:「IGNore」より