filesysman 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
filesysman/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from .filesysman import (
|
|
2
|
+
File,
|
|
3
|
+
Folder,
|
|
4
|
+
create_files,
|
|
5
|
+
delete_files,
|
|
6
|
+
rename_files,
|
|
7
|
+
get_files,
|
|
8
|
+
find_files,
|
|
9
|
+
rename_by_keyword,
|
|
10
|
+
delete_by_keyword,
|
|
11
|
+
get_files_count,
|
|
12
|
+
copy_files,
|
|
13
|
+
move_files,
|
|
14
|
+
organize_by_extension,
|
|
15
|
+
get_files_recursive,
|
|
16
|
+
find_files_recursive,
|
|
17
|
+
get_folders,
|
|
18
|
+
clear_empty_files,
|
|
19
|
+
clear_empty_folders,
|
|
20
|
+
)
|
filesysman/filesysman.py
ADDED
|
@@ -0,0 +1,1006 @@
|
|
|
1
|
+
"""Utilities for creating, deleting, renaming, searching, copying, moving,
|
|
2
|
+
organizing, and managing files, folders, and collections of files and folders.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from datetime import datetime as _datetime
|
|
6
|
+
from pathlib import Path as _Path
|
|
7
|
+
import shutil as _shutil
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class File:
|
|
11
|
+
"""Represents a single file and provides operations for managing it.
|
|
12
|
+
|
|
13
|
+
The class supports creating, deleting, renaming, copying, moving, reading,
|
|
14
|
+
writing, appending, and inspecting the represented file.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, file_path: str):
|
|
18
|
+
"""Initializes a File instance with the path of a file.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
file_path: The path of the file to represent.
|
|
22
|
+
"""
|
|
23
|
+
self.__file_path = _Path(file_path)
|
|
24
|
+
|
|
25
|
+
def __str__(self) -> str:
|
|
26
|
+
"""Returns the path of the represented file."""
|
|
27
|
+
return str(self.__file_path)
|
|
28
|
+
|
|
29
|
+
def create(
|
|
30
|
+
self,
|
|
31
|
+
folder_name: str = ".",
|
|
32
|
+
exist_ok: bool = True,
|
|
33
|
+
) -> None:
|
|
34
|
+
"""Creates the represented file in the current directory or a folder.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
folder_name: The name or path of the folder in which to create
|
|
38
|
+
the file.
|
|
39
|
+
exist_ok: Whether to ignore an existing file instead of raising
|
|
40
|
+
an error.
|
|
41
|
+
"""
|
|
42
|
+
folder = _Path(folder_name)
|
|
43
|
+
folder.mkdir(parents=True, exist_ok=True)
|
|
44
|
+
file = folder / self.__file_path
|
|
45
|
+
file.touch(exist_ok=exist_ok)
|
|
46
|
+
self.__file_path = file
|
|
47
|
+
|
|
48
|
+
def delete(self, missing_ok: bool = False) -> None:
|
|
49
|
+
"""Deletes the represented file.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
missing_ok: Whether to ignore the file if it does not exist.
|
|
53
|
+
"""
|
|
54
|
+
self.__file_path.unlink(missing_ok=missing_ok)
|
|
55
|
+
|
|
56
|
+
def rename(self, new_name: str) -> None:
|
|
57
|
+
"""Renames the represented file while keeping it in its current folder.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
new_name: The new name of the file.
|
|
61
|
+
"""
|
|
62
|
+
path = self.__file_path
|
|
63
|
+
new_path = path.rename(path.parent / new_name)
|
|
64
|
+
self.__file_path = new_path
|
|
65
|
+
|
|
66
|
+
def copy(self, destination: str = ".") -> None:
|
|
67
|
+
"""Copies the represented file to a destination folder.
|
|
68
|
+
|
|
69
|
+
The destination folder is created if it does not exist. The original
|
|
70
|
+
file remains unchanged, and the File instance continues to represent
|
|
71
|
+
the original file.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
destination: The name or path of the destination folder.
|
|
75
|
+
"""
|
|
76
|
+
_Path(destination).mkdir(parents=True, exist_ok=True)
|
|
77
|
+
_shutil.copy2(self.__file_path, destination)
|
|
78
|
+
|
|
79
|
+
def move(self, destination: str = ".") -> None:
|
|
80
|
+
"""Moves the represented file to a destination folder.
|
|
81
|
+
|
|
82
|
+
The destination folder is created if it does not exist. The File
|
|
83
|
+
instance is updated to represent the file at its new location.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
destination: The name or path of the destination folder.
|
|
87
|
+
"""
|
|
88
|
+
destination = _Path(destination)
|
|
89
|
+
destination.mkdir(parents=True, exist_ok=True)
|
|
90
|
+
_shutil.move(self.__file_path, destination)
|
|
91
|
+
self.__file_path = destination / self.name
|
|
92
|
+
|
|
93
|
+
def exists(self) -> bool:
|
|
94
|
+
"""Returns whether the represented file exists."""
|
|
95
|
+
return self.__file_path.exists()
|
|
96
|
+
|
|
97
|
+
def is_empty(self) -> bool:
|
|
98
|
+
"""Returns whether the represented file is empty."""
|
|
99
|
+
return self.size == 0
|
|
100
|
+
|
|
101
|
+
def read(self) -> str:
|
|
102
|
+
"""Returns the text content of the represented file."""
|
|
103
|
+
return _Path.read_text(self.__file_path)
|
|
104
|
+
|
|
105
|
+
def write(self, text: str) -> None:
|
|
106
|
+
"""Writes text to the represented file, replacing its existing
|
|
107
|
+
content.
|
|
108
|
+
"""
|
|
109
|
+
self.__file_path.write_text(text)
|
|
110
|
+
|
|
111
|
+
def append(self, text: str) -> None:
|
|
112
|
+
"""Adds text to the end of the represented file."""
|
|
113
|
+
with self.__file_path.open("a") as file:
|
|
114
|
+
file.write(text)
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def path(self) -> _Path:
|
|
118
|
+
"""Returns the path of the represented file."""
|
|
119
|
+
return self.__file_path
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def name(self) -> str:
|
|
123
|
+
"""Returns the name of the represented file."""
|
|
124
|
+
return self.__file_path.name
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def stem(self) -> str:
|
|
128
|
+
"""Returns the stem of the represented file."""
|
|
129
|
+
return self.__file_path.stem
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def suffix(self) -> str:
|
|
133
|
+
"""Returns the extension of the represented file."""
|
|
134
|
+
return self.__file_path.suffix
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def size(self) -> int:
|
|
138
|
+
"""Returns the size of the represented file in bytes."""
|
|
139
|
+
return self.__file_path.stat().st_size
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def parent(self) -> _Path:
|
|
143
|
+
"""Returns the path of the folder containing the file."""
|
|
144
|
+
return self.__file_path.parent
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def absolute_path(self) -> _Path:
|
|
148
|
+
"""Returns the absolute path of the represented file."""
|
|
149
|
+
return self.__file_path.absolute()
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def is_absolute(self) -> bool:
|
|
153
|
+
"""Returns whether the file's path is absolute."""
|
|
154
|
+
return self.__file_path.is_absolute()
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def created_time(self) -> _datetime:
|
|
158
|
+
"""Returns the file's creation time as a datetime object."""
|
|
159
|
+
return _datetime.fromtimestamp(self.__file_path.stat().st_ctime)
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def modified_time(self) -> _datetime:
|
|
163
|
+
"""Returns the file's last modification time as a datetime object."""
|
|
164
|
+
return _datetime.fromtimestamp(self.__file_path.stat().st_mtime)
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def accessed_time(self) -> _datetime:
|
|
168
|
+
"""Returns the file's last access time as a datetime object."""
|
|
169
|
+
return _datetime.fromtimestamp(self.__file_path.stat().st_atime)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class Folder:
|
|
173
|
+
"""Represents a single folder and provides operations for managing it.
|
|
174
|
+
|
|
175
|
+
The class supports creating, deleting, renaming, copying, moving, clearing,
|
|
176
|
+
and inspecting the represented folder.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
def __init__(self, folder_path: str):
|
|
180
|
+
"""Initializes a Folder instance with the path of a folder.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
folder_path: The path of the folder to represent.
|
|
184
|
+
"""
|
|
185
|
+
self.__folder_path = _Path(folder_path)
|
|
186
|
+
|
|
187
|
+
def __str__(self) -> str:
|
|
188
|
+
"""Returns the path of the represented folder."""
|
|
189
|
+
return str(self.__folder_path)
|
|
190
|
+
|
|
191
|
+
def create(
|
|
192
|
+
self,
|
|
193
|
+
folder_name: str = ".",
|
|
194
|
+
parents: bool = False,
|
|
195
|
+
exist_ok: bool = False,
|
|
196
|
+
) -> None:
|
|
197
|
+
"""Creates the represented folder in the current directory or another
|
|
198
|
+
folder.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
folder_name: The name or path of the folder in which to create
|
|
202
|
+
the folder.
|
|
203
|
+
parents: Whether to create missing parent folders.
|
|
204
|
+
exist_ok: Whether to ignore an existing folder instead of raising
|
|
205
|
+
an error.
|
|
206
|
+
"""
|
|
207
|
+
folder = _Path(folder_name) / self.__folder_path
|
|
208
|
+
folder.mkdir(parents=parents, exist_ok=exist_ok)
|
|
209
|
+
self.__folder_path = folder
|
|
210
|
+
|
|
211
|
+
def delete(self, filled_ok: bool = False) -> None:
|
|
212
|
+
"""Deletes the represented folder.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
filled_ok: Whether to delete the folder and all of its contents
|
|
216
|
+
if it is not empty.
|
|
217
|
+
"""
|
|
218
|
+
if filled_ok:
|
|
219
|
+
_shutil.rmtree(self.__folder_path)
|
|
220
|
+
else:
|
|
221
|
+
self.__folder_path.rmdir()
|
|
222
|
+
|
|
223
|
+
def rename(self, new_name: str) -> None:
|
|
224
|
+
"""Renames the represented folder while keeping it in its current
|
|
225
|
+
parent folder.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
new_name: The new name of the folder.
|
|
229
|
+
"""
|
|
230
|
+
folder = self.__folder_path
|
|
231
|
+
new_folder = folder.rename(folder.parent / new_name)
|
|
232
|
+
self.__folder_path = new_folder
|
|
233
|
+
|
|
234
|
+
def copy(self, destination: str = ".") -> None:
|
|
235
|
+
"""Copies the represented folder to a destination folder.
|
|
236
|
+
|
|
237
|
+
The original folder remains unchanged, and the Folder instance
|
|
238
|
+
continues to represent the original folder. If the destination folder
|
|
239
|
+
already exists, its contents are preserved and the copied contents are
|
|
240
|
+
added to it.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
destination: The name or path of the destination folder.
|
|
244
|
+
"""
|
|
245
|
+
_shutil.copytree(
|
|
246
|
+
self.__folder_path,
|
|
247
|
+
destination,
|
|
248
|
+
dirs_exist_ok=True,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
def move(self, destination: str = ".") -> None:
|
|
252
|
+
"""Moves the represented folder to a destination folder.
|
|
253
|
+
|
|
254
|
+
The destination folder is created if it does not exist. The represented
|
|
255
|
+
folder is moved inside the destination folder, and the Folder instance
|
|
256
|
+
is updated to represent the folder at its new location.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
destination: The name or path of the destination folder.
|
|
260
|
+
"""
|
|
261
|
+
destination = _Path(destination)
|
|
262
|
+
destination.mkdir(parents=True, exist_ok=True)
|
|
263
|
+
_shutil.move(self.__folder_path, destination)
|
|
264
|
+
self.__folder_path = destination / self.name
|
|
265
|
+
|
|
266
|
+
def clear(self) -> None:
|
|
267
|
+
"""Deletes all contents of the represented folder without deleting
|
|
268
|
+
the folder itself.
|
|
269
|
+
"""
|
|
270
|
+
for item in self.__folder_path.iterdir():
|
|
271
|
+
if item.is_file():
|
|
272
|
+
item.unlink()
|
|
273
|
+
else:
|
|
274
|
+
_shutil.rmtree(item)
|
|
275
|
+
|
|
276
|
+
def exists(self) -> bool:
|
|
277
|
+
"""Returns whether the represented folder exists."""
|
|
278
|
+
return self.__folder_path.exists()
|
|
279
|
+
|
|
280
|
+
def is_empty(self) -> bool:
|
|
281
|
+
"""Returns whether the represented folder is empty."""
|
|
282
|
+
return not any(self.__folder_path.iterdir())
|
|
283
|
+
|
|
284
|
+
@property
|
|
285
|
+
def path(self) -> _Path:
|
|
286
|
+
"""Returns the path of the represented folder."""
|
|
287
|
+
return self.__folder_path
|
|
288
|
+
|
|
289
|
+
@property
|
|
290
|
+
def name(self) -> str:
|
|
291
|
+
"""Returns the name of the represented folder."""
|
|
292
|
+
return self.__folder_path.name
|
|
293
|
+
|
|
294
|
+
@property
|
|
295
|
+
def parent(self) -> _Path:
|
|
296
|
+
"""Returns the path of the folder containing the folder."""
|
|
297
|
+
return self.__folder_path.parent
|
|
298
|
+
|
|
299
|
+
@property
|
|
300
|
+
def absolute_path(self) -> _Path:
|
|
301
|
+
"""Returns the absolute path of the represented folder."""
|
|
302
|
+
return self.__folder_path.absolute()
|
|
303
|
+
|
|
304
|
+
@property
|
|
305
|
+
def created_time(self) -> _datetime:
|
|
306
|
+
"""Returns the folder's creation time as a datetime object."""
|
|
307
|
+
return _datetime.fromtimestamp(self.__folder_path.stat().st_ctime)
|
|
308
|
+
|
|
309
|
+
@property
|
|
310
|
+
def modified_time(self) -> _datetime:
|
|
311
|
+
"""Returns the folder's last modification time as a datetime object."""
|
|
312
|
+
return _datetime.fromtimestamp(self.__folder_path.stat().st_mtime)
|
|
313
|
+
|
|
314
|
+
@property
|
|
315
|
+
def accessed_time(self) -> _datetime:
|
|
316
|
+
"""Returns the folder's last access time as a datetime object."""
|
|
317
|
+
return _datetime.fromtimestamp(self.__folder_path.stat().st_atime)
|
|
318
|
+
|
|
319
|
+
@property
|
|
320
|
+
def current_size(self) -> int:
|
|
321
|
+
"""Returns the total size in bytes of files directly inside the
|
|
322
|
+
folder.
|
|
323
|
+
"""
|
|
324
|
+
size = 0
|
|
325
|
+
for item in self.__folder_path.iterdir():
|
|
326
|
+
if item.is_file():
|
|
327
|
+
size += item.stat().st_size
|
|
328
|
+
return size
|
|
329
|
+
|
|
330
|
+
@property
|
|
331
|
+
def total_size(self) -> int:
|
|
332
|
+
"""Returns the total size in bytes of all files in the folder and
|
|
333
|
+
its subfolders.
|
|
334
|
+
"""
|
|
335
|
+
size = 0
|
|
336
|
+
for item in self.__folder_path.rglob("*"):
|
|
337
|
+
if item.is_file():
|
|
338
|
+
size += item.stat().st_size
|
|
339
|
+
return size
|
|
340
|
+
|
|
341
|
+
@property
|
|
342
|
+
def is_absolute(self) -> bool:
|
|
343
|
+
"""Returns whether the folder's path is absolute."""
|
|
344
|
+
return self.__folder_path.is_absolute()
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def create_files(
|
|
348
|
+
number: int,
|
|
349
|
+
file_name: str = "File",
|
|
350
|
+
sep: str = " ",
|
|
351
|
+
extension: str = ".txt",
|
|
352
|
+
folder_name: str = ".",
|
|
353
|
+
) -> None:
|
|
354
|
+
"""Creates multiple numbered files in the current directory or a folder.
|
|
355
|
+
|
|
356
|
+
The files are named using the specified base name, separator, number,
|
|
357
|
+
and extension. The extension is normalized to lowercase.
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
number: The number of files to create.
|
|
361
|
+
file_name: The base name to use for the files.
|
|
362
|
+
sep: The separator placed between the file name and number.
|
|
363
|
+
extension: The file extension.
|
|
364
|
+
folder_name: The name or path of the folder in which to create
|
|
365
|
+
the files.
|
|
366
|
+
|
|
367
|
+
Raises:
|
|
368
|
+
ValueError: If number is not positive.
|
|
369
|
+
"""
|
|
370
|
+
if number > 0:
|
|
371
|
+
extension = (
|
|
372
|
+
extension.lower()
|
|
373
|
+
if extension.startswith(".")
|
|
374
|
+
else f".{extension.lower()}"
|
|
375
|
+
)
|
|
376
|
+
folder = _Path(folder_name)
|
|
377
|
+
folder.mkdir(parents=True, exist_ok=True)
|
|
378
|
+
|
|
379
|
+
for number in range(1, number + 1):
|
|
380
|
+
(folder / f"{file_name}{sep}{number}{extension}").touch()
|
|
381
|
+
else:
|
|
382
|
+
raise ValueError("The number must be positive!")
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def delete_files(
|
|
386
|
+
number: int,
|
|
387
|
+
file_name: str,
|
|
388
|
+
sep: str = " ",
|
|
389
|
+
extension: str = ".txt",
|
|
390
|
+
folder_name: str = ".",
|
|
391
|
+
) -> None:
|
|
392
|
+
"""Deletes multiple numbered files from the current directory or a folder.
|
|
393
|
+
|
|
394
|
+
The files are identified using the specified base name, separator, number,
|
|
395
|
+
and extension. The extension is normalized to lowercase.
|
|
396
|
+
|
|
397
|
+
Args:
|
|
398
|
+
number: The number of files to delete.
|
|
399
|
+
file_name: The base name of the files.
|
|
400
|
+
sep: The separator placed between the file name and number.
|
|
401
|
+
extension: The file extension.
|
|
402
|
+
folder_name: The name or path of the folder containing the files.
|
|
403
|
+
|
|
404
|
+
Raises:
|
|
405
|
+
ValueError: If number is not positive.
|
|
406
|
+
"""
|
|
407
|
+
if number > 0:
|
|
408
|
+
extension = (
|
|
409
|
+
extension.lower()
|
|
410
|
+
if extension.startswith(".")
|
|
411
|
+
else f".{extension.lower()}"
|
|
412
|
+
)
|
|
413
|
+
folder = _Path(folder_name)
|
|
414
|
+
|
|
415
|
+
for number in range(1, number + 1):
|
|
416
|
+
(folder / f"{file_name}{sep}{number}{extension}").unlink(
|
|
417
|
+
missing_ok=True
|
|
418
|
+
)
|
|
419
|
+
else:
|
|
420
|
+
raise ValueError("The number must be positive!")
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def rename_files(
|
|
424
|
+
number: int,
|
|
425
|
+
old_name: str,
|
|
426
|
+
new_name: str,
|
|
427
|
+
old_sep: str = " ",
|
|
428
|
+
new_sep: str = " ",
|
|
429
|
+
extension: str = ".txt",
|
|
430
|
+
folder_name: str = ".",
|
|
431
|
+
) -> None:
|
|
432
|
+
"""Renames multiple numbered files while preserving their numbering.
|
|
433
|
+
|
|
434
|
+
The base name and separator can be changed while the extension is
|
|
435
|
+
normalized to lowercase.
|
|
436
|
+
|
|
437
|
+
Args:
|
|
438
|
+
number: The number of files to rename.
|
|
439
|
+
old_name: The current base name of the files.
|
|
440
|
+
new_name: The new base name for the files.
|
|
441
|
+
old_sep: The separator currently used between the file name and number.
|
|
442
|
+
new_sep: The separator to use between the new file name and number.
|
|
443
|
+
extension: The file extension of the files.
|
|
444
|
+
folder_name: The name or path of the folder containing the files.
|
|
445
|
+
|
|
446
|
+
Raises:
|
|
447
|
+
ValueError: If number is not positive.
|
|
448
|
+
"""
|
|
449
|
+
if number > 0:
|
|
450
|
+
extension = (
|
|
451
|
+
extension.lower()
|
|
452
|
+
if extension.startswith(".")
|
|
453
|
+
else f".{extension.lower()}"
|
|
454
|
+
)
|
|
455
|
+
folder = _Path(folder_name)
|
|
456
|
+
|
|
457
|
+
for number in range(1, number + 1):
|
|
458
|
+
path = folder / f"{old_name}{old_sep}{number}{extension}"
|
|
459
|
+
|
|
460
|
+
if path.exists():
|
|
461
|
+
new_path = folder / (
|
|
462
|
+
f"{new_name}{new_sep}{number}{extension}"
|
|
463
|
+
)
|
|
464
|
+
path.rename(new_path)
|
|
465
|
+
else:
|
|
466
|
+
raise ValueError("The number must be positive!")
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
def get_files(
|
|
470
|
+
folder_name: str = ".",
|
|
471
|
+
extension: str | None = None,
|
|
472
|
+
) -> list[_Path]:
|
|
473
|
+
"""Returns files from the specified folder, optionally filtered by extension.
|
|
474
|
+
|
|
475
|
+
Args:
|
|
476
|
+
folder_name: The name or path of the folder to search.
|
|
477
|
+
extension: The file extension used to filter the results.
|
|
478
|
+
|
|
479
|
+
Returns:
|
|
480
|
+
A list containing the paths of the matching files.
|
|
481
|
+
"""
|
|
482
|
+
files = []
|
|
483
|
+
folder = _Path(folder_name)
|
|
484
|
+
|
|
485
|
+
if extension:
|
|
486
|
+
extension = (
|
|
487
|
+
extension.lower()
|
|
488
|
+
if extension.startswith(".")
|
|
489
|
+
else f".{extension.lower()}"
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
for item in folder.glob(f"*{extension}"):
|
|
493
|
+
if item.is_file():
|
|
494
|
+
files.append(item)
|
|
495
|
+
else:
|
|
496
|
+
for item in folder.iterdir():
|
|
497
|
+
if item.is_file():
|
|
498
|
+
files.append(item)
|
|
499
|
+
|
|
500
|
+
return files
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def find_files(
|
|
504
|
+
keyword: str,
|
|
505
|
+
folder_name: str = ".",
|
|
506
|
+
extension: str | None = None,
|
|
507
|
+
) -> list[_Path]:
|
|
508
|
+
"""Returns files whose names contain a specified keyword.
|
|
509
|
+
|
|
510
|
+
Matching can optionally be filtered by file extension. The keyword must
|
|
511
|
+
not be empty.
|
|
512
|
+
|
|
513
|
+
Args:
|
|
514
|
+
keyword: The keyword to search for in file names.
|
|
515
|
+
folder_name: The name or path of the folder to search.
|
|
516
|
+
extension: The file extension used to filter the results.
|
|
517
|
+
|
|
518
|
+
Returns:
|
|
519
|
+
A list containing the paths of the matching files.
|
|
520
|
+
|
|
521
|
+
Raises:
|
|
522
|
+
ValueError: If keyword is empty.
|
|
523
|
+
"""
|
|
524
|
+
if not keyword:
|
|
525
|
+
raise ValueError("The keyword must not be empty!")
|
|
526
|
+
files = []
|
|
527
|
+
folder = _Path(folder_name)
|
|
528
|
+
keyword = keyword.lower()
|
|
529
|
+
|
|
530
|
+
if extension:
|
|
531
|
+
extension = (
|
|
532
|
+
extension.lower()
|
|
533
|
+
if extension.startswith(".")
|
|
534
|
+
else f".{extension.lower()}"
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
for item in folder.iterdir():
|
|
538
|
+
if item.is_file():
|
|
539
|
+
name = item.stem.lower()
|
|
540
|
+
exten = item.suffix.lower()
|
|
541
|
+
|
|
542
|
+
if extension:
|
|
543
|
+
if keyword in name and extension == exten:
|
|
544
|
+
files.append(item)
|
|
545
|
+
else:
|
|
546
|
+
if keyword in name:
|
|
547
|
+
files.append(item)
|
|
548
|
+
|
|
549
|
+
return files
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
def rename_by_keyword(
|
|
553
|
+
keyword: str,
|
|
554
|
+
new_name: str,
|
|
555
|
+
sep: str = " ",
|
|
556
|
+
extension: str | None = None,
|
|
557
|
+
folder_name: str = ".",
|
|
558
|
+
) -> None:
|
|
559
|
+
"""Renames files whose names contain a specified keyword using sequential
|
|
560
|
+
names.
|
|
561
|
+
|
|
562
|
+
Original file extensions are preserved, and files can optionally be
|
|
563
|
+
filtered by extension. The keyword must not be empty.
|
|
564
|
+
|
|
565
|
+
Args:
|
|
566
|
+
keyword: The keyword to search for in file names.
|
|
567
|
+
new_name: The base name to assign to matching files.
|
|
568
|
+
sep: The separator placed between the new file name and number.
|
|
569
|
+
extension: The file extension used to filter matching files.
|
|
570
|
+
folder_name: The name or path of the folder to search.
|
|
571
|
+
|
|
572
|
+
Raises:
|
|
573
|
+
ValueError: If keyword is empty.
|
|
574
|
+
"""
|
|
575
|
+
if not keyword:
|
|
576
|
+
raise ValueError("The keyword must not be empty!")
|
|
577
|
+
|
|
578
|
+
keyword = keyword.lower()
|
|
579
|
+
|
|
580
|
+
if extension:
|
|
581
|
+
extension = (
|
|
582
|
+
extension.lower()
|
|
583
|
+
if extension.startswith(".")
|
|
584
|
+
else f".{extension.lower()}"
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
num = 1
|
|
588
|
+
folder = _Path(folder_name)
|
|
589
|
+
|
|
590
|
+
for item in folder.iterdir():
|
|
591
|
+
if item.is_file():
|
|
592
|
+
name = item.stem.lower()
|
|
593
|
+
exten = item.suffix.lower()
|
|
594
|
+
|
|
595
|
+
if extension:
|
|
596
|
+
if keyword in name and extension == exten:
|
|
597
|
+
item.rename(folder / f"{new_name}{sep}{num}{exten}")
|
|
598
|
+
num += 1
|
|
599
|
+
else:
|
|
600
|
+
if keyword in name:
|
|
601
|
+
item.rename(folder / f"{new_name}{sep}{num}{exten}")
|
|
602
|
+
num += 1
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
def delete_by_keyword(
|
|
606
|
+
keyword: str,
|
|
607
|
+
extension: str | None = None,
|
|
608
|
+
folder_name: str = ".",
|
|
609
|
+
) -> None:
|
|
610
|
+
"""Deletes files whose names contain a specified keyword.
|
|
611
|
+
|
|
612
|
+
Files can optionally be filtered by extension. The keyword must not be
|
|
613
|
+
empty.
|
|
614
|
+
|
|
615
|
+
Args:
|
|
616
|
+
keyword: The keyword to search for in file names.
|
|
617
|
+
extension: The file extension used to filter the files.
|
|
618
|
+
folder_name: The name or path of the folder to search.
|
|
619
|
+
|
|
620
|
+
Raises:
|
|
621
|
+
ValueError: If keyword is empty.
|
|
622
|
+
"""
|
|
623
|
+
if not keyword:
|
|
624
|
+
raise ValueError("The keyword must not be empty!")
|
|
625
|
+
|
|
626
|
+
keyword = keyword.lower()
|
|
627
|
+
|
|
628
|
+
if extension:
|
|
629
|
+
extension = (
|
|
630
|
+
extension.lower()
|
|
631
|
+
if extension.startswith(".")
|
|
632
|
+
else f".{extension.lower()}"
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
folder = _Path(folder_name)
|
|
636
|
+
|
|
637
|
+
for item in folder.iterdir():
|
|
638
|
+
if item.is_file():
|
|
639
|
+
name = item.stem.lower()
|
|
640
|
+
|
|
641
|
+
if extension:
|
|
642
|
+
exten = item.suffix.lower()
|
|
643
|
+
|
|
644
|
+
if keyword in name and extension == exten:
|
|
645
|
+
item.unlink()
|
|
646
|
+
else:
|
|
647
|
+
if keyword in name:
|
|
648
|
+
item.unlink()
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def get_files_count(
|
|
652
|
+
folder_name: str = ".",
|
|
653
|
+
extension: str | None = None,
|
|
654
|
+
) -> int:
|
|
655
|
+
"""Returns the number of files in the specified folder, optionally
|
|
656
|
+
filtered by file extension.
|
|
657
|
+
|
|
658
|
+
Args:
|
|
659
|
+
folder_name: The name or path of the folder to search.
|
|
660
|
+
extension: The file extension used to filter the results.
|
|
661
|
+
|
|
662
|
+
Returns:
|
|
663
|
+
The number of matching files.
|
|
664
|
+
"""
|
|
665
|
+
if extension:
|
|
666
|
+
extension = (
|
|
667
|
+
extension.lower()
|
|
668
|
+
if extension.startswith(".")
|
|
669
|
+
else f".{extension.lower()}"
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
files_count = 0
|
|
673
|
+
folder = _Path(folder_name)
|
|
674
|
+
|
|
675
|
+
for item in folder.iterdir():
|
|
676
|
+
if item.is_file():
|
|
677
|
+
if extension:
|
|
678
|
+
exten = item.suffix.lower()
|
|
679
|
+
|
|
680
|
+
if extension == exten:
|
|
681
|
+
files_count += 1
|
|
682
|
+
else:
|
|
683
|
+
files_count += 1
|
|
684
|
+
|
|
685
|
+
return files_count
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
def copy_files(
|
|
689
|
+
source_folder: str = ".",
|
|
690
|
+
destination_folder: str = ".",
|
|
691
|
+
extension: str | None = None,
|
|
692
|
+
) -> None:
|
|
693
|
+
"""Copies files from a source folder to a destination folder.
|
|
694
|
+
|
|
695
|
+
Existing files in the destination folder are overwritten. Files can
|
|
696
|
+
optionally be filtered by file extension.
|
|
697
|
+
|
|
698
|
+
Args:
|
|
699
|
+
source_folder: The name or path of the folder containing the files
|
|
700
|
+
to copy.
|
|
701
|
+
destination_folder: The name or path of the folder to copy the files
|
|
702
|
+
to.
|
|
703
|
+
extension: The file extension used to filter the files to copy.
|
|
704
|
+
|
|
705
|
+
Raises:
|
|
706
|
+
FileExistsError: If the source and destination folders are the same.
|
|
707
|
+
"""
|
|
708
|
+
if source_folder == destination_folder:
|
|
709
|
+
raise FileExistsError(
|
|
710
|
+
f"'{source_folder}' Folder is the same as "
|
|
711
|
+
f"'{destination_folder}' Folder"
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
if extension:
|
|
715
|
+
extension = (
|
|
716
|
+
extension.lower()
|
|
717
|
+
if extension.startswith(".")
|
|
718
|
+
else f".{extension.lower()}"
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
source = _Path(source_folder)
|
|
722
|
+
destination = _Path(destination_folder)
|
|
723
|
+
destination.mkdir(parents=True, exist_ok=True)
|
|
724
|
+
|
|
725
|
+
for item in source.iterdir():
|
|
726
|
+
if item.is_file():
|
|
727
|
+
if extension:
|
|
728
|
+
exten = item.suffix.lower()
|
|
729
|
+
|
|
730
|
+
if extension == exten:
|
|
731
|
+
_shutil.copy2(item, destination)
|
|
732
|
+
else:
|
|
733
|
+
_shutil.copy2(item, destination)
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
def move_files(
|
|
737
|
+
source_folder: str = ".",
|
|
738
|
+
destination_folder: str = ".",
|
|
739
|
+
extension: str | None = None,
|
|
740
|
+
) -> None:
|
|
741
|
+
"""Moves files from a source folder to a destination folder.
|
|
742
|
+
|
|
743
|
+
Files can optionally be filtered by file extension.
|
|
744
|
+
|
|
745
|
+
Args:
|
|
746
|
+
source_folder: The name or path of the source folder.
|
|
747
|
+
destination_folder: The name or path of the destination folder.
|
|
748
|
+
extension: The file extension used to filter the files to move.
|
|
749
|
+
|
|
750
|
+
Raises:
|
|
751
|
+
FileExistsError: If the source and destination folders are the same.
|
|
752
|
+
shutil.Error: If a file cannot be moved to the destination folder.
|
|
753
|
+
"""
|
|
754
|
+
if source_folder == destination_folder:
|
|
755
|
+
raise FileExistsError(
|
|
756
|
+
f"'{source_folder}' Folder is the same as "
|
|
757
|
+
f"'{destination_folder}' Folder"
|
|
758
|
+
)
|
|
759
|
+
|
|
760
|
+
if extension:
|
|
761
|
+
extension = (
|
|
762
|
+
extension.lower()
|
|
763
|
+
if extension.startswith(".")
|
|
764
|
+
else f".{extension.lower()}"
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
source = _Path(source_folder)
|
|
768
|
+
destination = _Path(destination_folder)
|
|
769
|
+
destination.mkdir(parents=True, exist_ok=True)
|
|
770
|
+
|
|
771
|
+
for item in source.iterdir():
|
|
772
|
+
if item.is_file():
|
|
773
|
+
if extension:
|
|
774
|
+
exten = item.suffix.lower()
|
|
775
|
+
|
|
776
|
+
if extension == exten:
|
|
777
|
+
_shutil.move(item, destination)
|
|
778
|
+
else:
|
|
779
|
+
_shutil.move(item, destination)
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
def organize_by_extension(
|
|
783
|
+
folder_name: str = ".",
|
|
784
|
+
destination_folder: str | None = None,
|
|
785
|
+
no_extension_folder: str = "No Extension",
|
|
786
|
+
) -> None:
|
|
787
|
+
"""Organizes files into folders based on their file extensions.
|
|
788
|
+
|
|
789
|
+
Files with the same extension are moved into the same folder. If a
|
|
790
|
+
destination folder is specified, copies of the files are organized there
|
|
791
|
+
while the original files remain unchanged. Files without an extension are
|
|
792
|
+
placed in a folder with the specified name.
|
|
793
|
+
|
|
794
|
+
Args:
|
|
795
|
+
folder_name: The name or path of the folder containing the files
|
|
796
|
+
to organize.
|
|
797
|
+
destination_folder: The name or path of the folder in which to
|
|
798
|
+
organize copies of the files. If None, the files are organized
|
|
799
|
+
in the original folder.
|
|
800
|
+
no_extension_folder: The name of the folder in which to place files
|
|
801
|
+
without an extension.
|
|
802
|
+
|
|
803
|
+
Raises:
|
|
804
|
+
FileExistsError: If the source and destination folders are the same.
|
|
805
|
+
"""
|
|
806
|
+
if folder_name == destination_folder:
|
|
807
|
+
raise FileExistsError(
|
|
808
|
+
f"'{folder_name}' Folder is the same as "
|
|
809
|
+
f"'{destination_folder}' Folder"
|
|
810
|
+
)
|
|
811
|
+
|
|
812
|
+
folder = _Path(folder_name)
|
|
813
|
+
|
|
814
|
+
if destination_folder:
|
|
815
|
+
destination = _Path(destination_folder)
|
|
816
|
+
destination.mkdir(parents=True, exist_ok=True)
|
|
817
|
+
|
|
818
|
+
for item in folder.iterdir():
|
|
819
|
+
if item.is_file():
|
|
820
|
+
exten = item.suffix
|
|
821
|
+
|
|
822
|
+
if exten:
|
|
823
|
+
if destination_folder:
|
|
824
|
+
exten_folder = destination / exten.lstrip(".").upper()
|
|
825
|
+
exten_folder.mkdir(exist_ok=True)
|
|
826
|
+
_shutil.copy2(item, exten_folder)
|
|
827
|
+
else:
|
|
828
|
+
exten_folder = folder / exten.lstrip(".").upper()
|
|
829
|
+
exten_folder.mkdir(exist_ok=True)
|
|
830
|
+
_shutil.move(item, exten_folder)
|
|
831
|
+
else:
|
|
832
|
+
if destination_folder:
|
|
833
|
+
no_exten = destination / no_extension_folder
|
|
834
|
+
no_exten.mkdir(exist_ok=True)
|
|
835
|
+
_shutil.copy2(item, no_exten)
|
|
836
|
+
else:
|
|
837
|
+
no_exten = folder / no_extension_folder
|
|
838
|
+
no_exten.mkdir(exist_ok=True)
|
|
839
|
+
_shutil.move(item, no_exten)
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
def get_files_recursive(
|
|
843
|
+
folder_name: str = ".",
|
|
844
|
+
extension: str | None = None,
|
|
845
|
+
) -> list[_Path]:
|
|
846
|
+
"""Returns files from the specified folder and its subfolders, optionally
|
|
847
|
+
filtered by file extension.
|
|
848
|
+
|
|
849
|
+
Args:
|
|
850
|
+
folder_name: The name or path of the folder to search.
|
|
851
|
+
extension: The file extension used to filter the results.
|
|
852
|
+
|
|
853
|
+
Returns:
|
|
854
|
+
A list containing the paths of the matching files.
|
|
855
|
+
"""
|
|
856
|
+
if extension:
|
|
857
|
+
extension = (
|
|
858
|
+
extension.lower()
|
|
859
|
+
if extension.startswith(".")
|
|
860
|
+
else f".{extension.lower()}"
|
|
861
|
+
)
|
|
862
|
+
files = []
|
|
863
|
+
folder = _Path(folder_name)
|
|
864
|
+
|
|
865
|
+
for item in folder.rglob(f"*{extension if extension else ''}"):
|
|
866
|
+
if item.is_file():
|
|
867
|
+
files.append(item)
|
|
868
|
+
|
|
869
|
+
return files
|
|
870
|
+
|
|
871
|
+
|
|
872
|
+
def find_files_recursive(
|
|
873
|
+
keyword: str,
|
|
874
|
+
folder_name: str = ".",
|
|
875
|
+
extension: str | None = None,
|
|
876
|
+
) -> list[_Path]:
|
|
877
|
+
"""Returns files from the specified folder and its subfolders whose names
|
|
878
|
+
contain a specified keyword.
|
|
879
|
+
|
|
880
|
+
Matching can optionally be filtered by file extension. The keyword must
|
|
881
|
+
not be empty.
|
|
882
|
+
|
|
883
|
+
Args:
|
|
884
|
+
keyword: The keyword to search for in file names.
|
|
885
|
+
folder_name: The name or path of the folder to search.
|
|
886
|
+
extension: The file extension used to filter the results.
|
|
887
|
+
|
|
888
|
+
Returns:
|
|
889
|
+
A list containing the paths of the matching files.
|
|
890
|
+
|
|
891
|
+
Raises:
|
|
892
|
+
ValueError: If keyword is empty.
|
|
893
|
+
"""
|
|
894
|
+
if not keyword:
|
|
895
|
+
raise ValueError("The keyword must not be empty!")
|
|
896
|
+
if extension:
|
|
897
|
+
extension = (
|
|
898
|
+
extension.lower()
|
|
899
|
+
if extension.startswith(".")
|
|
900
|
+
else f".{extension.lower()}"
|
|
901
|
+
)
|
|
902
|
+
|
|
903
|
+
files = []
|
|
904
|
+
folder = _Path(folder_name)
|
|
905
|
+
|
|
906
|
+
for item in folder.rglob(f"*{extension if extension else ''}"):
|
|
907
|
+
if item.is_file():
|
|
908
|
+
if keyword.lower() in item.stem.lower():
|
|
909
|
+
files.append(item)
|
|
910
|
+
|
|
911
|
+
return files
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
def get_folders(
|
|
915
|
+
folder_name: str = ".",
|
|
916
|
+
recursive: bool = False,
|
|
917
|
+
) -> list[_Path]:
|
|
918
|
+
"""Returns folders from the specified folder, optionally including
|
|
919
|
+
subfolders.
|
|
920
|
+
|
|
921
|
+
Args:
|
|
922
|
+
folder_name: The name or path of the folder to search.
|
|
923
|
+
recursive: Whether to include folders inside subfolders.
|
|
924
|
+
|
|
925
|
+
Returns:
|
|
926
|
+
A list containing the paths of the matching folders.
|
|
927
|
+
"""
|
|
928
|
+
folders = []
|
|
929
|
+
folder = _Path(folder_name)
|
|
930
|
+
|
|
931
|
+
if recursive:
|
|
932
|
+
for item in folder.rglob("*"):
|
|
933
|
+
if item.is_dir():
|
|
934
|
+
folders.append(item)
|
|
935
|
+
else:
|
|
936
|
+
for item in folder.iterdir():
|
|
937
|
+
if item.is_dir():
|
|
938
|
+
folders.append(item)
|
|
939
|
+
|
|
940
|
+
return folders
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
def clear_empty_files(
|
|
944
|
+
folder_name: str = ".",
|
|
945
|
+
recursive: bool = False,
|
|
946
|
+
extension: str | None = None,
|
|
947
|
+
) -> None:
|
|
948
|
+
"""Deletes empty files from the specified folder.
|
|
949
|
+
|
|
950
|
+
Files can optionally be filtered by file extension. When recursive is
|
|
951
|
+
True, empty files inside subfolders are also deleted.
|
|
952
|
+
|
|
953
|
+
Args:
|
|
954
|
+
folder_name: The name or path of the folder to search.
|
|
955
|
+
recursive: Whether to check files inside subfolders.
|
|
956
|
+
extension: The file extension used to filter the files.
|
|
957
|
+
"""
|
|
958
|
+
if extension:
|
|
959
|
+
extension = (
|
|
960
|
+
extension.lower()
|
|
961
|
+
if extension.startswith(".")
|
|
962
|
+
else f".{extension.lower()}"
|
|
963
|
+
)
|
|
964
|
+
|
|
965
|
+
folder = _Path(folder_name)
|
|
966
|
+
|
|
967
|
+
if recursive:
|
|
968
|
+
for item in folder.rglob(f"*{extension if extension else ''}"):
|
|
969
|
+
if item.is_file() and item.stat().st_size == 0:
|
|
970
|
+
item.unlink()
|
|
971
|
+
else:
|
|
972
|
+
for item in folder.iterdir():
|
|
973
|
+
if item.is_file() and item.stat().st_size == 0:
|
|
974
|
+
if extension:
|
|
975
|
+
exten = item.suffix.lower()
|
|
976
|
+
|
|
977
|
+
if extension == exten:
|
|
978
|
+
item.unlink()
|
|
979
|
+
else:
|
|
980
|
+
item.unlink()
|
|
981
|
+
|
|
982
|
+
|
|
983
|
+
def clear_empty_folders(
|
|
984
|
+
folder_name: str = ".",
|
|
985
|
+
recursive: bool = False,
|
|
986
|
+
) -> None:
|
|
987
|
+
"""Deletes empty folders from the specified folder.
|
|
988
|
+
|
|
989
|
+
Only folders that are empty when they are checked are deleted. When
|
|
990
|
+
recursive is True, folders inside subfolders are also checked. A folder
|
|
991
|
+
that becomes empty after it has already been checked will not be deleted.
|
|
992
|
+
|
|
993
|
+
Args:
|
|
994
|
+
folder_name: The name or path of the folder to search.
|
|
995
|
+
recursive: Whether to check folders inside subfolders.
|
|
996
|
+
"""
|
|
997
|
+
folder = _Path(folder_name)
|
|
998
|
+
|
|
999
|
+
if recursive:
|
|
1000
|
+
for item in folder.rglob("*"):
|
|
1001
|
+
if item.is_dir() and not any(item.iterdir()):
|
|
1002
|
+
item.rmdir()
|
|
1003
|
+
else:
|
|
1004
|
+
for item in folder.iterdir():
|
|
1005
|
+
if item.is_dir() and not any(item.iterdir()):
|
|
1006
|
+
item.rmdir()
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: filesysman
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Utilities for managing files and folders in Python.
|
|
5
|
+
Author: Saif Bayoumy
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# filesysman
|
|
13
|
+
|
|
14
|
+
A simple Python library for creating, managing, searching, copying, moving, and organizing files and folders.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- Create and delete files and folders
|
|
19
|
+
- Read, write, and append file content
|
|
20
|
+
- Rename files and folders
|
|
21
|
+
- Copy and move files and folders
|
|
22
|
+
- Search for files by keyword
|
|
23
|
+
- Search recursively through subfolders
|
|
24
|
+
- Find files by extension
|
|
25
|
+
- Organize files by extension
|
|
26
|
+
- Get information about files and folders
|
|
27
|
+
- Count files
|
|
28
|
+
- Find and remove empty folders
|
|
29
|
+
- Work with multiple files at once
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
Install the package using pip:
|
|
34
|
+
|
|
35
|
+
pip install filesysman
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
### Working with Files
|
|
40
|
+
|
|
41
|
+
from filesysman import File
|
|
42
|
+
|
|
43
|
+
file = File("example.txt")
|
|
44
|
+
|
|
45
|
+
file.create()
|
|
46
|
+
file.write("Hello, World!")
|
|
47
|
+
|
|
48
|
+
print(file.read())
|
|
49
|
+
print(file.name)
|
|
50
|
+
print(file.suffix)
|
|
51
|
+
print(file.size)
|
|
52
|
+
|
|
53
|
+
### Working with Folders
|
|
54
|
+
|
|
55
|
+
from filesysman import Folder
|
|
56
|
+
|
|
57
|
+
folder = Folder("my_folder")
|
|
58
|
+
|
|
59
|
+
folder.create()
|
|
60
|
+
|
|
61
|
+
print(folder.exists())
|
|
62
|
+
print(folder.name)
|
|
63
|
+
print(folder.path)
|
|
64
|
+
|
|
65
|
+
### Working with Multiple Files
|
|
66
|
+
|
|
67
|
+
from filesysman import create_files, get_files
|
|
68
|
+
|
|
69
|
+
create_files(
|
|
70
|
+
"file1.txt",
|
|
71
|
+
"file2.txt",
|
|
72
|
+
"file3.txt"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
files = get_files()
|
|
76
|
+
|
|
77
|
+
for file in files:
|
|
78
|
+
print(file)
|
|
79
|
+
|
|
80
|
+
## Searching for Files
|
|
81
|
+
|
|
82
|
+
Search for files containing a specific keyword in their names:
|
|
83
|
+
|
|
84
|
+
from filesysman import find_files
|
|
85
|
+
|
|
86
|
+
files = find_files("report")
|
|
87
|
+
|
|
88
|
+
for file in files:
|
|
89
|
+
print(file)
|
|
90
|
+
|
|
91
|
+
You can also search recursively through subfolders:
|
|
92
|
+
|
|
93
|
+
from filesysman import find_files_recursive
|
|
94
|
+
|
|
95
|
+
files = find_files_recursive("report")
|
|
96
|
+
|
|
97
|
+
for file in files:
|
|
98
|
+
print(file)
|
|
99
|
+
|
|
100
|
+
## Organizing Files by Extension
|
|
101
|
+
|
|
102
|
+
Files can be organized into folders based on their extensions:
|
|
103
|
+
|
|
104
|
+
from filesysman import organize_by_extension
|
|
105
|
+
|
|
106
|
+
organize_by_extension()
|
|
107
|
+
|
|
108
|
+
For example:
|
|
109
|
+
|
|
110
|
+
Before:
|
|
111
|
+
|
|
112
|
+
folder/
|
|
113
|
+
├── photo.jpg
|
|
114
|
+
├── document.pdf
|
|
115
|
+
├── script.py
|
|
116
|
+
└── notes.txt
|
|
117
|
+
|
|
118
|
+
After:
|
|
119
|
+
|
|
120
|
+
folder/
|
|
121
|
+
├── JPG/
|
|
122
|
+
│ └── photo.jpg
|
|
123
|
+
├── PDF/
|
|
124
|
+
│ └── document.pdf
|
|
125
|
+
├── PY/
|
|
126
|
+
│ └── script.py
|
|
127
|
+
└── TXT/
|
|
128
|
+
└── notes.txt
|
|
129
|
+
|
|
130
|
+
## Main API
|
|
131
|
+
|
|
132
|
+
### Classes
|
|
133
|
+
|
|
134
|
+
- File
|
|
135
|
+
- Folder
|
|
136
|
+
|
|
137
|
+
### File and Folder Operations
|
|
138
|
+
|
|
139
|
+
- create_files()
|
|
140
|
+
- delete_files()
|
|
141
|
+
- rename_files()
|
|
142
|
+
- copy_files()
|
|
143
|
+
- move_files()
|
|
144
|
+
|
|
145
|
+
### Searching and Retrieving
|
|
146
|
+
|
|
147
|
+
- get_files()
|
|
148
|
+
- find_files()
|
|
149
|
+
- get_files_count()
|
|
150
|
+
- get_files_recursive()
|
|
151
|
+
- find_files_recursive()
|
|
152
|
+
- get_folders()
|
|
153
|
+
|
|
154
|
+
### Organization and Cleanup
|
|
155
|
+
|
|
156
|
+
- organize_by_extension()
|
|
157
|
+
- rename_by_keyword()
|
|
158
|
+
- delete_by_keyword()
|
|
159
|
+
- clear_empty_folders()
|
|
160
|
+
|
|
161
|
+
## Requirements
|
|
162
|
+
|
|
163
|
+
- Python 3.8 or newer
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
This project is licensed under the MIT License.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
filesysman/__init__.py,sha256=CWPC0uLqJZMevreRd-twKEuSo0ukGxC27hWWrXQpQv8,395
|
|
2
|
+
filesysman/filesysman.py,sha256=jGIO5GP1DgCl5v92J0apTM5tBVuPugncXr-1TtLZ9bM,31930
|
|
3
|
+
filesysman-1.0.0.dist-info/licenses/LICENSE,sha256=q7KJbcPCqUAvkBuI1QNc8Kg9XPfBfnNkLN9WKwudO8U,11
|
|
4
|
+
filesysman-1.0.0.dist-info/METADATA,sha256=lUxAn_z-99vAverogHYFrMS60lwapbFaUSk_4b4kzjs,3173
|
|
5
|
+
filesysman-1.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
6
|
+
filesysman-1.0.0.dist-info/top_level.txt,sha256=18ljtwWrbPlMPFixrziWBkfZnEIixTMFrGYtXj2tkyY,11
|
|
7
|
+
filesysman-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
MIT License
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
filesysman
|