{"id":6767,"library":"persistent","title":"Persistent Objects Base Class","description":"The `persistent` library provides a base class, `Persistent`, for creating translucent persistent Python objects. These objects can track their their own 'dirty' state, indicating when they have been modified and need to be saved by an external persistence layer (e.g., ZODB). It is a fundamental component for building object persistence systems. The current version is 6.5, with major releases typically aligning with Python version support updates and addressing core behavior changes.","status":"active","version":"6.5","language":"en","source_language":"en","source_url":"https://github.com/zopefoundation/persistent","tags":["persistence","object-persistence","zope","data-management"],"install":[{"cmd":"pip install persistent","lang":"bash","label":"Install stable version"}],"dependencies":[],"imports":[{"symbol":"Persistent","correct":"from persistent import Persistent"}],"quickstart":{"code":"from persistent import Persistent\n\nclass MyPersistentObject(Persistent):\n    def __init__(self, name=\"Default\"):\n        self.name = name\n        self.attributes = {} # A mutable dictionary\n\n    def update_name(self, new_name):\n        # Assigning to a direct attribute will mark the object dirty\n        self.name = new_name\n\n    def add_attribute(self, key, value):\n        # For objects managed by a persistence layer (e.g., ZODB), \n        # modifying this dict would also mark the parent dirty. \n        # Without a persistence layer, direct dict modification here\n        # does NOT automatically set _p_changed.\n        self.attributes[key] = value\n\n    def describe(self):\n        return f\"Name: {self.name}, Attributes: {self.attributes}\"\n\n# Create an instance\nmy_obj = MyPersistentObject(\"Initial Name\")\nprint(f\"1. Initial state: {my_obj.describe()}, _p_changed: {my_obj._p_changed}\")\n\n# Modifying a direct attribute marks it dirty\nmy_obj.update_name(\"New Name 1\")\nprint(f\"2. After update_name: {my_obj.describe()}, _p_changed: {my_obj._p_changed}\")\n\n# Simulate 'saving' (a persistence layer would set _p_changed to None)\nmy_obj._p_changed = None\nprint(f\"3. After 'saving': {my_obj.describe()}, _p_changed: {my_obj._p_changed}\")\n\n# Modifying the mutable dictionary directly (without a storage proxy)\n# does NOT automatically set _p_changed, which is a common gotcha.\nmy_obj.attributes['version'] = 1\nprint(f\"4. After modifying internal dict: {my_obj.describe()}, _p_changed: {my_obj._p_changed}\")\n\n# To ensure persistence layer detects changes in mutable objects,\n# you often need to manually set _p_changed or reassign the attribute\n# (e.g., obj.attributes = new_dict_or_copy) or rely on a proxy provided by the storage.\nmy_obj._p_changed = True\nprint(f\"5. Manually set _p_changed after internal change: {my_obj.describe()}, _p_changed: {my_obj._p_changed}\")","lang":"python","description":"Demonstrates the basic usage of `persistent.Persistent` by subclassing it and observing how its `_p_changed` attribute is affected by direct attribute assignments. It also highlights a common gotcha regarding changes to mutable attributes within the object when not managed by a full persistence layer."},"warnings":[{"fix":"Upgrade to Python 3.8 or newer to use `persistent` 6.x. For older Python versions, pin `persistent` to an earlier major version (e.g., `persistent<5` for Python 2.7, `persistent<6` for Python 3.7).","message":"Python 2.7, 3.4, 3.5, and 3.6 support was dropped in `persistent` version 5.0. Python < 3.8 support was dropped in version 6.0.","severity":"breaking","affected_versions":"< 5.0, < 6.0"},{"fix":"Code that relied on `isinstance(obj.__dict__, _PersistentDict)` or specific `_PersistentDict` methods will break. Treat `obj.__dict__` as a standard Python dictionary.","message":"The `__dict__` attribute of `Persistent` objects no longer returns a `_PersistentDict` but a plain `dict`.","severity":"breaking","affected_versions":">= 6.0.0"},{"fix":"Either reassign the mutable attribute (e.g., `obj.my_list = new_list`), manually set `obj._p_changed = True` after modifying the internal mutable object, or ensure the object is managed by a full persistence layer (like ZODB) that provides proxy objects for mutable attributes.","message":"The `_p_changed` attribute is only automatically set to `True` for direct attribute assignments (e.g., `obj.attr = value`). Changes to mutable objects *inside* a `Persistent` object (e.g., `obj.my_list.append(item)`, `obj.my_dict[key] = value`) will NOT automatically mark the parent object as dirty if not managed by an active persistence layer that proxies these mutable objects. This is a common source of data loss if not handled correctly.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Understand that `persistent` is a low-level building block. To persist objects to disk or a database, you will need to integrate it with a suitable persistence framework (e.g., ZODB).","message":"The `persistent` library provides the `Persistent` base class and change tracking mechanism, but it does NOT provide an object storage or database system itself. It is designed to be used in conjunction with a persistence layer like ZODB (Zope Object Database) to actually save and retrieve objects.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-15T00:00:00.000Z","next_check":"2026-07-14T00:00:00.000Z","problems":[]}