はじまり
うわ〜、cronをいちいち設定するのがめんどくせええ。
あー、cronって時差を考慮したり、どの*が分だっけ、曜日だっけ
って感じで、めんどいよね。
よし、今回はcronを生成するモジュールを作ってしまおうじゃないか!
お!作ってくれるか!いけいけ!
今回作ったモジュール
今回作ったモジュールは自分用ライブラリの中に作りました。ちゃんと見たい方は、このリポジトリにあります。
ソースはこちらになります。
generaltool.py
import datetime
def generate_cron_from_datetime_now(minutes_scheduled_later : int, time_difference : int = 0) -> str:
"""
e.g.
'0 19 * * *' # At 04:00. – https://crontab.guru"
"""
if type(minutes_scheduled_later) != int:
raise TypeError("TypeError: minutes_scheduled_later must be int type.")
if type(time_difference) != int:
raise TypeError("TypeError: time_difference must be int type.")
cron_minute = 0
cron_hour = 0
dt_now = datetime.datetime.now(
datetime.timezone(datetime.timedelta(hours=time_difference))
)
minutes_from_an_hour = 60
if dt_now.minute + minutes_scheduled_later >= minutes_from_an_hour:
cron_minute = dt_now.minute + minutes_scheduled_later - minutes_from_an_hour
cron_hour = dt_now.hour + 1
else:
cron_minute = dt_now.minute + minutes_scheduled_later
cron_hour = dt_now.hour
hours_from_a_day = 24
if cron_hour >= hours_from_a_day:
cron_hour = cron_hour - hours_from_a_day
cron = f"{cron_minute} {cron_hour} * * *"
return cron
実は、GitHub Actions内でcronを設定する際、時差の考慮が要らない?
cronを設定する時に、時差の考慮は必須です。
例えば、日本時間で生活している方は、時差を+9:00としてcronを生成するかどうかを考慮すべきでしょう。本モジュールですと、例えば、こんな風に時差を設けます。
minutes_scheduled_later = 10
time_difference = 9
cron = generate_cron_from_datetime_now(minutes_scheduled_later, time_difference)
しかしながら、サーバ時間が”Asia/Tokyo”に設定してあれば、おそらく必要ないでしょう。
そして、そこに加えて、GitHub Actions内でcronを設定する際は、時差の考慮が不要のようです。
例えば、この記事で紹介しているツールは、GitHub Actions内でPythonファイルを実行して、本モジュールから時差0:00(UTC)でcronを設定しています。
しかし、GitHub ActionsもUTCで実行されているみたいなので、GitHub Actions内のコンテナ内のOS時間をUTCにしていれば、cronのずれは発生しないということです。
なので、こんな感じで、time_difference(時差)を0にしてcronを設定します。
minutes_scheduled_later = 10
time_difference = 0
cron = generate_cron_from_datetime_now(minutes_scheduled_later, time_difference)
テストコード
今回のテストは、pytest-freezegun
を使用しました。とても使いやすかったので、おすすめです。
shell session
pip install pytest-freezegun
テストコードはこちらになります。
test_generaltool.py
import pytest
from src.landmasterlibrary.generaltool import generate_cron_from_datetime_now
class Test_Generaltool:
@pytest.mark.freeze_time("2022-01-12 13:23:43")
def test_generate_cron_from_datetime_now_1_1(self):
minutes_scheduled_later = 10
time_difference = 0
actual = generate_cron_from_datetime_now(minutes_scheduled_later, time_difference)
expected_hours = 13
expected_minutes = 33
expected = f"{expected_minutes} {expected_hours} * * *"
assert actual == expected
@pytest.mark.freeze_time("2022-01-12 13:53:43")
def test_generate_cron_from_datetime_now_1_2(self):
minutes_scheduled_later = 10
time_difference = 0
actual = generate_cron_from_datetime_now(minutes_scheduled_later, time_difference)
expected_hours = 14
expected_minutes = 3
expected = f"{expected_minutes} {expected_hours} * * *"
assert actual == expected
@pytest.mark.freeze_time("2022-01-12 23:53:43")
def test_generate_cron_from_datetime_now_1_3(self):
minutes_scheduled_later = 10
time_difference = 0
actual = generate_cron_from_datetime_now(minutes_scheduled_later, time_difference)
expected_hours = 0
expected_minutes = 3
expected = f"{expected_minutes} {expected_hours} * * *"
assert actual == expected
@pytest.mark.freeze_time("2022-01-12 13:23:43")
def test_generate_cron_from_datetime_now_2_1(self):
minutes_scheduled_later = 10
time_difference = 2
actual = generate_cron_from_datetime_now(minutes_scheduled_later, time_difference)
expected_hours = 15
expected_minutes = 33
expected = f"{expected_minutes} {expected_hours} * * *"
assert actual == expected
@pytest.mark.freeze_time("2022-01-12 13:23:43")
def test_generate_cron_from_datetime_now_2_2(self):
minutes_scheduled_later = 10
time_difference = -2
actual = generate_cron_from_datetime_now(minutes_scheduled_later, time_difference)
expected_hours = 11
expected_minutes = 33
expected = f"{expected_minutes} {expected_hours} * * *"
assert actual == expected
@pytest.mark.freeze_time("2022-01-12 13:23:43")
def test_generate_cron_from_datetime_now_3_1(self):
minutes_scheduled_later = 10
actual = generate_cron_from_datetime_now(minutes_scheduled_later)
expected_hours = 13
expected_minutes = 33
expected = f"{expected_minutes} {expected_hours} * * *"
assert actual == expected
@pytest.mark.freeze_time("2022-01-12 23:53:43")
def test_generate_cron_from_datetime_now_4_1(self):
minutes_scheduled_later = "10"
time_difference = 0
with pytest.raises(TypeError) as e:
actual = generate_cron_from_datetime_now(minutes_scheduled_later, time_difference)
@pytest.mark.freeze_time("2022-01-12 23:53:43")
def test_generate_cron_from_datetime_now_4_2(self):
minutes_scheduled_later = 10
time_difference = "0"
with pytest.raises(TypeError) as e:
actual = generate_cron_from_datetime_now(minutes_scheduled_later, time_difference)
@pytest.mark.freeze_time("2022-01-12 23:53:43")
def test_generate_cron_from_datetime_now_4_3(self):
minutes_scheduled_later = None
time_difference = 0
with pytest.raises(TypeError) as e:
actual = generate_cron_from_datetime_now(minutes_scheduled_later, time_difference)
@pytest.mark.freeze_time("2022-01-12 23:53:43")
def test_generate_cron_from_datetime_now_4_4(self):
minutes_scheduled_later = 10
time_difference = None
with pytest.raises(TypeError) as e:
actual = generate_cron_from_datetime_now(minutes_scheduled_later, time_difference)
@pytest.mark.freeze_time("2022-01-12 23:53:43")
def test_generate_cron_from_datetime_now_5_1(self):
with pytest.raises(TypeError) as e:
actual = generate_cron_from_datetime_now()
おしまい
pytest-freezegun
とは、また良いモジュールを見つけたね。
楽に、datetimeをモック出来るよね。
うん。スゴイ楽! 簡単だからテストコードも見やすいし。
これからもドンドン使っていこー!
ちなみに、cronの行でチラホラ出てきてたURLだけど、cronの設定方法を忘れてた時に役立つので、よければご参考ください。
あと、このページにあるcronチートシートもコメントアウトすれば、ソース内で使えると思うので、それもアリかも。
以上になります!
コメント