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.
This commit is contained in:
voussoir 2021-09-23 23:42:34 -07:00
parent 6751a5d6c9
commit 4a9051e617
No known key found for this signature in database
GPG key ID: 5F7554F8C26DACCB
64 changed files with 345 additions and 228 deletions

View file

@ -1,37 +1,24 @@
import argparse
import os
import re
import sys
from voussoirkit import interactive
from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import stringtools
from voussoirkit import vlogging
from voussoirkit import winglob
log = vlogging.getLogger(__name__, 'adbinstall')
def natural_sorter(x):
'''
Used for sorting files in 'natural' order instead of lexicographic order,
so that you get 1 2 3 4 5 6 7 8 9 10 11 12 13 ...
instead of 1 10 11 12 13 2 3 4 5 ...
Thank you Mark Byers
http://stackoverflow.com/a/11150413
'''
convert = lambda text: int(text) if text.isdigit() else text.lower()
alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
return alphanum_key(x)
def adbinstall_argparse(args):
patterns = pipeable.input_many(args.apks, skip_blank=True, strip=True)
apks = [file for pattern in patterns for file in winglob.glob(pattern)]
apks = pathclass.glob_many(patterns, files=True)
installs = []
for apk in apks:
apk = pathclass.Path(apk)
if apk.is_dir:
files = apk.glob('*.apk')
files.sort(key=lambda x: natural_sorter(x.basename.lower()))
files.sort(key=lambda x: stringtools.natural_sorter(x.basename.lower()))
apk = files[-1]
installs.append(apk)
@ -46,6 +33,8 @@ def adbinstall_argparse(args):
log.info(command)
os.system(command)
return 0
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -2,6 +2,7 @@ import os
import sys
from voussoirkit import pathclass
from voussoirkit import pipeable
def windows():
paths = os.getenv('PATH').strip(' ;').split(';')
@ -30,7 +31,9 @@ def main(argv):
executables = linux()
for executable in executables:
print(executable.absolute_path)
pipeable.stdout(executable.absolute_path)
return 0
if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:]))

View file

