diff -r 9fdeca5fdbf0 Doc/library/hashlib.rst --- a/Doc/library/hashlib.rst Mon Mar 28 06:13:52 2016 +0000 +++ b/Doc/library/hashlib.rst Wed Mar 30 15:45:54 2016 +0300 @@ -176,6 +176,29 @@ compute the digests of data sharing a common initial substring. +.. _hashlib-commandline: + +Command Line Interface +---------------------- + +.. versionadded:: 3.6 + +:mod:`hashlib` can also be invoked directly using the :option:`-m` switch of +the interpreter with a ``algorithm`` argument. Available algorithms +are :func:`md5`, :func:`sha1`, :func:`sha224`, :func:`sha256`, :func:`sha384`, +and :func:`sha512`. Additional algorithms may also be available depending upon +the OpenSSL library that Python uses on your platform. + +When a ``file`` argument is given the hash is calculated on the file:: + + $ python -m hashlib md5 /bin/sh + $ d985d0ea551c1253c2305140c583d11f + +With no ``file``, or when ``file`` is -, read standard input:: + + $ cat /bin/sh | python -m hashlib md5 + $ d985d0ea551c1253c2305140c583d11f + Key derivation -------------- diff -r 9fdeca5fdbf0 Lib/hashlib.py --- a/Lib/hashlib.py Mon Mar 28 06:13:52 2016 +0000 +++ b/Lib/hashlib.py Wed Mar 30 15:45:54 2016 +0300 @@ -1,3 +1,4 @@ +#! /usr/bin/env python3 #. Copyright (C) 2005-2010 Gregory P. Smith (greg@krypto.org) # Licensed to PSF under a Contributor Agreement. # @@ -215,3 +216,30 @@ # Cleanup locals() del __always_supported, __func_name, __get_hash del __py_new, __hash_new, __get_openssl_constructor + + +# Usable as a script... +def main(): + import argparse + import sys + import os + parser = argparse.ArgumentParser() + parser.add_argument('algorithm', action='store', + choices=algorithms_available, + help='Specify algorithm to use for hash calculation.') + parser.add_argument('file', metavar='FILE', type=argparse.FileType("rb"), + default=sys.stdin.buffer, nargs='?', + help='Calculate hash on given file. With no FILE, or when FILE is -, read standard input.') + args = parser.parse_args() + hash_obj = new(args.algorithm) + block_size = os.stat(args.file.fileno()).st_blksize + while True: + data = args.file.read(block_size) + hash_obj.update(data) + if len(data) < block_size: + break + print(hash_obj.hexdigest()) + + +if __name__ == '__main__': + main() diff -r 9fdeca5fdbf0 Lib/test/test_hashlib.py --- a/Lib/test/test_hashlib.py Mon Mar 28 06:13:52 2016 +0000 +++ b/Lib/test/test_hashlib.py Wed Mar 30 15:45:54 2016 +0300 @@ -18,7 +18,7 @@ import unittest import warnings from test import support -from test.support import _4G, bigmemtest, import_fresh_module +from test.support import _4G, bigmemtest, import_fresh_module, script_helper # Were we compiled --with-pydebug or with #define Py_DEBUG? COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount') @@ -526,5 +526,36 @@ self._test_pbkdf2_hmac(c_hashlib.pbkdf2_hmac) +class TestMain(unittest.TestCase): + supported_algorithms = hashlib.algorithms_available + data = b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + + def setUp(self): + with open(support.TESTFN, 'wb') as fp: + fp.write(self.data) + + def tearDown(self): + if os.path.exists(support.TESTFN): + os.unlink(support.TESTFN) + + def get_output(self, *args): + return script_helper.assert_python_ok('-m', 'hashlib', *args).out.rstrip() + + def expected_output(self, algorithm): + return hashlib.new(algorithm, self.data).hexdigest().encode("ascii") + + def test_calculate_file_hash(self): + for algorithm in self.supported_algorithms: + output = self.get_output(algorithm, support.TESTFN) + self.assertEqual(output, self.expected_output(algorithm)) + + def test_calculate_hash_from_stdin(self): + for algorithm in self.supported_algorithms: + with script_helper.spawn_python('-m', 'hashlib', algorithm) as proc: + out, err = proc.communicate(self.data) + self.assertEqual(out.rstrip(), self.expected_output(algorithm)) + self.assertIsNone(err) + + if __name__ == "__main__": unittest.main()