diff --git a/linux/macosx_cleanup.md b/linux/macosx_cleanup.md new file mode 100644 index 0000000..1902e3c --- /dev/null +++ b/linux/macosx_cleanup.md @@ -0,0 +1,21 @@ +# MacOS X Cleanup + +When you migrate from MacOS X to Linux, you will bring some unwanted dot files with you. + +This is a guide to finding and deleting those files. + +## Find Files +``` +find . \( -name ".DS_Store" -o -name "._*" -o -name ".localized" \) -type f -print +find . \( -name ".fseventsd" -o -name ".Spotlight-V100" -o -name ".Trashes" -o -name ".TemporaryItems" -o -name "__MACOSX" \) -type d -print +``` + +## Delete Files + +``` +find . \( -name ".DS_Store" -o -name "._*" -o -name ".localized" \) -type f -delete -print + +# Force-delete the directories (this handles non-empty ones) +find . \( -name ".fseventsd" -o -name ".Spotlight-V100" -o -name ".Trashes" -o -name ".TemporaryItems" -o -name "__MACOSX" \) -type d -exec rm -rf {} + -print +``` + diff --git a/python/apps/xv.py b/python/apps/xv.py index 68ddecf..503909f 100644 --- a/python/apps/xv.py +++ b/python/apps/xv.py @@ -1,6 +1,15 @@ -""" xv.py - Display an image. +r""" +usage: xv.py [-h] filename -Notes: +A simple image viewer and a tribute to the original xv image display editing program for the X Window System. + +positional arguments: + filename Image file to display. + +options: + -h, --help show this help message and exit + +NOTES: sudo apt-get install python3-pip sudo pip3 install pillow sudo apt-get install python3-pil.imagetk @@ -53,16 +62,16 @@ def __init__(self, filename, master=None): label.pack(side='bottom', fill='both', expand='yes') -def main(options): - image_display = ImageDisplay(options.filename) +def main(args): + image_display = ImageDisplay(args.filename) image_display.mainloop() return 0 if __name__ == '__main__': - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(description='A simple image viewer and a tribute to the original xv image display editing program for the X Window System.') parser.add_argument( 'filename', - help='Display an image.') + help='Image file to display.') - options = parser.parse_args() - sys.exit(main(options)) + args = parser.parse_args() + sys.exit(main(args)) diff --git a/python/lib/tests/README.md b/python/lib/tests/README.md index 951c8e4..5776a92 100644 --- a/python/lib/tests/README.md +++ b/python/lib/tests/README.md @@ -3,7 +3,7 @@ To run the unit manually tests from the command line: ``` -$ cd code\python +$ cd code/python $ python -m unittest discover -v -s ./lib/tests ``` diff --git a/python/ppt/ascii.py b/python/ppt/ascii.py new file mode 100644 index 0000000..4165cfa --- /dev/null +++ b/python/ppt/ascii.py @@ -0,0 +1,127 @@ +r""" +usage: ascii.py [-h] [-l] + +Prints an ASCII table similar to "man ascii" + +options: + -h, --help show this help message and exit + -l, --long Long version of the ASCII table. +""" + +import sys +import argparse + +long_text = r""" + Dec Hex Char Dec Hex Char + ──────────────────────────────────────────────────────────── + 0 00 NUL '\0' (null character) 64 40 @ + 1 01 SOH (start of heading) 65 41 A + 2 02 STX (start of text) 66 42 B + 3 03 ETX (end of text) 67 43 C + 4 04 EOT (end of transmission) 68 44 D + 5 05 ENQ (enquiry) 69 45 E + 6 06 ACK (acknowledge) 70 46 F + 7 07 BEL '\a' (bell) 71 47 G + 8 08 BS '\b' (backspace) 72 48 H + 9 09 HT '\t' (horizontal tab) 73 49 I + 10 0A LF '\n' (new line) 74 4A J + 11 0B VT '\v' (vertical tab) 75 4B K + 12 0C FF '\f' (form feed) 76 4C L + 13 0D CR '\r' (carriage ret) 77 4D M + 14 0E SO (shift out) 78 4E N + 15 0F SI (shift in) 79 4F O + 16 10 DLE (data link escape) 80 50 P + 17 11 DC1 (device control 1) 81 51 Q + 18 12 DC2 (device control 2) 82 52 R + 19 13 DC3 (device control 3) 83 53 S + 20 14 DC4 (device control 4) 84 54 T + 21 15 NAK (negative ack.) 85 55 U + 22 16 SYN (synchronous idle) 86 56 V + 23 17 ETB (end of trans. blk) 87 57 W + 24 18 CAN (cancel) 88 58 X + 25 19 EM (end of medium) 89 59 Y + 26 1A SUB (substitute) 90 5A Z + 27 1B ESC (escape) 91 5B [ + 28 1C FS (file separator) 92 5C \ '\\' + 29 1D GS (group separator) 93 5D ] + 30 1E RS (record separator) 94 5E ^ + 31 1F US (unit separator) 95 5F _ + 32 20 SPACE 96 60 ` + 33 21 ! 97 61 a + 34 22 " 98 62 b + 35 23 # 99 63 c + 36 24 $ 100 64 d + 37 25 % 101 65 e + 38 26 & 102 66 f + 39 27 ' 103 67 g + 40 28 ( 104 68 h + 41 29 ) 105 69 i + 42 2A * 106 6A j + 43 2B + 107 6B k + 44 2C , 108 6C l + 45 2D - 109 6D m + 46 2E . 110 6E n + 47 2F / 111 6F o + 48 30 0 112 70 p + 49 31 1 113 71 q + 50 32 2 114 72 r + 51 33 3 115 73 s + 52 34 4 116 74 t + 53 35 5 117 75 u + 54 36 6 118 76 v + 55 37 7 119 77 w + 56 38 8 120 78 x + 57 39 9 121 79 y + 58 3A : 122 7A z + 59 3B ; 123 7B { + 60 3C < 124 7C | + 61 3D = 125 7D } + 62 3E > 126 7E ~ + 63 3F ? 127 7F DEL""" + +short_text = r""" + 2 3 4 5 6 7 + ------------- + 0: 0 @ P ` p + 1: ! 1 A Q a q + 2: " 2 B R b r + 3: # 3 C S c s + 4: $ 4 D T d t + 5: % 5 E U e u + 6: & 6 F V f v + 7: ' 7 G W g w + 8: ( 8 H X h x + 9: ) 9 I Y i y + A: * : J Z j z + B: + ; K [ k { + C: , < L \ l | + D: - = M ] m } + E: . > N ^ n ~ + F: / ? O _ o DEL""" + + +def main(args): + if args.long: + print(long_text) + else: + print(short_text) + return 0 + + +def get_parser(): + parser = argparse.ArgumentParser( + description="Prints an ASCII table similar to \"man ascii\".") + parser.add_argument( + '-l', + '--long', + dest='long', + help='Long version of the ASCII table.', + action='store_true', + default=False) + return parser + + +if __name__ == '__main__': + parser = get_parser() + args = parser.parse_args() + sys.exit(main(args)) diff --git a/python/ppt/cal.py b/python/ppt/cal.py index 572464c..aee85ad 100644 --- a/python/ppt/cal.py +++ b/python/ppt/cal.py @@ -1,17 +1,17 @@ -""" cal - display a calendar +r""" +usage: cal.py [-h] [year ...] -Synopsis: - - cal.py [year] - -Description: - - cal displays a simple calendar. + Displays a simple calendar. With no argument, displays the calendar for the month of the current year. With argument, displays the calendar for the specified year. +positional arguments: + year Display a calendar for this year. + +options: + -h, --help show this help message and exit """ import argparse @@ -20,24 +20,30 @@ import datetime -def main(options): +def main(args): cal = calendar.TextCalendar(calendar.SUNDAY) - if not options.year: + if not args.year: now = datetime.datetime.now() cal.prmonth(now.year, now.month) else: - cal.pryear(int(options.year[0])) + cal.pryear(int(args.year[0])) return 0 if __name__ == '__main__': - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser( + description=""" Displays a simple calendar. + + With no argument, displays the calendar for the month of the current year. + + With argument, displays the calendar for the specified year.""", + formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument( 'year', - help='Display a calendar for the specified year.', + help='Display a calendar for this year.', nargs='*') - options = parser.parse_args() - sys.exit(main(options)) + args = parser.parse_args() + sys.exit(main(args)) diff --git a/python/ppt/colordiff.py b/python/ppt/colordiff.py index ce883dd..ab1819a 100644 --- a/python/ppt/colordiff.py +++ b/python/ppt/colordiff.py @@ -12,13 +12,13 @@ import sys import html -def main(options): +def main(args): result = 0 try: - with open(options.filename, 'r') as f: + with open(args.filename, 'r') as f: lines = f.readlines() except FileNotFoundError: - print(f"File '{options.filename}' not found.") + print(f"File '{args.filename}' not found.") resulte = 1 else: # Start HTML output @@ -63,6 +63,6 @@ def main(options): help='Name of file.', nargs='?') - options = parser.parse_args() + args = parser.parse_args() - sys.exit(main(options)) + sys.exit(main(args)) diff --git a/python/ppt/diff.py b/python/ppt/diff.py index 4434330..e457eac 100644 --- a/python/ppt/diff.py +++ b/python/ppt/diff.py @@ -1,3 +1,29 @@ +r""" +usage: diff.py [-h] [-i INCLUDE] [-e EXCLUDE] [-s SUBDIR] [-b] [-r] [-q] arg arg + +Produces differences in the POSIX default format (see http://www.unix.com/man-page/POSIX/1posix/diff/), which is the same as +the Gnu diff "normal format" (see http://www.gnu.org/software/diffutils/manual/diffutils.html#Normal). + +This program is based on Python's difflib sample program, Tools/Scripts/diff.py. + + +positional arguments: + arg Names of files or directories to compare. + +options: + -h, --help show this help message and exit + -i INCLUDE, --include INCLUDE + Inclusion regex to apply to file path when performing recursive directory comparisons. + -e EXCLUDE, --exclude EXCLUDE + Exclusion regex to apply to file path when performing recursive directory comparisons. + -s SUBDIR, --subdir SUBDIR + Subdirectory exlusion regex to apply to subdirectory names when performing recursive directory + comparisons. + -b, --binary Compare and report on binary files. + -r, --recursive Recursively compare subdirectories. + -q, --brief When performing recursive directory comparisons, only report when files are different. +""" + import argparse import sys import filecmp @@ -83,16 +109,16 @@ def compare_text_files(file1, file2): return pdiff(file1content, file2content) -def compare_directories(options, dir1, dir2): +def compare_directories(args, dir1, dir2): dc = filecmp.dircmp(dir1, dir2) incre = None - if options.include: - incre = re.compile(options.include) + if args.include: + incre = re.compile(args.include) excre = None - if options.exclude: - excre = re.compile(options.exclude) + if args.exclude: + excre = re.compile(args.exclude) # Apply inclusion/exclusion to "only in" files too. # Only print the header if there is at least one file that passes @@ -150,7 +176,7 @@ def compare_directories(options, dir1, dir2): printed_title = False try: for difference in compare_text_files(file1, file2): - if options.brief: + if args.brief: # Found a difference. Report it. No need to continue. print('%s %s : files are different' % (file1, file2)) break @@ -165,13 +191,13 @@ def compare_directories(options, dir1, dir2): sys.stdout.writelines(difference) except UnicodeDecodeError: # Binary file. - if options.binary and not compare_binary_files(file1, file2): + if args.binary and not compare_binary_files(file1, file2): print('%s %s : binary files are different' % (file1, file2)) # Descend down the tree into common directories. subre = None - if options.subdir: - subre = re.compile(options.subdir) + if args.subdir: + subre = re.compile(args.subdir) for common_dir in dc.common_dirs: # Skip this subdirectory if subdirectory filter is in effect and # subdirectory matches. Notice the fullmatch() here, not search(). @@ -179,12 +205,12 @@ def compare_directories(options, dir1, dir2): continue compare_directories( - options, + args, os.path.join(dc.left, common_dir), os.path.join(dc.right, common_dir)) -def main(options): +def main(args): """ This program is based on Python's difflib sample program, Tools/Scripts/diff.py. @@ -193,13 +219,13 @@ def main(options): See http://www.unix.com/man-page/POSIX/1posix/diff/ """ - if not options.recursive: - file1 = options.arg[0] + if not args.recursive: + file1 = args.arg[0] if not os.path.isfile(file1): print('ERROR: %s is not a file' % (file1)) return 1 - file2 = options.arg[1] + file2 = args.arg[1] if not os.path.isfile(file2): print('ERROR: %s is not a file' % (file2)) return 1 @@ -207,15 +233,23 @@ def main(options): try: sys.stdout.writelines(compare_text_files(file1, file2)) except UnicodeDecodeError: - if options.binary and not compare_binary_files(file1, file2): + if args.binary and not compare_binary_files(file1, file2): print('%s %s : binary files are different' % (file1, file2)) else: - compare_directories(options, options.arg[0], options.arg[1]) + compare_directories(args, args.arg[0], args.arg[1]) return 0 def get_parser(): - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser( + description="""Produces differences in the POSIX default format +(see http://www.unix.com/man-page/POSIX/1posix/diff/), +which is the same as the Gnu diff "normal format" +(see http://www.gnu.org/software/diffutils/manual/diffutils.html#Normal). + +This program is based on Python's difflib sample program, Tools/Scripts/diff.py. + """, + formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument( '-i', @@ -269,7 +303,7 @@ def get_parser(): if __name__ == '__main__': parser = get_parser() - options = parser.parse_args() + args = parser.parse_args() - sys.exit(main(options)) + sys.exit(main(args)) diff --git a/python/ppt/find.py b/python/ppt/find.py index 708ebf7..2a3ccec 100644 --- a/python/ppt/find.py +++ b/python/ppt/find.py @@ -1,4 +1,4 @@ -""" find - Find files recursively and optionally filter with regular expression. +r""" find - Find files recursively and optionally filter with regular expression. usage: find.py [-h] [-a] [-e EXCLUDE] path [regex ...] @@ -24,8 +24,8 @@ def find(path, aall, regex): # Exclusion regular expression. excre = None - if options.exclude: - excre = re.compile(options.exclude) + if args.exclude: + excre = re.compile(args.exclude) for root, dirs, files in os.walk(path): if excre is not None: @@ -49,13 +49,13 @@ def find(path, aall, regex): print(os.path.join(root, f)) -def main(options): - if not os.path.isdir(options.path): - print('ERROR: %s is not a directory' % options.path()) +def main(args): + if not os.path.isdir(args.path): + print('ERROR: %s is not a directory' % args.path()) else: - if options.ignore_case: - options.regex = '(?i)' + options.regex - find(options.path, options.aall, options.regex) + if args.ignore_case: + args.regex = '(?i)' + args.regex + find(args.path, args.aall, args.regex) if __name__ == '__main__': parser = argparse.ArgumentParser() @@ -92,6 +92,6 @@ def main(options): nargs='?', default=None) - options = parser.parse_args() + args = parser.parse_args() - sys.exit(main(options)) + sys.exit(main(args)) diff --git a/python/ppt/head.py b/python/ppt/head.py index 094831f..0286601 100644 --- a/python/ppt/head.py +++ b/python/ppt/head.py @@ -1,11 +1,12 @@ -""" head - Print the first line(s) of a file or standard input. +r""" +usage: head.py [-h] [-n LINES] [filename] -usage: head.py [-h] [-n LINES] [arg [arg ...]] +Print the first line(s) of a file or standard input. positional arguments: - arg Name of file. + filename Name of file. -optional arguments: +options: -h, --help show this help message and exit -n LINES, --lines LINES Print the first LINES lines of the file. @@ -21,14 +22,14 @@ def printlines(f, n): print(f.readline().strip()) -def main(numlines, options): +def main(numlines, args): result = 0 - if (numlines is not None) and (options.lines is not None): + if (numlines is not None) and (args.lines is not None): # Specified both. print('ERROR: cannot specify both - and -n/--lines') result = 1 else: - if (numlines is None) and (options.lines is None): + if (numlines is None) and (args.lines is None): # Specified neither. # Default is 10 according to the man page. numlines = 10 @@ -37,16 +38,16 @@ def main(numlines, options): # (The neither case is handled above). if numlines is not None: pass - elif options.lines is not None: - numlines = options.lines + elif args.lines is not None: + numlines = args.lines - if not options.filename: + if not args.filename: # No filename given on the command line. # Process data directly from stdin. printlines(sys.stdin, numlines) else: # Process data from the file. - with open(options.filename, 'r') as f: + with open(args.filename, 'r') as f: printlines(f, numlines) return result @@ -63,7 +64,7 @@ def main(numlines, options): numlines = int(m.group('digits')) del(sys.argv[1]) - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(description='Print the first line(s) of a file or standard input.') parser.add_argument( '-n', @@ -78,6 +79,6 @@ def main(numlines, options): help='Name of file.', nargs='?') - options = parser.parse_args() + args = parser.parse_args() - sys.exit(main(numlines, options)) + sys.exit(main(numlines, args)) diff --git a/python/ppt/html2text.py b/python/ppt/html2text.py index b34d9f2..02c77d4 100644 --- a/python/ppt/html2text.py +++ b/python/ppt/html2text.py @@ -1,13 +1,17 @@ -""" html2text - Display a web page or HTML file as text +r""" +usage: html2text.py [-h] [-f] source -Synopsis: +Display a web page or HTML file as text. - html2text.py [-f] source +positional arguments: + source Source URL or filename (use -f for file). -Description: +options: + -h, --help show this help message and exit + -f, --file Read input from this file. - html2text displays a web page or HTML file as text. +NOTES: The argument is the URL of a web page to retrieve, or if you use the -f option, the name of an HTML file. @@ -20,14 +24,14 @@ from bs4 import BeautifulSoup import textwrap -def main(options): +def main(args): # Get HTML page or file. html = '' - if options.file: - with open(options.source[0], 'r') as myfile: + if args.file: + with open(args.source[0], 'r') as myfile: html = myfile.read() else: - html = urllib.urlopen(options.source[0]).read() + html = urllib.urlopen(args.source[0]).read() # Filter out non-utf-8 characters in web pages which advertise utf-8 # but then provide characters outside of utf-8. @@ -55,13 +59,13 @@ def main(options): # Pretty print it. for chunk in chunks: if chunk: - print '%s\n' % ('\n'.join(textwrap.wrap(chunk))) + [print(x) for x in '\n'.join(textwrap.wrap(chunk))] return 0 if __name__ == '__main__': - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(description='Display a web page or HTML file as text.') parser.add_argument( '-f', '--file', @@ -73,5 +77,5 @@ def main(options): 'source', help='Source URL or filename (use -f for file).', nargs=1) - options = parser.parse_args() - sys.exit(main(options)) + args = parser.parse_args() + sys.exit(main(args)) diff --git a/python/ppt/lorem.py b/python/ppt/lorem.py new file mode 100644 index 0000000..f3839f3 --- /dev/null +++ b/python/ppt/lorem.py @@ -0,0 +1,101 @@ +r""" +usage: lorem.py [-h] bytes + +Generate lorem ipsum text of exactly the specified byte length. + +positional arguments: + bytes desired length of the output in bytes + +options: + -h, --help show this help message and exit +""" + +import argparse +import random +import sys + +# Classic lorem ipsum words (kept small for simplicity) +LOREM_WORDS = [ + 'lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing', + 'elit', 'sed', 'do', 'eiusmod', 'tempor', 'incididunt', 'ut', 'labore', + 'et', 'dolore', 'magna', 'aliqua', 'enim', 'ad', 'minim', 'veniam', + 'quis', 'nostrud', 'exercitation', 'ullamco', 'laboris', 'nisi', + 'aliquip', 'ex', 'ea', 'commodo', 'consequat' +] + +def generate_lorem_bytes(target_bytes: int) -> str: + if target_bytes <= 0: + return '' + + result = [] + current_bytes = 0 + used_newline = False + + while current_bytes < target_bytes: + word = random.choice(LOREM_WORDS) + + # Decide separator: mostly space, sometimes newline (but not too many) + if random.random() < 0.08 and used_newline < 3 and current_bytes > 20: + separator = '\n' + used_newline += 1 + else: + separator = ' ' + + candidate = word + separator + candidate_bytes = len(candidate.encode('utf-8')) + + if current_bytes + candidate_bytes <= target_bytes: + result.append(candidate) + current_bytes += candidate_bytes + else: + # Last piece — fill remaining bytes (may cut word) + remaining = target_bytes - current_bytes + if remaining >= 1: + # Try to take as much of the word as possible + partial = '' + for char in word: + if len((partial + char).encode('utf-8')) <= remaining: + partial += char + else: + break + result.append(partial) + break + + text = ''.join(result).rstrip(' \n') + + # Make sure we didn't undershoot too much — pad with spaces if needed + final_bytes = len(text.encode('utf-8')) + if final_bytes < target_bytes: + text += ' ' * (target_bytes - final_bytes) + + return text + + +def main(bytes): + # Generate the text. + text = generate_lorem_bytes(bytes) + + # Output the result. + print(text, end='') # no extra newline at end + + return 0 + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description='Generate lorem ipsum text of exactly the specified byte length.') + parser.add_argument( + 'bytes', + type=int, + help='desired length of the output in bytes') + + args = parser.parse_args() + + if args.bytes < 0: + parser.error('number of bytes cannot be negative') + + if args.bytes == 0: + print('', end='') + sys.exit(0) + + sys.exit(main(args.bytes)) diff --git a/python/ppt/mac2dos.py b/python/ppt/mac2dos.py index 9ce7388..9fff283 100644 --- a/python/ppt/mac2dos.py +++ b/python/ppt/mac2dos.py @@ -13,12 +13,12 @@ import sys import re -def main(options): - with open(options.filepath, 'rb') as fin: +def main(args): + with open(args.filepath, 'rb') as fin: data = fin.read() newdata = re.sub(b'\r', b'\r\n', data) if newdata != data: - with open(options.filepath, 'wb') as fout: + with open(args.filepath, 'wb') as fout: fout.write(newdata) return 0 @@ -27,5 +27,5 @@ def main(options): parser.add_argument( 'filepath', help='Path to file to convert.') - options = parser.parse_args() - sys.exit(main(options)) + args = parser.parse_args() + sys.exit(main(args)) diff --git a/python/ppt/printenv.py b/python/ppt/printenv.py index 1ca9773..ee120ce 100644 --- a/python/ppt/printenv.py +++ b/python/ppt/printenv.py @@ -26,13 +26,13 @@ import sys -def main(options): +def main(args): # Return 0 on success, 1 if an error occurs. result = 0 - if not options.name: + if not args.name: print('\n'.join([ '%s=%s' % (k, v) for (k, v) in os.environ.items() ])) else: - name = options.name[0] + name = args.name[0] if name not in os.environ: result = 1 else: @@ -47,5 +47,5 @@ def main(options): 'name', help='Print the value of the specified environment variable.', nargs='*') - options = parser.parse_args() - sys.exit(main(options)) + args = parser.parse_args() + sys.exit(main(args)) diff --git a/python/ppt/sort.py b/python/ppt/sort.py index 8c5d843..b56a0d4 100644 --- a/python/ppt/sort.py +++ b/python/ppt/sort.py @@ -1,3 +1,14 @@ +r"""usage: sort.py [-h] [path] + +Sorts the specified file or standard input. + +positional arguments: + path Path to file to be sorted. + +options: + -h, --help show this help message and exit +""" + import argparse import sys @@ -10,14 +21,14 @@ def sort(f): [print(d.strip()) for d in data] -def main(options): - if not options.path: +def main(args): + if not args.path: # No filename given on the command line. # Process data directly from stdin. sort(sys.stdin) else: # Read from the file. - with open(options.path, 'r') as f: + with open(args.path, 'r') as f: sort(f) # If file open fails, we default to reading stdin, @@ -25,10 +36,10 @@ def main(options): return 0 if __name__ == '__main__': - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(description='Sorts the specified file or standard input.') parser.add_argument( 'path', help='Path to file to be sorted.', nargs='?') - options = parser.parse_args() - sys.exit(main(options)) + args = parser.parse_args() + sys.exit(main(args)) diff --git a/python/ppt/tests/testdiff.py b/python/ppt/tests/testdiff.py index 2a8d935..bb50f75 100644 --- a/python/ppt/tests/testdiff.py +++ b/python/ppt/tests/testdiff.py @@ -55,12 +55,12 @@ def test_diff_two_files(self): with warnings.catch_warnings(): warnings.simplefilter('ignore', ResourceWarning) parser = ppt.diff.get_parser() - options = parser.parse_args([ os.path.join(DATA_DIR, 'foo.txt'), os.path.join(DATA_DIR, 'bar.txt') ]) + args = parser.parse_args([ os.path.join(DATA_DIR, 'foo.txt'), os.path.join(DATA_DIR, 'bar.txt') ]) # Capture stdout when running. out = io.StringIO() with redirect_stdout(out): - ppt.diff.main(options) + ppt.diff.main(args) # Compare the results. captured_stdout = out.getvalue() @@ -78,12 +78,12 @@ def test_diff_another_two_files(self): with warnings.catch_warnings(): warnings.simplefilter('ignore', ResourceWarning) parser = ppt.diff.get_parser() - options = parser.parse_args([ os.path.join(DATA_DIR, 'file1.txt'), os.path.join(DATA_DIR, 'file2.txt') ]) + args = parser.parse_args([ os.path.join(DATA_DIR, 'file1.txt'), os.path.join(DATA_DIR, 'file2.txt') ]) # Capture stdout when running. out = io.StringIO() with redirect_stdout(out): - ppt.diff.main(options) + ppt.diff.main(args) # Compare the results. captured_stdout = out.getvalue() @@ -107,12 +107,12 @@ def test_diff_recursively_compare_two_directory_trees(self): make_example_dir(os.path.join(DATA_DIR, 'example/dir2/common_dir')) parser = ppt.diff.get_parser() - options = parser.parse_args([ '-r', os.path.join(DATA_DIR, 'example/dir1'), os.path.join(DATA_DIR, 'example/dir2') ]) + args = parser.parse_args([ '-r', os.path.join(DATA_DIR, 'example/dir1'), os.path.join(DATA_DIR, 'example/dir2') ]) # Capture stdout when running. out = io.StringIO() with redirect_stdout(out): - ppt.diff.main(options) + ppt.diff.main(args) # Compare the results. captured_stdout = out.getvalue() diff --git a/python/ppt/tests/testwc.py b/python/ppt/tests/testwc.py index 52a101c..7ab7965 100644 --- a/python/ppt/tests/testwc.py +++ b/python/ppt/tests/testwc.py @@ -100,8 +100,8 @@ def test_wc_file(self): io_out = io.StringIO() with redirect_stdout(io_out): parser = ppt.wc.get_parser() - options = parser.parse_args([f['sw'], DATA_FILE]) - ppt.wc.main(options) + args = parser.parse_args([f['sw'], DATA_FILE]) + ppt.wc.main(args) # Parse the result and compare to expected value. captured_stdout = io_out.getvalue() @@ -117,8 +117,8 @@ def test_wc_file2(self): io_out = io.StringIO() with redirect_stdout(io_out): parser = ppt.wc.get_parser() - options = parser.parse_args([DATA_FILE]) - ppt.wc.main(options) + args = parser.parse_args([DATA_FILE]) + ppt.wc.main(args) # Compare the results. captured_stdout = io_out.getvalue() @@ -131,7 +131,7 @@ def test_wc_stdin(self): with warnings.catch_warnings(): warnings.simplefilter('ignore', ResourceWarning) parser = ppt.wc.get_parser() - options = parser.parse_args([]) + args = parser.parse_args([]) # Feed the file in via stdin. # Unfortunately, passing the file handle doesn't work. @@ -151,7 +151,7 @@ def test_wc_stdin(self): # Capture stdout when running. io_out = io.StringIO() with redirect_stdout(io_out): - ppt.wc.main(options) + ppt.wc.main(args) # Compare the results. captured_stdout = io_out.getvalue() diff --git a/python/ppt/touch.py b/python/ppt/touch.py index 1d4888d..e7cda7a 100644 --- a/python/ppt/touch.py +++ b/python/ppt/touch.py @@ -1,9 +1,15 @@ -""" touch.py +r""" +usage: touch.py [-h] path [path ...] - DESCRIPTION - Update file access/modification time. +Update file access/modification time. - SEE ALSO: +positional arguments: + path Path(s) of file(s) to be touched. + +options: + -h, --help show this help message and exit + +SEE ALSO: https://man7.org/linux/man-pages/man1/touch.1.html """ import argparse @@ -30,15 +36,19 @@ def touch(paths): _mkdir(p) _utime(p) -def main(options): - touch(options.path) +def main(args): + touch(args.path) return 0 if __name__ == '__main__': - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser( + description='Update file access/modification time.', + epilog="""SEE ALSO: + https://man7.org/linux/man-pages/man1/touch.1.html""", + formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument( 'path', help='Path(s) of file(s) to be touched.', nargs='+') - options = parser.parse_args() - sys.exit(main(options)) + args = parser.parse_args() + sys.exit(main(args)) diff --git a/python/ppt/wc.py b/python/ppt/wc.py index f2544c7..ee22a48 100644 --- a/python/ppt/wc.py +++ b/python/ppt/wc.py @@ -38,7 +38,7 @@ def count_requested(count): return num_requested -def word_count_text(options, text, filepath): +def word_count_text(args, text, filepath): # For stdin (filepath == ''), just use the length of # the incoming text. bytes_count = len(text) @@ -55,16 +55,16 @@ def word_count_text(options, text, filepath): 'requests' : { 'bytes' : { 'count' : bytes_count, - 'requested' : options.bytes }, + 'requested' : args.bytes }, 'chars' : { 'count' : len(text), - 'requested' : options.chars }, + 'requested' : args.chars }, 'lines' : { 'count' : text.count('\n'), - 'requested' : options.lines }, + 'requested' : args.lines }, 'words' : { 'count' : len(re.findall(r'[^\s]+', text)), - 'requested' : options.words} }, + 'requested' : args.words} }, 'filepath' : filepath } if count_requested(count) == 0: @@ -77,10 +77,10 @@ def word_count_text(options, text, filepath): return count -def word_count_file(options, filepath): +def word_count_file(args, filepath): with open(filepath, 'r', encoding='utf-8') as fin: text = fin.read() - return word_count_text(options, text, filepath) + return word_count_text(args, text, filepath) def measure_width(count): @@ -92,7 +92,7 @@ def measure_width(count): return width -def word_count_filenames(options): +def word_count_filenames(args): total = { 'bytes' : { 'count' : 0, @@ -110,8 +110,8 @@ def word_count_filenames(options): # Find the width of the widest requested count. widths = [] counts = [] - for filepath in options.filenames: - count = word_count_file(options, filepath) + for filepath in args.filenames: + count = word_count_file(args, filepath) width = measure_width(count) counts.append(count) widths.append(width) @@ -155,29 +155,29 @@ def print_counts_by_file(count, width): print(f"{' '.join(counts)} {count['filepath']}") -def main(options): - if len(options.filenames) == 0: +def main(args): + if len(args.filenames) == 0: # No filenames given on the command line. - if options.files0: + if args.files0: # Read NUL-terminated input filenames from stdin. filenames = sys.stdin.read() # Split on NUL and throw away last one due to last NUL terminator. - options.filenames = filenames.split('\x00')[:-1] - counts, widths, total = word_count_filenames(options) + args.filenames = filenames.split('\x00')[:-1] + counts, widths, total = word_count_filenames(args) else: # Process data directly from stdin. text = sys.stdin.read() - count = word_count_text(options, text, '') + count = word_count_text(args, text, '') width = measure_width(count) print_counts_by_file(count, width) - elif len(options.filenames) == 1: + elif len(args.filenames) == 1: # Just one file on the command line. - count = word_count_file(options, options.filenames[0]) + count = word_count_file(args, args.filenames[0]) width = measure_width(count) print_counts_by_file(count, width) else: # Multiple files. - counts, widths, total = word_count_filenames(options) + counts, widths, total = word_count_filenames(args) for count in counts: print_counts_by_file(count, max(widths)) @@ -187,6 +187,7 @@ def main(options): totals.append(f"{total[c]['count']:>{max(widths)}}") print(f"{' '.join(totals)} total") + return 0 def get_parser(): @@ -234,5 +235,6 @@ def get_parser(): if __name__ == '__main__': parser = get_parser() - options = parser.parse_args() - main(options) + args = parser.parse_args() + sys.exit(main(args)) + diff --git a/python/tutorials/jsonpp.py b/python/tutorials/jsonpp.py index b144eca..c29e648 100644 --- a/python/tutorials/jsonpp.py +++ b/python/tutorials/jsonpp.py @@ -7,15 +7,15 @@ def pp(f, sort): j = json.load(f) print(json.dumps(j, sort_keys=sort, indent=4, separators=(',', ': '))) -def main(options): - if not options.filename: +def main(args): + if not args.filename: # No filename given on the command line. # Process data directly from stdin. - pp(sys.stdin, options.sort) + pp(sys.stdin, args.sort) else: # Read from the file. - with open(options.filename, 'r') as f: - pp(f, options.sort) + with open(args.filename, 'r') as f: + pp(f, args.sort) return 0 if __name__ == '__main__': @@ -31,5 +31,5 @@ def main(options): 'filename', help='Name of file to read.', nargs='?') - options = parser.parse_args() - sys.exit(main(options)) + args = parser.parse_args() + sys.exit(main(args)) diff --git a/python/tutorials/kafkapeer.py b/python/tutorials/kafkapeer.py index 266bd47..d942640 100644 --- a/python/tutorials/kafkapeer.py +++ b/python/tutorials/kafkapeer.py @@ -278,19 +278,19 @@ def compose_message(self): return msg.encode('utf-8') -def main(options): +def main(args): appid = uuid.uuid4() logging.info('**********************************************************************') logging.info('appid = %s' % (appid)) logging.info('**********************************************************************') - mp = MarcoPolo(options.server[0], options.port[0], options.topic[0], appid) + mp = MarcoPolo(args.server[0], args.port[0], args.topic[0], appid) mp.start() - pp = PingPong(options.server[0], options.port[0], options.topic[0], appid) + pp = PingPong(args.server[0], args.port[0], args.topic[0], appid) pp.start() - hb = HeartbeatId(options.server[0], options.port[0], options.topic[0], 3, appid) + hb = HeartbeatId(args.server[0], args.port[0], args.topic[0], 3, appid) hb.start() while not mp.stopped() or not pp.stopped(): @@ -330,5 +330,5 @@ def main(options): help='Topic to participate in.', nargs=1) - options = parser.parse_args() - sys.exit(main(options)) + args = parser.parse_args() + sys.exit(main(args)) diff --git a/python/tutorials/llplot.py b/python/tutorials/llplot.py index a93d66c..0096391 100644 --- a/python/tutorials/llplot.py +++ b/python/tutorials/llplot.py @@ -7,10 +7,10 @@ # pip3 install --user https://github.com/matplotlib/basemap/archive/master.zip import mpl_toolkits.basemap -def main(options): +def main(args): lon = [] lat = [] - with open(options.points) as pf: + with open(args.points) as pf: reader = csv.DictReader(pf, delimiter=',') for row in reader: lon.append(float(row['longitude'])) @@ -18,8 +18,8 @@ def main(options): # Minimum default margin. margin = max([max(lat) - min(lat), max(lon) - min(lon)]) - if options.margin: - margin = options.margin + if args.margin: + margin = args.margin lat_min = min(lat) - margin lat_max = max(lat) + margin lon_min = min(lon) - margin @@ -50,6 +50,7 @@ def main(options): m.scatter(lons, lats, marker = '.', color='r', zorder=5) matplotlib.pyplot.show() + return 0 if __name__ == '__main__': parser = argparse.ArgumentParser() @@ -62,5 +63,5 @@ def main(options): parser.add_argument( 'points', help='CSV file of points to plot. Should have columns \'latitude\' and \'longitude\` (column order unimportant).') - options = parser.parse_args() - sys.exit(main(options)) + args = parser.parse_args() + sys.exit(main(args)) diff --git a/python/tutorials/logging_example2.py b/python/tutorials/logging_example2.py index 295ff67..43f54ad 100644 --- a/python/tutorials/logging_example2.py +++ b/python/tutorials/logging_example2.py @@ -48,8 +48,8 @@ def get_logger(prefix, path): logger.addHandler(file_handler) return logger -def main(options): - logger = get_logger('logging_example2', options.path[0]) +def main(args): + logger = get_logger('logging_example2', args.path[0]) # Now log some messages: logger.info('logging_example.py') @@ -69,6 +69,6 @@ def main(options): nargs='*', # We will only use the first one. default='.') - options = parser.parse_args() + args = parser.parse_args() - sys.exit(main(options)) + sys.exit(main(args)) diff --git a/python/tutorials/mp3tags.py b/python/tutorials/mp3tags.py index 749d670..8dc5c31 100644 --- a/python/tutorials/mp3tags.py +++ b/python/tutorials/mp3tags.py @@ -40,13 +40,13 @@ def print_tags(album, debug, path): audiofile.tag.title)) -def main(options): +def main(args): result = 0 - if not os.path.isfile(options.path): - print('ERROR: %s is not a file.' % options.path()) + if not os.path.isfile(args.path): + print('ERROR: %s is not a file.' % args.path()) result = 1 else: - print_tags(options.album, options.debug, options.path) + print_tags(args.album, args.debug, args.path) return result @@ -73,6 +73,6 @@ def main(options): 'path', help='Path to MP3 file.') - options = parser.parse_args() + args = parser.parse_args() - sys.exit(main(options)) + sys.exit(main(args)) diff --git a/python/tutorials/socketserve.py b/python/tutorials/socketserve.py index 31f64a1..36fe3c4 100644 --- a/python/tutorials/socketserve.py +++ b/python/tutorials/socketserve.py @@ -36,16 +36,16 @@ def __init__(self, server_address, handler_class): logger.debug('__init__()') -def main(options): +def main(args): logging.basicConfig( level=logging.DEBUG, format='%(asctime)s %(name)s %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S') logger = logging.getLogger('main()') - logger.info('Starting up PingPongSever at %s:%d' % (options.address[0], int(options.port[0]))) + logger.info('Starting up PingPongSever at %s:%d' % (args.address[0], int(args.port[0]))) - server_address = (options.address[0], int(options.port[0])) + server_address = (args.address[0], int(args.port[0])) with PingPongServer(server_address, Handler) as server: server.serve_forever() logger.info('Returned from PingPongServer.serve_forever()') @@ -65,6 +65,6 @@ def main(options): help='Port number of the socket.', nargs=1) - options = parser.parse_args() - sys.exit(main(options)) + args = parser.parse_args() + sys.exit(main(args)) diff --git a/python/tutorials/tcpclient.py b/python/tutorials/tcpclient.py index 5f7c36e..83709cc 100644 --- a/python/tutorials/tcpclient.py +++ b/python/tutorials/tcpclient.py @@ -12,7 +12,7 @@ def timer_callback(event): event.set() -def main(options): +def main(args): logging.basicConfig( level=logging.DEBUG, format='%(asctime)s %(name)s %(levelname)s - %(message)s', @@ -20,8 +20,8 @@ def main(options): logger = logging.getLogger('main()') t = lib.network.TcpClient() - t.connect(options.address[0], int(options.port[0])) - logger.info('connected to %s:%d' % (options.address[0], int(options.port[0]))) + t.connect(args.address[0], int(args.port[0])) + logger.info('connected to %s:%d' % (args.address[0], int(args.port[0]))) while True: t.sendall('ping'.encode('utf-8')) @@ -58,6 +58,6 @@ def main(options): help='Port number of the server.', nargs=1) - options = parser.parse_args() - sys.exit(main(options)) + args = parser.parse_args() + sys.exit(main(args)) diff --git a/python/tutorials/tcpserve.py b/python/tutorials/tcpserve.py index 25bb11c..93f17bb 100644 --- a/python/tutorials/tcpserve.py +++ b/python/tutorials/tcpserve.py @@ -11,15 +11,15 @@ import lib.network -def main(options): +def main(args): logging.basicConfig( level=logging.DEBUG, format='%(asctime)s %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S') t = lib.network.TcpServer() - t.bind(options.address[0], int(options.port[0])) - logging.info('main() bound to %s:%d' % (options.address[0], int(options.port[0]))) + t.bind(args.address[0], int(args.port[0])) + logging.info('main() bound to %s:%d' % (args.address[0], int(args.port[0]))) shutdown = False while not shutdown: @@ -58,6 +58,6 @@ def main(options): help='Port number of the socket.', nargs=1) - options = parser.parse_args() - sys.exit(main(options)) + args = parser.parse_args() + sys.exit(main(args)) diff --git a/python/tutorials/udpreceive.py b/python/tutorials/udpreceive.py index 49412b0..32056bd 100644 --- a/python/tutorials/udpreceive.py +++ b/python/tutorials/udpreceive.py @@ -9,9 +9,9 @@ import lib.network -def main(options): +def main(args): u = lib.network.UdpReceiver() - u.bind(options.address[0], int(options.port[0])) + u.bind(args.address[0], int(args.port[0])) data, address = u.recvfrom(1024) print(data.decode('ascii').rstrip()) return 0 @@ -30,6 +30,6 @@ def main(options): help='Port of the UDP endpoint to receive from.', nargs=1) - options = parser.parse_args() - sys.exit(main(options)) + args = parser.parse_args() + sys.exit(main(args)) diff --git a/python/tutorials/udpsend.py b/python/tutorials/udpsend.py index fd08130..d85c763 100644 --- a/python/tutorials/udpsend.py +++ b/python/tutorials/udpsend.py @@ -9,9 +9,9 @@ import lib.network -def main(options): +def main(args): u = lib.network.UdpSender() - u.sendto(options.address[0], int(options.port[0]), options.message[0]) + u.sendto(args.address[0], int(args.port[0]), args.message[0]) return 0 @@ -33,6 +33,6 @@ def main(options): help='Message to send (use double quotes to contain whitespace).', nargs=1) - options = parser.parse_args() - sys.exit(main(options)) + args = parser.parse_args() + sys.exit(main(args)) diff --git a/python/tutorials/xmlpp.py b/python/tutorials/xmlpp.py index b680de7..4aa90f6 100644 --- a/python/tutorials/xmlpp.py +++ b/python/tutorials/xmlpp.py @@ -8,14 +8,14 @@ def pp(f): xml.etree.ElementTree.indent(ndm) print(xml.etree.ElementTree.tostring(ndm, encoding='unicode')) -def main(options): - if not options.filename: +def main(args): + if not args.filename: # No filename given on the command line. # Process data directly from stdin. pp(sys.stdin) else: # Read from the file. - with open(options.filename, 'r') as f: + with open(args.filename, 'r') as f: pp(f) return 0 @@ -25,5 +25,5 @@ def main(options): 'filename', help='Name of file to read.', nargs='?') - options = parser.parse_args() - sys.exit(main(options)) + args = parser.parse_args() + sys.exit(main(args))