@ -7,16 +7,13 @@ Merge two or more files by performing bitwise or on their bits.
> bitwise_or file1 file2 --output file3
'''
import argparse
import os
import sys
from voussoirkit import betterhelp
from voussoirkit import interactive
from voussoirkit import operatornotify
from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import vlogging
from voussoirkit import winglob
log = vlogging.getLogger(__name__, 'bitwise_or')
@ -24,8 +21,7 @@ CHUNK_SIZE = 2**20
def bitwise_or_argparse(args):
patterns = pipeable.input_many(args.files, skip_blank=True, strip=True)
files = [file for pattern in patterns for file in winglob.glob(pattern)]
files = [pathclass.Path(file) for file in files]
files = pathclass.glob_many(patterns, files=True)
if len(files) < 2:
log.fatal('Need at least two input files.')

View file

@ -23,6 +23,8 @@ def breplace_argparse(args):
command = f'x.replace("{replace_from}", "{replace_to}")'
brename.brename(command, autoyes=args.autoyes, recurse=args.recurse)
return 0
def main(argv):
parser = argparse.ArgumentParser(__doc__)

View file

@ -13,6 +13,8 @@ def clipboard_argparse(args):
text = text.replace('\r', '')
print(text)
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -1,6 +1,5 @@
import argparse
import codecs
import os
import pyperclip
import re
import sys
@ -71,6 +70,8 @@ def contentreplace_argparse(args):
except UnicodeDecodeError:
log.error('%s encountered unicode decode error.', file.absolute_path)
return 0
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -1,26 +1,32 @@
import argparse
import os
import sys
import zlib
from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import winglob
from voussoirkit import vlogging
log = vlogging.getLogger(__name__, 'crc32')
def crc32_argparse(args):
files = (
file
for pattern in pipeable.input_many(args.patterns)
for file in winglob.glob(pattern)
if os.path.isfile(file)
)
return_status = 0
patterns = pipeable.input_many(args.patterns, skip_blank=True, strip=True)
files = pathclass.glob_many(patterns, files=True)
for file in files:
try:
with open(file, 'rb') as handle:
crc = zlib.crc32(handle.read())
print(hex(crc)[2:].rjust(8, '0'), file)
crc = hex(crc)[2:].rjust(8, '0')
pipeable.stdout(f'{crc} {file}')
except Exception as e:
print(file, e)
log.error('%s %s', file, e)
return_status = 1
return return_status
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

36
crlf.py
View file

@ -1,28 +1,46 @@
'''
Convert LF line endings to CRLF.
'''
import argparse
import sys
from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import winglob
CR = b'\x0D'
LF = b'\x0A'
CRLF = CR + LF
def crlf(filename):
with open(filename, 'rb') as handle:
def crlf(file):
with file.open('rb') as handle:
content = handle.read()
original = content
content = content.replace(CRLF, LF)
content = content.replace(LF, CRLF)
with open(filename, 'wb') as handle:
if content == original:
return
with file.open('wb') as handle:
handle.write(content)
def main(args):
for line in pipeable.go(args, strip=True, skip_blank=True):
for filename in winglob.glob(line):
pipeable.stdout(filename)
crlf(filename)
def crlf_argparse(args):
patterns = pipeable.input_many(args.patterns, skip_blank=True, strip=True)
files = pathclass.glob_many(patterns)
for file in files:
crlf(file)
pipeable.stdout(file)
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('patterns')
parser.set_defaults(func=crlf_argparse)
args = parser.parse_args(argv)
return args.func(args)
if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:]))

26
crop.py
View file

@ -1,13 +1,12 @@
import argparse
import os
import PIL.Image
import sys
from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import winglob
def crop(filename, crops, *, inplace=False):
image = PIL.Image.open(filename)
def crop(file, crops, *, inplace=False):
image = PIL.Image.open(file.absolute_path)
if len(crops) == 2:
crops.extend(image.size)
@ -20,24 +19,27 @@ def crop(filename, crops, *, inplace=False):
image = image.crop(crops)
if inplace:
newname = filename
newname = file
else:
suffix = '_'.join(str(x) for x in crops)
suffix = f'_{suffix}'
(base, extension) = os.path.splitext(filename)
newname = base + suffix + extension
base = file.replace_extension('').basename
newname = file.parent.with_child(base + suffix).add_extension(file.extension)
pipeable.stdout(newname)
image.save(newname, exif=image.info.get('exif', b''), quality=100)
pipeable.stdout(newname.absolute_path)
image.save(newname.absolute_path, exif=image.info.get('exif', b''), quality=100)
def crop_argparse(args):
filenames = winglob.glob(args.pattern)
for filename in filenames:
patterns = pipeable.input(args.pattern, skip_blank=True, strip=True)
files = pathclass.glob_many(patterns, files=True)
for file in files:
crop(
filename,
file,
crops=args.crops,
inplace=args.inplace,
)
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -1,14 +1,19 @@
import os
import shutil
import sys
from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import winglob
for pattern in pipeable.go(skip_blank=True):
for name in winglob.glob(pattern):
if os.path.isfile(name):
pipeable.stdout(name)
os.remove(name)
elif os.path.isdir(name):
pipeable.stdout(name)
shutil.rmtree(name)
def main(argv):
for path in pathclass.glob_many(pipeable.go(argv, skip_blank=True)):
if path.is_file:
pipeable.stdout(path.absolute_path)
os.remove(path.absolute_path)
elif path.is_dir:
pipeable.stdout(path.absolute_path)
shutil.rmtree(path.absolute_path)
return 0
if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:]))

View file

@ -45,6 +45,8 @@ def directory_discrepancy_argparse(args):
for discrepancy in sorted(files2.difference(files1)):
print(discrepancy)
return 0
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -55,6 +55,8 @@ def drawquarter_argparse(args):
print(output_filename.relative_path)
piece.save(output_filename.absolute_path)
return 0
def main(argv):
parser = argparse.ArgumentParser()

View file

@ -3,17 +3,17 @@ import sys
from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import winglob
def empty_directories_argparse(args):
patterns = pipeable.input_many(args.patterns, skip_blank=True, strip=True)
directories = (pathclass.Path(d) for pattern in patterns for d in winglob.glob(pattern))
directories = (d for d in directories if d.is_dir)
directories = pathclass.glob_many(patterns, directories=True)
for directory in directories:
if len(directory.listdir()) == 0:
pipeable.stdout(directory.absolute_path)
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -25,6 +25,8 @@ def eval_argparse(args):
x = line
pipeable.stdout(eval(args.eval_string))
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -29,7 +29,6 @@ import bs4
import requests
import sys
import tenacity
import time
from voussoirkit import betterhelp
from voussoirkit import downloady

View file

@ -138,6 +138,8 @@ def ffstreams_argparse(args):
moveto=args.moveto,
)
return 0
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -16,11 +16,10 @@ argv = sys.argv[1:]
randname = [random.choice(string.digits) for x in range(12)]
randname = int(''.join(randname))
for pattern in argv:
for path in winglob.glob(pattern):
path = pathclass.Path(path)
newname = str(randname).rjust(12, '0') + path.dot_extension
randname += 1
newname = path.parent.with_child(newname)
os.rename(path.absolute_path, newname.absolute_path)
print('%s -> %s' % (path.absolute_path, newname.basename))
for path in pathclass.glob_many(argv):
newname = str(randname).rjust(12, '0') + path.dot_extension
randname += 1
newname = path.parent.with_child(newname)
os.rename(path.absolute_path, newname.absolute_path)
print('%s -> %s' % (path.absolute_path, newname.basename))

View file

@ -9,15 +9,12 @@ import string
import sys
from voussoirkit import pathclass
from voussoirkit import winglob
argv = sys.argv[1:]
for pattern in argv:
for path in winglob.glob(pattern):
path = pathclass.Path(path)
newname = [random.choice(string.ascii_lowercase) for x in range(9)]
newname = ''.join(newname) + path.dot_extension
newname = path.parent.with_child(newname)
os.rename(path.absolute_path, newname.absolute_path)
print('%s -> %s' % (path.absolute_path, newname.basename))
for path in pathclass.glob_many(argv):
newname = [random.choice(string.ascii_lowercase) for x in range(9)]
newname = ''.join(newname) + path.dot_extension
newname = path.parent.with_child(newname)
os.rename(path.absolute_path, newname.absolute_path)
print('%s -> %s' % (path.absolute_path, newname.basename))

View file

@ -9,15 +9,12 @@ import string
import sys
from voussoirkit import pathclass
from voussoirkit import winglob
argv = sys.argv[1:]
for pattern in argv:
for path in winglob.glob(pattern):
path = pathclass.Path(path)
newname = [random.choice(string.digits) for x in range(12)]
newname = ''.join(newname) + path.dot_extension
newname = path.parent.with_child(newname)
os.rename(path.absolute_path, newname.absolute_path)
print('%s -> %s' % (path.absolute_path, newname.basename))
for path in pathclass.glob_many(argv):
newname = [random.choice(string.digits) for x in range(12)]
newname = ''.join(newname) + path.dot_extension
newname = path.parent.with_child(newname)
os.rename(path.absolute_path, newname.absolute_path)
print('%s -> %s' % (path.absolute_path, newname.basename))

View file

@ -42,6 +42,7 @@ def filepull(pull_from='.', autoyes=False):
def filepull_argparse(args):
filepull(pull_from=args.pull_from, autoyes=args.autoyes)
return 0
def main(argv):
parser = argparse.ArgumentParser()

View file

@ -3,7 +3,7 @@ import PIL.Image
import sys
from voussoirkit import pathclass
from voussoirkit import winglob
from voussoirkit import pipeable
def grayscale(filename, *, inplace=False):
filename = pathclass.Path(filename)
@ -20,18 +20,23 @@ def grayscale(filename, *, inplace=False):
image = PIL.Image.open(filename.absolute_path)
image = image.convert('LA').convert(image.mode)
print(f'{new_filename.absolute_path}')
image.save(new_filename.absolute_path, exif=image.info.get('exif', b''))
return new_filename
def grayscale_argparse(args):
filenames = winglob.glob(args.pattern)
for filename in filenames:
grayscale(filename, inplace=args.inplace)
patterns = pipeable.input_many(args.patterns, skip_blank=True, strip=True)
files = pathclass.glob_many(patterns, files=True)
for file in files:
new_filename = grayscale(file, inplace=args.inplace)
if new_filename:
pipeable.stdout(new_filename.absolute_path)
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('pattern')
parser.add_argument('patterns', nargs='+')
parser.add_argument('--inplace', action='store_true')
parser.set_defaults(func=grayscale_argparse)

View file

@ -11,6 +11,7 @@ def groupsof_argparse(args):
for chunk in chunks:
chunk = args.separator.join(chunk)
pipeable.stdout(chunk)
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -65,6 +65,8 @@ def hash_hardlink_argparse(args):
send2trash.send2trash(follower.absolute_path)
os.link(leader.absolute_path, follower.absolute_path)
return 0
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -27,7 +27,11 @@ def root():
def heresmyclipboard_argparse(args):
log.info(f'Starting server on port {args.port}, pid={os.getpid()}')
site.run(host='0.0.0.0', port=args.port)
try:
site.run(host='0.0.0.0', port=args.port)
except KeyboardInterrupt:
pass
return 0
@vlogging.main_decorator
def main(argv):

View file

@ -5,7 +5,6 @@ import argparse
import PIL.Image
import sys
def full_hex(h):
h = h.replace('#', '')
if len(h) in [3, 4]:
@ -28,6 +27,7 @@ def make_hexpng(h, width=1, height=1):
def hexpng_argparse(args):
make_hexpng(args.hex_value, width=args.width, height=args.height)
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -131,43 +131,59 @@ def load_image(filename):
return image
def build_ico_header_blob(image_count):
datablob = (b''
+ little(0, 2) # reserved
+ little(1, 2) # 1 = ico type
+ little(image_count, 2)
)
datablob = b''.join([
# reserved
little(0, 2),
# 1 = ico type
little(1, 2),
little(image_count, 2),
])
return datablob
def build_icon_directory_blob(image, offset_from_start):
(width, height) = image.size
datablob = (b''
+ little(width if width < 256 else 0, 1)
+ little(height if height < 256 else 0, 1)
+ little(0, 1) # colors in palette
+ little(0, 1) # reserved
+ little(1, 2) # color planes
+ little(32, 2) # bit depth
+ little((width * height * 4) + BMP_HEADER_LENGTH, 4) # image bytes length
+ little(offset_from_start, 4)
)
datablob = b''.join([
little(width if width < 256 else 0, 1),
little(height if height < 256 else 0, 1),
# colors in palette
little(0, 1),
# reserved
little(0, 1),
# color planes
little(1, 2),
# bit depth
little(32, 2),
# image bytes length
little((width * height * 4) + BMP_HEADER_LENGTH, 4),
little(offset_from_start, 4),
])
return datablob
def build_image_data_blob(image):
datablob = (b''
+ little(40, 4) # header size
+ little(image.size[0], 4)
datablob = b''.join([
# header size
little(40, 4),
little(image.size[0], 4),
# "Even if the AND mask is not supplied, if the image is in Windows BMP
# format, the BMP header must still specify a doubled height." - wikipedia
+ little(image.size[1] * 2, 4)
+ little(1, 2) # color planes
+ little(32, 2) # bit depth
+ little(0, 4) # no compression
+ little(0, 4) # bytes length, inferred
+ little(0, 4) # hor print
+ little(0, 4) # ver print
+ little(0, 4) # palette
+ little(0, 4) # important palette
)
little(image.size[1] * 2, 4),
# color planes
little(1, 2),
# bit depth
little(32, 2),
# no compression
little(0, 4),
# bytes length, inferred
little(0, 4),
# hor print
little(0, 4),
# ver print
little(0, 4),
# palette
little(0, 4),
# important palette
little(0, 4),
])
pixeldata = []
# Image.getdata() is a list of (r, g, b, a) channels
# But the BMP are written (b, g, r, a)
@ -214,11 +230,10 @@ def images_to_ico(images):
final_data = b''.join(datablobs)
return final_data
if __name__ == '__main__':
try:
inputfiles = sys.argv[1:]
except:
except Exception:
print('Please provide an image file')
raise SystemExit
print('Iconifying', inputfiles)

View file

@ -1,12 +1,24 @@
import argparse
import sys
from voussoirkit import betterhelp
from voussoirkit import pathclass
from voussoirkit import pipeable
def inodes_argparse(args):
patterns = pipeable.input_many(args.patterns, skip_blank=True, strip=True)
files = pathclass.glob_many(patterns, files=True)
for file in files:
pipeable.stdout(f'{file.stat.st_dev} {file.stat.st_ino} {file.relative_path}')
return 0
def main(argv):
for file in pathclass.cwd().listdir():
if not file.is_file:
continue
print(file.stat.st_dev, file.stat.st_ino, file.relative_path)
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('patterns', nargs='+')
parser.set_defaults(func=inodes_argparse)
return betterhelp.single_main(argv, parser, __doc__)
if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:]))

View file

@ -17,7 +17,7 @@ def inputrename_argparse(args):
files = (file for file in pathclass.cwd().listdir() if args.keyword in file.basename)
prev = None
for file in files:
print(file.relative_path)
pipeable.stderr(file.relative_path)
this = input('> ')
if this == '' and prev is not None:
this = prev
@ -27,6 +27,8 @@ def inputrename_argparse(args):
os.rename(file.absolute_path, new_name.absolute_path)
prev = this
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -154,6 +154,7 @@ def main(argv):
check_forever()
except KeyboardInterrupt:
pass
return 0
if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:]))

View file

@ -12,7 +12,9 @@ def linenumbers_argparse(args):
digits = len(str(len(lines)))
form = '{no:>0%d} | {line}' % digits
for (index, line) in enumerate(lines):
print(form.format(no=index+1, line=line))
pipeable.stdout(form.format(no=index+1, line=line))
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -3,7 +3,6 @@ lint_argparse_returns
=====================
'''
import ast
import argparse
import sys
from voussoirkit import pathclass

