Skip to main content

Calendar Scheduler for Recurring Events

PyPI version Pylint Unit tests

ru

Features

  • Executes an action function on a schedule.
  • Thread-safe: events can be added and canceled from different threads.
  • Supports intervals from milliseconds to years.
  • Uses the standard sched scheduler.
  • Timezone support.
  • Syntax similar to datetime.
  • Events can be added at any time: before or after the scheduler starts, and from any thread.
  • No additional packages required.

Installation

Install via pip:

pip install calsched

Install via uv:

uv pip install calsched

Import

from calsched import CalendarScheduler

Creating the Scheduler

scheduler = CalendarScheduler()

Running the Scheduler

Start the scheduler using the run() method:

scheduler.run()

However, it will immediately exit if no events are scheduled. At least one event must be scheduled before starting.

Example that prints the current time every second:

import datetime
from calsched import CalendarScheduler

def print_time():
    print(datetime.datetime.now())

scheduler = CalendarScheduler()
scheduler.enter_every_second_event(action=print_time)
scheduler.run()

In this case, run() blocks the thread in which it is called.

The action function is executed in the same thread as the run() method.

To allow adding events after the scheduler has started, you can add a placeholder event before calling run() to keep the scheduler active:

scheduler = CalendarScheduler()
stub_event = scheduler.enter_hourly_event(action=lambda: None)
scheduler.run()

You can stop run() by canceling the placeholder event:

scheduler.cancel(stub_event)

If there are multiple events, you need to cancel all events to stop the scheduler.

Example of running in a separate thread:

import datetime
import threading
from time import sleep
from calsched import CalendarScheduler

def print_time():
    print(datetime.datetime.now())

scheduler = CalendarScheduler()
stub_event = scheduler.enter_hourly_event(action=lambda: None)
thread = threading.Thread(target=scheduler.run)
thread.start()
sleep(1.0)

event = scheduler.enter_every_second_event(action=print_time)
sleep(5.0)
scheduler.cancel(stub_event)
scheduler.cancel(event)
thread.join()

Adding Events

To add events, use the enter_*_event() methods:

  • enter_every_millisecond_event(action=my_action, interval=N) – my_action() will be called every N milliseconds.
  • enter_every_second_event(action=my_action, interval=N) – my_action() will be called every N seconds.
  • enter_every_minute_event(action=my_action, interval=N) – my_action() will be called every N minutes.
  • enter_hourly_event(action=my_action, interval=N) – my_action() will be called every N hours.
  • enter_daily_event(action=my_action, interval=N) – my_action() will be called every N days.
  • enter_weekly_event(action=my_action, interval=N) – my_action() will be called every N weeks.
  • enter_monthly_event(action=my_action, interval=N) – my_action() will be called every N months.
  • enter_yearly_event(action=my_action, interval=N) – my_action() will be called every N years.

The interval parameter must be greater than or equal to 1 and has no upper limit.

By default, the interval is 1. That is, if you do not specify the interval parameter, the event will be triggered every 1 second, 1 minute, etc., depending on the method. The exception is enter_every_millisecond_event(), where the default interval is 100 milliseconds, since a 1-millisecond interval is not feasible on real machines.

Example of a daily event that prints the current time:

import datetime
from calsched import CalendarScheduler
    
def print_time():
    print(datetime.datetime.now())

scheduler = CalendarScheduler()
scheduler.enter_daily_event(action=print_time)
scheduler.run()

Time Parameters

An event can be scheduled for a specific moment: a particular second, minute, hour, day, and/or month.

The enter_yearly_event() method allows you to specify month, day, hour, minute, and second. Example: an event that runs 5 minutes before New Year (i.e., December 31 at 23:55:00):

scheduler.enter_yearly_event(action=my_action, month=12, day=31, hour=23, minute=55, second=0)

For monthly events, you can specify day, hour, minute, and second. Example: an event that runs on the first day of each month at 04:00:00:

scheduler.enter_monthly_event(action=my_action, day=1, hour=4, minute=0, second=0)

For monthly events, you cannot specify month, as it does not make sense. Similarly for other event types:

scheduler.enter_weekly_event(action=my_action, weekday=3, hour=4, minute=0, second=0)
scheduler.enter_daily_event(action=my_action, hour=4, minute=0, second=0)
scheduler.enter_hourly_event(action=my_action, minute=0, second=0)
scheduler.enter_every_minute_event(action=my_action, second=0)

The enter_every_second_event and enter_every_millisecond_event methods do not allow specifying execution time.

Time parameter value ranges:

  • 0 <= second <= 59,
  • 0 <= minute <= 59,
  • 0 <= hour <= 23,
  • 1 <= day <= 31,
  • 0 <= weekday <= 6 (Monday is 0, Sunday is 6),
  • 1 <= month <= 12.

In enter_monthly_event() and enter_yearly_event(), if the specified day is greater than the number of days in the month, the event will be executed on the last day of the month. For example, if you specify day=31, the event will run on January 31, then on February 28 or 29 (in a leap year).

Start and End Time for Periodic Events

You can specify the start and end time for a periodic event using the start_time and end_time parameters. If not specified, the event will run indefinitely starting from the current time.

  • start_time – time of the first execution.
  • end_time – time until which the event will be executed.

The start_time and end_time parameters are specified as timestamps (seconds since the Unix epoch, i.e., January 1, 1970). Typically, these values are obtained using time.time() or datetime.timestamp().

The time range is from start_time up to, but not including, end_time.

All enter_*_event() methods allow specifying start_time and end_time. Example: an event that runs every day from January 1, 2024, to January 1, 2025 (not inclusive):

start_time = datetime.datetime(2024, 1, 1, 0, 0, 0).timestamp()
end_time = datetime.datetime(2025, 1, 1, 0, 0, 0).timestamp()
scheduler.enter_daily_event(action=my_action, start_time=start_time, end_time=end_time)

Action Function Arguments

The action function can accept arguments. Use the action_args parameter for positional arguments and action_kwargs for keyword arguments.

All enter_*_event() methods allow specifying action_args and action_kwargs.

Example:

def my_action(arg1, arg2):
    pass

scheduler.enter_daily_event(action=my_action, action_args=("Hello",), action_kwargs={"arg2": 123})

The scheduler will call the function as follows:

my_action("Hello", arg2=123)

Metadata

Release files for calsched 1.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for calsched 1.1.1
File Size Uploaded
calsched-1.1.1.tar.gz 12.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for calsched 1.1.1
File Interpreter ABI Platform
calsched-1.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 21.1 kB

Release files / calsched-1.1.1.tar.gz

Download URL calsched-1.1.1.tar.gz
Size 12.9 kB
Tags Source
SHA-256 checksum
How to use checksums
1940dd0bcf227cb51cfd403bc472c16965bc9b694143cd4837f1436d03a80459
BLAKE2b-256 checksum
How to use checksums
30170b8920efffe73c87e6240964597317673c109240df1c7199f059db4220bd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.6.12

Release files / calsched-1.1.1-py3-none-any.whl

Download URL calsched-1.1.1-py3-none-any.whl
Size 8.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
76414f703f76fa1f04875455a581247aa332ad7c6a9a5843870b78da80dd5740
BLAKE2b-256 checksum
How to use checksums
26ee281f4616171651f528eba903bf6dde8f7d7e544933a97ec0be672a11a853
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.6.12

Release history Release notifications | RSS feed

This release

1.1.1 This release

2 release files

1.1.0

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page