PyObjC CoreData Framework
PyObjC-framework-CoreData provides Python wrappers for Apple's Core Data framework on macOS. Core Data is a powerful framework that offers generalized and automated solutions for object life-cycle management, object graph persistence, and change tracking. This package, currently at version 12.1, is part of the broader PyObjC project, which typically sees minor releases every few months and major version updates roughly annually to align with macOS SDK changes.
Warnings
- breaking PyObjC has consistently dropped support for older Python versions with major releases. Python 3.9 was dropped in v12.0, and Python 3.8 was dropped in v11.0. Future major versions will likely continue this trend.
- breaking In CoreData, the constant `NSFetchRequestExpressionType` was removed as part of framework metadata updates in PyObjC 12.0.
- gotcha The PyObjC bridge's handling of Objective-C initializer (`init` family) methods changed in v11.1 to correctly model ARC behavior, where `init` methods steal a reference to `self` and return a new one. This aligns with `clang`'s documentation.
- gotcha Subclassing `NSManagedObject` and overriding `__new__` requires careful handling of `__init__`. In PyObjC 10.3, support for calling `__init__` when `__new__` was provided by PyObjC was dropped, then partially reintroduced in 10.3.1 for user-implemented `__new__` methods. Code relying on the PyObjC-provided `__new__` still cannot use `__init__`.
- gotcha PyObjC 11.0 introduced experimental support for Python 3.13's free-threading (PEP 703). While an important step, `NSPersistentContainer` and Core Data contexts (`NSManagedObjectContext`) are generally not thread-safe. Each thread requiring data access should have its own `NSManagedObjectContext`.
- gotcha In Core Data, you do not directly 'save' an `NSManagedObject`. Instead, changes made to objects within an `NSManagedObjectContext` are saved by calling the `save()` method on the context itself. Failing to save the context will result in unsaved changes, even if objects were created or modified.
Install
-
pip install pyobjc-framework-coredata
Imports
- CoreData
import CoreData
- objc
import objc
Quickstart
import CoreData
import objc
import os
# 1. Define the Managed Object Model programmatically
model = CoreData.NSManagedObjectModel.alloc().init()
entity = CoreData.NSEntityDescription.alloc().init()
entity.name = "Event"
entity.managedObjectClassName = "Event"
attribute_name = CoreData.NSAttributeDescription.alloc().init()
attribute_name.name = "name"
attribute_name.attributeType = CoreData.NSStringAttributeType
attribute_name.optional = False
attribute_timestamp = CoreData.NSAttributeDescription.alloc().init()
attribute_timestamp.name = "timestamp"
attribute_timestamp.attributeType = CoreData.NSDateAttributeType
attribute_timestamp.optional = False
entity.properties = [attribute_name, attribute_timestamp]
model.entities = [entity]
# 2. Create a Persistent Container
# Use a unique in-memory store for a quick runnable example
container = CoreData.NSPersistentContainer.alloc().initWithName_managedObjectModel_(
"MyCoreDataApp", model
)
# Configure an in-memory store for the quickstart
store_description = CoreData.NSPersistentStoreDescription.alloc().init()
store_description.URL = None # For in-memory store
store_description.type = CoreData.NSInMemoryStoreType
container.persistentStoreDescriptions = [store_description]
def load_stores(completion_handler):
container.loadPersistentStoresWithCompletionHandler_(completion_handler)
# Convert Python function to an Objective-C block using objc.block
# This is necessary because loadPersistentStoresWithCompletionHandler_ expects an Objective-C block
@objc.block
def store_load_handler(store_description, error):
if error:
print(f"Failed to load persistent store: {error}")
else:
print(f"Successfully loaded store: {store_description.URL().path()}")
# 3. Get the Managed Object Context
context = container.viewContext
# 4. Create and Save an NSManagedObject
Event = context.persistentStoreCoordinator().managedObjectModel().entitiesByName().get("Event")
new_event = CoreData.NSManagedObject.alloc().initWithEntity_insertIntoManagedObjectContext_(
Event, context
)
new_event.setValue_forKey_("Python Demo Event", "name")
new_event.setValue_forKey_(CoreData.NSDate.date(), "timestamp")
# Save changes to the context (which saves to the store)
try:
context.save()
print(f"Saved new event: {new_event.name()} at {new_event.timestamp()}")
except Exception as e:
print(f"Error saving context: {e}")
# 5. Fetch NSManagedObjects
fetch_request = CoreData.NSFetchRequest.fetchRequestWithEntityName_("Event")
events = context.executeFetchRequest_error_(fetch_request, objc.NULL)
if events:
print("Fetched Events:")
for event_obj in events:
print(f" - Name: {event_obj.name()}, Timestamp: {event_obj.timestamp()}")
else:
print("No events found.")
load_stores(store_load_handler)
# To keep the application running for asynchronous operations (if not in a GUI app)
# For simple script, this might not be strictly needed as the completion handler finishes sync if in-memory.
# In a real app, you'd use NSApplication.run() or similar.