View file

@ -1,5 +1,4 @@
from voussoirkit import pipeable
for line in pipeable.go():
pipeable.stdout(line.lower())

View file

@ -8,17 +8,15 @@ import sys
from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import winglob
def moveall_argparse(args):
files = (
pathclass.Path(file)
for pattern in pipeable.input(args.source)
for file in winglob.glob(pattern)
)
destination = pathclass.Path(args.destination)
patterns = pipeable.input(args.source, skip_blank=True, strip=True)
files = pathclass.glob_many(patterns)
if not destination.is_dir:
destination = pathclass.Path(args.destination)
try:
destination.assert_is_directory()
except pathclass.NotDirectory:
pipeable.stderr('destination must be a directory.')
return 1
@ -39,6 +37,8 @@ def moveall_argparse(args):
pipeable.stdout(new_path.absolute_path)
shutil.move(file.absolute_path, new_path.absolute_path)
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -11,7 +11,6 @@ import sys
from voussoirkit import bytestring
def parse_rules(lines):
rules = []
for (times, title) in lines[::-1]:
@ -125,7 +124,7 @@ def _unitconvert(value):
else:
return bytestring.parsebytes(value)
def example_argparse(args):
def mp3slice_argparse(args):
if len(args.rules) == 1 and os.path.isfile(args.rules[0]):
rules = read_rulefile(args.rules[0])
else:
@ -155,14 +154,14 @@ def example_argparse(args):
command = 'ffmpeg -i "%s" %s' % (args.input_filename, outputters)
print(command)
os.system(command)
return 0
def main(argv):
parser = argparse.ArgumentParser()
parser.add_argument('input_filename')
parser.add_argument('rules', nargs='+', default=None)
parser.set_defaults(func=example_argparse)
parser.set_defaults(func=mp3slice_argparse)
args = parser.parse_args(argv)
return args.func(args)

