Initial commit
This commit is contained in:
parent
c610612ed3
commit
0ed1b94969
1
.gitignore
vendored
1
.gitignore
vendored
@ -137,4 +137,3 @@ dmypy.json
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
|
27
.pre-commit-config.yaml
Normal file
27
.pre-commit-config.yaml
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
default_language_version:
|
||||
python: python3.8
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 20.8b1
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v3.4.0
|
||||
hooks:
|
||||
- id: check-added-large-files
|
||||
- id: check-merge-conflict
|
||||
- id: debug-statements
|
||||
- id: end-of-file-fixer
|
||||
- id: fix-encoding-pragma
|
||||
- id: trailing-whitespace
|
||||
- id: name-tests-test
|
||||
exclude: tests/(common.py|util.py|(helpers|integration/factories)/(.+).py)
|
||||
- repo: https://github.com/asottile/reorder_python_imports
|
||||
rev: v2.4.0
|
||||
hooks:
|
||||
- id: reorder-python-imports
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v0.800
|
||||
hooks:
|
||||
- id: mypy
|
@ -1,3 +1,3 @@
|
||||
# paddy-files
|
||||
|
||||
Zero-padding numeric filenames
|
||||
Zero-padding numeric filenames
|
||||
|
128
paddy
Executable file
128
paddy
Executable file
@ -0,0 +1,128 @@
|
||||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import argparse
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
||||
|
||||
num_matcher = re.compile(r"([0-9]+)")
|
||||
|
||||
|
||||
def extract_numbers(s: str) -> str:
|
||||
number_match = num_matcher.search(s)
|
||||
if number_match:
|
||||
return number_match.group(1)
|
||||
return ""
|
||||
|
||||
|
||||
def calc_pad_length(files: List[Path]) -> int:
|
||||
max_len = 0
|
||||
for f in files:
|
||||
numbers = extract_numbers(f.name)
|
||||
max_len = max(len(numbers), max_len)
|
||||
return max_len
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Pads numbers in file names so they consistently align and sort"
|
||||
)
|
||||
parser.add_argument("files", nargs="+", metavar="file", help="Files to be renamed")
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--length",
|
||||
type=int,
|
||||
help="Length of numbers after padding (default: auto)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--force",
|
||||
action="store_true",
|
||||
help="Force rename, even if file at destination exists",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v", "--verbose", action="store_true", help="Print all actions"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="Print actions only without modifying any file. Implies --verbose",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--ignore",
|
||||
metavar="REGEX",
|
||||
help="Regular expression used to ignore files matching the name",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ignore-files",
|
||||
nargs="+",
|
||||
metavar="IGNOREFILE",
|
||||
help="Files to ignore for renaming. Must add -- before positional arguments",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Dry run implies verbose
|
||||
if args.dry_run:
|
||||
args.verbose = True
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
|
||||
# Compile ignore pattern, if provided
|
||||
ignore_matcher: Optional[re.Pattern] = None
|
||||
if args.ignore:
|
||||
ignore_matcher = re.compile(args.ignore)
|
||||
|
||||
# Build list of files to act on
|
||||
p = Path(".")
|
||||
files = []
|
||||
for f in args.files:
|
||||
if args.ignore_files and f in args.ignore_files:
|
||||
continue
|
||||
if ignore_matcher and ignore_matcher.match(f):
|
||||
continue
|
||||
files.append(p / f)
|
||||
|
||||
pad_len = args.length
|
||||
if pad_len is None:
|
||||
pad_len = calc_pad_length(files)
|
||||
|
||||
if args.verbose:
|
||||
print(f"Padding to {pad_len}")
|
||||
|
||||
status = 0
|
||||
for f in files:
|
||||
numbers = extract_numbers(f.name)
|
||||
if len(numbers) == pad_len:
|
||||
if args.verbose:
|
||||
print(f"{f.name} is already padded.")
|
||||
continue
|
||||
|
||||
# Pad number and get destination path
|
||||
new_numbers = numbers.zfill(pad_len)
|
||||
new_name = num_matcher.sub(new_numbers, f.name, count=1)
|
||||
new_file = f.parent / new_name
|
||||
|
||||
# Possibly rename unless exists or forced
|
||||
if not new_file.exists() or args.force:
|
||||
if args.verbose:
|
||||
print(f"Rename {f.name} to {new_file.name}")
|
||||
f.rename(new_file)
|
||||
else:
|
||||
print(
|
||||
f"Could not ename {f.name} to {new_file.name}. Destination file exists."
|
||||
)
|
||||
status = 1
|
||||
|
||||
return status
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
Loading…
Reference in New Issue
Block a user