Skip to main content

A Python library to interact with local Mozilla Thunderbird profiles and mail client data on Linux.

Project description

pythonbird

PyPI Python Versions License PyPI Downloads

A lightweight, zero-dependency Python library for interacting with local Mozilla Thunderbird profiles, mailboxes, address books, and native compose windows on Linux.

Features

  • Profile Auto-Detection: Detects standard, Snap, and Flatpak Thunderbird profile directories.
  • Manual Profile Selection: Allows applications to work with a specific profile or a backed-up profile directory.
  • Preference Parsing: Reads configured email accounts and supported values from prefs.js.
  • Local Mail Reader: Reads messages from Thunderbird Mbox files.
  • Folder Discovery: Lists and resolves local and account mail folders.
  • Message Search: Filters by sender, recipient, subject, content, date, and attachments.
  • Typed Models: Returns Message, Attachment, and Contact objects.
  • Attachment Extraction: Inspects and saves files attached to messages.
  • Export: Saves messages as EML or exports folders to JSON.
  • High-Level API: Groups profile, mail, contacts, search, compose, and export operations in Thunderbird.
  • Memory-Efficient Iteration: Supports reading large mailboxes without loading every message into memory.
  • MIME Decoding: Decodes encoded headers, declared character sets, plain-text bodies, and HTML bodies.
  • Address Book Access: Reads contacts from Thunderbird SQLite address books in read-only mode.
  • Native Compose Window: Opens a Thunderbird compose window with a recipient, subject, body, and optional attachment.
  • Safe Process Launching: Starts Thunderbird without passing message fields through a system shell.

Documentation

See the complete Developer Guide and API Reference.

Release history is available in CHANGELOG.md.

Requirements

  • Linux
  • Python 3.9 or newer
  • Mozilla Thunderbird for compose-window functionality

Reading an explicitly supplied profile directory does not require Thunderbird to be installed. Opening a compose window requires an available Thunderbird command.

Installation

Using Poetry

poetry add pythonbird

Using pip

pip install pythonbird

Quick Start

The recommended API in version 0.2.2 uses one high-level object:

from pythonbird import Thunderbird

tb = Thunderbird()

print(tb.accounts())
print(tb.folders())

for message in tb.messages("Inbox", limit=20):
    print(message.subject, message.sender)

results = tb.search(
    "Inbox",
    subject="report",
    has_attachments=True,
)

for attachment in results[0].attachments:
    attachment.save("downloads/")

tb.export_json("inbox.json", folder="Inbox")

The low-level ThunderbirdLinux, ThunderbirdMail, and ThunderbirdContacts APIs from version 0.1.x remain available.

from pythonbird import (
    ThunderbirdContacts,
    ThunderbirdLinux,
    ThunderbirdMail,
)

# Automatically detect the active Thunderbird profile.
tb = ThunderbirdLinux()

print(f"Active profile: {tb.profile_dir}")
print(f"Configured accounts: {tb.get_mail_accounts()}")

# Read up to 100 messages from the local Inbox.
mail_manager = ThunderbirdMail(tb)
messages = mail_manager.get_local_inbox_messages(limit=100)

for message in messages:
    print(f"From: {message['from']}")
    print(f"Subject: {message['subject']}")
    print(f"Body: {message['body'][:100]}")
    print()

# Read contacts from abook.sqlite.
contact_manager = ThunderbirdContacts(tb)
contacts = contact_manager.get_all_contacts()

for contact in contacts:
    print(f"{contact['name']}: {contact['email']}")

# Open a native Thunderbird compose window.
tb.open_compose_window(
    to="developer@example.com",
    subject="Draft created with pythonbird",
    body="Hello from pythonbird!",
)

Using a Specific Profile

Automatic profile detection is used by default:

tb = ThunderbirdLinux()

A profile can also be supplied explicitly:

tb = ThunderbirdLinux(
    profile_dir="/home/user/.thunderbird/example.default-release",
)

This is useful when:

  • multiple profiles exist;
  • Thunderbird uses a custom profile location;
  • a backed-up profile needs to be inspected;
  • tests should not use the real user profile.

The launch command can also be supplied explicitly:

tb = ThunderbirdLinux(
    profile_dir="/path/to/profile",
    command=["flatpak", "run", "org.mozilla.Thunderbird"],
)

Reading Mail

Read the Local Inbox

mail_manager = ThunderbirdMail(tb)

messages = mail_manager.get_local_inbox_messages(limit=50)

Each message is returned as a dictionary:

{
    "id": 0,
    "from": "Sender <sender@example.com>",
    "to": "Recipient <recipient@example.com>",
    "subject": "Message subject",
    "date": "Fri, 10 Jul 2026 12:00:00 +0300",
    "body": "Preferred plain-text body",
    "text_body": "Plain-text body",
    "html_body": "<p>HTML body</p>",
}

