vobject
Parse and generate iCalendar and vCard files with a full-featured Python library.
Repository Health
Technical Analysis
VObject is a Python library for parsing and generating iCalendar (.ics) and vCard (.vcf) files. It converts RFC 5545 calendar data and RFC 6350 contact data into native Python objects — dates become datetime instances, recurrence rules become dateutil.rrule rruleset objects — and serializes them back to spec-compliant text.
Originally built for the Open Source Applications Foundation’s Chandler project, VObject was later maintained by Eventable before being revived under the py-vobject organization after a period of inactivity. It ships two CLI utilities, ics_diff for comparing calendar files and change_tz for shifting event timezones, alongside the core parsing API.
What You Get
- Component tree parsing -
readComponents/readOneturn raw .ics/.vcf text into a tree ofComponentandContentLineobjects. - Native Python types - dates, datetimes, and recurrence rules are converted to
datetimeanddateutil.rrule.rrulesetinstances automatically. - Behavior-based validation -
Behaviorsubclasses per component type (VEVENT, VTODO, VCARD, etc.) drive encoding, decoding, and spec validation. - Round-trip serialization - modified or newly built objects serialize back to valid RFC 5545/6350 text.
- Bundled CLI tools -
ics_diffandchange_tzscripts for calendar diffing and timezone correction.
Common Use Cases
- Calendar sync tools - reading and writing .ics files exchanged between calendaring systems (CalDAV clients, scheduling apps).
- Contact import/export - parsing vCard files from address books or CRM exports into structured Python data.
- Timezone correction scripts - using
change_tzto move events between timezones when source data is mislabeled. - Calendar diffing - using
ics_diffto find meaningful differences between two .ics files ignoring irrelevant ordering.
Under The Hood
Architecture VObject organizes calendar and vCard data as a tree of VBase-derived objects (Component and ContentLine, defined in vobject/base.py) that is assembled by readComponents/readOne, converted to native Python types via transformChildrenToNative(), and reconstituted by serialize(); domain-specific logic — validation rules, knownChildren tables, encoding/decoding, and factory behavior — lives outside this generic tree in per-format Behavior subclasses (behavior.py’s abstract Behavior class, extended by icalendar.py for VCALENDAR/VEVENT/VTODO/etc. and vcard.py for VCARD), which base.py’s registerBehavior/getBehavior registry looks up by component name and version string so the same parser core drives two unrelated formats; a DEFAULT_MAX_NESTING guard in base.py caps recursive descent to prevent stack overflow on malformed input, and the ics_diff/change_tz CLI scripts sit as thin consumers on top of this same object model rather than a separate code path.
Tech Stack VObject is pure-Python (99.97% by bytes per GitHub’s language breakdown) targeting Python 3.9+ (pyproject.toml, dynamic-versioned via flit_core >=3.4 as the PEP 517 build backend), with a minimal runtime dependency footprint — python-dateutil (>=2.5 or >=2.7 depending on interpreter version) for RFC 5545 recurrence-rule parsing into rruleset objects, and pytz (>=2019.1) for timezone data, with an internal fallback Pytz shim in icalendar.py so pytz stays optional at runtime; console-script entry points (ics_diff, change_tz) are registered via [project.scripts]; the dev extra pulls in pytest for testing, flake8/pylint/black/isort for linting and formatting, pre-commit for git hooks, sphinx for documentation, and flit for packaging — no web framework, database, or ORM is involved since this is a pure data-transformation library.
Code Quality Testing uses pytest across 9 files in tests/ (test_vobject.py, test_icalendar.py, test_vcards.py, test_behaviors.py, test_calendar_serialization.py, test_change_tz.py, test_cli.py, test_compatibility.py, test_vtodo.py) totaling 77 test functions plus doctests embedded in vobject/init.py’s module docstring, run across a CI matrix of Python 3.9 through 3.14 (.github/workflows/test.yml); error handling is explicit and typed via custom exception classes (VObjectError, ParseError, ValidateError, NativeError defined in base.py) rather than bare excepts, though most of the codebase (base.py, behavior.py, vcard.py) predates PEP 484 type hints — only icalendar.py and ics_diff.py carry function-level type annotations; naming largely follows a legacy camelCase convention (readComponents, transformChildrenToNative) rather than PEP 8 snake_case, a holdover from the project’s pre-2010 Chandler-project origins; black, flake8, isort, and pylint are configured in pyproject.toml and enforced via a separate pre-commit.yml CI workflow.
API Design VObject’s public surface is deliberately small: readComponents/readOne to parse, iCalendar()/vCard() factory functions plus newFromBehavior to build objects from scratch, and .serialize() to emit spec text, with attribute-style access (cal.vevent.summary.value) replacing verbose tree-traversal calls; its most distinctive design choice is transforming parsed data into native Python types automatically — RRULE lines become dateutil.rrule.rruleset instances you can iterate directly, dates become datetime.date/datetime objects — so callers work with real Python semantics instead of re-parsing ICAL grammar themselves, and the same generic Component/ContentLine/Behavior machinery drives both iCalendar and vCard with no format-specific parser duplication; getting started requires minimal boilerplate, though the doctest-style docs and camelCase API show the project’s age relative to more modern, fully type-hinted Python libraries in this space.