|
34 | 34 | Self, |
35 | 35 | cast, |
36 | 36 | overload, |
| 37 | + Optional, |
37 | 38 | ) |
38 | 39 | import warnings |
39 | 40 |
|
|
269 | 270 |
|
270 | 271 | from pandas.io.formats.style import Styler |
271 | 272 |
|
| 273 | + from pandas.core.frame_versioning import DataFrameSnapshotStore |
| 274 | + |
272 | 275 | # --------------------------------------------------------------------- |
273 | 276 | # Docstring templates |
274 | 277 |
|
@@ -14353,6 +14356,93 @@ def values(self) -> np.ndarray: |
14353 | 14356 | ['monkey', nan, None]], dtype=object) |
14354 | 14357 | """ |
14355 | 14358 | return self._mgr.as_array() |
| 14359 | + |
| 14360 | + def snapshot(self, name: Optional[str] = None) -> str: |
| 14361 | + """ |
| 14362 | + Create a named snapshot of this DataFrame and return the snapshot id. |
| 14363 | +
|
| 14364 | + Parameters |
| 14365 | + ---------- |
| 14366 | + name : str, optional |
| 14367 | + Optional snapshot name. If not provided a timestamped id is returned. |
| 14368 | +
|
| 14369 | + Returns |
| 14370 | + ------- |
| 14371 | + str |
| 14372 | + Snapshot id. |
| 14373 | + """ |
| 14374 | + store = _ensure_snapshot_store(self) |
| 14375 | + return store.snapshot(self, name=name) |
| 14376 | + |
| 14377 | + def restore(self, name: str, inplace: bool = False): |
| 14378 | + """ |
| 14379 | + Restore a previously created snapshot. |
| 14380 | +
|
| 14381 | + Parameters |
| 14382 | + ---------- |
| 14383 | + name : str |
| 14384 | + Snapshot id returned by :meth:`DataFrame.snapshot`. |
| 14385 | + inplace : bool, default False |
| 14386 | + If True, mutate this DataFrame to match the snapshot. Otherwise return |
| 14387 | + a restored copy. |
| 14388 | +
|
| 14389 | + Returns |
| 14390 | + ------- |
| 14391 | + DataFrame or None |
| 14392 | + Restored DataFrame when ``inplace=False``, otherwise None. |
| 14393 | + """ |
| 14394 | + store = getattr(self, "_version_snapshots", None) |
| 14395 | + if store is None: |
| 14396 | + raise KeyError(f"No snapshots present for this DataFrame (requested: {name})") |
| 14397 | + restored = store.restore(name) |
| 14398 | + if inplace: |
| 14399 | + # Replace internal state. Using _mgr replacement is more correct than __dict__ update. |
| 14400 | + # Many pandas internals use the attribute _mgr for BlockManager. Use it cautiously. |
| 14401 | + try: |
| 14402 | + # pandas >= 1.x use _mgr (BlockManager); adapt if different in your branch. |
| 14403 | + object.__setattr__(self, "_mgr", restored._mgr) |
| 14404 | + # also copy other key attrs |
| 14405 | + object.__setattr__(self, "axes", restored.axes) |
| 14406 | + object.__setattr__(self, "_item_cache", restored._item_cache) |
| 14407 | + except Exception: |
| 14408 | + # fallback: shallow __dict__ update (less safe) |
| 14409 | + self.__dict__.update(restored.__dict__) |
| 14410 | + return None |
| 14411 | + return restored |
| 14412 | + |
| 14413 | + def list_snapshots(self) -> list[str]: |
| 14414 | + """ |
| 14415 | + List snapshot ids for this DataFrame. |
| 14416 | + """ |
| 14417 | + store = getattr(self, "_version_snapshots", None) |
| 14418 | + return store.list() if store is not None else [] |
| 14419 | + |
| 14420 | + def drop_snapshot(self, name: str) -> None: |
| 14421 | + """ |
| 14422 | + Drop a snapshot by id. |
| 14423 | + """ |
| 14424 | + store = getattr(self, "_version_snapshots", None) |
| 14425 | + if store is None: |
| 14426 | + raise KeyError(f"No snapshots present for this DataFrame (requested drop: {name})") |
| 14427 | + store.drop(name) |
| 14428 | + |
| 14429 | + def clear_snapshots(self) -> None: |
| 14430 | + """ |
| 14431 | + Clear all snapshots for this DataFrame. |
| 14432 | + """ |
| 14433 | + store = getattr(self, "_version_snapshots", None) |
| 14434 | + if store is not None: |
| 14435 | + store.clear() |
| 14436 | + |
| 14437 | + def snapshot_info(self, name: Optional[str] = None) -> dict: |
| 14438 | + """ |
| 14439 | + Return metadata for all snapshots or a single snapshot. |
| 14440 | + """ |
| 14441 | + store = getattr(self, "_version_snapshots", None) |
| 14442 | + if store is None: |
| 14443 | + return {"count": 0, "snapshots": []} |
| 14444 | + return store.info(name) |
| 14445 | + |
14356 | 14446 |
|
14357 | 14447 |
|
14358 | 14448 | def _from_nested_dict( |
@@ -14390,3 +14480,12 @@ def _reindex_for_setitem( |
14390 | 14480 | "incompatible index of inserted column with frame index" |
14391 | 14481 | ) from err |
14392 | 14482 | return reindexed_value, None |
| 14483 | + |
| 14484 | +def _ensure_snapshot_store(self) -> DataFrameSnapshotStore: |
| 14485 | + # attach a per-instance store to DataFrame |
| 14486 | + store = getattr(self, "_version_snapshots", None) |
| 14487 | + if store is None: |
| 14488 | + store = DataFrameSnapshotStore() |
| 14489 | + # attach to object |
| 14490 | + object.__setattr__(self, "_version_snapshots", store) |
| 14491 | + return store |
0 commit comments