cmd/sole_subdir_lift.py
Ethan Dalool 4a9051e617
Big migrations and linting.
With pathclass.glob_many, we can clean up and feel more confident
about many programs that use pipeable to take glob patterns.

Added return 0 to all programs that didn't have it, so we have
consistent and explicit command line return values.

Other linting and whitespace.
2021-09-23 23:42:45 -07:00

69 lines
2.1 KiB
Python

import argparse
import collections
import os
import shutil
import sys
from voussoirkit import passwordy
from voussoirkit import pathclass
from voussoirkit import spinal
from voussoirkit import vlogging
log = vlogging.getLogger(__name__, 'sole_subdir_lift')
def sole_lift_argparse(args):
starting = pathclass.Path(args.starting)
queue = collections.deque()
queue.extend(spinal.walk(starting, yield_files=False, yield_directories=True))
while len(queue) > 0:
directory = queue.popleft()
if not directory.exists:
log.debug('%s no longer exists.', directory)
continue
if directory not in starting:
log.debug('%s is outside of starting.', directory)
continue
children = directory.listdir()
child_count = len(children)
if child_count != 1:
log.debug('%s has %d children.', directory, child_count)
continue
child = children[0]
if not child.is_dir:
log.debug('%s contains a file, not a dir.', directory)
continue
log.info('Lifting contents of %s.', child.absolute_path)
# child is renamed to random hex so that the grandchildren we are about
# to lift don't have name conflicts with the child dir itself.
# Consider .\abc\abc where the grandchild can't be moved.
temp_dir = directory.with_child(passwordy.urandom_hex(32))
os.rename(child.absolute_path, temp_dir.absolute_path)
for grandchild in temp_dir.listdir():
shutil.move(grandchild.absolute_path, directory.absolute_path)
if temp_dir.listdir():
raise Exception()
os.rmdir(temp_dir.absolute_path)
queue.append(directory.parent)
return 0
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('starting', nargs='?', default='.')
parser.set_defaults(func=sole_lift_argparse)
args = parser.parse_args(argv)
return args.func(args)
if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:]))