Skip to main content

Liturgical calendar library for calculating Catholic liturgical dates and calendars

Project description

Romcal

A Python library for calculating Catholic liturgical dates and generating liturgical calendars. Powered by Rust via UniFFI bindings.

For the Rust library, see romcal. For command-line usage, see the CLI documentation.

Installation

pip install romcal

Or with uv:

uv add romcal

Note: Pre-built wheels are available for Python 3.11-3.14 on Linux (x86_64, aarch64, musl), macOS (Intel, Apple Silicon), and Windows (x64, ARM64). For other platforms, Rust is required to build from source.

Quick Start

from romcal import Romcal

# Create a default instance
romcal = Romcal()

# Get a specific liturgical date
easter = romcal.get_date("easter_sunday", 2026)
print(easter)  # "2026-04-05"

# Generate the liturgical calendar for year 2026
calendar = romcal.liturgical_calendar(2026)

# Access a specific date
christmas = calendar.get("2026-12-25")
if christmas:
    print(christmas[0]["fullname"])  # "The Nativity of the Lord"

Configuration

Using Keyword Arguments

from romcal import CalendarContext, Romcal

# With calendar and locale
romcal1 = Romcal(calendar="france", locale="fr")

# With full configuration
romcal2 = Romcal(
    calendar="france",
    locale="fr",
    context=CalendarContext.LITURGICAL,
    epiphany_on_sunday=True,
    ascension_on_sunday=True,
    corpus_christi_on_sunday=True,
)

Configuration Options

Option Type Default Description
calendar str "general_roman" Calendar ID (e.g., "france", "united_states")
locale str "en" Locale code (e.g., "fr", "es")
context CalendarContext GREGORIAN GREGORIAN (Jan-Dec) or LITURGICAL (Advent-Advent)
epiphany_on_sunday bool False Celebrate Epiphany on Sunday (Jan 2-8) instead of Jan 6
ascension_on_sunday bool False Celebrate Ascension on Sunday instead of Thursday
corpus_christi_on_sunday bool True Celebrate Corpus Christi on Sunday instead of Thursday
easter_calculation_type EasterCalculationType GREGORIAN GREGORIAN or JULIAN Easter calculation
calendar_definitions_json str None JSON string of calendar definitions
resources_json str None JSON string of locale resources

Loading Calendar Data

Without loading data, only the Proper of Time is available. To include the General Roman Calendar, particular calendars, and localized names, load calendar definitions and resources:

import json
from pathlib import Path
from romcal import Romcal

DATA_DIR = Path("data")

def load_calendar_definitions():
    """Load all calendar definitions from the data folder."""
    definitions = []
    for json_file in (DATA_DIR / "definitions").rglob("*.json"):
        with open(json_file) as f:
            definitions.append(json.load(f))
    return definitions

def load_resources():
    """Load all resources from the data folder."""
    resources_dir = DATA_DIR / "resources"
    resources = []

    # Group files by locale
    files_by_locale = {}
    for json_file in resources_dir.rglob("*.json"):
        locale = json_file.parent.name
        files_by_locale.setdefault(locale, []).append(json_file)

    # Merge files for each locale
    for locale, locale_files in files_by_locale.items():
        metadata = None
        entities = {}

        for file in locale_files:
            with open(file) as f:
                content = json.load(f)
            if file.name == "meta.json":
                metadata = content.get("metadata")
            elif file.name.startswith("entities.") and "entities" in content:
                entities.update(content["entities"])

        resources.append({
            "locale": locale,
            "metadata": metadata,
            "entities": entities if entities else None,
        })

    return resources

# Create instance with loaded data
romcal = Romcal(
    calendar="france",
    locale="fr",
    calendar_definitions_json=json.dumps(load_calendar_definitions()),
    resources_json=json.dumps(load_resources()),
)

API

Romcal()

Creates a new Romcal instance.

from romcal import Romcal

# Default configuration
romcal1 = Romcal()

# With calendar and locale
romcal2 = Romcal(calendar="france", locale="fr")

# With partial configuration
romcal3 = Romcal(
    calendar="france",
    locale="fr",
    epiphany_on_sunday=True,
)

Romcal Instance

liturgical_calendar(year)

Generate the complete liturgical calendar for a given year.

calendar = romcal.liturgical_calendar(2026)
# calendar is dict[str, list[dict]]
# Keys are dates in "YYYY-MM-DD" format

for date, days in calendar.items():
    for day in days:
        print(f"{date}: {day['fullname']} ({day['rank']})")

mass_calendar(year)

Generate a mass-centric view of the calendar organized by civil date and mass time.

mass_calendar = romcal.mass_calendar(2026)
# mass_calendar is dict[str, list[dict]]

