Compare commits

..

No commits in common. "12c3ac9c051f2ae4e8d68465eec5dfbb1b4ea13b" and "a7d5c21c459915ddf8875e7bcf63a657924c52e2" have entirely different histories.

4개의 변경된 파일0개의 추가작업 그리고 132개의 파일을 삭제

2
.gitignore vendored
파일 보기

@ -114,5 +114,3 @@ dmypy.json
# Pyre type checker # Pyre type checker
.pyre/ .pyre/
# VIM
*.swp

파일 보기

@ -1,15 +0,0 @@
[metadata]
name = sorted-copy
version = 0.1
description = Copy files and directories recursively sorted and one by one to the destination. Mainly useful for USB sticks/MP3 players.
[options]
scripts = sorted-co.py
install_requires =
click
[flake8]
max-line-length = 120
exclude = .git,__pycache__,*.egg-info,*lib/python*
ignore = E241,E741,W503,W504

파일 보기

@ -1,4 +0,0 @@
import setuptools
setuptools.setup()

파일 보기

@ -1,111 +0,0 @@
#!/usr/bin/python3
from pathlib import Path
import shutil
from typing import Any, Generator, Tuple, Union
import click
def _to_pathlib_path(ctx: click.Context, param: Any, value: str) \
-> Union[Path, Tuple[Path]]:
if isinstance(value, tuple):
return tuple(Path(v) for v in value)
else:
return Path(value)
def get_directories(directory: Path) -> Generator[Path, None, None]:
"""Return all directories below given directory in sorted order
This returns first all directories of a directory before returning
subdirectories of the sorted subdirectories.
"""
directories = [directory]
for d in directories:
for p in sorted(d.iterdir()):
if p.is_dir():
directories.append(p)
yield p
def get_files(directory: Path) -> Generator[Path, None, None]:
"""Traverse the given directory and its sorted subdirectory and yield files
See `get_directories()` for a hint to the order of directories traversed.
"""
if not directory.is_dir():
msg = f"get_files() needs a directory but got {directory}"
raise RuntimeError(msg)
directories = [directory]
for d in directories:
for p in sorted(d.iterdir()):
if p.is_dir():
directories.append(p)
elif p.is_file():
yield p
def relative_path(base: Path, p: Path) -> Path:
"""Return the part of `p` relative to `base` as relative Path"""
for a, b in zip(base.parts, p.parts):
if a != b:
raise ValueError('Base {} is not the base of {}'.format(a, b))
return Path('/'.join(p.parts[len(base.parts):]))
@click.command()
@click.argument('srces', nargs=-1, type=click.Path(exists=True, file_okay=True,
dir_okay=True, resolve_path=True), callback=_to_pathlib_path,
required=True)
@click.argument('dest', type=click.Path(exists=True, dir_okay=True,
file_okay=False, resolve_path=True), callback=_to_pathlib_path,
required=True)
@click.option('--verbose', is_flag=True)
def main(srces: Tuple[Path], dest: Path, verbose: bool) -> None:
"""Copy `srces` recursively and sorted one by one to `dest
This is mainly useful for USB sticks and MP3 players that take the order of
files created in the filesystem instead of the order of the sorted names to
play files.
"""
for src in srces:
if src.is_file():
# copy this file over directly
rel_path = Path(src.name)
dest_path = dest / rel_path
if verbose:
click.echo('copy {} -> {}'.format(src, dest_path))
shutil.copy(src, dest_path)
continue
if src.is_dir():
dest_path = dest / src.name
if verbose:
click.echo('mkdir {}'.format(dest_path))
dest_path.mkdir()
# create all directories first
for p in get_directories(src):
rel_path = Path(src.name) / relative_path(src, p)
dest_path = dest / rel_path
if verbose:
click.echo('mkdir {}'.format(dest_path))
dest_path.mkdir()
# copy all files over
for p in get_files(src):
rel_path = Path(src.name) / relative_path(src, p)
dest_path = dest / rel_path
if verbose:
click.echo('copy {} -> {}'.format(p, dest_path))
shutil.copy(p, dest_path)
continue
click.echo(f"Warning: Not copying {src}. Neither file nor directory")
if __name__ == '__main__':
import sys
sys.exit(main())