cron-descriptor

A Python library that converts cron expressions into plain-English, localized descriptions.

Library
PyPI
v2.1.0
186stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
64/100Good
Development Activity56
Maintenance48
Community72
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
75/100Good
Architecture78
Code Quality85
Innovation55
Learning Curve80

cron-descriptor parses standard cron expressions and turns them into human-readable sentences, so a schedule like */10 * * * * becomes “Every 10 minutes” instead of a string only a scheduler can love. It supports 5-, 6-, and 7-field expressions (with optional seconds and year fields), all the special characters cron authors rely on (* / , - ? L W #), and configurable output casing (Sentence, Title, Lower).

The library is a Python port of the well-known .NET cron-expression-descriptor project, and it carries that project’s most distinctive feature forward: built-in localization for roughly 31 languages via gettext .mo catalogs, so the same expression can be described in English, German, Japanese, or Russian without any extra work from the caller. It’s a small, dependency-light utility aimed at admin dashboards, cron-job management tools, and any UI that needs to explain a schedule to a non-technical user.

What You Get

  • get_description() helper - a one-line function that takes a cron string and returns its English (or locale-specific) description.
  • ExpressionDescriptor class - full control over casing type, verbosity, day-of-week indexing, 12/24-hour format, and locale via an Options object or keyword arguments.
  • 5/6/7-field expression support - handles standard 5-field cron as well as extended forms with seconds and/or a year field.
  • ~31 bundled locales - human-readable output in English, German, French, Spanish, Japanese, Chinese (Simplified & Traditional), Russian, Hebrew, Arabic-adjacent locales, and more, via gettext .mo files.
  • Typed exception hierarchy - FormatError, MissingFieldError, and WrongArgumentError give callers specific, catchable failure modes instead of bare exceptions.
  • CLI entry point (__main__.py) - the package can be invoked directly to describe an expression from the command line.

Common Use Cases

  • Cron-job management UIs - showing a plain-English summary of a schedule next to the raw cron string so non-technical users understand when a job runs.
  • Admin dashboards for schedulers - explaining Celery beat, Airflow, or Kubernetes CronJob schedules to operators without requiring them to read cron syntax.
  • Configuration validation tooling - using the bundled ExpressionValidator to catch malformed cron expressions before they’re saved.
  • Localized SaaS products - generating schedule descriptions in the end user’s own language via the built-in locale catalogs, rather than hand-rolling translations.

Under The Hood

Architecture cron-descriptor is a small layered pipeline rather than a framework: ExpressionDescriptor (cron_descriptor/ExpressionDescriptor.py) is the public entry point, which on construction hands the raw expression string to ExpressionParser (ExpressionParser.py) to split it into positional parts (seconds, minute, hour, day-of-month, month, day-of-week, and optional year), validated along the way via ExpressionValidator.py. get_description() then walks those parsed parts and assembles per-field sentence fragments through StringBuilder, translating each fragment through a GetText wrapper around Python’s gettext module, which loads a locale-specific .mo catalog from cron_descriptor/locale/. Configuration (casing, verbosity, locale, 12/24-hour format) flows through a single Options dataclass-like object passed at construction or supplied as keyword arguments, which are validated with hasattr/setattr against Options and rejected via WrongArgumentError if unknown. There’s no dependency-injection container or plugin system; the four collaborators (ExpressionParser, ExpressionValidator, GetText, StringBuilder) are plain constructor-injected objects. Because ExpressionDescriptor’s field-description methods index into _expression_parts by position, changing ExpressionParser’s output ordering would be the one change most likely to break every downstream description method at once.

Tech Stack Pure Python (99% of the codebase per GitHub’s language breakdown), targeting Python 3.9 through 3.14 with from __future__ import annotations used throughout for forward-referencing type hints. The only runtime dependency is typing_extensions (for Unpack/TypedDict kwargs typing on older Python versions); dev/test dependencies are ruff, mypy (run in strict mode per pyproject.toml), polib, and pytest. Packaging uses a standard pyproject.toml with setuptools as the build backend, dynamic versioning sourced from cron_descriptor.__version__, and .mo locale files included as package data. Translation source files are .po/.mo pairs managed with GNU Gettext tooling (xgettext, msgfmt), with Poedit recommended for contributors adding new languages.

Code Quality The project has a real, matrix-tested CI pipeline (.github/workflows/python-test.yml) running on Python 3.9–3.14 (including prereleases) via a code-check.sh script that chains ruff, mypy --strict, and pytest. The tests/ directory has nine focused test modules (test_api.py, test_casing.py, test_exceptions.py, test_formats.py — 392 lines of expression/description fixtures, test_import.py, test_locale.py, test_validator.py, test_verbosity.py), indicating deliberate coverage of casing behavior, exception paths, locale loading, and format edge cases rather than a token smoke test. pyproject.toml enables ruff’s full ALL rule set (with a short, justified ignore list) and mypy strict mode across both cron_descriptor and tests, and a pre-commit hook (code-check.sh symlinked into .git/hooks/pre-commit) is offered to contributors. Exceptions are typed and explicit (FormatError, MissingFieldError, WrongArgumentError) rather than generic.

What Makes It Unique The standout technical choice is the breadth of first-class localization: rather than hard-coding English sentence templates, every field description is routed through gettext, and the project ships and maintains translation catalogs for roughly 31 languages contributed by a large community of translators — a level of i18n investment that’s unusual for a narrowly-scoped utility library. Combined with strict typing (mypy strict, full ruff rule set) and support for the less-common 6- and 7-field cron variants (seconds and year), it reads as a mature, well-governed port of the original .NET project rather than a quick script.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search