@@ -632,6 +632,21 @@ def to_b2z(self, overwrite=False, filename=None) -> os.PathLike[Any] | str:
632632 -------
633633 filename : str
634634 The absolute path to the created b2z file.
635+
636+ Examples
637+ --------
638+ Pack a directory-backed store into a zip store::
639+
640+ with blosc2.DictStore("data.b2d", mode="w") as dstore:
641+ dstore["/values"] = np.arange(10)
642+
643+ with blosc2.DictStore("data.b2d", mode="r") as dstore:
644+ dstore.to_b2z(filename="data.b2z", overwrite=True)
645+
646+ ``filename`` can also be passed positionally::
647+
648+ with blosc2.DictStore("data.b2d", mode="r") as dstore:
649+ dstore.to_b2z("copy.b2z", overwrite=True)
635650 """
636651 if isinstance (overwrite , str | os .PathLike ) and filename is None :
637652 filename = overwrite
@@ -641,6 +656,7 @@ def to_b2z(self, overwrite=False, filename=None) -> os.PathLike[Any] | str:
641656 raise ValueError ("Cannot call to_b2z() on a .b2z DictStore opened in read mode." )
642657
643658 b2z_path = self .b2z_path if filename is None else filename
659+ b2z_path = os .fspath (b2z_path )
644660 if not b2z_path .endswith (".b2z" ):
645661 raise ValueError ("b2z_path must have a .b2z extension" )
646662
@@ -669,6 +685,68 @@ def to_b2z(self, overwrite=False, filename=None) -> os.PathLike[Any] | str:
669685 zf .write (self .estore_path , arcname )
670686 return os .path .abspath (b2z_path )
671687
688+ def to_b2d (self , dirname = None , * , overwrite : bool = False ) -> os .PathLike [Any ] | str :
689+ """
690+ Serialize store contents to a b2d directory.
691+
692+ Parameters
693+ ----------
694+ dirname : str, optional
695+ If provided, use this directory instead of the default b2d path.
696+ overwrite : bool, optional
697+ If True, overwrite the existing b2d directory if it exists.
698+ Default is False.
699+
700+ Returns
701+ -------
702+ dirname : str
703+ The absolute path to the created b2d directory.
704+
705+ Examples
706+ --------
707+ Unpack a zip-backed store into a directory-backed store::
708+
709+ with blosc2.DictStore("data.b2z", mode="r") as dstore:
710+ dstore.to_b2d("data.b2d", overwrite=True)
711+
712+ with blosc2.DictStore("data.b2d", mode="r") as dstore:
713+ values = dstore["/values"][:]
714+
715+ Copy an existing directory-backed store to another ``.b2d`` directory::
716+
717+ with blosc2.DictStore("data.b2d", mode="r") as dstore:
718+ dstore.to_b2d("backup.b2d", overwrite=True)
719+ """
720+ b2d_path = self .localpath if dirname is None and not self .is_zip_store else dirname
721+ if b2d_path is None :
722+ b2d_path = (
723+ self .b2z_path [:- 4 ] + ".b2d" if self .b2z_path .endswith (".b2z" ) else self .b2z_path + ".b2d"
724+ )
725+ b2d_path = os .fspath (b2d_path )
726+ if not b2d_path .endswith (".b2d" ):
727+ raise ValueError ("b2d_path must have a .b2d extension" )
728+
729+ target_path = os .path .abspath (b2d_path )
730+ source_path = os .path .abspath (self .working_dir )
731+ if not self .is_zip_store and target_path == source_path :
732+ return target_path
733+
734+ if os .path .exists (target_path ):
735+ if not overwrite :
736+ raise FileExistsError (f"'{ target_path } ' already exists. Use overwrite=True to overwrite." )
737+ if os .path .isdir (target_path ):
738+ shutil .rmtree (target_path )
739+ else :
740+ os .remove (target_path )
741+
742+ if self .is_zip_store and self .mode == "r" :
743+ os .makedirs (target_path , exist_ok = True )
744+ with zipfile .ZipFile (self .b2z_path , "r" ) as zf :
745+ zf .extractall (target_path )
746+ else :
747+ shutil .copytree (self .working_dir , target_path )
748+ return target_path
749+
672750 def _get_zip_offsets (self ) -> dict [str , dict [str , int ]]:
673751 """Get offset and length of all files in the zip archive."""
674752 self .offsets = {} # Reset offsets
0 commit comments