diff --git a/crontask/__init__.py b/crontask/__init__.py index 45a64a0..5905c65 100644 --- a/crontask/__init__.py +++ b/crontask/__init__.py @@ -13,7 +13,7 @@ from . import _version from .contrib import sentry -__all__ = ["cron", "scheduler", "VERSION", "__version__"] +__all__ = ["VERSION", "__version__", "cron", "scheduler"] __version__ = _version.version VERSION = _version.version_tuple @@ -62,9 +62,11 @@ def decorator(task: Task) -> Task: # so in Berlin Monday is 0, and Sunday is 6. We use literals to avoid # confusion. Literals are also more readable and crontab conform. if any(i.isdigit() for i in day_schedule): - raise ValueError( - "Please use a literal day of week (Mon, Tue, Wed, Thu, Fri, Sat, Sun) or *" + msg = ( + "Please use a literal day of week " + "(Mon, Tue, Wed, Thu, Fri, Sat, Sun) or *" ) + raise ValueError(msg) trigger = CronTrigger.from_crontab( schedule, timezone=timezone.get_default_timezone(), diff --git a/crontask/management/commands/crontask.py b/crontask/management/commands/crontask.py index 617894e..c791cb0 100644 --- a/crontask/management/commands/crontask.py +++ b/crontask/management/commands/crontask.py @@ -20,7 +20,8 @@ def kill_softly(signum, frame): """Raise a KeyboardInterrupt to stop the scheduler and release the lock.""" signame = signal.Signals(signum).name - raise KeyboardInterrupt(f"Received {signame} ({signum}), shutting down…") + msg = f"Received {signame} ({signum}), shutting down…" + raise KeyboardInterrupt(msg) class Command(BaseCommand): diff --git a/crontask/utils.py b/crontask/utils.py index c7592b2..ad3f8a4 100644 --- a/crontask/utils.py +++ b/crontask/utils.py @@ -16,7 +16,7 @@ def extend(self, additional_time=None, replace_ttl=False): if redis_url := get_settings().REDIS_URL: import redis - from redis.exceptions import LockError, LockNotOwnedError # noqa + from redis.exceptions import LockError, LockNotOwnedError redis_client = redis.Redis.from_url(redis_url) lock = redis_client.lock( diff --git a/pyproject.toml b/pyproject.toml index c2c12ce..cdea914 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,18 +78,28 @@ name = "crontask" [tool.ruff] src = [ "crontask", "tests" ] [tool.ruff.lint] -select = [ - "B", # flake8-bugbear - "C", # flake8-comprehensions - "D", # pydocstyle - "E", # pycodestyle errors - "F", # pyflakes - "I", # isort - "S", # flake8-bandit - "UP", # pyupgrade - "W", # pycodestyle warnings +extend-select = [ + "C90", + "D", + "D213", + "DJ", + "E", + "G", + "N", + "PERF", + "PT", + "PTH", + "RET", + "S", + "SIM", + "SLOT", + "T20", + "TRY", + "UP", + "W", ] -ignore = [ "B904", "D1", "E501", "S101" ] +ignore = [ "D1", "D203", "D212", "E501", "RUF01" ] +per-file-ignores."tests/**" = [ "DJ006", "DJ007", "DJ008", "PLR2004", "S101", "S105", "S106", "S311", "S603", "S607" ] isort.combine-as-imports = true isort.force-wrap-aliases = true isort.section-order = [ "future", "standard-library", "third-party", "first-party", "local-folder" ] diff --git a/tests/contrib/test_sentry.py b/tests/contrib/test_sentry.py index e0387d4..610203e 100644 --- a/tests/contrib/test_sentry.py +++ b/tests/contrib/test_sentry.py @@ -32,7 +32,7 @@ def test_monitor_config__cron_trigger__with_day_of_week(): @pytest.mark.parametrize( - "seconds,expected", + ("seconds", "expected"), [ (60, {"type": "interval", "value": 1, "unit": "minute"}), (300, {"type": "interval", "value": 5, "unit": "minute"}), @@ -65,7 +65,7 @@ def test_monitor_config__unsupported_interval_trigger(seconds): @pytest.mark.parametrize( - "kwargs,expected", + ("kwargs", "expected"), [ ({"years": 1}, {"type": "interval", "value": 1, "unit": "year"}), ({"months": 2}, {"type": "interval", "value": 2, "unit": "month"}), @@ -87,7 +87,7 @@ def test_monitor_config__calendar_interval_trigger(kwargs, expected): CalendarIntervalTrigger( months=1, days=1, timezone=timezone.get_default_timezone() ), - DateTrigger(run_date=datetime.datetime(2021, 1, 1)), + DateTrigger(run_date=datetime.datetime(2021, 1, 1, tzinfo=datetime.UTC)), ], ) def test_monitor_config__unsupported(trigger): diff --git a/tests/test_commands.py b/tests/test_commands.py index e1c9636..f5436ae 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -17,7 +17,7 @@ def test_kill_softly(): class Testcrontask: - @pytest.fixture() + @pytest.fixture def patch_launch(self, monkeypatch): monkeypatch.setattr( "crontask.management.commands.crontask.Command.launch_scheduler", @@ -94,10 +94,12 @@ def test_load_tasks__optional_tasks(self, patch_launch, monkeypatch): def test_locked(self): """A lock was already acquired by another process.""" pytest.importorskip("redis", reason="redis is not installed") - with utils.redis_client.lock("crontask-lock", blocking_timeout=0): - with io.StringIO() as stderr: - call_command("crontask", stderr=stderr) - assert "Another scheduler is already running." in stderr.getvalue() + with ( + utils.redis_client.lock("crontask-lock", blocking_timeout=0), + io.StringIO() as stderr, + ): + call_command("crontask", stderr=stderr) + assert "Another scheduler is already running." in stderr.getvalue() def test_locked_no_refresh(self, monkeypatch): """A lock was acquired, but it was not refreshed.""" @@ -143,12 +145,14 @@ def mock_signal(signum, handler): signal_calls.append(signum) return original_signal(signum, handler) - with patch("signal.signal", side_effect=mock_signal): - with io.StringIO() as stdout: - call_command("crontask", stdout=stdout) - assert signal.SIGHUP in signal_calls - assert signal.SIGTERM in signal_calls - assert signal.SIGINT in signal_calls + with ( + patch("signal.signal", side_effect=mock_signal), + io.StringIO() as stdout, + ): + call_command("crontask", stdout=stdout) + assert signal.SIGHUP in signal_calls + assert signal.SIGTERM in signal_calls + assert signal.SIGINT in signal_calls @pytest.mark.skipif(sys.platform != "win32", reason="Windows-specific test") def test_launch_scheduler_windows_signals(self, monkeypatch): @@ -164,13 +168,15 @@ def mock_signal(signum, handler): signal_calls.append(signum) return original_signal(signum, handler) - with patch("signal.signal", side_effect=mock_signal): - with io.StringIO() as stdout: - call_command("crontask", stdout=stdout) - assert signal.SIGTERM in signal_calls - assert signal.SIGINT in signal_calls - if hasattr(signal, "SIGBREAK"): - assert signal.SIGBREAK in signal_calls - assert not any( - sig == getattr(signal, "SIGHUP", None) for sig in signal_calls - ) + with ( + patch("signal.signal", side_effect=mock_signal), + io.StringIO() as stdout, + ): + call_command("crontask", stdout=stdout) + assert signal.SIGTERM in signal_calls + assert signal.SIGINT in signal_calls + if hasattr(signal, "SIGBREAK"): + assert signal.SIGBREAK in signal_calls + assert not any( + sig == getattr(signal, "SIGHUP", None) for sig in signal_calls + ) diff --git a/tests/test_tasks.py b/tests/test_tasks.py index b65b3d9..c73dd09 100644 --- a/tests/test_tasks.py +++ b/tests/test_tasks.py @@ -92,7 +92,7 @@ def test_cron__trigger_attribute(): ) def test_cron__error(schedule): assert not scheduler.remove_all_jobs() - with pytest.raises(ValueError) as e: + with pytest.raises(ValueError, match="Please use a literal day of week") as e: cron(schedule)(tasks.heartbeat) assert ( "Please use a literal day of week (Mon, Tue, Wed, Thu, Fri, Sat, Sun) or *" diff --git a/tests/testapp/settings.py b/tests/testapp/settings.py index 6716342..d77eb05 100644 --- a/tests/testapp/settings.py +++ b/tests/testapp/settings.py @@ -21,7 +21,7 @@ # See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = "django-insecure--@w!1qsi6azo&=a**ia3b43k^@q9aor_rqyu=1i5yd-zinpo)0" # noqa: S105 +SECRET_KEY = "django-insecure--@w!1qsi6azo&=a**ia3b43k^@q9aor_rqyu=1i5yd-zinpo)0" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True