View file

@ -3,17 +3,17 @@ import sys
from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import winglob
def nonempty_directories_argparse(args):
patterns = pipeable.input_many(args.patterns, skip_blank=True, strip=True)
directories = (pathclass.Path(d) for pattern in patterns for d in winglob.glob(pattern))
directories = (d for d in directories if d.is_dir)
directories = pathclass.glob_many(patterns, directories=True)
for directory in directories:
if len(directory.listdir()) != 0:
pipeable.stdout(directory.absolute_path)
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -1,26 +1,31 @@
import argparse
import itertools
import sys
from voussoirkit import pipeable
def shuffle_argparse(args):
def pickn_argparse(args):
if args.count < 1:
pipeable.stderr('count must be >= 1.')
return 1
lines = pipeable.input(args.source, read_files=True, skip_blank=True, strip=True)
lines = list(lines)
lines = lines[:args.count]
for line in lines:
for line in itertools.islice(lines, args.count):
pipeable.stdout(line)
# Exhaust the rest of stdin so we don't get Broken Pipe error
for line in lines:
pass
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('source')
parser.add_argument('count', type=int)
parser.set_defaults(func=shuffle_argparse)
parser.set_defaults(func=pickn_argparse)
args = parser.parse_args(argv)
return args.func(args)

View file

