Use new betterhelp.
This commit is contained in:
parent
389f22faff
commit
8616cdc5dd
29 changed files with 1133 additions and 706 deletions
|
@ -1,11 +1,3 @@
|
|||
'''
|
||||
bitwise_or
|
||||
==========
|
||||
|
||||
Merge two or more files by performing bitwise or on their bits.
|
||||
|
||||
> bitwise_or file1 file2 --output file3
|
||||
'''
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
|
@ -40,6 +32,8 @@ def bitwise_or_argparse(args):
|
|||
pass
|
||||
elif args.overwrite:
|
||||
pass
|
||||
elif not pipeable.in_tty():
|
||||
return 1
|
||||
elif not interactive.getpermission(f'Overwrite "{output.absolute_path}"?'):
|
||||
return 1
|
||||
|
||||
|
@ -61,14 +55,27 @@ def bitwise_or_argparse(args):
|
|||
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
Merge two or more files by performing bitwise or on their bits.
|
||||
That is, every byte of the output file will be the bitwise or of the
|
||||
corresponding byte from all of the input files.
|
||||
''',
|
||||
)
|
||||
|
||||
parser.add_argument('files', nargs='+')
|
||||
parser.add_argument('--output', required=True)
|
||||
parser.add_argument('--overwrite', action='store_true')
|
||||
parser.add_argument('--output', required=True, type=pathclass.Path)
|
||||
parser.add_argument(
|
||||
'--overwrite',
|
||||
action='store_true',
|
||||
help='''
|
||||
Provide this flag if the output file already exists and you'd like to
|
||||
overwrite it.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=bitwise_or_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, __doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
68
blankimage.py
Normal file
68
blankimage.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
import argparse
|
||||
import PIL.Image
|
||||
import sys
|
||||
|
||||
from voussoirkit import betterhelp
|
||||
from voussoirkit import vlogging
|
||||
|
||||
log = vlogging.getLogger(__name__, 'blankimage')
|
||||
|
||||
def blankimage_argparse(args):
|
||||
if args.width and args.height:
|
||||
size = (args.width, args.height)
|
||||
else:
|
||||
size = (512, 512)
|
||||
|
||||
if args.color:
|
||||
color = args.color
|
||||
else:
|
||||
color = (255, 255, 255, 255)
|
||||
|
||||
image = PIL.Image.new('RGBA', size, color=color)
|
||||
for filename in args.names:
|
||||
image.save(filename)
|
||||
|
||||
return 0
|
||||
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
Create a blank image file.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'names',
|
||||
nargs='+',
|
||||
help='''
|
||||
One or more filenames. The same image will be saved to each.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--width',
|
||||
default=None,
|
||||
type=int,
|
||||
help='''
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--height',
|
||||
default=None,
|
||||
type=int,
|
||||
help='''
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--color',
|
||||
default=None,
|
||||
type=str,
|
||||
help='''
|
||||
A hex color like #fff or #0a0a0a or #ff0000ff.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=blankimage_argparse)
|
||||
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
121
brename.py
121
brename.py
|
@ -1,19 +1,3 @@
|
|||
'''
|
||||
Batch rename files by providing a string to be `eval`ed, using variable `x` as
|
||||
the current filename. Yes I know this is weird, but for certain tasks it's just
|
||||
too quick and easy to pass up.
|
||||
|
||||
Examples:
|
||||
|
||||
Prefix all the files:
|
||||
brename.py "f'Test_{x}'"
|
||||
|
||||
Rename files to their index with 0 padding:
|
||||
brename.py "f'{index1:>03}{dot_ext}'"
|
||||
|
||||
Keep the first word and extension:
|
||||
brename.py "(x.split(' ')[0] + dot_ext) if ' ' in x else x"
|
||||
'''
|
||||
import argparse
|
||||
import os
|
||||
import random
|
||||
|
@ -125,53 +109,70 @@ def brename_argparse(args):
|
|||
recurse=args.recurse,
|
||||
)
|
||||
|
||||
DOCSTRING = '''
|
||||
brename - batch file renaming
|
||||
=============================
|
||||
|
||||
> brename.py eval_string <flags>
|
||||
|
||||
eval_string:
|
||||
A string which will be evaluated by Python's eval. The name of the file or
|
||||
folder will be in the variable `x`. In addition, many other variables are
|
||||
provided for your convenience:
|
||||
`quote` ("), `apostrophe` (') so you don't have to escape command quotes.
|
||||
`hyphen` (-) because leading hyphens often cause problems with argparse.
|
||||
`stringtools` entire stringtools module. See voussoirkit/stringtools.py.
|
||||
`space` ( ), `dot` (.), `underscore` (_) so you don't have to add quotes to
|
||||
your command while using these common characters.
|
||||
`index` the file's index within the loop.
|
||||
`index1` the file's index+1, in case you want your names to start from 1.
|
||||
`parent` a pathclass.Path object for the directory containing the file.
|
||||
`cwd` a pathclass.Path object for the cwd of this program session.
|
||||
`noext` the name of the file, but without its extension.
|
||||
`ext` the file's extension, with no dot.
|
||||
|
||||
-y | --yes:
|
||||
Accept the results without confirming.
|
||||
|
||||
--recurse:
|
||||
Recurse into subfolders and rename those files too.
|
||||
|
||||
--naturalsort:
|
||||
Before renaming, the files will be sorted using natural sort instead of the
|
||||
default lexicographic sort. Natural sort means that "My file 20" will come
|
||||
before "My file 100" because 20<100. Lexicographic sort means 100 will come
|
||||
first because 1 is before 2.
|
||||
The purpose of this flag is so your index and index1 variables are applied
|
||||
in the order you desire.
|
||||
'''
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
parser.add_argument('transformation', help='python command using x as variable name')
|
||||
parser.add_argument('-y', '--yes', dest='autoyes', action='store_true')
|
||||
parser.add_argument('--recurse', action='store_true')
|
||||
parser.add_argument('--naturalsort', action='store_true')
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
Batch rename files by providing a string to be `eval`ed, using variable `x` as
|
||||
the current filename. Yes I know this is weird, but for certain tasks it's just
|
||||
too quick and easy to pass up.
|
||||
''',
|
||||
)
|
||||
parser.examples = [
|
||||
{'args': ['f\'Test_{x}\''], 'comment': 'Prefix all the files'},
|
||||
{'args': ['f\'{index1:>03}{dot_ext}\''], 'comment': 'Rename files to their index with 0 padding'},
|
||||
{'args': ['(x.split(space)[0] + dot_ext) if space in x else x'], 'comment': 'Keep the first word and extension'},
|
||||
]
|
||||
parser.add_argument(
|
||||
'transformation',
|
||||
help='''
|
||||
A string which will be evaluated by Python's eval. The name of the file or
|
||||
folder will be in the variable `x`. In addition, many other variables are
|
||||
provided for your convenience:
|
||||
`quote` ("), `apostrophe` (') so you don't have to escape command quotes.
|
||||
`hyphen` (-) because leading hyphens often cause problems with argparse.
|
||||
`stringtools` entire stringtools module. See voussoirkit/stringtools.py.
|
||||
`space` ( ), `dot` (.), `underscore` (_) so you don't have to add quotes to
|
||||
your command while using these common characters.
|
||||
`index` the file's index within the loop.
|
||||
`index1` the file's index+1, in case you want your names to start from 1.
|
||||
`parent` a pathclass.Path object for the directory containing the file.
|
||||
`cwd` a pathclass.Path object for the cwd of this program session.
|
||||
`noext` the name of the file, but without its extension.
|
||||
`ext` the file's extension, with no dot.
|
||||
`dot_ext` the file's extension, with dot.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-y',
|
||||
'--yes',
|
||||
dest='autoyes',
|
||||
action='store_true',
|
||||
help='''
|
||||
Accept the results without confirming.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--recurse',
|
||||
action='store_true',
|
||||
help='''
|
||||
Recurse into subfolders and rename those files too.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--naturalsort',
|
||||
action='store_true',
|
||||
help='''
|
||||
Before renaming, the files will be sorted using natural sort instead of the
|
||||
default lexicographic sort. Natural sort means that "My file 20" will come
|
||||
before "My file 100" because 20<100. Lexicographic sort means 100 will come
|
||||
first because 1 is before 2.
|
||||
The purpose of this flag is so your index and index1 variables are applied
|
||||
in the order you desire.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=brename_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, DOCSTRING)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
|
@ -1,36 +1,3 @@
|
|||
'''
|
||||
contentreplace - find-and-replace en masse
|
||||
==========================================
|
||||
|
||||
> contentreplace filename_glob replace_from replace_to <flags>
|
||||
|
||||
filename_glob:
|
||||
A glob pattern that targets the files of interest.
|
||||
|
||||
replace_from:
|
||||
String to be replaced.
|
||||
|
||||
replace_to:
|
||||
String with which to replace.
|
||||
|
||||
flags:
|
||||
--recurse:
|
||||
If provided, we will recurse into subdirectories and look for glob matches
|
||||
there too. If not provided, only files in the cwd are affected.
|
||||
|
||||
--regex:
|
||||
If provided, the given replace_from, replace_to will be treated as regex
|
||||
strings. If not provided, we use regular str.replace
|
||||
|
||||
--clip_prompt:
|
||||
If you want to do contentreplace with unicode that is difficult to enter
|
||||
into your terminal, or multi-line strings that don't work as command line
|
||||
arguments, this option might help you. The program will wait for you to put
|
||||
the text of interest into your clipboard and press Enter.
|
||||
|
||||
--yes:
|
||||
If provided, replacements will occur automatically without prompting.
|
||||
'''
|
||||
import argparse
|
||||
import codecs
|
||||
import pyperclip
|
||||
|
@ -106,18 +73,69 @@ def contentreplace_argparse(args):
|
|||
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
parser.add_argument('filename_glob')
|
||||
parser.add_argument('replace_from')
|
||||
parser.add_argument('replace_to')
|
||||
parser.add_argument('--yes', dest='autoyes', action='store_true')
|
||||
parser.add_argument('--recurse', action='store_true')
|
||||
parser.add_argument('--regex', dest='do_regex', action='store_true')
|
||||
parser.add_argument('--clip_prompt', '--clip-prompt', action='store_true')
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
find-and-replace en masse
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'filename_glob',
|
||||
help='''
|
||||
A glob pattern that targets the files of interest.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'replace_from',
|
||||
help='''
|
||||
String to be replaced. You can use backslash-escaped symbols like
|
||||
\\n for newline.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'replace_to',
|
||||
help='''
|
||||
String with which to replace. Can use backslash-escaped symbols.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--yes',
|
||||
dest='autoyes',
|
||||
action='store_true',
|
||||
help='''
|
||||
If provided, replacements will occur automatically without prompting.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--recurse',
|
||||
action='store_true',
|
||||
help='''
|
||||
If provided, we will recurse into subdirectories and look for glob matches
|
||||
there too. If not provided, only files in the cwd are affected.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--regex',
|
||||
dest='do_regex',
|
||||
action='store_true',
|
||||
help='''
|
||||
If provided, the given replace_from, replace_to will be treated as regex
|
||||
strings. If not provided, we use regular str.replace.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--clip_prompt',
|
||||
'--clip-prompt',
|
||||
action='store_true',
|
||||
help='''
|
||||
If you want to do contentreplace with unicode that is difficult to enter
|
||||
into your terminal, or multi-line strings that don't work as command line
|
||||
arguments, this option might help you. The program will wait for you to put
|
||||
the text of interest into your clipboard and press Enter.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=contentreplace_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, __doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
|
@ -1,16 +1,3 @@
|
|||
'''
|
||||
directory_discrepancy
|
||||
=====================
|
||||
|
||||
This program compares two directory and shows which files exist in each
|
||||
directory that do not exist in the other.
|
||||
|
||||
> directory_discrepancy dir1 dir2
|
||||
|
||||
flags:
|
||||
--recurse:
|
||||
Also check subdirectories.
|
||||
'''
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
|
@ -48,14 +35,24 @@ def directory_discrepancy_argparse(args):
|
|||
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
This program compares two directory and shows which files exist in each
|
||||
directory that do not exist in the other.
|
||||
''',
|
||||
)
|
||||
parser.add_argument('dir1')
|
||||
parser.add_argument('dir2')
|
||||
parser.add_argument('--recurse', action='store_true')
|
||||
parser.add_argument(
|
||||
'--recurse',
|
||||
action='store_true',
|
||||
help='''
|
||||
Also check subdirectories.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=directory_discrepancy_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, __doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
64
fdroidapk.py
64
fdroidapk.py
|
@ -1,22 +1,3 @@
|
|||
'''
|
||||
fdroidapk - F-Droid APK downloader
|
||||
==================================
|
||||
|
||||
> fdroidapk package_names <flags>
|
||||
|
||||
package_names:
|
||||
One or more package names to download, separated by spaces. You can find
|
||||
the package name in the URL on f-droid.org.
|
||||
For example, com.nutomic.syncthingandroid from the URL
|
||||
https://f-droid.org/en/packages/com.nutomic.syncthingandroid/
|
||||
|
||||
--destination path:
|
||||
Alternative path to download the apk files to. Default is cwd.
|
||||
|
||||
--folders:
|
||||
If provided, each apk will be downloaded into a separate folder named after
|
||||
the package.
|
||||
'''
|
||||
import argparse
|
||||
import bs4
|
||||
import io
|
||||
|
@ -33,6 +14,7 @@ from voussoirkit import httperrors
|
|||
from voussoirkit import operatornotify
|
||||
from voussoirkit import pathclass
|
||||
from voussoirkit import pipeable
|
||||
from voussoirkit import progressbars
|
||||
from voussoirkit import vlogging
|
||||
|
||||
log = vlogging.getLogger(__name__, 'fdroidapk')
|
||||
|
@ -52,7 +34,7 @@ def download_file(url, path):
|
|||
return downloady.download_file(
|
||||
url,
|
||||
path,
|
||||
callback_progress=downloady.Progress2,
|
||||
progressbar=progressbars.bar1_bytestring,
|
||||
timeout=30,
|
||||
)
|
||||
|
||||
|
@ -88,8 +70,7 @@ def normalize_package_name(package_name):
|
|||
|
||||
@pipeable.ctrlc_return1
|
||||
def fpk_argparse(args):
|
||||
destination = pathclass.Path(args.destination)
|
||||
destination.assert_is_directory()
|
||||
args.destination.assert_is_directory()
|
||||
|
||||
return_status = 0
|
||||
|
||||
|
@ -117,10 +98,10 @@ def fpk_argparse(args):
|
|||
apk_url = f'https://f-droid.org/repo/{apk_basename}'
|
||||
|
||||
if args.folders:
|
||||
this_dest = destination.with_child(package)
|
||||
this_dest = args.destination.with_child(package)
|
||||
this_dest.makedirs(exist_ok=True)
|
||||
else:
|
||||
this_dest = destination
|
||||
this_dest = args.destination
|
||||
this_dest = this_dest.with_child(apk_basename)
|
||||
|
||||
if this_dest.exists:
|
||||
|
@ -145,14 +126,39 @@ def fpk_argparse(args):
|
|||
@operatornotify.main_decorator(subject='fdroidapk.py')
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser = argparse.ArgumentParser(description='F-Droid APK downloader.')
|
||||
|
||||
parser.add_argument('packages', nargs='+')
|
||||
parser.add_argument('--folders', action='store_true')
|
||||
parser.add_argument('--destination', default='.')
|
||||
parser.add_argument(
|
||||
'packages',
|
||||
nargs='+',
|
||||
type=str,
|
||||
help='''
|
||||
One or more package names to download, separated by spaces. You can find
|
||||
the package name in the URL on f-droid.org.
|
||||
For example, com.nutomic.syncthingandroid from the URL
|
||||
https://f-droid.org/en/packages/com.nutomic.syncthingandroid/
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--folders',
|
||||
action='store_true',
|
||||
help='''
|
||||
If provided, each apk will be downloaded into a separate folder named after
|
||||
the package.
|
||||
If omitted, the apks are downloaded into the destination folder directly.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--destination',
|
||||
default=pathclass.cwd(),
|
||||
type=pathclass.Path,
|
||||
help='''
|
||||
Alternative path to download the apk files to. Default is cwd.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=fpk_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, __doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
27
filepull.py
27
filepull.py
|
@ -1,6 +1,3 @@
|
|||
'''
|
||||
Pull all of the files in nested directories into the current directory.
|
||||
'''
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
@ -9,10 +6,15 @@ from voussoirkit import interactive
|
|||
from voussoirkit import pathclass
|
||||
from voussoirkit import pipeable
|
||||
from voussoirkit import spinal
|
||||
from voussoirkit import winglob
|
||||
|
||||
def filepull(pull_from='.', autoyes=False):
|
||||
def filepull(pull_from='.', globs=None, autoyes=False):
|
||||
start = pathclass.Path(pull_from)
|
||||
files = [file for d in start.listdir_directories() for file in d.walk_files()]
|
||||
files = [
|
||||
file
|
||||
for d in start.listdir_directories()
|
||||
for file in spinal.walk(d, glob_filenames=globs)
|
||||
]
|
||||
|
||||
if len(files) == 0:
|
||||
pipeable.stderr('No files to move')
|
||||
|
@ -45,12 +47,23 @@ def filepull(pull_from='.', autoyes=False):
|
|||
return 1
|
||||
|
||||
def filepull_argparse(args):
|
||||
return filepull(pull_from=args.pull_from, autoyes=args.autoyes)
|
||||
return filepull(pull_from=args.pull_from, globs=args.glob, autoyes=args.autoyes)
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
Pull all of the files in nested directories into the current directory.
|
||||
''',
|
||||
)
|
||||
|
||||
parser.add_argument('pull_from', nargs='?', default='.')
|
||||
parser.add_argument(
|
||||
'--glob',
|
||||
nargs='+',
|
||||
help='''
|
||||
Only pull files whose basename matches any of these glob patterns.
|
||||
''',
|
||||
)
|
||||
parser.add_argument('-y', '--yes', dest='autoyes', action='store_true')
|
||||
parser.set_defaults(func=filepull_argparse)
|
||||
|
||||
|
|
64
fuchsiatransparent.py
Normal file
64
fuchsiatransparent.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
import argparse
|
||||
import PIL.Image
|
||||
import sys
|
||||
|
||||
from voussoirkit import betterhelp
|
||||
from voussoirkit import imagetools
|
||||
from voussoirkit import pathclass
|
||||
from voussoirkit import pipeable
|
||||
from voussoirkit import vlogging
|
||||
|
||||
log = vlogging.getLogger(__name__, 'fuchsiatransparent')
|
||||
|
||||
FUCHSIA = (255, 0, 255, 255)
|
||||
TRANSPARENT = (0, 0, 0, 0)
|
||||
|
||||
def fuchsiatransparent_argparse(args):
|
||||
patterns = pipeable.input_many(args.patterns)
|
||||
files = pathclass.glob_many_files(patterns)
|
||||
for file in files:
|
||||
image = PIL.Image.open(file.absolute_path)
|
||||
if image.mode == 'RGB':
|
||||
image = image.convert('RGBA')
|
||||
if image.mode == 'RGBA':
|
||||
image = imagetools.replace_color(image, FUCHSIA, TRANSPARENT)
|
||||
else:
|
||||
log.info('Can\'t process %s', file.absolute_path)
|
||||
continue
|
||||
|
||||
if args.inplace:
|
||||
outpath = file
|
||||
else:
|
||||
outname = file.replace_extension('').basename + '_transparent'
|
||||
outpath = file.parent.with_child(outname).add_extension(file.extension)
|
||||
|
||||
pipeable.stderr(outpath.absolute_path)
|
||||
image.save(outpath.absolute_path)
|
||||
return 0
|
||||
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
Replace #FF00FF colored pixels with transparent.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'patterns',
|
||||
help='''
|
||||
One or more glob patterns for input files.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--inplace',
|
||||
action='store_true',
|
||||
help='''
|
||||
Overwrite the input file instead of saving it as _transparent.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=fuchsiatransparent_argparse)
|
||||
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
|
@ -127,6 +127,7 @@ def getcrx_argparse(args):
|
|||
else:
|
||||
auto_overwrite = None
|
||||
|
||||
return_status = 0
|
||||
for extension_id in extension_ids:
|
||||
try:
|
||||
download_crx(extension_id, auto_overwrite=auto_overwrite)
|
||||
|
@ -136,7 +137,8 @@ def getcrx_argparse(args):
|
|||
else:
|
||||
log.error(traceback.format_exc())
|
||||
pipeable.stderr('Resuming...')
|
||||
return 0
|
||||
return_status = 1
|
||||
return return_status
|
||||
|
||||
@operatornotify.main_decorator(subject='getcrx')
|
||||
@vlogging.main_decorator
|
||||
|
|
32
getpid.py
32
getpid.py
|
@ -1,17 +1,3 @@
|
|||
'''
|
||||
getpid
|
||||
======
|
||||
|
||||
Get PIDs for running processes that match the given process name.
|
||||
|
||||
Error level will be 0 if any processes are found, 1 if none are found.
|
||||
|
||||
> getpid process_name
|
||||
|
||||
Examples:
|
||||
> getpid python.exe
|
||||
> getpid chrome.exe
|
||||
'''
|
||||
import argparse
|
||||
import psutil
|
||||
import sys
|
||||
|
@ -32,12 +18,24 @@ def getpid_argparse(args):
|
|||
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
Get PIDs for running processes that match the given process name.
|
||||
|
||||
parser.add_argument('process_name')
|
||||
Error level will be 0 if any processes are found, 1 if none are found.
|
||||
''',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'process_name',
|
||||
type=str,
|
||||
help='''
|
||||
Name like "python.exe" or "chrome" as it appears in your task manager / ps.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=getpid_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, __doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
137
gitcheckup.py
137
gitcheckup.py
|
@ -1,50 +1,3 @@
|
|||
'''
|
||||
gitcheckup
|
||||
==========
|
||||
|
||||
This program helps you check the commit and push status of your favorite git
|
||||
repositories. The output looks like this:
|
||||
|
||||
[ ][P] D:\\Git\\cmd (~1)
|
||||
[C][P] D:\\Git\\Etiquette
|
||||
[ ][P] D:\\Git\\voussoirkit (+1)
|
||||
[C][ ] D:\\Git\\YCDL (↑3)
|
||||
|
||||
To specify the list of git directories, you may either:
|
||||
- Create a gitcheckup.txt file in the same directory as this file, where every
|
||||
line contains an absolute path to the directory, or
|
||||
- Pass directories as a series of positional arguments to this program.
|
||||
|
||||
> gitcheckup.py <flags>
|
||||
> gitcheckup.py dir1 dir2 <flags>
|
||||
|
||||
flags:
|
||||
--fetch:
|
||||
Run `git fetch --all` in each directory.
|
||||
|
||||
--pull:
|
||||
Run `git pull --all` in each directory.
|
||||
|
||||
--push:
|
||||
Run `git push` in each directory.
|
||||
|
||||
--run <command>:
|
||||
Run `git <command>` in each directory. You can use \- to escape - in your
|
||||
git arguments, since they would confuse this program's argparse.
|
||||
If this is used, any --fetch, --pull, --push is ignored.
|
||||
|
||||
--add path:
|
||||
Add path to the gitcheckup.txt file.
|
||||
|
||||
--remove path:
|
||||
Remove path from the gitcheckup.txt file.
|
||||
|
||||
Examples:
|
||||
> gitcheckup
|
||||
> gitcheckup --fetch
|
||||
> gitcheckup D:\\Git\\cmd D:\\Git\\YCDL --pull
|
||||
> gitcheckup --run add README.md
|
||||
'''
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
|
@ -349,19 +302,91 @@ def gitcheckup_argparse(args):
|
|||
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
This program helps you check the commit and push status of your favorite git
|
||||
repositories. The output looks like this:
|
||||
|
||||
parser.add_argument('directories', nargs='*')
|
||||
parser.add_argument('--fetch', dest='do_fetch', action='store_true')
|
||||
parser.add_argument('--pull', dest='do_pull', action='store_true')
|
||||
parser.add_argument('--push', dest='do_push', action='store_true')
|
||||
parser.add_argument('--add', dest='add_directory')
|
||||
parser.add_argument('--run', dest='run_command', nargs='+')
|
||||
parser.add_argument('--remove', dest='remove_directory')
|
||||
[ ][P] D:\\Git\\cmd (~1)
|
||||
[C][P] D:\\Git\\Etiquette
|
||||
[ ][P] D:\\Git\\voussoirkit (+1)
|
||||
[C][ ] D:\\Git\\YCDL (↑3)
|
||||
''',
|
||||
)
|
||||
parser.examples = [
|
||||
'',
|
||||
'--fetch',
|
||||
'D:\\Git\\cmd D:\\Git\\YCDL --pull',
|
||||
'--run add README.md',
|
||||
]
|
||||
|
||||
parser.add_argument(
|
||||
'directories',
|
||||
nargs='*',
|
||||
help='''
|
||||
One or more directories to check up.
|
||||
If omitted, you should have a file called gitcheckup.txt in the same
|
||||
directory as this file, where every line contains an absolute path to
|
||||
a directory.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--fetch',
|
||||
dest='do_fetch',
|
||||
action='store_true',
|
||||
help='''
|
||||
Run `git fetch --all` in each directory.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--pull',
|
||||
dest='do_pull',
|
||||
action='store_true',
|
||||
help='''
|
||||
Run `git pull --all` in each directory.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--push',
|
||||
dest='do_push',
|
||||
action='store_true',
|
||||
help='''
|
||||
Run `git push` in each directory.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--run',
|
||||
dest='run_command',
|
||||
nargs='+',
|
||||
type=str,
|
||||
help='''
|
||||
Run `git <command>` in each directory. You can use \- to escape - in your
|
||||
git arguments, since they would confuse this program's argparse.
|
||||
If this is used, any --fetch, --pull, --push is ignored.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--add',
|
||||
dest='add_directory',
|
||||
metavar='path',
|
||||
type=str,
|
||||
help='''
|
||||
Add path to the gitcheckup.txt file.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--remove',
|
||||
dest='remove_directory',
|
||||
metavar='path',
|
||||
type=str,
|
||||
help='''
|
||||
Remove path from the gitcheckup.txt file.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=gitcheckup_argparse)
|
||||
|
||||
try:
|
||||
return betterhelp.single_main(argv, parser, docstring=__doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
except GitCheckupException as exc:
|
||||
print(exc)
|
||||
return 1
|
||||
|
|
|
@ -85,11 +85,17 @@
|
|||
# | 40 | n | Pixel bytes, r, g, b, a. |
|
||||
# |________|______________|_______________________________________________________|
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import PIL.Image
|
||||
import sys
|
||||
|
||||
from voussoirkit import betterhelp
|
||||
from voussoirkit import imagetools
|
||||
from voussoirkit import pipeable
|
||||
from voussoirkit import vlogging
|
||||
|
||||
log = vlogging.get_logger(__name__, 'icoconvert')
|
||||
|
||||
ICO_HEADER_LENGTH = 6
|
||||
ICON_DIRECTORY_ENTRY_LENGTH = 16
|
||||
|
@ -230,17 +236,36 @@ def images_to_ico(images):
|
|||
final_data = b''.join(datablobs)
|
||||
return final_data
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
inputfiles = sys.argv[1:]
|
||||
except Exception:
|
||||
print('Please provide an image file')
|
||||
raise SystemExit
|
||||
print('Iconifying', inputfiles)
|
||||
images = [load_image(filename) for filename in inputfiles]
|
||||
def icoconvert_argparse(args):
|
||||
log.info('Iconifying %s', args.files)
|
||||
images = [load_image(filename) for filename in args.files]
|
||||
|
||||
final_data = images_to_ico(images)
|
||||
name = os.path.splitext(inputfiles[0])[0] + '.ico'
|
||||
output_file = open(name, 'wb')
|
||||
|
||||
iconame = os.path.splitext(args.files[0])[0] + '.ico'
|
||||
output_file = open(iconame, 'wb')
|
||||
output_file.write(final_data)
|
||||
output_file.close()
|
||||
print('Finished %s.' % name)
|
||||
pipeable.stderr(iconame)
|
||||
return 0
|
||||
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
Create a Windows .ico icon file from one or more images.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'files',
|
||||
nargs='+',
|
||||
help='''
|
||||
One or more image files to put into the ico.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=icoconvert_argparse)
|
||||
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
18
inodes.py
18
inodes.py
|
@ -13,12 +13,22 @@ def inodes_argparse(args):
|
|||
return 0
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
parser.add_argument('patterns', nargs='+')
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
Show the st_dev, st_ino of files.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'patterns',
|
||||
nargs='+',
|
||||
help='''
|
||||
One or more glob patterns. Supports pipeable !c clipboard, !i stdin
|
||||
lines of patterns.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=inodes_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, __doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
|
@ -1,22 +1,3 @@
|
|||
'''
|
||||
named_python
|
||||
============
|
||||
|
||||
Because Python is interpreted, when you look at the task manager / process list
|
||||
you'll see that every running python instance has the same name, python.exe.
|
||||
This script helps you name the executables so they stand out.
|
||||
|
||||
For the time being this script doesn't automatically call your new exe, you
|
||||
have to write a second command to actually run it. I tried using
|
||||
subprocess.Popen to spawn the new python with the rest of argv but the behavior
|
||||
was different on Linux and Windows and neither was really clean.
|
||||
|
||||
> named_python name
|
||||
|
||||
Examples:
|
||||
> named_python myserver && python-myserver server.py --port 8080
|
||||
> named_python hnarchive && python-hnarchive hnarchive.py livestream
|
||||
'''
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
@ -40,12 +21,29 @@ def namedpython_argparse(args):
|
|||
return 0
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
Because Python is interpreted, when you look at the task manager / process list
|
||||
you'll see that every running python instance has the same name, python.exe.
|
||||
This script helps you name the executables so they stand out.
|
||||
|
||||
parser.add_argument('name')
|
||||
For the time being this script doesn't automatically call your new exe, you
|
||||
have to write a second command to actually run it. I tried using
|
||||
subprocess.Popen to spawn the new python with the rest of argv but the behavior
|
||||
was different on Linux and Windows and neither was really clean.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'name',
|
||||
type=str,
|
||||
help='''
|
||||
If you invoke this script with python.exe, a hardlink python-{name}.exe
|
||||
will be created. Also works with pythonw.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=namedpython_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, __doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
|
@ -1,17 +1,10 @@
|
|||
'''
|
||||
no smart quotes
|
||||
===============
|
||||
|
||||
Replace smart quotes and smart apostrophes with regular ASCII values.
|
||||
|
||||
Just say no to smart quotes!
|
||||
'''
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
from voussoirkit import betterhelp
|
||||
from voussoirkit import pathclass
|
||||
from voussoirkit import pipeable
|
||||
from voussoirkit import spinal
|
||||
from voussoirkit import vlogging
|
||||
|
||||
|
@ -27,8 +20,9 @@ def replace_smartquotes(text):
|
|||
return text
|
||||
|
||||
def nosmartquotes_argparse(args):
|
||||
globs = list(pipeable.input_many(args.patterns))
|
||||
files = spinal.walk(
|
||||
glob_filenames=args.filename_glob,
|
||||
glob_filenames=globs,
|
||||
exclude_filenames={THIS_FILE},
|
||||
recurse=args.recurse,
|
||||
)
|
||||
|
@ -43,19 +37,39 @@ def nosmartquotes_argparse(args):
|
|||
continue
|
||||
|
||||
file.write('w', text, encoding='utf-8')
|
||||
print(file.absolute_path)
|
||||
pipeable.stdout(file.absolute_path)
|
||||
|
||||
return 0
|
||||
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
Replace smart quotes and smart apostrophes with regular ASCII values.
|
||||
|
||||
parser.add_argument('filename_glob')
|
||||
parser.add_argument('--recurse', action='store_true')
|
||||
Just say no to smart quotes!
|
||||
''',
|
||||
)
|
||||
parser.examples = [
|
||||
'*.md --recurse',
|
||||
]
|
||||
parser.add_argument(
|
||||
'patterns',
|
||||
nargs='+',
|
||||
help='''
|
||||
One or more glob patterns for input files.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--recurse',
|
||||
action='store_true',
|
||||
help='''
|
||||
If provided, recurse into subdirectories and process those files too.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=nosmartquotes_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, __doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
|
@ -1,11 +1,3 @@
|
|||
'''
|
||||
This program deletes all empty directories which are children of the given
|
||||
starting directory. The starting directory itself will not be deleted even
|
||||
if it is empty.
|
||||
|
||||
> prune_dirs .
|
||||
> prune_dirs C:\\somepath
|
||||
'''
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
@ -46,12 +38,17 @@ def prune_dirs_argparse(args):
|
|||
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
This program deletes all empty directories which are children of the given
|
||||
starting directory. The starting directory itself will not be deleted even
|
||||
if it is empty.
|
||||
''',
|
||||
)
|
||||
parser.add_argument('starting')
|
||||
parser.set_defaults(func=prune_dirs_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, docstring=__doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
193
rarpar.py
193
rarpar.py
|
@ -460,64 +460,6 @@ def rarpar(
|
|||
|
||||
# COMMAND LINE #####################################################################################
|
||||
|
||||
DOCSTRING = '''
|
||||
rarpar
|
||||
======
|
||||
|
||||
> rarpar path <flags>
|
||||
|
||||
path:
|
||||
The input file or directory to rarpar.
|
||||
|
||||
--volume X | X% | min(A, B) | max(A, B):
|
||||
Split rars into volumes of this many megabytes. Should be
|
||||
An integer number of megabytes, or;
|
||||
A percentage "X%" to calculate volumes as X% of the file size, down to
|
||||
a 1 MB minimum, or;
|
||||
A string "min(A, B)" or "max(A, B)" where A and B follow the above rules.
|
||||
|
||||
--rec X:
|
||||
An integer to generate X% recovery record in the rars.
|
||||
See winrar documentation for information about recovery records.
|
||||
|
||||
--rev X:
|
||||
An integer to generate X% recovery volumes.
|
||||
Note that winrar's behavior is the number of revs will always be less than
|
||||
the number of rars. If you don't split volumes, you will have 1 rar and
|
||||
thus 0 revs even if you ask for 100% rev.
|
||||
See winrar documentation for information about recovery volumes.
|
||||
|
||||
--par X:
|
||||
A number to generate X% recovery with par2.
|
||||
|
||||
--basename X:
|
||||
A basename for the rar and par files. You will end up with
|
||||
basename.partXX.rar and basename.par2.
|
||||
Without this argument, the default basename is "{basename} ({timestamp})".
|
||||
Your string may include {basename}, {timestamp} and/or {date} including the
|
||||
braces to insert that value there.
|
||||
|
||||
--compression X:
|
||||
Level of compression. Can be "store" or "max" or integer 0-5.
|
||||
|
||||
--password X:
|
||||
A password with which to encrypt the rar files.
|
||||
|
||||
--workdir X:
|
||||
The directory in which the rars and pars will be generated while the
|
||||
program is working.
|
||||
|
||||
--moveto X:
|
||||
The directory to which the rars and pars will be moved after the program
|
||||
has finished working.
|
||||
|
||||
--recycle:
|
||||
The input file or directory will be recycled at the end.
|
||||
|
||||
--dry:
|
||||
Print the commands that will be run, but don't actually run them.
|
||||
'''
|
||||
|
||||
def rarpar_argparse(args):
|
||||
status = 0
|
||||
try:
|
||||
|
@ -549,24 +491,127 @@ def rarpar_argparse(args):
|
|||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
parser.add_argument('path')
|
||||
parser.add_argument('--volume')
|
||||
parser.add_argument('--rec')
|
||||
parser.add_argument('--rev')
|
||||
parser.add_argument('--par')
|
||||
parser.add_argument('--basename')
|
||||
parser.add_argument('--compression')
|
||||
parser.add_argument('--password')
|
||||
parser.add_argument('--profile', dest='rar_profile')
|
||||
parser.add_argument('--workdir', default='.')
|
||||
parser.add_argument('--moveto')
|
||||
parser.add_argument('--recycle', dest='recycle_original', action='store_true')
|
||||
parser.add_argument('--dictionary', dest='dictionary_size')
|
||||
parser.add_argument('--solid', action='store_true')
|
||||
parser.add_argument('--dry', action='store_true')
|
||||
parser.add_argument(
|
||||
'path',
|
||||
type=pathclass.Path,
|
||||
help='''
|
||||
The input file or directory to rarpar.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--volume',
|
||||
help='''
|
||||
Split rars into volumes of this many megabytes. Should be:
|
||||
- An integer number of megabytes, or
|
||||
- A percentage "X%" to calculate volumes as X% of the file size, down to
|
||||
a 1 MB minimum, or
|
||||
- A string "min(A, B)" or "max(A, B)" where A and B follow the above rules.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--rec',
|
||||
type=int,
|
||||
help='''
|
||||
An integer to generate X% recovery record in the rars.
|
||||
See winrar documentation for information about recovery records.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--rev',
|
||||
type=int,
|
||||
help='''
|
||||
An integer to generate X% recovery volumes.
|
||||
Note that winrar's behavior is the number of revs will always be less than
|
||||
the number of rars. If you don't split volumes, you will have 1 rar and
|
||||
thus 0 revs even if you ask for 100% rev.
|
||||
See winrar documentation for information about recovery volumes.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--par',
|
||||
type=int,
|
||||
help='''
|
||||
A number to generate X% recovery with par2.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--basename',
|
||||
type=str,
|
||||
help='''
|
||||
A basename for the rar and par files. You will end up with
|
||||
basename.partXX.rar and basename.par2.
|
||||
Without this argument, the default basename is "{basename} ({timestamp})".
|
||||
Your string may include {basename}, {timestamp} and/or {date} including the
|
||||
braces to insert that value there.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--compression',
|
||||
help='''
|
||||
Level of compression. Can be "store" or "max" or integer 0-5.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--password',
|
||||
type=str,
|
||||
help='''
|
||||
A password with which to encrypt the rar files.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--profile', dest='rar_profile',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--workdir',
|
||||
type=pathclass.Path,
|
||||
default='.',
|
||||
help='''
|
||||
The directory in which the rars and pars will be generated while the
|
||||
program is working.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--moveto',
|
||||
type=pathclass.Path,
|
||||
help='''
|
||||
The directory to which the rars and pars will be moved after the program
|
||||
has finished working.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--recycle',
|
||||
dest='recycle_original',
|
||||
action='store_true',
|
||||
help='''
|
||||
The input file or directory will be recycled at the end.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--dictionary',
|
||||
dest='dictionary_size',
|
||||
help='''
|
||||
Larger dictionary sizes can improve compression in exchange for higher
|
||||
memory usage. Accepted values are 128k, 256k, 512k, 1m, 2m, 4m, 8m, 16m,
|
||||
32m, 64m, 128m, 256m, 512m, 1g.
|
||||
'''
|
||||
)
|
||||
parser.add_argument(
|
||||
'--solid',
|
||||
action='store_true',
|
||||
help='''
|
||||
Generate a 'solid' rar archive. See winrar's documentation for details.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--dry',
|
||||
action='store_true',
|
||||
help='''
|
||||
Print the commands that will be run, but don't actually run them.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=rarpar_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, DOCSTRING)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
|
@ -1,32 +1,3 @@
|
|||
'''
|
||||
reg_extension_icon
|
||||
==================
|
||||
|
||||
This script edits the windows registry HKEY_CLASSES_ROOT to assign a file
|
||||
extension icon and optionally a human-friendly name string.
|
||||
|
||||
Must run as administrator.
|
||||
|
||||
WARNING, if the extension is already associated with a program, or is otherwise
|
||||
connected to a progid, this will break it.
|
||||
|
||||
> reg_extension_icon ico_file <flags>
|
||||
|
||||
ico_file:
|
||||
Filepath of the icon file.
|
||||
|
||||
--extension:
|
||||
If you omit this option, your file should be named "png.ico" or "py.ico" to
|
||||
set the icon for png and py types. If the name of your ico file is not the
|
||||
name of the extension you want to control, specify the extension here.
|
||||
|
||||
--name:
|
||||
A human-friendly name string which will show on Explorer under the "Type"
|
||||
column and in the properties dialog.
|
||||
|
||||
--shellopen:
|
||||
A command-line string to use as the shell\open\command
|
||||
'''
|
||||
import argparse
|
||||
import sys
|
||||
import winreg
|
||||
|
@ -97,16 +68,56 @@ def extension_registry_argparse(args):
|
|||
)
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
This script edits the windows registry HKEY_CLASSES_ROOT to assign a file
|
||||
extension icon and optionally a human-friendly name string.
|
||||
|
||||
parser.add_argument('ico_file')
|
||||
parser.add_argument('--extension', default=None)
|
||||
parser.add_argument('--name', default=None)
|
||||
parser.add_argument('--shellopen', default=None)
|
||||
parser.add_argument('--yes', dest='autoyes', action='store_true')
|
||||
Must run as administrator.
|
||||
|
||||
WARNING, if the extension is already associated with a program, or is otherwise
|
||||
connected to a progid, this will break it.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'ico_file',
|
||||
help='''
|
||||
Filepath of the icon file.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--extension',
|
||||
default=None,
|
||||
help='''
|
||||
If you omit this option, your file should be named "png.ico" or "py.ico" to
|
||||
set the icon for png and py types. If the name of your ico file is not the
|
||||
name of the extension you want to control, specify the extension here.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
type=str,
|
||||
default=None,
|
||||
help='''
|
||||
A human-friendly name string which will show on Explorer under the "Type"
|
||||
column and in the properties dialog.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--shellopen',
|
||||
default=None,
|
||||
help='''
|
||||
A command-line string to use as the shell\\open\\command
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--yes',
|
||||
dest='autoyes',
|
||||
action='store_true',
|
||||
)
|
||||
parser.set_defaults(func=extension_registry_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, __doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
|
@ -1,17 +1,3 @@
|
|||
'''
|
||||
reserve_disk_space
|
||||
==================
|
||||
|
||||
Exits with status of 0 if the disk has the requested amount of space, 1 if not.
|
||||
|
||||
> reserve_disk_space reserve [drive]
|
||||
|
||||
reserve:
|
||||
A string like "50g" or "100 gb"
|
||||
|
||||
drive:
|
||||
Filepath to the drive you want to check. Defaults to cwd drive.
|
||||
'''
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
|
@ -39,13 +25,29 @@ def reserve_disk_space_argparse(args):
|
|||
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
parser.add_argument('reserve')
|
||||
parser.add_argument('drive', nargs='?', default='.')
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
Exits with status of 0 if the disk has the requested amount of space, 1 if not.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'reserve',
|
||||
type=str,
|
||||
help='''
|
||||
A string like "50g" or "100 gb"
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'drive',
|
||||
nargs='?',
|
||||
default='.',
|
||||
help='''
|
||||
Filepath to the drive you want to check.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=reserve_disk_space_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, __doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
195
resize.py
195
resize.py
|
@ -1,65 +1,3 @@
|
|||
'''
|
||||
resize
|
||||
======
|
||||
|
||||
Resize image files.
|
||||
|
||||
> resize patterns <flags>
|
||||
|
||||
patterns:
|
||||
One or more glob patterns for input files.
|
||||
Uses pipeable to support !c clipboard, !i stdin lines of glob patterns.
|
||||
|
||||
flags:
|
||||
--width X:
|
||||
--height X:
|
||||
New dimensions for the image. If either of these is omitted, then that
|
||||
dimension will be calculated automatically based on the aspect ratio.
|
||||
|
||||
--break_aspect_ratio:
|
||||
If provided, the given --width and --height will be used exactly. You will
|
||||
need to provide both --width and --height.
|
||||
|
||||
If omitted, the image will be resized to fit within the bounds provided by
|
||||
--width and --height while preserving its aspect ratio.
|
||||
|
||||
--output X:
|
||||
A string that controls the output filename format. Suppose the input file
|
||||
was myphoto.jpg. You can use these variables in your format string:
|
||||
{base} = myphoto
|
||||
{filename} = myphoto.jpg
|
||||
{width} = an integer
|
||||
{height} = an integer
|
||||
{extension} = .jpg
|
||||
|
||||
You may omit {extension} from your format string and it will automatically
|
||||
be added to the end, unless you already provided a different extension.
|
||||
|
||||
If your format string only designates a basename, output files will go to
|
||||
the same directory as the corresponding input file. If your string contains
|
||||
path separators, all output files will go to that directory.
|
||||
The directory
|
||||
part is not formatted with the variables.
|
||||
|
||||
--inplace:
|
||||
Overwrite the input files. Cannot be used along with --output.
|
||||
Be careful!
|
||||
|
||||
--nearest:
|
||||
If provided, use nearest-neighbor scaling to preserve pixelated images.
|
||||
If omitted, use antialiased scaling.
|
||||
|
||||
--only_shrink:
|
||||
If the input image is smaller than the requested dimensions, do nothing.
|
||||
Useful when globbing in a directory with many differently sized images.
|
||||
|
||||
--quality X:
|
||||
JPEG compression quality.
|
||||
|
||||
--scale X:
|
||||
Scale the image by factor X.
|
||||
Use this option instead of --width, --height.
|
||||
'''
|
||||
import argparse
|
||||
import os
|
||||
import PIL.Image
|
||||
|
@ -77,15 +15,6 @@ log = vlogging.getLogger(__name__, 'resize')
|
|||
OUTPUT_INPLACE = sentinel.Sentinel('output inplace')
|
||||
DEFAULT_OUTPUT_FORMAT = '{base}_{width}x{height}{extension}'
|
||||
|
||||
def resize_core(
|
||||
image,
|
||||
height=None,
|
||||
only_shrink=False,
|
||||
scale=None,
|
||||
width=None,
|
||||
):
|
||||
pass
|
||||
|
||||
def resize(
|
||||
filename,
|
||||
*,
|
||||
|
@ -215,21 +144,121 @@ def resize_argparse(args):
|
|||
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser = argparse.ArgumentParser(description='Resize image files.')
|
||||
parser.examples = [
|
||||
'myphoto.jpg --scale 0.5 --inplace',
|
||||
'*.jpg --only_shrink --width 500 --height 500 --output thumbs\\{base}.jpg',
|
||||
'sprite*.png sprite*.bmp --width 1024 --nearest --output {base}_big{extension}',
|
||||
]
|
||||
|
||||
parser.add_argument('patterns', nargs='+')
|
||||
parser.add_argument('--width', type=int, default=None)
|
||||
parser.add_argument('--height', type=int, default=None)
|
||||
parser.add_argument('--inplace', action='store_true')
|
||||
parser.add_argument('--nearest', dest='nearest_neighbor', action='store_true')
|
||||
parser.add_argument('--only_shrink', '--only-shrink', action='store_true')
|
||||
parser.add_argument('--break_aspect_ratio', '--break-aspect-ratio', action='store_true')
|
||||
parser.add_argument('--output', default=None)
|
||||
parser.add_argument('--scale', type=float, default=None)
|
||||
parser.add_argument('--quality', type=int, default=100)
|
||||
parser.add_argument(
|
||||
'patterns',
|
||||
nargs='+',
|
||||
type=str,
|
||||
help='''
|
||||
One or more glob patterns for input files.
|
||||
Uses pipeable to support !c clipboard, !i stdin lines of glob patterns.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--width',
|
||||
type=int,
|
||||
default=None,
|
||||
help='''
|
||||
New width of the image. If --width is omitted and --height is given, then
|
||||
width will be calculated automatically based on the aspect ratio.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--height',
|
||||
type=int,
|
||||
default=None,
|
||||
help='''
|
||||
New width of the image. If --height is omitted and --width is given, then
|
||||
height will be calculated automatically based on the aspect ratio.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--break_aspect_ratio',
|
||||
'--break-aspect-ratio',
|
||||
action='store_true',
|
||||
help='''
|
||||
If provided, the given --width and --height will be used exactly. You will
|
||||
need to provide both --width and --height.
|
||||
|
||||
If omitted, the image will be resized to fit within the bounds provided by
|
||||
--width and --height while preserving its aspect ratio.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--inplace',
|
||||
action='store_true',
|
||||
help='''
|
||||
Overwrite the input files. Cannot be used along with --output.
|
||||
Be careful!
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--nearest',
|
||||
'--nearest_neighbor',
|
||||
'--nearest-neighbor',
|
||||
dest='nearest_neighbor',
|
||||
action='store_true',
|
||||
help='''
|
||||
If provided, use nearest-neighbor scaling to preserve pixelated images.
|
||||
If omitted, use antialiased scaling.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--only_shrink',
|
||||
'--only-shrink',
|
||||
action='store_true',
|
||||
help='''
|
||||
If the input image is smaller than the requested dimensions, do nothing.
|
||||
Useful when globbing in a directory with many differently sized images.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--output',
|
||||
default=None,
|
||||
help='''
|
||||
A string that controls the output filename format. Suppose the input file
|
||||
was myphoto.jpg. You can use these variables in your format string:
|
||||
{base} = myphoto
|
||||
{filename} = myphoto.jpg
|
||||
{width} = an integer
|
||||
{height} = an integer
|
||||
{extension} = .jpg
|
||||
|
||||
You may omit {extension} from your format string and it will automatically
|
||||
be added to the end, unless you already provided a different extension.
|
||||
|
||||
If your format string only designates a basename, output files will go to
|
||||
the same directory as the corresponding input file. If your string contains
|
||||
path separators, all output files will go to that directory.
|
||||
The directory part is not formatted with the variables.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--scale',
|
||||
type=float,
|
||||
default=None,
|
||||
help='''
|
||||
Scale the image by this factor, where 1.00 is regular size.
|
||||
Use this option instead of --width, --height.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--quality',
|
||||
type=int,
|
||||
default=100,
|
||||
help='''
|
||||
JPEG compression quality.
|
||||
'''
|
||||
)
|
||||
parser.set_defaults(func=resize_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, __doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
37
retry.py
37
retry.py
|
@ -2,6 +2,7 @@ import argparse
|
|||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from voussoirkit import betterhelp
|
||||
|
||||
class NoMoreRetries(Exception):
|
||||
pass
|
||||
|
@ -43,15 +44,39 @@ def retry_argparse(args):
|
|||
)
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
Run a command line command multiple times until it returns 0.
|
||||
''',
|
||||
)
|
||||
|
||||
parser.add_argument('command', nargs='+')
|
||||
parser.add_argument('--limit', type=int, default=None)
|
||||
parser.add_argument('--sleep', type=float, default=None)
|
||||
parser.add_argument(
|
||||
'command',
|
||||
nargs='+',
|
||||
help='''
|
||||
A command line command. You may need to put this after -- to avoid
|
||||
confusion with arguments to this program.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--limit',
|
||||
type=int,
|
||||
default=None,
|
||||
help='''
|
||||
Maximum number of retries before giving up.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--sleep',
|
||||
type=float,
|
||||
default=None,
|
||||
help='''
|
||||
Number of seconds of sleep between each retry.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=retry_argparse)
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
return args.func(args)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
67
shortcut.py
67
shortcut.py
|
@ -38,29 +38,6 @@ def shortcut(lnk_name, target, start_in=None, icon=None):
|
|||
shortcut.write()
|
||||
return lnk
|
||||
|
||||
DOCSTRING = '''
|
||||
shortcut
|
||||
========
|
||||
|
||||
> shortcut lnk_path target <flags>
|
||||
|
||||
lnk_path:
|
||||
The filepath of the lnk file you want to create.
|
||||
|
||||
target:
|
||||
The filepath of the target file and any additional arguments separated
|
||||
by spaces. If you want to include an argument that starts with hyphens,
|
||||
consider putting this last and use `--` to indicate the end of named
|
||||
arguments. For example:
|
||||
> shortcut game.lnk --icon game.ico -- javaw.exe -jar game.jar
|
||||
|
||||
--start-in:
|
||||
Directory to use as CWD for the program.
|
||||
|
||||
--icon:
|
||||
Path to an .ico file.
|
||||
'''
|
||||
|
||||
def shortcut_argparse(args):
|
||||
try:
|
||||
lnk = shortcut(
|
||||
|
@ -77,14 +54,48 @@ def shortcut_argparse(args):
|
|||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.examples = [
|
||||
'game.lnk --icon game.ico -- javaw.exe -jar game.jar',
|
||||
'game.lnk --icon game.ico -- 4 8 6',
|
||||
]
|
||||
|
||||
parser.add_argument('lnk_name')
|
||||
parser.add_argument('target', nargs='+')
|
||||
parser.add_argument('--start_in', '--start-in', '--startin', default=None)
|
||||
parser.add_argument('--icon', default=None)
|
||||
parser.add_argument(
|
||||
'lnk_name',
|
||||
help='''
|
||||
The filepath of the lnk file you want to create.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'target',
|
||||
nargs='+',
|
||||
type=int,
|
||||
help='''
|
||||
The filepath of the target file and any additional arguments separated
|
||||
by spaces. If you want to include an argument that starts with hyphens,
|
||||
consider putting this last and use `--` to indicate the end of named
|
||||
arguments, since they might otherwise be mistaken for arguments to this
|
||||
program.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--start_in',
|
||||
'--start-in',
|
||||
'--startin',
|
||||
default=None,
|
||||
help='''
|
||||
Directory to use as CWD for the program.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--icon',
|
||||
default=None,
|
||||
help='''
|
||||
Path to an .ico file.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=shortcut_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, DOCSTRING)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
34
stitch.py
34
stitch.py
|
@ -2,6 +2,7 @@ import PIL.Image
|
|||
import argparse
|
||||
import sys
|
||||
|
||||
from voussoirkit import betterhelp
|
||||
from voussoirkit import pathclass
|
||||
from voussoirkit import pipeable
|
||||
from voussoirkit import sentinel
|
||||
|
@ -49,14 +50,35 @@ def main(argv):
|
|||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
parser.add_argument('image_files', nargs='+')
|
||||
parser.add_argument('--output', required=True)
|
||||
parser.add_argument('--horizontal', action='store_true')
|
||||
parser.add_argument('--vertical', action='store_true')
|
||||
parser.add_argument('--gap', type=int, default=0)
|
||||
parser.add_argument(
|
||||
'--output',
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
'--horizontal',
|
||||
action='store_true',
|
||||
help='''
|
||||
Stitch the images together horizontally.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--vertical',
|
||||
action='store_true',
|
||||
help='''
|
||||
Stitch the images together vertically.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--gap',
|
||||
type=int,
|
||||
default=0,
|
||||
help='''
|
||||
This many pixels of transparent gap between each row / column.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=stitch_argparse)
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
return args.func(args)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
86
svgrender.py
86
svgrender.py
|
@ -1,29 +1,3 @@
|
|||
'''
|
||||
svgrender
|
||||
=========
|
||||
|
||||
Calls the Inkscape command line to render svg files to png. A link to inkscape
|
||||
should be on your PATH.
|
||||
|
||||
> svgrender svg_file scales <flags>
|
||||
|
||||
scales:
|
||||
One or more integers. Each integer will be the size of one output file.
|
||||
|
||||
flags:
|
||||
--destination:
|
||||
A path to a directory where the png files should be saved. By default,
|
||||
they go to the same folder as the svg file.
|
||||
|
||||
--y:
|
||||
By default, the scales control the width of the output image.
|
||||
Pass this if you want the scales to control the height.
|
||||
|
||||
--basename-only:
|
||||
By default, the png filenames will have suffixes like _{scale}.
|
||||
Pass this if you want the png to have the same name as the svg file.
|
||||
Naturally, this only works if you're only using a single scale.
|
||||
'''
|
||||
import argparse
|
||||
import glob
|
||||
import os
|
||||
|
@ -79,11 +53,10 @@ def svgrender(filepath, scales, destination, scale_suffix=True, axis='x'):
|
|||
|
||||
def svgrender_argparse(args):
|
||||
svg_paths = glob.glob(args.svg_filepath)
|
||||
scales = [int(x) for x in args.scales]
|
||||
for svg_path in svg_paths:
|
||||
svgrender(
|
||||
svg_path,
|
||||
scales,
|
||||
scales=args.scales,
|
||||
destination=args.destination,
|
||||
scale_suffix=args.scale_suffix,
|
||||
axis='y' if args.y else 'x',
|
||||
|
@ -93,16 +66,57 @@ def svgrender_argparse(args):
|
|||
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
parser.add_argument('svg_filepath')
|
||||
parser.add_argument('scales', nargs='+')
|
||||
parser.add_argument('--destination', default=None)
|
||||
parser.add_argument('--y', dest='y', action='store_true')
|
||||
parser.add_argument('--basename_only', '--basename-only', dest='scale_suffix', action='store_false')
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
Calls the Inkscape command line to render svg files to png. A link to Inkscape
|
||||
should be on your PATH.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'svg_filepath',
|
||||
help='''
|
||||
Input svg file to be rendered.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'scales',
|
||||
type=int,
|
||||
nargs='+',
|
||||
help='''
|
||||
One or more integers. Each integer will be the size of one output file.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--destination',
|
||||
default=None,
|
||||
help='''
|
||||
A path to a directory where the png files should be saved. By default,
|
||||
they go to the same folder as the svg file.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--y',
|
||||
dest='y',
|
||||
action='store_true',
|
||||
help='''
|
||||
By default, the scales control the width of the output image.
|
||||
Pass this if you want the scales to control the height.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--basename_only',
|
||||
'--basename-only',
|
||||
dest='scale_suffix',
|
||||
action='store_false',
|
||||
help='''
|
||||
By default, the png filenames will have suffixes like _{scale}.
|
||||
Pass this if you want the png to have the same name as the svg file.
|
||||
Naturally, this only works if you're only using a single scale.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=svgrender_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, __doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
||||
|
|
|
@ -1,21 +1,3 @@
|
|||
'''
|
||||
tempeditor
|
||||
==========
|
||||
|
||||
This program allows you to use your preferred text editor as an intermediate
|
||||
step in a processing pipeline. The user will use the text editor to edit a temp
|
||||
file, and when they close the editor the contents of the temp file will be sent
|
||||
to stdout.
|
||||
|
||||
Command line usage:
|
||||
|
||||
> tempeditor [--text X]
|
||||
|
||||
--text X:
|
||||
The initial text in the document.
|
||||
Uses pipeable to support !c clipboard, !i stdin.
|
||||
If not provided, the user starts with a blank document.
|
||||
'''
|
||||
import argparse
|
||||
import os
|
||||
import shlex
|
||||
|
@ -90,12 +72,29 @@ def tempeditor_argparse(args):
|
|||
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
This program allows you to use your preferred text editor as an
|
||||
intermediate step in a processing pipeline. The user will use the text
|
||||
editor to edit a temp file, and when they close the editor the contents
|
||||
of the temp file will be sent to stdout.
|
||||
''',
|
||||
)
|
||||
|
||||
parser.add_argument('--text', dest='initial_text', default=None)
|
||||
parser.add_argument(
|
||||
'--text',
|
||||
dest='initial_text',
|
||||
default=None,
|
||||
type=str,
|
||||
help='''
|
||||
The initial text in the document.
|
||||
Uses pipeable to support !c clipboard, !i stdin.
|
||||
If not provided, the user starts with a blank document.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=tempeditor_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, __doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
102
threaded_dl.py
102
threaded_dl.py
|
@ -1,40 +1,3 @@
|
|||
'''
|
||||
threaded_dl
|
||||
===========
|
||||
|
||||
> threaded_dl links thread_count filename_format <flags>
|
||||
|
||||
links:
|
||||
The name of a file containing links to download, one per line.
|
||||
Uses pipeable to support !c clipboard, !i stdin lines of urls.
|
||||
|
||||
thread_count:
|
||||
Integer number of threads to use for downloading.
|
||||
|
||||
filename_format:
|
||||
A string that controls the names of the downloaded files. Uses Python's
|
||||
brace-style formatting. Available formatters are:
|
||||
- {basename}: The name of the file as indicated by the URL.
|
||||
E.g. example.com/image.jpg -> image.jpg
|
||||
- {extension}: The extension of the file as indicated by the URL, including
|
||||
the dot. E.g. example.com/image.jpg -> .jpg
|
||||
- {index}: The index of this URL within the sequence of all downloaded URLs.
|
||||
Starts from 0.
|
||||
- {now}: The unix timestamp at which this download job was started. It might
|
||||
be ugly but at least it's unambiguous when doing multiple download batches
|
||||
with similar filenames.
|
||||
|
||||
flags:
|
||||
--bytespersecond X:
|
||||
Limit the overall download speed to X bytes per second. Uses
|
||||
bytestring.parsebytes to support strings like "1m", "500k", "2 mb", etc.
|
||||
|
||||
--headers X:
|
||||
;
|
||||
|
||||
--timeout X:
|
||||
Integer number of seconds to use as HTTP request timeout for each download.
|
||||
'''
|
||||
import argparse
|
||||
import ast
|
||||
import os
|
||||
|
@ -194,7 +157,7 @@ def threaded_dl(
|
|||
ui_thread.join()
|
||||
|
||||
def ui_thread_func(meter, pool, stop_event):
|
||||
if pipeable.OUT_PIPE:
|
||||
if pipeable.stdout_pipe():
|
||||
return
|
||||
|
||||
while not stop_event.is_set():
|
||||
|
@ -236,17 +199,62 @@ def threaded_dl_argparse(args):
|
|||
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
parser.add_argument('url_file')
|
||||
parser.add_argument('thread_count', type=int)
|
||||
parser.add_argument('filename_format', nargs='?', default='{now}_{index}_{basename}')
|
||||
parser.add_argument('--bytespersecond', default=None)
|
||||
parser.add_argument('--timeout', default=15)
|
||||
parser.add_argument('--headers', nargs='+', default=None)
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
'url_file',
|
||||
metavar='links',
|
||||
help='''
|
||||
The name of a file containing links to download, one per line.
|
||||
Uses pipeable to support !c clipboard, !i stdin lines of urls.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'thread_count',
|
||||
type=int,
|
||||
help='''
|
||||
Integer number of threads to use for downloading.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'filename_format',
|
||||
nargs='?',
|
||||
type=str,
|
||||
default='{now}_{index}_{basename}',
|
||||
help='''
|
||||
A string that controls the names of the downloaded files. Uses Python's
|
||||
brace-style formatting. Available formatters are:
|
||||
- {basename}: The name of the file as indicated by the URL.
|
||||
E.g. example.com/image.jpg -> image.jpg
|
||||
- {extension}: The extension of the file as indicated by the URL, including
|
||||
the dot. E.g. example.com/image.jpg -> .jpg
|
||||
- {index}: The index of this URL within the sequence of all downloaded URLs.
|
||||
Starts from 0.
|
||||
- {now}: The unix timestamp at which this download job was started. It might
|
||||
be ugly but at least it's unambiguous when doing multiple download batches
|
||||
with similar filenames.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--bytespersecond',
|
||||
default=None,
|
||||
help='''
|
||||
Limit the overall download speed to X bytes per second. Uses
|
||||
bytestring.parsebytes to support strings like "1m", "500k", "2 mb", etc.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--timeout',
|
||||
default=15,
|
||||
help='''
|
||||
Integer number of seconds to use as HTTP request timeout for each download.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--headers', nargs='+', default=None,
|
||||
)
|
||||
parser.set_defaults(func=threaded_dl_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, __doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
30
touch.py
30
touch.py
|
@ -1,10 +1,8 @@
|
|||
'''
|
||||
Create the file, or update the last modified timestamp.
|
||||
'''
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
from voussoirkit import betterhelp
|
||||
from voussoirkit import pipeable
|
||||
from voussoirkit import winglob
|
||||
|
||||
|
@ -24,13 +22,31 @@ def touch_argparse(args):
|
|||
return 0
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
Create blank files, or update mtimes of existing files.
|
||||
''',
|
||||
)
|
||||
|
||||
parser.add_argument('patterns', nargs='+')
|
||||
parser.add_argument(
|
||||
'patterns',
|
||||
type=str,
|
||||
nargs='+',
|
||||
help='''
|
||||
One or more filenames or glob patterns.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--sleep',
|
||||
type=float,
|
||||
default=None,
|
||||
help='''
|
||||
Sleep for this many seconds between touching each file.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=touch_argparse)
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
return args.func(args)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
|
@ -1,16 +1,3 @@
|
|||
'''
|
||||
wait_for_internet
|
||||
=================
|
||||
|
||||
This program will block until internet access is available. It can be useful to
|
||||
run this program before running another program that expects an internet
|
||||
connection.
|
||||
|
||||
> wait_for_internet timeout
|
||||
|
||||
timeout:
|
||||
An integer number of seconds, after which to give up and return 1.
|
||||
'''
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
|
@ -27,12 +14,23 @@ def wait_for_internet_argparse(args):
|
|||
|
||||
@vlogging.main_decorator
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
parser.add_argument('timeout', type=int)
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
This program will block until internet access is available. It can be useful to
|
||||
run this program before running another program that expects an internet
|
||||
connection.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'timeout',
|
||||
type=int,
|
||||
help='''
|
||||
An integer number of seconds, after which to give up and return 1.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=wait_for_internet_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, __doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
|
@ -1,21 +1,3 @@
|
|||
'''
|
||||
watchforlinks
|
||||
=============
|
||||
|
||||
This program will continuously watch your clipboard for URLs and save them to
|
||||
individual files. The files will have randomly generated names. The current
|
||||
contents of your clipboard will be erased.
|
||||
|
||||
> watchforlinks [extension] <flags>
|
||||
|
||||
extension:
|
||||
The saved files will have this extension.
|
||||
If not provided, the default is "generic".
|
||||
|
||||
flags:
|
||||
--regex X:
|
||||
A regex pattern. Only URLs that match this pattern will be saved.
|
||||
'''
|
||||
import argparse
|
||||
import pyperclip
|
||||
import re
|
||||
|
@ -64,13 +46,35 @@ def watchforlinks_argparse(args):
|
|||
return 0
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser = argparse.ArgumentParser(
|
||||
description='''
|
||||
This program will continuously watch your clipboard for http:// and
|
||||
https:// URLs and save them to individual files. The files will have
|
||||
randomly generated names. The current contents of your clipboard will
|
||||
be erased.
|
||||
''',
|
||||
)
|
||||
|
||||
parser.add_argument('extension', nargs='?', default='generic')
|
||||
parser.add_argument('--regex', default=None)
|
||||
parser.add_argument(
|
||||
'extension',
|
||||
nargs='?',
|
||||
type=str,
|
||||
default='generic',
|
||||
help='''
|
||||
The saved files will have this extension.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--regex',
|
||||
type=str,
|
||||
default=None,
|
||||
help='''
|
||||
A regex pattern. Only URLs that match this pattern will be saved.
|
||||
''',
|
||||
)
|
||||
parser.set_defaults(func=watchforlinks_argparse)
|
||||
|
||||
return betterhelp.single_main(argv, parser, __doc__)
|
||||
return betterhelp.go(parser, argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
|
|
Loading…
Reference in a new issue