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

リサちゃん
うわ〜、cronをいちいち設定するのがめんどくせええ。

135ml
あー、cronって時差を考慮したり、どの*が分だっけ、曜日だっけ? って感じで、めんどいよね。

リサちゃん
よし、今回はcronを生成するモジュールを作ってしまおうじゃないか!

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 = 10time_difference = 9cron = 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 = 10time_difference = 0cron = 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()おしまい

135ml
`pytest-freezegun`とは、また良いモジュールを見つけたね。楽に、datetimeをモック出来るよね。

リサちゃん
うん、スゴイ楽でテストコードも見やすくなった!
補足
cronの設定方法を忘れてた時に役立ちました。ちなみに、cronの行でチラホラ出てきてたURLです。
Crontab.guru - The cron schedule expression generator
An easy to use editor for crontab schedules.
crontab.guru
このページにあるcronチートシートもコメントアウトすれば、ソース内で使えると思うので、それもアリかも。
Github Actions チートシート
zenn.dev

以上になります!
記事を共有
この記事が役に立ったなら、ぜひ他の人と共有してください!
【Python】cronを生成するモジュールを作った
https://endorphinbath.com/posts/python-make-cron/ 【GAS、Google Spreadsheet】ブログに使用した画像をGoogleドライブで管理するために書いたスクリプト
【画像編集】Affinity Designerで論理差分(Boolean Difference)などの論理処理を行う時に気をつけること
関連記事 スマート
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で文字列を配列に分割するスクリプトを掲載します。分割文字は配列で指定するように作っています。
ランダム記事 ランダム