@ -38,11 +38,12 @@ def pip_download(package):
os.rename(os.path.join(tmpdir.name, filename), os.path.join(new_directory, filename))
tmpdir.cleanup()
def pip_download_argparse(args):
for package in args.packages:
pip_download(package)
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -14,7 +14,6 @@ from voussoirkit import betterhelp
from voussoirkit import spinal
from voussoirkit import pathclass
def prune_dirs(starting):
starting = pathclass.Path(starting)
walker = spinal.walk(starting, yield_directories=True, yield_files=False)
@ -36,7 +35,6 @@ def prune_dirs(starting):
directory = double_check.pop()
pruneme(directory)
def prune_dirs_argparse(args):
return prune_dirs(args.starting)

View file

@ -28,7 +28,6 @@ def make_randomfile(length, filename=None):
f.close()
print('Created %s' % filename)
bytes = listget(sys.argv, 1, None)
if bytes is None:
bytes = 2 ** 10

View file

@ -1,11 +1,14 @@
import os
import send2trash
import sys
from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import winglob
for pattern in pipeable.go(skip_blank=True):
for name in winglob.glob(pattern):
name = os.path.abspath(name)
pipeable.stdout(name)
send2trash.send2trash(name)
def main(argv):
for path in pathclass.glob_many(pipeable.go(argv, skip_blank=True)):
pipeable.stdout(path.absolute_path)
send2trash.send2trash(path.absolute_path)
return 0
if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:]))

