import argparse import base64 import sys from base64 import b64decode as b64decodeValidate from base64 import encodebytes as b64encodebytes from timeit import default_timer as timer import pybase64 def bench_one(duration, data, enc, dec, encbytes, altchars=None, validate=False): duration = duration / 2.0 if not validate and altchars is None: number = 0 time = timer() while True: encodedcontent = encbytes(data) number += 1 if timer() - time > duration: break iter = number time = timer() while iter > 0: encodedcontent = encbytes(data) iter -= 1 time = timer() - time print( "{:<32s} {:9.3f} MB/s ({:,d} bytes -> {:,d} bytes)".format( encbytes.__module__ + "." + encbytes.__name__ + ":", ((number * len(data)) / (1024.0 * 1024.0)) / time, len(data), len(encodedcontent), ) ) number = 0 time = timer() while True: encodedcontent = enc(data, altchars=altchars) number += 1 if timer() - time > duration: break iter = number time = timer() while iter > 0: encodedcontent = enc(data, altchars=altchars) iter -= 1 time = timer() - time print( "{:<32s} {:9.3f} MB/s ({:,d} bytes -> {:,d} bytes)".format( enc.__module__ + "." + enc.__name__ + ":", ((number * len(data)) / (1024.0 * 1024.0)) / time, len(data), len(encodedcontent), ) ) number = 0 time = timer() while True: decodedcontent = dec(encodedcontent, altchars=altchars, validate=validate) number += 1 if timer() - time > duration: break iter = number time = timer() while iter > 0: decodedcontent = dec(encodedcontent, altchars=altchars, validate=validate) iter -= 1 time = timer() - time print( "{0:<32s} {1:9.3f} MB/s ({3:,d} bytes -> {2:,d} bytes)".format( dec.__module__ + "." + dec.__name__ + ":", ((number * len(data)) / (1024.0 * 1024.0)) / time, len(data), len(encodedcontent), ) ) assert decodedcontent == data def readall(file): if file == sys.stdin: if hasattr(file, "buffer"): # Python 3 does not honor the binary flag, # read from the underlying buffer return file.buffer.read() else: return file.read() # do not close the file else: try: return file.read() finally: file.close() def writeall(file, data): if file == sys.stdout: if hasattr(file, "buffer"): # Python 3 does not honor the binary flag, # write to the underlying buffer file.buffer.write(data) else: file.write(data) # do not close the file else: try: file.write(data) finally: file.close() def benchmark(args): print(__package__ + " " + pybase64.get_version()) data = readall(args.input) for altchars in [None, b"-_"]: for validate in [False, True]: print( "bench: altchars={:s}, validate={:s}".format( repr(altchars), repr(validate) ) ) bench_one( args.duration, data, pybase64.b64encode, pybase64.b64decode, pybase64.encodebytes, altchars, validate, ) bench_one( args.duration, data, base64.b64encode, b64decodeValidate, b64encodebytes, altchars, validate, ) def encode(args): data = readall(args.input) data = pybase64.b64encode(data, args.altchars) writeall(args.output, data) def decode(args): data = readall(args.input) data = pybase64.b64decode(data, args.altchars, args.validate) writeall(args.output, data) class LicenseAction(argparse.Action): def __init__( self, option_strings, license=None, dest=argparse.SUPPRESS, default=argparse.SUPPRESS, help="show license information and exit", ): super().__init__( option_strings=option_strings, dest=dest, default=default, nargs=0, help=help, ) self.license = license def __call__(self, parser, namespace, values, option_string=None): print(self.license) parser.exit() def main(args=None): # main parser parser = argparse.ArgumentParser( prog=__package__, description=__package__ + " command-line tool." ) parser.add_argument( "-V", "--version", action="version", version=__package__ + " " + pybase64.get_version(), ) parser.add_argument( "--license", action=LicenseAction, license=pybase64.get_license_text() ) # create sub-parsers subparsers = parser.add_subparsers(help="tool help") # benchmark parser benchmark_parser = subparsers.add_parser("benchmark", help="-h for usage") benchmark_parser.add_argument( "-d", "--duration", metavar="D", dest="duration", type=float, default=1.0, help="expected duration for a single encode or decode test", ) benchmark_parser.add_argument( "input", type=argparse.FileType("rb"), help="input file used for the benchmark" ) benchmark_parser.set_defaults(func=benchmark) # encode parser encode_parser = subparsers.add_parser("encode", help="-h for usage") encode_parser.add_argument( "input", type=argparse.FileType("rb"), help="input file to be encoded" ) group = encode_parser.add_mutually_exclusive_group() group.add_argument( "-u", "--url", action="store_const", const=b"-_", dest="altchars", help="use URL encoding", ) group.add_argument( "-a", "--altchars", dest="altchars", help="use alternative characters for encoding", ) encode_parser.add_argument( "-o", "--output", dest="output", type=argparse.FileType("wb"), default=sys.stdout, help="encoded output file (default to stdout)", ) encode_parser.set_defaults(func=encode) # decode parser decode_parser = subparsers.add_parser("decode", help="-h for usage") decode_parser.add_argument( "input", type=argparse.FileType("rb"), help="input file to be decoded" ) group = decode_parser.add_mutually_exclusive_group() group.add_argument( "-u", "--url", action="store_const", const=b"-_", dest="altchars", help="use URL decoding", ) group.add_argument( "-a", "--altchars", dest="altchars", help="use alternative characters for decoding", ) decode_parser.add_argument( "-o", "--output", dest="output", type=argparse.FileType("wb"), default=sys.stdout, help="decoded output file (default to stdout)", ) decode_parser.add_argument( "--no-validation", dest="validate", action="store_false", help="disable validation of the input data", ) decode_parser.set_defaults(func=decode) # ready, parse if args is None: args = sys.argv[1:] if len(args) == 0: args = ["-h"] args = parser.parse_args(args=args) args.func(args) if __name__ == "__main__": main()