# Evening masses appear on the previous civil day
easter_vigil_day = mass_calendar.get("2026-04-04")
if easter_vigil_day:
    vigil = next((m for m in easter_vigil_day if m["mass_time"] == "EASTER_VIGIL"), None)
    if vigil:
        print(vigil["liturgical_date"])  # "2026-04-05"

get_date(id, year)

Get a liturgical date by its ID.

easter = romcal.get_date("easter_sunday", 2026)      # "2026-04-05"
ash_wed = romcal.get_date("ash_wednesday", 2026)     # "2026-02-18"
pentecost = romcal.get_date("pentecost_sunday", 2026) # "2026-05-24"
christmas = romcal.get_date("christmas", 2026)        # "2026-12-25"

Any date ID from the liturgical calendar can be used (e.g., easter_sunday, christmas, ordinary_time_5_monday).

Properties

Access the resolved configuration:

print(romcal.calendar)                # "france"
print(romcal.locale)                  # "fr"
print(romcal.epiphany_on_sunday)      # True
print(romcal.ascension_on_sunday)     # False
print(romcal.corpus_christi_on_sunday) # True
print(romcal.easter_calculation_type)  # "GREGORIAN"
print(romcal.context)                  # "GREGORIAN"

Key Types

For detailed documentation on liturgical types (seasons, ranks, precedence, colors, cycles, mass times), see the romcal documentation.

Error Handling

All operations may raise RomcalError:

from romcal import Romcal, RomcalError

try:
    romcal = Romcal()
    # Year must be >= 1583 (Gregorian calendar adoption)
    calendar = romcal.liturgical_calendar(1500)
except RomcalError as e:
    print(f"Romcal error: {e}")

Development

Requirements

  • Python 3.11 or later
  • uv (recommended) or pip
  • Rust 1.85 or later

Setup

cd bindings/python

# Create virtual environment
uv venv

# Install build tools
uv pip install maturin uniffi-bindgen

# Build and install the native extension
uv run maturin develop

# Install dev dependencies (pytest, ruff, etc.)
uv pip install pytest taskipy ruff mypy

Available Tasks

Using taskipy:

task build          # maturin build --release
task develop        # maturin develop
task generate-types # Generate Pydantic types from JSON schema
task test           # pytest tests/ -v
task test-run       # pytest tests/ (without verbose)
task format         # ruff format .
task format-check   # ruff format --check .
task lint           # ruff check .
task lint-fix       # ruff check --fix .
task typecheck      # mypy src/

Testing

task test      # Run tests with verbose output
task test-run  # Run tests once

Project Structure

bindings/python/
├── src/
│   └── romcal/
│       ├── __init__.py    # Main entry point, API wrapper
│       ├── types.py       # Generated types from JSON schema (Pydantic)
│       └── _uniffi/       # Generated UniFFI bindings
├── tests/
│   ├── conftest.py        # Pytest fixtures (data loading)
│   ├── test_config.py     # Configuration tests
│   ├── test_calendar.py   # Calendar generation tests
│   └── test_data_loading.py # Data loading tests
├── examples/
│   └── basic_usage.py     # Usage example with data loading
└── pyproject.toml         # Project configuration

Running Examples

# Basic usage example (loads data from /data folder)
python examples/basic_usage.py

Regenerating Types

If you modify Rust types in core/src/, you need to regenerate types.py:

uv run task generate-types

This uses datamodel-codegen to generate Pydantic models from JSON schema.

Related

License

Apache License 2.0. See LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

