22
33from __future__ import annotations
44
5+ import functools
6+ import shutil
57from collections .abc import Iterator
68from logging import INFO , NullHandler , getLogger
79from pathlib import Path
1618logger .setLevel (INFO )
1719
1820
21+ class ProjectDeletedError (Exception ):
22+ """A method of a Project was called but the Project is marked as deleted."""
23+
24+
25+ def _raise_if_deleted (method ):
26+ """Raise if the underlying Project has been deleted, otherwise execute `method`.
27+
28+ This wrapper makes it safe to "delete" a Project, even if the Project instance
29+ still exists.
30+ """
31+
32+ @functools .wraps (method )
33+ def wrapper (self , * args , ** kwargs ):
34+ if self ._storage_initialized is not True :
35+ raise ProjectDeletedError (
36+ "This Project instance is marked as deleted. "
37+ "Please re-create a Project and discard the current one."
38+ )
39+
40+ return method (self , * args , ** kwargs )
41+
42+ return wrapper
43+
44+
1945class Project :
2046 """
2147 A collection of items persisted in a storage.
@@ -95,16 +121,38 @@ def __init__(
95121 # Initialize repositories with dedicated storages
96122 self ._item_repository = ItemRepository (DiskCacheStorage (item_storage_dirpath ))
97123
124+ self ._storage_initialized = True
125+
98126 # Check if the project should rejoin a server
99127 from skore .project ._launch import ServerInfo # avoid circular import
100128
101129 self ._server_info = ServerInfo .rejoin (self )
102130
103- def clear (self ):
104- """Clear the project."""
131+ @_raise_if_deleted
132+ def clear (self , delete_project = False ):
133+ """Remove all items from the project.
134+
135+ .. warning::
136+ Clearing the project with `delete_project=True` will invalidate the whole
137+ `Project` instance, making it unusable.
138+ A new Project instance can be created using the :class:`skore.Project`
139+ constructor or the :func:`skore.open` function.
140+
141+ Parameters
142+ ----------
143+ delete_project : bool
144+ If set, the project will be deleted entirely.
145+ """
146+ if delete_project :
147+ self ._storage_initialized = False
148+ del self ._item_repository
149+ shutil .rmtree (self .path )
150+ return
151+
105152 for item_key in self ._item_repository :
106153 self ._item_repository .delete_item (item_key )
107154
155+ @_raise_if_deleted
108156 def put (
109157 self ,
110158 key : str ,
@@ -150,6 +198,7 @@ def put(
150198 ),
151199 )
152200
201+ @_raise_if_deleted
153202 def get (
154203 self ,
155204 key : str ,
@@ -211,6 +260,7 @@ def dto(item):
211260
212261 raise ValueError ('`version` should be -1, "all", or an integer' )
213262
263+ @_raise_if_deleted
214264 def keys (self ) -> list [str ]:
215265 """
216266 Get all keys of items stored in the project.
@@ -222,6 +272,7 @@ def keys(self) -> list[str]:
222272 """
223273 return self ._item_repository .keys ()
224274
275+ @_raise_if_deleted
225276 def __iter__ (self ) -> Iterator [str ]:
226277 """
227278 Yield the keys of items stored in the project.
@@ -233,6 +284,7 @@ def __iter__(self) -> Iterator[str]:
233284 """
234285 yield from self ._item_repository
235286
287+ @_raise_if_deleted
236288 def delete (self , key : str ):
237289 """Delete the item corresponding to ``key`` from the Project.
238290
@@ -248,6 +300,7 @@ def delete(self, key: str):
248300 """
249301 self ._item_repository .delete_item (key )
250302
303+ @_raise_if_deleted
251304 def set_note (self , key : str , note : str , * , version = - 1 ):
252305 """Attach a note to key ``key``.
253306
@@ -277,6 +330,7 @@ def set_note(self, key: str, note: str, *, version=-1):
277330 """
278331 return self ._item_repository .set_item_note (key = key , note = note , version = version )
279332
333+ @_raise_if_deleted
280334 def get_note (self , key : str , * , version = - 1 ) -> Union [str , None ]:
281335 """Retrieve a note previously attached to key ``key``.
282336
@@ -306,6 +360,7 @@ def get_note(self, key: str, *, version=-1) -> Union[str, None]:
306360 """
307361 return self ._item_repository .get_item_note (key = key , version = version )
308362
363+ @_raise_if_deleted
309364 def delete_note (self , key : str , * , version = - 1 ):
310365 """Delete a note previously attached to key ``key``.
311366
@@ -333,6 +388,7 @@ def delete_note(self, key: str, *, version=-1):
333388 """
334389 return self ._item_repository .delete_item_note (key = key , version = version )
335390
391+ @_raise_if_deleted
336392 def shutdown_web_ui (self ):
337393 """Shutdown the web UI server if it is running."""
338394 if self ._server_info is None :
0 commit comments