Skip to content

Commit

Permalink
quick file rotation util
Browse files Browse the repository at this point in the history
  • Loading branch information
mahmoud committed Nov 28, 2024
1 parent 60ecb20 commit 3bfcfdd
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 2 deletions.
37 changes: 36 additions & 1 deletion boltons/fileutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def __set__(self, fp_obj, value):
' or one or more of %r'
% (invalid_chars, value, self._perm_chars))

sort_key = lambda c: self._perm_val[c]
def sort_key(c): return self._perm_val[c]
new_value = ''.join(sorted(set(value),
key=sort_key, reverse=True))
setattr(fp_obj, self.attribute, new_value)
Expand Down Expand Up @@ -687,3 +687,38 @@ def __enter__(self):
def __exit__(self, exc_type, exc_val, exc_tb):
return


def rotate_file(filename, *, keep: int = 5):
"""
If *filename.ext* exists, it will be moved to *filename.1.ext*,
with all conflicting filenames being moved up by one, dropping any files beyond *keep*.
After rotation, *filename* will be available for creation as a new file.
Fails if *filename* is not a file or if *keep* is not > 0.
"""
if keep < 1:
raise ValueError(f'expected "keep" to be >=1, not {keep}')
if not os.path.exists(filename):
return
if not os.path.isfile(filename):
raise ValueError(f'expected {filename} to be a file')

fn_root, fn_ext = os.path.splitext(filename)
kept_names = []
for i in range(1, keep + 1):
if fn_ext:
kept_names.append(f'{fn_root}.{i}{fn_ext}')
else:
kept_names.append(f'{fn_root}.{i}')

fns = [filename] + kept_names
for orig_name, kept_name in reversed(list(zip(fns, fns[1:]))):
if not os.path.exists(orig_name):
continue
os.rename(orig_name, kept_name)

if os.path.exists(kept_names[-1]):
os.remove(kept_names[-1])

return
58 changes: 57 additions & 1 deletion tests/test_fileutils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import os.path





from boltons import fileutils
from boltons.fileutils import FilePerms, iter_find_files

Expand Down Expand Up @@ -33,4 +37,56 @@ def _to_baseless_list(paths):

boltons_parent = os.path.dirname(BOLTONS_PATH)
assert 'fileutils.py' in _to_baseless_list(iter_find_files(boltons_parent, patterns=['*.py']))
assert 'fileutils.py' not in _to_baseless_list(iter_find_files(boltons_parent, patterns=['*.py'], max_depth=0))
assert 'fileutils.py' not in _to_baseless_list(iter_find_files(boltons_parent, patterns=['*.py'], max_depth=0))


def test_rotate_file_no_rotation(tmp_path):
file_path = tmp_path / 'test_file.txt'
fileutils.rotate_file(file_path)
assert not file_path.exists()


def test_rotate_file_one_rotation(tmp_path):
file_path = tmp_path / 'test_file.txt'
file_path.write_text('test content')
assert file_path.exists()

fileutils.rotate_file(file_path)
assert not file_path.exists()
assert (tmp_path / 'test_file.1.txt').exists()


def test_rotate_file_full_rotation(tmp_path):
file_path = tmp_path / 'test_file.txt'
file_path.write_text('test content 0')
for i in range(1, 5):
cur_path = tmp_path / f'test_file.{i}.txt'
cur_path.write_text(f'test content {i}')
assert cur_path.exists()

fileutils.rotate_file(file_path, keep=5)
assert not file_path.exists()

for i in range(1, 5):
cur_path = tmp_path / f'test_file.{i}.txt'
assert cur_path.read_text() == f'test content {i-1}'

assert not (tmp_path / 'test_file.5.txt').exists()

def test_rotate_file_full_rotation_no_ext(tmp_path):
file_path = tmp_path / 'test_file'
file_path.write_text('test content 0')
for i in range(1, 5):
cur_path = tmp_path / f'test_file.{i}'
cur_path.write_text(f'test content {i}')
assert cur_path.exists()

fileutils.rotate_file(file_path, keep=5)
assert not file_path.exists()

for i in range(1, 5):
cur_path = tmp_path / f'test_file.{i}'
assert cur_path.read_text() == f'test content {i-1}'

assert not (tmp_path / 'test_file.5').exists()

0 comments on commit 3bfcfdd

Please sign in to comment.