https://github.com/appenz/maccal
A Python library to access local macOS calendars via EventKit and PyObjC. Works with macOS 26 Tahoe.
https://github.com/appenz/maccal
Last synced: 4 months ago
JSON representation
A Python library to access local macOS calendars via EventKit and PyObjC. Works with macOS 26 Tahoe.
- Host: GitHub
- URL: https://github.com/appenz/maccal
- Owner: appenz
- License: apache-2.0
- Created: 2026-02-27T23:39:55.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2026-03-12T19:49:02.000Z (4 months ago)
- Last Synced: 2026-03-30T17:36:45.249Z (4 months ago)
- Language: Python
- Size: 40 KB
- Stars: 0
- Watchers: 1
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# macCal
A Python library to access local macOS calendars via EventKit and PyObjC. Works with macOS 26 Tahoe.
## Overview
- Written in 100% python, easy to audit and extend
- Can search events, find free time, add/edit/delete events
- Fully typed
- Apache 2.0
Written by Guido Appenzeller, guido@appenzeller.net. Feedback and PR's welcome.
## Requirements
- macOS 14+ (Sonoma or later)
- Python 3.13, but probably works with earlier versions
- Calendar access permission
## Installation
With pip:
```bash
pip install maccal
```
For development:
```bash
git clone https://github.com/appenz/maccal
cd maccal
make install
```
## Quick Start
```python
from datetime import datetime, timedelta, timezone
from maccal import CalendarStore
store = CalendarStore()
# List calendars
for cal in store.list_calendars():
print(f"{cal.title} ({cal.type.name})")
# Get events for the next 7 days
now = datetime.now(tz=timezone.utc)
events = store.get_events(now, now + timedelta(days=7))
for event in events:
print(f"{event.start:%H:%M} {event.title}")
# Search events
results = store.find_events("standup", start=now, end=now + timedelta(days=30))
# Search specific fields
results = store.find_events(
"@acme.com",
start=now, end=now + timedelta(days=30),
fields=["attendee_email"],
)
# Create an event
event = store.add_event(
title="Team Lunch",
start=datetime(2026, 3, 15, 12, 0, tzinfo=timezone.utc),
end=datetime(2026, 3, 15, 13, 0, tzinfo=timezone.utc),
calendar="Personal",
location="Cafe",
)
# Find free time
slots = store.find_free_time(
start=datetime(2026, 3, 15, 9, 0, tzinfo=timezone.utc),
end=datetime(2026, 3, 15, 17, 0, tzinfo=timezone.utc),
duration=timedelta(minutes=30),
)
```
## Searchable Fields
When using `find_events`, you can restrict search to specific fields:
- `title`, `location`, `notes`, `url`, `calendar`
- `organizer_name`, `organizer_email`
- `attendee_name`, `attendee_email`
Pass `fields=None` (default) to search all fields.
## Recurring Events
When querying events, recurring events are automatically expanded into individual
occurrences. When editing or deleting a recurring event, you must specify which
occurrences to affect:
```python
store.update_event(
event.event_id,
title="New Title",
span="this", # "this" or "future"
occurrence_date=event.occurrence_date,
)
```
Omitting `occurrence_date` for a recurring event raises `ValueError`.
## Development
```bash
make install # Install with dev dependencies
make test # Run tests
make lint # Check code style
make format # Auto-format code
make build # Build package
make benchmark # Run performance benchmarks
```
## License
Apache 2.0. See [LICENSE](LICENSE).