Python bindings for Apple's MailKit framework, enabling developers to create mail extensions that integrate with macOS Mail applications
—
Extension manager for controlling Mail extensions, reloading content blockers, and managing visible messages. This module provides the core functionality for managing Mail extension lifecycle and operations.
Manager for Mail extension operations and reloading. This is the primary class for controlling extension behavior and triggering system updates.
class MEExtensionManager:
def reloadContentBlockerWithIdentifier_completionHandler_(
self,
identifier: str, # Content blocker identifier
completionHandler # Completion handler: (NSError or None) -> None
):
"""
Reload a content blocker with the specified identifier.
Args:
identifier: The unique identifier of the content blocker to reload
completionHandler: Completion handler called when reload completes,
receives an NSError if the operation failed, None on success
"""
def reloadVisibleMessagesWithCompletionHandler_(
self,
completionHandler # Completion handler: (NSError or None) -> None
):
"""
Reload all visible messages in the Mail application.
Args:
completionHandler: Completion handler called when reload completes,
receives an NSError if the operation failed, None on success
"""Base view controller class for Mail extensions. Provides the foundation for creating custom view controllers in Mail extensions.
class MEExtensionViewController:
"""
Base view controller for Mail extensions.
This class provides the foundation for creating custom view controllers
that integrate with the Mail application interface.
Note: This class cannot be instantiated directly using init() or new().
Subclass this class to create custom extension view controllers.
"""import MailKit
# Reload a specific content blocker
def reload_content_blocker(extension_manager, blocker_id):
"""Reload a content blocker and handle the result."""
def completion_handler(error):
if error is None:
print(f"Successfully reloaded content blocker: {blocker_id}")
else:
print(f"Failed to reload content blocker {blocker_id}: {error}")
extension_manager.reloadContentBlockerWithIdentifier_completionHandler_(
identifier=blocker_id,
completionHandler=completion_handler
)
# Example usage
# extension_manager would be provided by the Mail framework
# reload_content_blocker(extension_manager, "com.example.mailblocker")import MailKit
# Reload all visible messages
def reload_visible_messages(extension_manager):
"""Reload all visible messages and handle the result."""
def completion_handler(error):
if error is None:
print("Successfully reloaded visible messages")
else:
print(f"Failed to reload visible messages: {error}")
extension_manager.reloadVisibleMessagesWithCompletionHandler_(
completionHandler=completion_handler
)
# Example usage
# extension_manager would be provided by the Mail framework
# reload_visible_messages(extension_manager)import MailKit
class MailExtensionManager:
"""Utility class for managing Mail extension operations."""
def __init__(self, extension_manager):
"""
Initialize with a MEExtensionManager instance.
Args:
extension_manager: MEExtensionManager instance from the Mail framework
"""
self.extension_manager = extension_manager
self.reload_callbacks = {}
def reload_content_blocker_async(self, blocker_id, callback=None):
"""
Asynchronously reload a content blocker.
Args:
blocker_id: Identifier of the content blocker
callback: Optional callback function to call on completion
"""
def completion_handler(error):
success = error is None
if callback:
callback(blocker_id, success, error)
# Store result for later retrieval
self.reload_callbacks[blocker_id] = {
"success": success,
"error": error,
"timestamp": __import__("time").time()
}
self.extension_manager.reloadContentBlockerWithIdentifier_completionHandler_(
identifier=blocker_id,
completionHandler=completion_handler
)
def reload_visible_messages_async(self, callback=None):
"""
Asynchronously reload visible messages.
Args:
callback: Optional callback function to call on completion
"""
def completion_handler(error):
success = error is None
if callback:
callback(success, error)
self.extension_manager.reloadVisibleMessagesWithCompletionHandler_(
completionHandler=completion_handler
)
def get_last_reload_result(self, blocker_id):
"""
Get the result of the last reload operation for a content blocker.
Args:
blocker_id: Identifier of the content blocker
Returns:
dict: Result information or None if no reload has been performed
"""
return self.reload_callbacks.get(blocker_id)
# Example usage
# manager = MailExtensionManager(extension_manager_instance)
#
# def reload_callback(blocker_id, success, error):
# if success:
# print(f"Content blocker {blocker_id} reloaded successfully")
# else:
# print(f"Failed to reload content blocker {blocker_id}: {error}")
#
# manager.reload_content_blocker_async("com.example.blocker", reload_callback)import MailKit
class BatchExtensionManager:
"""Manager for batch extension operations."""
def __init__(self, extension_manager):
self.extension_manager = extension_manager
self.pending_operations = {}
def reload_multiple_content_blockers(self, blocker_ids, completion_callback=None):
"""
Reload multiple content blockers and track completion.
Args:
blocker_ids: List of content blocker identifiers
completion_callback: Called when all operations complete
"""
batch_id = f"batch_{__import__('time').time()}"
self.pending_operations[batch_id] = {
"total": len(blocker_ids),
"completed": 0,
"results": {},
"callback": completion_callback
}
def individual_completion(blocker_id, error):
batch_info = self.pending_operations[batch_id]
batch_info["completed"] += 1
batch_info["results"][blocker_id] = {
"success": error is None,
"error": error
}
# Check if batch is complete
if batch_info["completed"] == batch_info["total"]:
if batch_info["callback"]:
batch_info["callback"](batch_id, batch_info["results"])
# Clean up
del self.pending_operations[batch_id]
# Start all reload operations
for blocker_id in blocker_ids:
self.extension_manager.reloadContentBlockerWithIdentifier_completionHandler_(
identifier=blocker_id,
completionHandler=lambda error, bid=blocker_id: individual_completion(bid, error)
)
return batch_id
def get_batch_status(self, batch_id):
"""
Get the status of a batch operation.
Args:
batch_id: Batch operation identifier
Returns:
dict: Batch status information or None if not found
"""
return self.pending_operations.get(batch_id)
# Example usage
# batch_manager = BatchExtensionManager(extension_manager_instance)
#
# def batch_completion(batch_id, results):
# print(f"Batch {batch_id} completed:")
# for blocker_id, result in results.items():
# status = "SUCCESS" if result["success"] else "FAILED"
# print(f" {blocker_id}: {status}")
#
# blocker_list = ["com.example.blocker1", "com.example.blocker2", "com.example.blocker3"]
# batch_id = batch_manager.reload_multiple_content_blockers(blocker_list, batch_completion)import MailKit
import time
class RobustExtensionManager:
"""Extension manager with retry logic and error handling."""
def __init__(self, extension_manager, max_retries=3, retry_delay=1.0):
self.extension_manager = extension_manager
self.max_retries = max_retries
self.retry_delay = retry_delay
self.retry_attempts = {}
def reload_content_blocker_with_retry(self, blocker_id, final_callback=None):
"""
Reload content blocker with automatic retry on failure.
Args:
blocker_id: Content blocker identifier
final_callback: Callback called after all retries exhausted
"""
attempt_key = f"{blocker_id}_{time.time()}"
self.retry_attempts[attempt_key] = 0
def attempt_reload():
attempt_count = self.retry_attempts[attempt_key]
def completion_handler(error):
if error is None:
# Success - clean up and notify
if attempt_key in self.retry_attempts:
del self.retry_attempts[attempt_key]
if final_callback:
final_callback(blocker_id, True, None, attempt_count + 1)
else:
# Failure - check if we should retry
self.retry_attempts[attempt_key] += 1
current_attempt = self.retry_attempts[attempt_key]
if current_attempt < self.max_retries:
print(f"Retrying content blocker reload {blocker_id} (attempt {current_attempt + 1}/{self.max_retries})")
# Schedule retry after delay
__import__("threading").Timer(self.retry_delay, attempt_reload).start()
else:
# All retries exhausted
if attempt_key in self.retry_attempts:
del self.retry_attempts[attempt_key]
if final_callback:
final_callback(blocker_id, False, error, current_attempt)
self.extension_manager.reloadContentBlockerWithIdentifier_completionHandler_(
identifier=blocker_id,
completionHandler=completion_handler
)
# Start first attempt
attempt_reload()
# Example usage
# robust_manager = RobustExtensionManager(extension_manager_instance, max_retries=3, retry_delay=2.0)
#
# def final_callback(blocker_id, success, error, attempts):
# if success:
# print(f"Content blocker {blocker_id} reloaded successfully after {attempts} attempts")
# else:
# print(f"Failed to reload content blocker {blocker_id} after {attempts} attempts: {error}")
#
# robust_manager.reload_content_blocker_with_retry("com.example.blocker", final_callback)Install with Tessl CLI
npx tessl i tessl/pypi-pyobjc-framework-mailkit