View file

@ -5,7 +5,7 @@ from voussoirkit import pipeable
for line in pipeable.go():
if os.path.isfile(line):
print('Recycling', line)
pipeable.stdout(line)
send2trash.send2trash(line)
else:
print('Not a file', line)
pipeable.stderr('Not a file', line)

View file

@ -12,6 +12,9 @@ from voussoirkit import bytestring
from voussoirkit import imagetools
from voussoirkit import pipeable
from voussoirkit import spinal
from voussoirkit import vlogging
log = vlogging.getLogger(__name__, 'rejpg')
PIL.ImageFile.LOAD_TRUNCATED_IMAGES = True
@ -24,7 +27,7 @@ def rejpg_argparse(args):
bytes_saved = 0
remaining_size = 0
for filename in files:
print(filename)
log.info('Processing %s.', filename)
bytesio = io.BytesIO()
image = PIL.Image.open(filename)
@ -43,9 +46,11 @@ def rejpg_argparse(args):
f.write(new_bytes)
f.close()
print('Saved', bytestring.bytestring(bytes_saved))
print('Remaining are', bytestring.bytestring(remaining_size))
log.info('Saved', bytestring.bytestring(bytes_saved))
log.info('Remaining are', bytestring.bytestring(remaining_size))
return 0
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -2,6 +2,9 @@
Repeat the input as many times as you want.
> repeat "hello" 8
> repeat "yowza" inf
> echo hi | repeat !i 4
'''
import argparse
@ -9,30 +12,36 @@ import sys
from voussoirkit import pipeable
def repeat_inf(text):
try:
while True:
pipeable.stdout(text)
except KeyboardInterrupt:
return 0
def repeat_times(text, times):
try:
times = int(times)
except ValueError:
pipeable.stderr('times should be an integer >= 1.')
return 1
if times < 1:
pipeable.stderr('times should be >= 1.')
return 1
try:
for t in range(times):
pipeable.stdout(text)
except KeyboardInterrupt:
return 1
def repeat_argparse(args):
text = pipeable.input(args.text, split_lines=False)
if args.times == 'inf':
try:
while True:
print(text)
except KeyboardInterrupt:
return 0
return repeat_inf(text)
else:
try:
times = int(args.times)
except ValueError:
pipeable.stderr('times should be an integer >= 1.')
return 1
if times < 1:
pipeable.stderr('times should be >= 1.')
return 1
try:
for t in range(times):
print(text)
except KeyboardInterrupt:
return 1
return repeat_times(text, args.times)
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -2,7 +2,6 @@ import sys
from voussoirkit import pipeable
lines = pipeable.input(sys.argv[1])
replace_from = sys.argv[2]
replace_to = sys.argv[3]

View file

@ -1,5 +1,4 @@
from voussoirkit import pipeable
for line in pipeable.go():
print(repr(line))

View file

@ -38,7 +38,6 @@ from voussoirkit import imagetools
from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import vlogging
from voussoirkit import winglob
log = vlogging.getLogger(__name__, 'resize')
@ -102,10 +101,11 @@ def resize(
image.save(new_name.absolute_path, exif=image.info.get('exif', b''), quality=quality)
def resize_argparse(args):
filenames = winglob.glob(args.pattern)
for filename in filenames:
patterns = pipeable.input(args.pattern, skip_blank=True, strip=True)
files = pathclass.glob_many(patterns, files=True)
for file in files:
resize(
filename,
file,
args.new_w,
args.new_h,
inplace=args.inplace,
@ -115,6 +115,8 @@ def resize_argparse(args):
quality=args.quality,
)
return 0
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -4,13 +4,17 @@ Reverse a string.
import argparse
import sys
from voussoirkit import pipeable
def reverse_argparse(args):
print(''.join(reversed(args.string)))
text = pipeable.input(args.text, split_lines=False)
pipeable.stdout(''.join(reversed(text)))
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('string')
parser.add_argument('text')
parser.set_defaults(func=reverse_argparse)
args = parser.parse_args(argv)

View file

@ -8,8 +8,8 @@ from voussoirkit import pipeable
def reverse_argparse(args):
lines = list(pipeable.input(args.lines))
lines.reverse()
print('\n'.join(lines))
pipeable.stdout('\n'.join(reversed(lines)))
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -1,28 +1,29 @@
import argparse
import os
import PIL.Image
import sys
from voussoirkit import imagetools
from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import vlogging
from voussoirkit import winglob
log = vlogging.getLogger(__name__, 'rotate')
def rotate_argparse(args):
if args.angle is None and not args.exif:
pipeable.stderr('Either an angle or --exif must be provided.')
log.fatal('Either an angle or --exif must be provided.')
return 1
filenames = winglob.glob(args.pattern)
for filename in filenames:
image = PIL.Image.open(filename)
patterns = pipeable.input(args.pattern, skip_blank=True, strip=True)
files = pathclass.glob_many(patterns, files=True)
for file in files:
image = PIL.Image.open(file.absolute_path)
if args.exif:
(new_image, exif) = imagetools.rotate_by_exif(image)
if new_image is image:
log.debug('%s doesn\'t need exif rotation.', filename)
log.debug('%s doesn\'t need exif rotation.', file.absolute_path)
continue
image = new_image
else:
@ -30,16 +31,19 @@ def rotate_argparse(args):
image = image.rotate(args.angle, expand=True)
if args.inplace:
newname = filename
newname = file
else:
if args.exif:
suffix = f'_exifrot'
else:
suffix = f'_{args.angle}'
(base, extension) = os.path.splitext(filename)
newname = base + suffix + extension
base = file.replace_extension('').basename
newname = base + suffix
newname = file.parent.with_child(newname).add_extension(file.extension)
pipeable.stdout(newname)
image.save(newname, exif=exif, quality=args.quality)
image.save(file.absolute_path, exif=exif, quality=args.quality)
@vlogging.main_decorator
def main(argv):

View file

@ -251,6 +251,7 @@ def _search_argparse(args):
result_count += 1
if args.show_count:
print('%d items.' % result_count)
return 0
@pipeable.ctrlc_return1
def search_argparse(args):

View file

@ -12,6 +12,8 @@ def shuffle_argparse(args):
for line in lines:
pipeable.stdout(line)
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -14,7 +14,8 @@ def main(argv):
elif path.is_dir:
total += spinal.get_dir_size(path)
print(total)
pipeable.stdout(total)
return 0
if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:]))

View file

@ -53,6 +53,8 @@ def sole_lift_argparse(args):
os.rmdir(temp_dir.absolute_path)
queue.append(directory.parent)
return 0
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -14,6 +14,8 @@ def sorted_argparse(args):
for line in lines:
pipeable.stdout(line)
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -12,6 +12,8 @@ def printstderr_argparse(args):
for line in text:
pipeable.stderr(line)
return 0
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -12,6 +12,8 @@ def printstdout_argparse(args):
for line in text:
pipeable.stdout(line)
return 0
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -2,10 +2,10 @@ import PIL.Image
import argparse
import sys
from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import sentinel
from voussoirkit import vlogging
from voussoirkit import winglob
log = vlogging.getLogger(__name__, 'stitch')
@ -14,8 +14,8 @@ HORIZONTAL = sentinel.Sentinel('horizontal')
def stitch_argparse(args):
patterns = pipeable.input_many(args.image_files, skip_blank=True, strip=True)
files = [file for pattern in patterns for file in winglob.glob(pattern)]
images = [PIL.Image.open(file) for file in files]
files = pathclass.glob_many(patterns, files=True)
images = [PIL.Image.open(file.absolute_path) for file in files]
if args.vertical:
direction = VERTICAL
else:
@ -42,6 +42,7 @@ def stitch_argparse(args):
log.info(args.output)
final_image.save(args.output)
return 0
@vlogging.main_decorator
def main(argv):

3
sum.py
View file

@ -1,5 +1,4 @@
from voussoirkit import pipeable
total = sum(float(x) for x in pipeable.go() if x.strip())
pipeable.stdout(f'{total}\n')
pipeable.stdout(f'{total}')

View file

@ -88,6 +88,8 @@ def svgrender_argparse(args):
axis='y' if args.y else 'x',
)
return 0
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -8,6 +8,10 @@ import time
from voussoirkit import bytestring
from voussoirkit import downloady
from voussoirkit import pipeable
from voussoirkit import vlogging
log = vlogging.getLogger(__name__, 'threaded_dl')
downloady.log.setLevel(vlogging.WARNING)
def clean_url_list(urls):
for url in urls:
@ -29,7 +33,7 @@ def clean_url_list(urls):
yield url
def download_thread(url, filename, *, bytespersecond=None, headers=None, timeout=None):
print(f' Starting "{filename}"')
log.info(f'Starting "{filename}"')
downloady.download_file(
url,
filename,
@ -37,7 +41,7 @@ def download_thread(url, filename, *, bytespersecond=None, headers=None, timeout
headers=headers,
timeout=timeout,
)
print(f'+Finished "{filename}"')
log.info(f'Finished "{filename}"')
def remove_finished(threads):
return [t for t in threads if t.is_alive()]
@ -87,7 +91,7 @@ def threaded_dl(
)
if os.path.exists(filename):
print(f'Skipping existing file "{filename}"')
log.info(f'Skipping existing file "{filename}"')
else:
kwargs = {
@ -103,7 +107,7 @@ def threaded_dl(
while len(threads) > 0:
threads = remove_finished(threads)
print(f'{len(threads)} threads remaining\r', end='', flush=True)
pipeable.stderr(f'{len(threads)} threads remaining\r', end='')
time.sleep(0.1)
def threaded_dl_argparse(args):
@ -132,6 +136,9 @@ def threaded_dl_argparse(args):
timeout=args.timeout,
)
return 0
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -21,6 +21,8 @@ def touch_argparse(args):
os.utime(filename)
pipeable.stdout(filename)
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -10,6 +10,7 @@ def unique_argparse(args):
if line not in seen:
pipeable.stdout(line)
seen.add(line)
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -42,6 +42,7 @@ def watchforlinks_argparse(args):
loop_forever(extension=args.extension, regex=args.regex)
except KeyboardInterrupt:
pass
return 0
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)

View file

@ -4,10 +4,8 @@ import sys
from voussoirkit import bytestring
filename = os.path.abspath(sys.argv[1])
def zerofile(filename, length):
if os.path.exists(filename):
raise ValueError(f'{filename} already exists.')