romcal-4.0.0b5.tar.gz (188.9 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

romcal-4.0.0b5-py3-none-win_arm64.whl (752.9 kB view details)

Uploaded Python 3Windows ARM64

romcal-4.0.0b5-py3-none-win_amd64.whl (791.0 kB view details)

Uploaded Python 3Windows x86-64

romcal-4.0.0b5-py3-none-musllinux_1_2_x86_64.whl (1.2 MB view details)

Uploaded Python 3musllinux: musl 1.2+ x86-64

romcal-4.0.0b5-py3-none-musllinux_1_2_aarch64.whl (1.2 MB view details)

Uploaded Python 3musllinux: musl 1.2+ ARM64

romcal-4.0.0b5-py3-none-manylinux_2_24_x86_64.whl (997.2 kB view details)

Uploaded Python 3manylinux: glibc 2.24+ x86-64

romcal-4.0.0b5-py3-none-manylinux_2_24_aarch64.whl (985.8 kB view details)

Uploaded Python 3manylinux: glibc 2.24+ ARM64

romcal-4.0.0b5-py3-none-macosx_11_0_arm64.whl (910.1 kB view details)

Uploaded Python 3macOS 11.0+ ARM64

romcal-4.0.0b5-py3-none-macosx_10_12_x86_64.whl (926.3 kB view details)

Uploaded Python 3macOS 10.12+ x86-64

File details

Details for the file romcal-4.0.0b5.tar.gz.

File metadata

  • Download URL: romcal-4.0.0b5.tar.gz
  • Upload date:
  • Size: 188.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for romcal-4.0.0b5.tar.gz
Algorithm Hash digest
SHA256 357c20cd497a022aad7c4db191aae3d0b8974c8c7ef99b474b4ae279315096f6
MD5 d36ba59e00b37e82467a451b11557d5c
BLAKE2b-256 27f08eedb6c28d3f7091be6c1456805b4398e0bd0c4d4aa07eb7f4b2cc2c43c0

See more details on using hashes here.

File details

Details for the file romcal-4.0.0b5-py3-none-win_arm64.whl.

File metadata

  • Download URL: romcal-4.0.0b5-py3-none-win_arm64.whl
  • Upload date:
  • Size: 752.9 kB
  • Tags: Python 3, Windows ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for romcal-4.0.0b5-py3-none-win_arm64.whl
Algorithm Hash digest
SHA256 55b277e03854aa7121df18c0f878ff850b6f8df7bb26282304576dd68e7a6dd5
MD5 8c396a013c7517fa99785b090ad6c9a4
BLAKE2b-256 4a79a1eaa5b053e61caa8510e47276ce7dceb116c44f6280ee6588c71652ad50

See more details on using hashes here.

File details

Details for the file romcal-4.0.0b5-py3-none-win_amd64.whl.

File metadata

  • Download URL: romcal-4.0.0b5-py3-none-win_amd64.whl
  • Upload date:
  • Size: 791.0 kB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for romcal-4.0.0b5-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 0e1389d41a2965825a521b03298d3256b0992d4c4b516c9faf8956599247b059
MD5 53f1ae20a9013593f3b0d66e5f6bd114
BLAKE2b-256 7ed2313f713567e2ec5fcba67140de2048df56f9e42c89d20021a7fc3ec7d908

See more details on using hashes here.

File details

Details for the file romcal-4.0.0b5-py3-none-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for romcal-4.0.0b5-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 632e5ef85290251a575b1c171bf3860c28fc289d7c5b421a2ae795e5bdd0385d
MD5 25ec94ee0274aedd231c74e3345d82e0
BLAKE2b-256 8f8ce92c7381de8d57518f874ba9421ae1f50d51ba031858d0528df98bac9567

See more details on using hashes here.

File details

Details for the file romcal-4.0.0b5-py3-none-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for romcal-4.0.0b5-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 1b112aa9cd2d3dd972ba897c316dded2b9730f5578ae1b5f37948b8257ed7dc3
MD5 dc43159d8b0dcd882752025ae65560b2
BLAKE2b-256 3614fb3d4f431bfded08b6d6b48580c0dfdaeb7416640a57f1ce53ca8a18e21b

See more details on using hashes here.

File details

Details for the file romcal-4.0.0b5-py3-none-manylinux_2_24_x86_64.whl.

File metadata

File hashes

Hashes for romcal-4.0.0b5-py3-none-manylinux_2_24_x86_64.whl
Algorithm Hash digest
SHA256 8bec6764a073994bedfc9113b3045817485eef5c7178ac12f829d33fb2a65a0f
MD5 0b5a94713034e2a8656127222e3f617b
BLAKE2b-256 de586b5567b7983c097ddce681d44072bda78a1657a7d30d4c165e227ec63b33

See more details on using hashes here.

File details

Details for the file romcal-4.0.0b5-py3-none-manylinux_2_24_aarch64.whl.

File metadata

File hashes

Hashes for romcal-4.0.0b5-py3-none-manylinux_2_24_aarch64.whl
Algorithm Hash digest
SHA256 899cd2c7fc86419eaabc6e6092302798c9e764da57977755dca259bb19118c1b
MD5 8ef219d84c0c0de644f98e6ec23abac2
BLAKE2b-256 3a6837ae260cce6fa78f79ec55aac722832945f26cefac3e4a6727428e2fad4e

See more details on using hashes here.

File details

Details for the file romcal-4.0.0b5-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for romcal-4.0.0b5-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 a183008a3c095e576760dbe4775b810c899428178bebc7f3945f988cc2b3d2e5
MD5 7d9757a86e6f777cbeed6d2c3c66e4df
BLAKE2b-256 1a0ac4c5a5dc3eb255112990bfa69982767e91b1dc3e09bd196e19ae598fe6c7

See more details on using hashes here.

File details

Details for the file romcal-4.0.0b5-py3-none-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for romcal-4.0.0b5-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 1c2859d2c23a85917787aaf277fbd44c1e13244759e3a4e9888426b4c299adb8
MD5 11eb1e262fcf9f774ddc1bea62375767
BLAKE2b-256 fe69c7af62d9dad334873a9d8398ccb7a19102bfe2b3705646d3538b387f016c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page