-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1. new class - FileManager - to make sure no changes are done to targ…
…et folder 2. tests to the class 3. modify the script to use it in all file and folder operations
- Loading branch information
Showing
4 changed files
with
255 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
from pathlib import Path | ||
import shutil | ||
import os | ||
|
||
|
||
class FileManagerError(Exception): | ||
pass | ||
|
||
|
||
class ProtectedPathError(FileManagerError): | ||
def __init__(self, message): | ||
super().__init__(message) | ||
|
||
|
||
class FileManager: | ||
_instance = None | ||
protected_dirs = set() | ||
|
||
def __new__(cls, *args, **kwargs): | ||
if not cls._instance: | ||
cls._instance = super(FileManager, cls).__new__(cls, *args, **kwargs) | ||
return cls._instance | ||
|
||
def add_protected_dir(self, dir_path): | ||
protected_dir = Path(dir_path).resolve() | ||
if protected_dir not in self.protected_dirs: | ||
self.protected_dirs.add(protected_dir) | ||
|
||
def is_protected_path(self, path): | ||
path = Path(path).resolve() | ||
if self.protected_dirs is None: # This should never happen in real life | ||
raise FileManagerError("Protected directories not set") | ||
return any(path == protected_dir or protected_dir in path.parents for protected_dir in self.protected_dirs) | ||
|
||
def move_file(self, src, dst): | ||
src_path = Path(src).resolve() | ||
dst_path = Path(dst).resolve() | ||
|
||
if self.is_protected_path(src_path) or self.is_protected_path(dst_path): | ||
raise ProtectedPathError( | ||
f"Operation not allowed: Attempt to move protected file or to protected directory: {src} -> {dst}") | ||
|
||
shutil.move(src_path, dst_path) | ||
return True | ||
|
||
def copy_file(self, src, dst): | ||
src_path = Path(src).resolve() | ||
dst_path = Path(dst).resolve() | ||
|
||
if self.is_protected_path(dst_path): | ||
raise ProtectedPathError( | ||
f"Operation not allowed: Attempt to copy file to protected directory: {src} -> {dst}") | ||
|
||
shutil.copy2(src_path, dst_path) | ||
return True | ||
|
||
def delete_file(self, file_path): | ||
file_path = Path(file_path).resolve() | ||
|
||
if self.is_protected_path(file_path): | ||
raise ProtectedPathError(f"Operation not allowed: Attempt to delete protected file: {file_path}") | ||
|
||
os.remove(file_path) | ||
return True | ||
|
||
def make_dirs(self, dir_path): | ||
dir_path = Path(dir_path).resolve() | ||
|
||
if self.is_protected_path(dir_path): | ||
raise ProtectedPathError(f"Operation not allowed: Attempt to create directory in protected path: {dir_path}") | ||
|
||
os.makedirs(dir_path) | ||
return True | ||
|
||
def rmdir(self, dir_path): | ||
dir_path = Path(dir_path).resolve() | ||
|
||
if self.is_protected_path(dir_path): | ||
raise ProtectedPathError(f"Operation not allowed: Attempt to delete protected directory: {dir_path}") | ||
|
||
os.rmdir(dir_path) | ||
return True | ||
|
||
def reset_protected_dirs(self): | ||
self.protected_dirs = set() | ||
return self |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
import file_manager | ||
from tests.helpers_testing import * | ||
|
||
# FileManager suppose to protect some directories from being moved, copied or deleted. | ||
|
||
|
||
def test_move_file(setup_teardown): | ||
source_dir, target_dir, move_to_dir, common_args = setup_teardown | ||
setup_test_files(range(1, 6), [2]) | ||
fm = file_manager.FileManager() | ||
fm.add_protected_dir(target_dir) | ||
file_to_move = os.path.join(source_dir, "1.jpg") | ||
dst_file = os.path.join(target_dir, "1.jpg") | ||
|
||
# move to protected directory should fail | ||
with pytest.raises(file_manager.ProtectedPathError): | ||
fm.move_file(file_to_move, dst_file) | ||
|
||
# move from unprotected directory to unprotected directory should work | ||
fm.move_file(file_to_move, os.path.join(move_to_dir, "1.jpg")) | ||
assert os.path.exists(os.path.join(move_to_dir, "1.jpg")) | ||
assert not os.path.exists(file_to_move) | ||
|
||
# move from protected directory should fail too | ||
file_to_move = os.path.join(target_dir, "2.jpg") | ||
with pytest.raises(file_manager.ProtectedPathError): | ||
fm.move_file(file_to_move, os.path.join(move_to_dir, "2.jpg")) | ||
assert os.path.exists(file_to_move) | ||
assert not os.path.exists(os.path.join(move_to_dir, "2.jpg")) | ||
|
||
|
||
def test_copy_file(setup_teardown): | ||
source_dir, target_dir, move_to_dir, common_args = setup_teardown | ||
setup_test_files(range(1, 6), [2, 3]) | ||
fm = file_manager.FileManager() | ||
fm.add_protected_dir(target_dir) | ||
file_to_copy = os.path.join(source_dir, "1.jpg") | ||
dst_file = os.path.join(target_dir, "1.jpg") | ||
|
||
# copy from unprotected directory to protected directory should fail | ||
with pytest.raises(file_manager.ProtectedPathError): | ||
fm.copy_file(file_to_copy, dst_file) | ||
|
||
# copy from unprotected directory to unprotected directory should work | ||
fm.copy_file(file_to_copy, os.path.join(move_to_dir, "1.jpg")) | ||
assert os.path.exists(os.path.join(move_to_dir, "1.jpg")) | ||
assert os.path.exists(file_to_copy) | ||
|
||
# copy from protected directory to unprotected directory should work | ||
file_to_copy = os.path.join(target_dir, "2.jpg") | ||
fm.copy_file(file_to_copy, os.path.join(move_to_dir, "2.jpg")) | ||
assert os.path.exists(os.path.join(move_to_dir, "2.jpg")) | ||
assert os.path.exists(file_to_copy) | ||
|
||
# copy from protected directory to protected directory should fail | ||
file_to_copy = os.path.join(target_dir, "3.jpg") | ||
with pytest.raises(file_manager.ProtectedPathError): | ||
fm.copy_file(file_to_copy, os.path.join(target_dir, "4.jpg")) | ||
assert os.path.exists(file_to_copy) | ||
assert not os.path.exists(os.path.join(target_dir, "4.jpg")) | ||
|
||
|
||
def test_delete_file(setup_teardown): | ||
source_dir, target_dir, move_to_dir, common_args = setup_teardown | ||
setup_test_files(range(1, 6), [2, 3]) | ||
fm = file_manager.FileManager() | ||
fm.add_protected_dir(target_dir) | ||
file_to_delete = os.path.join(source_dir, "1.jpg") | ||
|
||
# delete from unprotected directory should work | ||
fm.delete_file(file_to_delete) | ||
assert not os.path.exists(file_to_delete) | ||
|
||
# delete from protected directory should fail | ||
file_to_delete = os.path.join(target_dir, "2.jpg") | ||
with pytest.raises(file_manager.ProtectedPathError): | ||
fm.delete_file(file_to_delete) | ||
assert os.path.exists(file_to_delete) | ||
|
||
|
||
def test_make_dirs(setup_teardown): | ||
source_dir, target_dir, move_to_dir, common_args = setup_teardown | ||
fm = file_manager.FileManager() | ||
fm.add_protected_dir(target_dir) | ||
dir_to_make = os.path.join(source_dir, "new_dir") | ||
|
||
# make dir in unprotected directory should work | ||
fm.make_dirs(dir_to_make) | ||
assert os.path.exists(dir_to_make) | ||
|
||
# make dir in protected directory should fail | ||
dir_to_make = os.path.join(target_dir, "new_dir") | ||
with pytest.raises(file_manager.ProtectedPathError): | ||
fm.make_dirs(dir_to_make) | ||
assert not os.path.exists(dir_to_make) | ||
|
||
# makedirs should work with multiple levels | ||
dir_to_make = os.path.join(source_dir, "another_new_dir", "sub_dir", "sub_sub_dir") | ||
fm.make_dirs(dir_to_make) | ||
assert os.path.exists(dir_to_make) | ||
|
||
|
||
def test_rmdir(setup_teardown): | ||
source_dir, target_dir, move_to_dir, common_args = setup_teardown | ||
fm = file_manager.FileManager() | ||
fm.add_protected_dir(target_dir) | ||
dir_to_remove = os.path.join(source_dir, "new_dir") | ||
os.makedirs(dir_to_remove) | ||
|
||
# remove dir in unprotected directory should work | ||
fm.rmdir(dir_to_remove) | ||
assert not os.path.exists(dir_to_remove) | ||
|
||
# remove dir in protected directory should fail | ||
dir_to_remove = os.path.join(target_dir, "new_dir") | ||
os.makedirs(dir_to_remove) | ||
with pytest.raises(file_manager.ProtectedPathError): | ||
fm.rmdir(dir_to_remove) | ||
assert os.path.exists(dir_to_remove) | ||
|
||
# rmdir should work with multiple levels | ||
dir_to_remove = os.path.join(source_dir, "another_new_dir", "sub_dir", "sub_sub_dir") | ||
os.makedirs(dir_to_remove) | ||
fm.rmdir(dir_to_remove) | ||
assert not os.path.exists(dir_to_remove) | ||
|
||
|
||
# The FileManager class should be a singleton, so we should not be able to create multiple instances of it. | ||
def test_singleton(): | ||
fm1 = file_manager.FileManager() | ||
fm2 = file_manager.FileManager() | ||
assert fm1 is fm2 | ||
assert fm1 == fm2 | ||
assert fm1 is not None | ||
assert fm2 is not None | ||
|
||
|
||
def test_reset_protected_dirs(): | ||
fm = file_manager.FileManager() | ||
fm.add_protected_dir("C:\\") | ||
fm.add_protected_dir("D:\\") | ||
fm.reset_protected_dirs() | ||
assert len(fm.protected_dirs) == 0 | ||
assert fm.protected_dirs == set() |