試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

バージョン2.5以上のPythonでハッシュ計算のモジュールをhashlibへ移行する

Mandriva Linuxの2009.1ではPythonのバージョンが2.5系から2.6系に上がったのだが、「PythonでファイルサイズとMD5/SHA-1ダイジェストの計算結果を整形して出力」のスクリプトを実行したときに

[スクリプトの場所]:6: DeprecationWarning: the md5 module is deprecated; use hashlib instead
  import md5
[スクリプトの場所]:7: DeprecationWarning: the sha module is deprecated; use the hashlib module instead
  import sha

という警告が出た。「md5」や「sha」のモジュールは古いので「hashlib」を代わりに使用するようにとのこと。ただしこのhashlibモジュールはPythonのバージョン2.5からでないと利用できない。
(2009/7/9)このメッセージは、説明の通り、プログラムを書いた人に対する通知となる。Pythonアプリケーションのどれかを実行していてこのメッセージが出たとしても、動作に影響が出たりするわけではない。*1プログラム作成者がそのプログラムの将来のバージョンにおいて使用モジュールをhashlibに移行するべきということを知るための役に立てばよい。

hashlibモジュールの使い方

まずは

  • md5()
  • sha1()
  • sha224()
  • sha256()
  • sha384()
  • sha512()

のいずれか、使用したいハッシュアルゴリズムに合わせたコンストラクタによりハッシュオブジェクトを作成して、その後以下のメンバ関数を呼ぶ。

  • update(): データに引数のデータを追加・繰り返し呼ぶと繰り返し末尾に追加される
  • digest(): ダイジェスト文字列を得る
  • hexdigest(): 16進表記のダイジェスト文字列を得る
  • copy(): オブジェクトの複製を返す

データはコンストラクタの引数として最初から代入することもできるため、場合によっては「生成 + update()」と2段階の手順を踏む必要はない。
PythonでファイルサイズとMD5/SHA-1ダイジェストの計算結果を整形して出力」のコードをhashlibモジュールで書き直すと下のようになる。

#! /usr/bin/python
# -*- coding: utf-8 -*-

from __future__ import print_function

import os
import sys
import hashlib
import locale

class Digest:
  md5 = None
  sha1 = None

def calc_digest (f, hashalgo, calcfunc):
  """
  ファイルを開き、ダイジェスト計算用関数を呼び出してDigestクラスのメンバに格納
  f       : 開かれたファイルオブジェクト
  hashalgo: ハッシュアルゴリズムの文字列(「md5」か「sha1」)
  calcfunc: ダイジェスト計算の関数(「hashlib.md5」か「hashlib.sha1」)
  """
  exec ('Digest.{0} = "{1}"'.format (hashalgo, calcfunc (f.read ()).hexdigest ()))


# メイン処理
fileslist = sys.argv[1:]

# 引数が足りない場合に使用法を表示
if not fileslist:
  sys.exit ('使用法: {0} [ファイル...]\nファイル情報をテーブルに表示します'.format (__file__))

# 3桁ごとのコンマ付けにロケール指定が必要
locale.setlocale (locale.LC_ALL, '')

# 出力結果文字列
results = []

# 各ファイルごとに処理
for infile in fileslist:

  # ファイルを開いてダイジェスト計算
  try:
    f = open (infile, 'rb')
    calc_digest (f, 'md5', hashlib.md5)
    os.lseek (f.fileno (), 0, 0)  # ファイルの先頭に移動する必要がある
    calc_digest (f, 'sha1', hashlib.sha1)
    f.close ()
  except IOError as e:
    print ('エラー: ファイルの読み書きに失敗しました: {0}'.format (infile), file=sys.stderr)
    continue  # 開けなかったら次のファイルへ

  # ファイルサイズを取得する
  try:
    info = os.stat (infile)
  except OSError as e:
    print ('エラー: ファイル情報が参照できません: {0}'.format (infile), file=sys.stderr)
    continue

  # 結果を整形して表示
  print ('-' * 80)
  print ('''<table>
<caption>ファイル情報: {0}</caption>
<thead>
<tr><th>項目</th><th>値</th></tr>
</thead>
<tbody>
<tr><td>ファイルサイズ</td><td>{1}バイト</td></tr>
<tr><td>MD5</td><td>{2}</td></tr>
<tr><td>SHA-1</td><td>{3}</td></tr>
</tbody>
</table>'''.format (os.path.basename (infile),
                    locale.format ('%d', info.st_size, True),
                    Digest.md5,
                    Digest.sha1))

# 全てエラーの場合は末尾の区切り線は出力しない
if Digest.md5 and Digest.sha1:
  results.append ('-' * 80)

# 結果を出力
for r in results:
  print (r)

下は「PythonでファイルサイズとMD5/SHA-1ダイジェストの計算結果を整形して出力」のコードとの差分。

--- fileinfo-md5sha.py
+++ fileinfo-hashlib.py
@@ -5,8 +5,7 @@
 
 import os
 import sys
-import md5
-import sha
+import hashlib
 import locale
 
 class Digest:
@@ -42,9 +41,9 @@
   # ファイルを開いてダイジェスト計算
   try:
     f = open (infile, 'rb')
-    calc_digest (f, 'md5', md5.new)
+    calc_digest (f, 'md5', hashlib.md5)
     os.lseek (f.fileno (), 0, 0)  # ファイルの先頭に移動する必要がある
-    calc_digest (f, 'sha1', sha.new)
+    calc_digest (f, 'sha1', hashlib.sha1)
     f.close ()
   except IOError as e:
     print ('エラー: ファイルの読み書きに失敗しました: {0}'.format (infile), file=sys.stderr)
@@ -58,8 +57,8 @@
     continue
 
   # 結果を整形して表示
-  results.append ('-' * 80)
-  results.append ('''<table>
+  print ('-' * 80)
+  print ('''<table>
 <caption>ファイル情報: {0}</caption>
 <thead>
 <tr><th>項目</th><th>値</th></tr>

基本的に使い方は同じなので、ハッシュオブジェクトの生成の仕方だけを変えている。
(2014/11/20)細かい部分を色々と修正

参考URL:

使用したバージョン:

*1:ただし将来のバージョンのPythonで「md5」や「sha」のモジュールは利用できなくなるかもしれない