Coverage for src/mafw_tools/file_tools.py: 100%
44 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-04 11:04 +0100
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-04 11:04 +0100
1# Copyright 2025 European Union
2# Author: Bulgheroni Antonio (antonio.bulgheroni@ec.europa.eu)
3# SPDX-License-Identifier: EUPL-1.2
4"""
5File tools module.
7This module provides utilities for handling file operations, particularly
8pickle serialization/deserialization and file name generation.
10.. versionadded:: 1.0.0
12Functions:
13 save_to_pickle: Save dictionary-like objects to a pickle file
14 load_from_pickle: Load objects from a pickle file
15 generate_new_file_name: Generate a new file name based on an original file name
17Example usage::
19 >>> from mafw_tools.file_tools import save_to_pickle, load_from_pickle
20 >>> data = {'key': 'value'}
21 >>> save_to_pickle('data.pkl', zipped=True, my_data=data)
22 >>> loaded_data = load_from_pickle('data.pkl')
23"""
24import pickle
25import zipfile
26from pathlib import Path
27from typing import Any
30def save_to_pickle(filepath: str | Path, zipped: bool = True, **kwargs):
31 """
32 Save a dictionary-like object to a pickle file.
34 This function is very helpful when there is the need to save multiple objects
35 to the same binary file. The object to be serialized and saved to the file
36 must be provided in the form of keyword arguments.
38 Here is an example:
40 .. code-block:: python
42 my_first_object = [1, 2, 3, 4]
43 my_second_object = dict(field1='abc', field2='cba')
45 save_to_pickle(
46 'my_pickle_file.sav',
47 zipped=False,
48 object1=my_first_object,
49 object2=my_second_object,
50 )
52 A dictionary object in the form of :python:`{object1 : my_first_object, object2 : my_second_object}`
53 is saved in the pickle file.
55 :param filepath: The output file name
56 :type filepath: str | Path
57 :param zipped: Flag to select if the output file should be compressed or not. Defaults to True
58 :type zipped: bool, optional
59 :param kwargs: The objects to the saved.
60 """
61 objects_dict = kwargs
63 if isinstance(filepath, str):
64 filepath = Path(filepath)
66 if zipped:
67 # be sure about the extension
68 if filepath.suffix == '.zip':
69 filepath = filepath.with_suffix('.sav')
71 with open(filepath, 'wb') as f:
72 pickle.dump(objects_dict, f)
74 if zipped:
75 with zipfile.ZipFile(filepath.with_suffix('.zip'), 'w', compression=zipfile.ZIP_LZMA) as zip:
76 zip.write(filepath, arcname=filepath.name)
77 filepath.unlink()
80def load_from_pickle(filepath: str | Path) -> dict[str, Any]:
81 """
82 Load objects from a pickle file.
84 This function loads objects that were previously saved using :func:`save_to_pickle`.
85 It handles both compressed and uncompressed pickle files automatically.
87 :param filepath: The path to the pickle file to load
88 :type filepath: str | Path
89 :return: Dictionary containing the loaded objects
90 :rtype: dict[str, Any]
91 """
92 if isinstance(filepath, str):
93 filepath = Path(filepath)
95 zipped = zipfile.is_zipfile(filepath)
97 if zipped:
98 with zipfile.ZipFile(filepath) as zip:
99 with zip.open(filepath.with_suffix('.sav').name) as file:
100 objects_dict = pickle.load(file)
101 else:
102 with open(filepath, 'rb') as file:
103 objects_dict = pickle.load(file)
105 return objects_dict
108def generate_new_file_name(
109 original_file_name: str | Path, new_base_path: str | Path, extra_suffix: str = None, new_extension: str = None
110) -> Path:
111 """
112 Generate a new file name including its full path based on an original file name.
114 The generated file name will have the new base path provided, an extra suffix
115 (if provided) just before the extension, and if a new extension is provided
116 then this is also replaced.
118 :param original_file_name: The starting file name. It can be the full file name of an image. It does not need to be the full path. The filename is just fine.
119 :type original_file_name: str | Path
120 :param new_base_path: The new base path to be appended to the newly generated filename.
121 :type new_base_path: str | Path
122 :param extra_suffix: A string to be added just before the file extension.
123 :type extra_suffix: str, optional, defaults to None
124 :param new_extension: A new extension to replace the old one.
125 :type new_extension: str, optional, defaults to None
126 :return: The newly generated file name
127 :rtype: Path
128 """
130 if isinstance(original_file_name, str):
131 original_file_name = Path(original_file_name)
133 if isinstance(new_base_path, str):
134 new_base_path = Path(new_base_path)
136 # be sure that the new path exists.
137 new_base_path.mkdir(exist_ok=True, parents=True)
139 if new_extension:
140 if not new_extension.startswith('.'):
141 new_extension = '.' + new_extension
142 new_file_name = Path(original_file_name.with_suffix(new_extension).name)
143 else:
144 new_file_name = Path(original_file_name.name)
146 if extra_suffix:
147 if not extra_suffix.startswith('_'):
148 extra_suffix = '_' + extra_suffix
149 new_file_name = Path(new_file_name.stem + extra_suffix + new_file_name.suffix)
151 return new_base_path / new_file_name