Iterate Over a Large Inbox

for message in mail_manager.iter_local_inbox_messages():
    print(message["subject"])

This avoids creating a list containing every message in memory.

Read Another Mbox File

messages = mail_manager.get_mbox_messages(
    "/home/user/.thunderbird/profile/Mail/Local Folders/Sent",
    limit=100,
)

An arbitrary Mbox file can also be iterated:

for message in mail_manager.iter_mbox_messages(
    "/path/to/mailbox",
):
    print(message["subject"])

Reading Contacts

contact_manager = ThunderbirdContacts(tb)
contacts = contact_manager.get_all_contacts()

By default, contacts are read from:

<profile>/abook.sqlite

A different database can be supplied:

contacts = contact_manager.get_all_contacts(
    database_path="/path/to/address-book.sqlite",
)

Database and schema errors raise ThunderbirdContactsError by default.

To return an empty list instead:

contacts = contact_manager.get_all_contacts(strict=False)

Opening a Compose Window

tb.open_compose_window(
    to="friend@example.com",
    subject="Report",
    body="The report is attached.",
    attachment_path="/home/user/Documents/report.pdf",
)

The attachment must exist. Otherwise, FileNotFoundError is raised.

Thunderbird is launched without shell=True.

Error Handling

from pythonbird import (
    ThunderbirdContacts,
    ThunderbirdContactsError,
    ThunderbirdLinux,
)

try:
    tb = ThunderbirdLinux()
    contacts = ThunderbirdContacts(tb).get_all_contacts()

except FileNotFoundError as error:
    print(f"Required profile, executable, or file was not found: {error}")

except PermissionError as error:
    print(f"Access was denied: {error}")

except ThunderbirdContactsError as error:
    print(f"Address book could not be read: {error}")

Running Tests

If Poetry dependencies are already installed:

poetry run python -m pytest -v

If the Poetry environment is already activated:

python -m pytest -v

The tests use temporary mock profiles and do not read the user's real Thunderbird profile.

Development Checks

Format the code:

poetry run black .

Run linting:

poetry run flake8 pythonbird tests

Run tests:

poetry run python -m pytest -v

Build the package:

poetry build

Project Structure

pythonbird/
├── pythonbird/
│   ├── __init__.py
│   ├── core.py
│   ├── mail.py
│   └── contacts.py
├── tests/
│   ├── test_core.py
│   ├── test_compose.py
│   ├── test_contacts.py
│   └── test_mail_encoding.py
├── CHANGELOG.md
├── GUIDE.md
├── LICENSE
├── README.md
├── poetry.lock
└── pyproject.toml

Safety

Pythonbird is designed to avoid modifying Thunderbird profile data:

  • SQLite address books are opened in read-only mode;
  • mailbox messages are read without intentionally modifying their contents;
  • Thunderbird is launched without shell=True;
  • attachment paths are validated before opening the compose window.

Applications should still keep backups of important Thunderbird profiles. No library can guarantee zero risk when files may be changed concurrently by another process.

Version

Current version: 0.2.2

License

This project is licensed under the MIT License.

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

pythonbird-0.2.2.tar.gz (14.3 kB view details)

Uploaded Source

Built Distribution

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

pythonbird-0.2.2-py3-none-any.whl (14.3 kB view details)

Uploaded Python 3

File details

Details for the file pythonbird-0.2.2.tar.gz.

File metadata

  • Download URL: pythonbird-0.2.2.tar.gz
  • Upload date:
  • Size: 14.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pythonbird-0.2.2.tar.gz
Algorithm Hash digest
SHA256 f8bfb0cb69809b6d1509fd14d663c5e21b302e943800e84aebdf8b8fac545d38
MD5 60d822126ea16f817aaf0f31d5161ba7
BLAKE2b-256 62affca2478655611818559f437a43e5efbb4aae2d13fc291469638fe63f8ef2

See more details on using hashes here.

Provenance

The following attestation bundles were made for pythonbird-0.2.2.tar.gz:

Publisher: publish.yml on rchbld/pythonbird

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pythonbird-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: pythonbird-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 14.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pythonbird-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 a1c531b0ed1f0c27358e8cc2c28ef1f733632dbfd56b8b927b4af9a916a4ffd4
MD5 199269ad50b908cb76c324c1c49601c0
BLAKE2b-256 b49e3a974550279108394e1c8639a5251100c75f80224d939a7257e70d4c4fab

See more details on using hashes here.

Provenance

The following attestation bundles were made for pythonbird-0.2.2-py3-none-any.whl:

Publisher: publish.yml on rchbld/pythonbird

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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