diff --git a/flamenco_worker/runner.py b/flamenco_worker/runner.py index d6113fec995c9d7ae2f55a44eec45138b27f6995..a1027eeb5fe8eb59d34c0a816a8c7197c44d154c 100644 --- a/flamenco_worker/runner.py +++ b/flamenco_worker/runner.py @@ -288,6 +288,44 @@ class SleepCommand(AbstractCommand): await self.worker.register_log('Done sleeping for %s seconds' % time_in_seconds) +def _timestamped_path(path: Path) -> Path: + """Returns the path with its modification time appended to the name.""" + + from datetime import datetime + + mtime = path.stat().st_mtime + mdatetime = datetime.fromtimestamp(mtime) + + # Make the ISO-8601 timestamp a bit more eye- and filename-friendly. + iso = mdatetime.isoformat().replace('T', '_').replace(':', '') + dst = path.with_name('%s-%s' % (path.name, iso)) + + return dst + + +def _unique_path(path: Path) -> Path: + """Returns the path, or if it exists, the path with a unique suffix.""" + + import re + + suf_re = re.compile(r'~([0-9]+)$') + + # See which suffixes are in use + max_nr = 0 + for altpath in path.parent.glob(path.name + '~*'): + m = suf_re.search(altpath.name) + if not m: + continue + + suffix = m.group(1) + try: + suffix = int(suffix) + except ValueError: + continue + max_nr = max(max_nr, suffix) + return path.with_name(path.name + '~%i' % (max_nr + 1)) + + @command_executor('move_out_of_way') class MoveOutOfWayCommand(AbstractCommand): def validate(self, settings: dict): @@ -300,9 +338,6 @@ class MoveOutOfWayCommand(AbstractCommand): return 'src must be a string' async def execute(self, settings: dict): - from pathlib import Path - from datetime import datetime - src = Path(settings['src']) if not src.exists(): self._log.info('Render output path %s does not exist, not moving out of way', src) @@ -310,23 +345,10 @@ class MoveOutOfWayCommand(AbstractCommand): 'not moving out of way', self.command_name, src) return - mtime = src.stat().st_mtime - mdatetime = datetime.fromtimestamp(mtime) - dst = src.with_name('%s-%s' % (src.name, mdatetime.isoformat())) - + dst = _timestamped_path(src) if dst.exists(): self._log.debug('Destination %s exists, finding one that does not', dst) - # See which suffixes are in use - max_nr = 0 - for path in dst.parent.glob(dst.name + '*'): - suffix = path.name.split('-')[-1] - try: - suffix = int(suffix) - except ValueError: - continue - self._log.debug('Found suffix %r', suffix) - max_nr = max(max_nr, suffix) - dst = dst.with_name(dst.name + '-%i' % (max_nr + 1)) + dst = _unique_path(dst) self._log.debug('New destination is %s', dst) self._log.info('Moving %s to %s', src, dst) diff --git a/tests/test_runner_move_out_of_way.py b/tests/test_runner_move_out_of_way.py index 4593db4540ea8afa5f84129d151905a698800254..8b7c66b2b5456b609b1d2a6a5901a2f26ed414ba 100644 --- a/tests/test_runner_move_out_of_way.py +++ b/tests/test_runner_move_out_of_way.py @@ -1,3 +1,6 @@ +from pathlib import Path +import os + from test_runner import AbstractCommandTest @@ -20,8 +23,6 @@ class MoveOutOfWayTest(AbstractCommandTest): del self.tmpdir def test_nonexistant_source(self): - from pathlib import Path - src = Path(self.tmpdir.name) / 'nonexistant-dir' task = self.cmd.run({'src': str(src)}) ok = self.loop.run_until_complete(task) @@ -30,9 +31,6 @@ class MoveOutOfWayTest(AbstractCommandTest): self.assertFalse(src.exists()) def test_existing_source(self): - from pathlib import Path - import os - src = Path(self.tmpdir.name) / 'existing-dir' src.mkdir() (src / 'src-contents').touch() @@ -43,14 +41,11 @@ class MoveOutOfWayTest(AbstractCommandTest): ok = self.loop.run_until_complete(task) self.assertTrue(ok) - dst = src.with_name('existing-dir-2012-03-02T19:18:12') + dst = src.with_name('existing-dir-2012-03-02_191812') self.assertTrue(dst.exists()) self.assertFalse(src.exists()) def test_source_is_file(self): - from pathlib import Path - import os - src = Path(self.tmpdir.name) / 'existing-file' src.touch(exist_ok=False) os.utime(str(src), (1330712280, 1330712292)) # fixed (atime, mtime) for testing @@ -59,29 +54,26 @@ class MoveOutOfWayTest(AbstractCommandTest): ok = self.loop.run_until_complete(task) self.assertTrue(ok) - dst = src.with_name('existing-file-2012-03-02T19:18:12') + dst = src.with_name('existing-file-2012-03-02_191812') self.assertTrue(dst.exists()) self.assertTrue(dst.is_file()) self.assertFalse(src.exists()) def test_existing_source_and_dest(self): - from pathlib import Path - import os - src = Path(self.tmpdir.name) / 'existing-dir' src.mkdir() (src / 'src-contents').touch() os.utime(str(src), (1330712280, 1330712292)) # fixed (atime, mtime) for testing - existing_dst = src.with_name('existing-dir-2012-03-02T19:18:12') + existing_dst = src.with_name('existing-dir-2012-03-02_191812') existing_dst.mkdir() (existing_dst / 'dst-existing-contents').touch() - existing_dst2 = src.with_name('existing-dir-2012-03-02T19:18:12-2') + existing_dst2 = src.with_name('existing-dir-2012-03-02_191812~2') existing_dst2.mkdir() (existing_dst2 / 'dst2-existing-contents').touch() - existing_dst4 = src.with_name('existing-dir-2012-03-02T19:18:12-4') + existing_dst4 = src.with_name('existing-dir-2012-03-02_191812~4') existing_dst4.mkdir() (existing_dst4 / 'dst4-existing-contents').touch() @@ -98,7 +90,7 @@ class MoveOutOfWayTest(AbstractCommandTest): self.assertTrue((existing_dst4 / 'dst4-existing-contents').exists()) # The source should have been moved to the new destination. - new_dst = existing_dst.with_name('existing-dir-2012-03-02T19:18:12-5') + new_dst = existing_dst.with_name('existing-dir-2012-03-02_191812~5') self.assertTrue(new_dst.exists()) self.assertTrue((new_dst / 'src-contents').exists())