パスワード生成スクリプトをきっかけにoptparseを使ってみました

会社ではPerlで使い捨てスクリプトを書いたり、書かなかったりしているbonlifeです。id:nullpobugさんのパスワード生成スクリプトを参考にさせていただき、Pythonの標準モジュール「optparse」を使うサンプルを書いてみました。使い慣れておいて、ちょっとしたツールを作る時にサッと使いたいところですね。ということで、早速スクリプトです。(色々と追記してしまったので、ちょっとだけ長めです。)

  • gen_passwd.py
import sys
import string
import random
from optparse import OptionParser

allowed_chars = (string.letters + string.digits).translate(string.maketrans('',''),'lO0')

def gen_pass_core(passlen):
    password = ''
    for i in range(passlen):
        password += allowed_chars[random.randrange(len(allowed_chars))]
    return password

def gen_pass(passlen,strong):
    if strong:
        if passlen < 3:
            raise Exception, 'password length should be larger than 3'
        while True:
            password = gen_pass_core(passlen)
            if (password.isalpha() == False and
                password.isdigit() == False and
                password.isupper() == False and
                password.islower() == False):
                return password
                break
    else:
        return gen_pass_core(passlen)

def main():
    usage = "usage: %prog [options] length count"
    version = "%prog 1.0"

    parser = OptionParser(usage=usage,version=version)
    parser.add_option("-s","--strong",action="store_true",dest="strong",help="generate strong password")
    (options, args) = parser.parse_args()

    if len(args) != 2:
        parser.print_help()
        exit()

    passlen = int(args[0])
    count   = int(args[1])

    for i in range(count):
        print gen_pass(passlen,options.strong)

if __name__ == "__main__":
    main()

少しだけ説明です。
まず、パスワードに使える文字列の生成です。文字列リテラルで直接定義しても全く問題ないのですが、Pythonクックブックの1章を読んで覚えたてのstringモジュールの定数などを使ってみました。translateを使って指定した文字列を削除したり。
続いて、本題のoptparse。何かしらoptionを定義しないとつまらないので、引数を取らない -s (--strong) というオプションを付けてみることにしました。

    parser.add_option("-s","--strong",action="store_true",dest="strong",help="generate strong password")
    (options, args) = parser.parse_args()

上記のようにすると、-s あるいは --strong が指定された場合に、action の store_true が効いて、options.strong が True になります。これを利用して、gen_pass() の部分で強制的にstrongなパスワードにするかどうかを制御します。(help で指定した文字列は、引数に -h あるいは --help が指定された場合に使われます。)
次は、strong を強制する場合の処理について説明します。

            if (password.isalpha() == False and
                password.isdigit() == False and
                password.isupper() == False and
                password.islower() == False):
                return password

見ていただければ分かると思いますが、アルファベットのみではなく、数字のみでもなく、大文字のみでもなく、小文字のみでもない場合に、はじめてreturnしています。(これで本当にstrongか、というのは気にしない方向で。あくまでもオプションを指定する練習です。) この条件に当てはまるまで、外側の while ループで何度も繰り返しています。この処理を追加した結果、パスワード文字列が3文字以上でなければ、passwordがreturnされず、永遠にループされてしまうことになりました。そこで、以下のように例外を発生させることに。

    if strong:
        if passlen < 3:
            raise Exception, 'password length should be larger than 3'

本当は、Exceptionクラスを継承した独自の例外クラスを定義して、その例外をraiseした方が良いのかもしれませんが、今回はとりあえず止まれば良いや、的な発想で、素のExceptionを使ってみました。
そんなこんなで完成したスクリプトの使い方は以下の通りです。

C:\test\python>python gen_passwd.py --version
gen_passwd.py 1.0
C:\test\python>python gen_passwd.py 8 3
whbRu5he
gcZCtrHT
BEvFZfAd
C:\test\python>python gen_passwd.py -s 8 3
ExY3WXwm
r8xyacH4
V66WGn6R
C:\test\python>python gen_passwd.py -s 2 3
Traceback (most recent call last):
  File "gen_passwd.py", line 48, in <module>
    main()
  File "gen_passwd.py", line 45, in main
    print gen_pass(passlen,options.strong)
  File "gen_passwd.py", line 17, in gen_pass
    raise Exception, 'password length should be larger than 3'
Exception: password length should be larger than 3

毎日Pythonに触れていると、少しずつ手に馴染んでくる気がする今日この頃です。