diff --git a/obsah/__init__.py b/obsah/__init__.py index a3203a4..9f7a82f 100755 --- a/obsah/__init__.py +++ b/obsah/__init__.py @@ -582,7 +582,8 @@ def rotate_log(log_path: str): backup_path = f"{log_path[:-4]}.{timestamp}.log" else: backup_path = f"{log_path}.{timestamp}" - os.rename(log_path, backup_path) + with contextlib.suppress(FileNotFoundError): + os.rename(log_path, backup_path) def main(cliargs=None, application_config=ApplicationConfig): # pylint: disable=R0914 """ diff --git a/tests/test_log_rotate.py b/tests/test_log_rotate.py index 9249be6..22b43a9 100644 --- a/tests/test_log_rotate.py +++ b/tests/test_log_rotate.py @@ -25,3 +25,22 @@ def test_rotate_log(tmp_path): assert 'test.log' not in log_dir_contents[0].name assert 'test.' in log_dir_contents[0].name assert not log_file.exists() + +def test_rotate_log_tolerates_concurrent_rotation(tmp_path): + log_file = tmp_path / 'test.log' + log_file.touch() + stolen = tmp_path / 'stolen.log' + original_exists = os.path.exists + + def exists_then_steal(path): + result = original_exists(path) + if path == str(log_file) and result and not stolen.exists(): + os.rename(path, str(stolen)) + return result + + with mock.patch('os.path.exists', side_effect=exists_then_steal): + obsah.rotate_log(str(log_file)) # must not raise + + assert stolen.exists() + assert not log_file.exists() +