私は自分の経験を共有したかったので、関数によって返される値を確認することが重要である理由についてお話したいと思います。 例としてpythonとctypesを取り上げます。 少し前に、かなり興味深いバグに遭遇しました。その本質は、スクリプトがLinuxシステムで実行されたときに不正確なデータがあったが、トラックバックがなく、Windowsシステムではすぐにトラックバックがあったことです。 コードを調べると、strptime()関数に送られる日付データが間違っていることが原因であることがわかりました。 それでは、Pythonでstrptime()関数を使用する例を見てみましょう。
Windowsでは、datetimeモジュールのstrptime()関数を使用できます
有効な日付の例:
from datetime import datetime date_str = "30-10-2016 16:18" format_str = "%d-%m-%Y %H:%M" dt = datetime.strptime(date_str, format_str) print repr(str(dt))
この場合に表示される内容は次のとおりです。
2016-10-30 16:18:00
上記のコードで日付文字列を誤ったものに置き換えた場合:
date_str = "10/30/2016 16:fhadjkfh"
次の出力が表示されます。
File "E:\Python27\lib\_strptime.py", line 325, in _strptime (data_string, format)) ValueError: time data '30-10-2016 16:fhadjkfh' does not match format '%d-%m-%Y %H:%M'
Linuxを使用する場合、strctime()関数をlibcライブラリからインポートして使用することもできます
Cのstrptime()関数の詳細については、 こちらをお読みください 。 この場合、日付パラメーターは次の構造で保存されることに注意してください。
struct tm { int tm_sec; /* Seconds (0-60) */ int tm_min; /* Minutes (0-59) */ int tm_hour; /* Hours (0-23) */ int tm_mday; /* Day of the month (1-31) */ int tm_mon; /* Month (0-11) */ int tm_year; /* Year - 1900 */ int tm_wday; /* Day of the week (0-6, Sunday = 0) */ int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */ int tm_isdst; /* Daylight saving time */ };
Pythonがctypesモジュールを操作するときにstrptime()を使用する方法は次のとおりです。
from ctypes import * libc = CDLL('libc.so.6') class TM(Structure): _fields_ = [ ("tm_sec", c_int), ("tm_min", c_int), ("tm_hour", c_int), ("tm_mday", c_int), ("tm_mon", c_int), ("tm_year", c_int), ("tm_wday", c_int), ("tm_yday", c_int), ("tm_isdst", c_int) ] tm_struct = TM() for field_name, field_type in tm_struct._fields_: print("{}: {}".format(field_name, getattr(tm_struct, field_name))) strptime = libc.strptime strptime.restype = c_char_p date_str = "30-10-2016 16:18" format_str = "%d-%m-%Y %H:%M" rez = strptime(date_str, format_str, pointer(tm_struct)) print("######") for field_name, field_type in tm_struct._fields_: print("{}: {}".format(field_name, getattr(tm_struct, field_name))) print "strptime returned: %s" % repr(rez)
そして、次の結論が表示されます
tm_sec: 0 tm_min: 0 tm_hour: 0 tm_mday: 0 tm_mon: 0 tm_year: 0 tm_wday: 0 tm_yday: 0 tm_isdst: 0 ###### tm_sec: 0 tm_min: 18 tm_hour: 16 tm_mday: 30 tm_mon: 9 tm_year: 116 tm_wday: 0 tm_yday: 303 tm_isdst: 0 strptime returned: ''
ここで、tm_structオブジェクトのフィールドはゼロで初期化され、空の文字列はstrptime()関数によって返される値になることに注意することが重要です。
上記のコードで日付文字列を誤ったものに置き換えた場合:
date_str = "10-30-2016fahdkjfa 16:18"
次に、次の出力が表示されます(簡潔にするために、作成後にtm_structオブジェクトのフィールドの値の出力を削除しました)。
tm_sec: 0 tm_min: 0 tm_hour: 0 tm_mday: 30 tm_mon: 9 tm_year: 116 tm_wday: 0 tm_yday: 0 tm_isdst: 0 strptime returned: None
ここで、tm_structオブジェクトの日付が正しくない場合、誤ったデータの前に日付行で認識できるフィールドのみが変更され、残りのフィールドはゼロ値のままになることがわかります。 また、strptime()関数自体はNone
を返します。 同時に、トラックバックはありません。 このため、注意して、関数から返される値を確認することが重要です。
ここでの正しい呼び出しオプションは、たとえば次のとおりです。
# '' None False, None if strptime(date_str, format_str, pointer(tm_struct)) is None: raise ValueError("datestring `{}` does not match expected format `{}`".format(date_str, format_str))
ここで、たとえば、独自のスケジューラアグリゲーターが何かあると想像してみましょう。 また、コードが正しくない場合、アグリゲーターが表示するスケジュールと、それを受け取ったサイトのスケジュールとの違いをユーザーが確認した場合にのみ、このようなバグに気付くことができます。