【Python】cronを生成するモジュールを作った

844 語
4 分
【Python】cronを生成するモジュールを作った

はじまり#

リサちゃん avatar
リサちゃん
うわ〜、cronをいちいち設定するのがめんどくせええ。
135ml avatar
135ml
あー、cronって時差を考慮したり、どの*が分だっけ、曜日だっけ? って感じで、めんどいよね。
リサちゃん avatar
リサちゃん
よし、今回はcronを生成するモジュールを作ってしまおうじゃないか!
135ml avatar
135ml
お!作ってくれるか!

今回作ったモジュール#

今回作ったモジュールのソースはこちらになります。

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:

Terminal window
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()

おしまい#

135ml avatar
135ml
`pytest-freezegun`とは、また良いモジュールを見つけたね。楽に、datetimeをモック出来るよね。
リサちゃん avatar
リサちゃん
うん、スゴイ楽でテストコードも見やすくなった!

補足#

cronの設定方法を忘れてた時に役立ちました。ちなみに、cronの行でチラホラ出てきてたURLです。

このページにあるcronチートシートもコメントアウトすれば、ソース内で使えると思うので、それもアリかも。

以上になります!

記事を共有

この記事が役に立ったなら、ぜひ他の人と共有してください!

【Python】cronを生成するモジュールを作った
https://endorphinbath.com/posts/python-make-cron/
著者
kinkinbeer135ml
公開日
2022-04-02
ライセンス
CC BY-NC-SA 4.0
関連記事 スマート
1
【Python】inputを使った処理をpytestでunittestしたい(monkeypatchでmockする)
Code Pythonスクリプトをpytestする時に、input()のようなビルトイン関数が入っている時にmockする方法を紹介します。monkeypatchを使用します。
2
【Python】Pydanticのvalidatorが非推奨だからfield_validatorを使って2段階バリデーションを実装する
Code @validatorはdeprecatedになってるし、@field_validatorにはpreとかalwaysフラグが無いから、from pydantic.functional_validators import field_validatorでインポートしたfield_validatorを使用する方法と、BeforeValidatorとAfterValidatorをAnnotateの中に入れる実装方法を試した。
3
【Python】pytestで同じディレクトリのモジュールをimportして、"ModuleNotFoundError: No module named"を出さなくする
Code Pythonスクリプトをpytestするとき、"ModuleNotFoundError: No module named"が表示されてしまった場合、この記事の方法でそのエラーが解決するかも。
4
【Python】.pyファイルにある関数とメソッドを全て取得する
Code Pythonで.pyファイルの中に記述されている関数およびメソッドを全て取得するスクリプトを掲載します。
5
【Python】複数の区切り文字を指定して文字列を配列に分割する
Code Pythonで文字列を配列に分割するスクリプトを掲載します。分割文字は配列で指定するように作っています。
ランダム記事 ランダム
Profile Image of the Author
kinkinbeer135ml
SIerをやめて、プログラミングを勉強しています。※Amazonアソシエイトに参加しています。
お知らせ
私のブログへようこそ!これはサンプルのお知らせです。
音楽
カバー

音楽

再生中なし

0:00 0:00
歌詞なし
カテゴリ
タグ
サイト統計
記事
287
カテゴリー
8
タグ
93
総文字数
486,174
運用日数
0
最終活動
0 日前

目次