テンプレート自体はEclipseのWindow ー> Preferences。
PyDev ー> Editor ー> Templates。
ここのModule: CLI(agarose)でテンプレートが見れます。
インタープリターはpython2.7になっているので修正が必要です。
変数がいくつか設定されていますけど、代入されるのは${module}、${year}、${isodate}の3つだけで他は変数名が展開されるだけです。
とりあえず動かせるところまでもっていきます。
PyDevパースペクティブにしてPyDevプロジェクトフォルダで右クリック ー> New ー> PyDev Module。
Nameに適当な名前を入れてFinish。
今回はcli.pyという名前にました
Module: CLI(argparse)を選択してOK。
そのままでは使えないので2to3で変換します。
まずcli.pyを保存します。
変換するcli.pyを右クリック ー> PyDev ー> Apply 2To3 (lib2to3 must be in PYTHONPATH) 。
オプションの-wを入力してからRun with specified parametersをクリック。
これでエラーが消えます。
31行目でDEBUG = 1となっているので、Runするとヘルプが表示されます。
pq@pq-VirtualBox:~/eclipse-workspace/Proj1$ python3 cli.py -hvr
usage: cli.py [-h] [-r] [-v] [-i RE] [-e RE] [-V] path [path ...]
cli -- shortdesc
Created by user_name on 2019-06-07.
Copyright 2019 organization_name. All rights reserved.
Licensed under the Apache License 2.0
http://www.apache.org/licenses/LICENSE-2.0
Distributed on an "AS IS" basis without warranties
or conditions of any kind, either express or implied.
USAGE
positional arguments:
path paths to folder(s) with source file(s) [default: None]
optional arguments:
-h, --help show this help message and exit
-r, --recursive recurse into subfolders [default: False]
-v, --verbose set verbosity level [default: None]
-i RE, --include RE only include paths matching this regex pattern. Note:
exclude is given preference over include. [default:
None]
-e RE, --exclude RE exclude paths matching this regex pattern. [default:
None]
-V, --version show program's version number and exit
DEBUG = 0に変更して引数を渡すとその引数が出力されます。-hと-v以外のオプションは実装されていません。
Module: CLI(agarose)を書き換える
エラーと関係のない変更点
shebangを#!/usr/bin/python3に変更。
%sをformat()メソッドに書き換え。
ただし、%(prog)sや %(default)sはArgumentParser オブジェクトとadd_argument() メソッドの引数のフォーマット指定子なのでそのままにします。
文字列の+演算もformat()メソッドに書き換えました。
関数str()はとくに必要なさそうなので削除しました。
class CLIError(Exception):
'''Generic exception to raise and log different fatal errors.'''
def __init__(self, message):
super().__init__(message)
self.message = "E: {}".format(message)
def __repr__(self):
return self.message
ユーザー定義例外の作り方がいまいちよくわからないのですが、メッセージの文字列の変更が反映されていなかったので、とりあえず__repr__()メソッドを書き換えました。エラーが出るところを修正
-vオプションをつけていないとき、verbose>0はNoneと比較しているので型が違うといわれてしまいます。
これは-vオプションのdefaultを指定していないためなので、default=0とすると解決します。
-vオプションの数も出力するようにしました。
例えば-vvとすると2を出力します。
PROFILE = 0をPROFILE = 1とするとプロファイリングができますが、pstats.Stats()のstreamに渡すファイルオブジェクトがバイナリモードだと書き込めないというエラーがでるのでその修正が必要です。
if PROFILE:
import cProfile, pstats
pr = cProfile.Profile()
pr.run('main()')
with open("profile_stats.txt", "w") as f:
stats = pstats.Stats(pr, stream=f).strip_dirs().sort_stats('cumulative')
stats.print_stats()
stats.print_callers()
stats.print_callees()
プロファイリングデータはファイルに出力せずに直接pstats.Stats()に渡すようにしています。プロファイルの結果は同じディレクトリのprofile_stats.txtに出力されます。
ついでに同じファイルにprint_callers()とprint_callees()も出力しています。
ncallsが5/4とか分数になっているのはどういうことでしょう。
機能の追加
TESTRUN = 0をTESTRUN = 1にするとdoctestが実施されますが、何も定義されていないので、main()関数に定義してみました。
def main(argv=None):
'''Command line options.
>>> main(("/home",))
/home
0
'''
最後の0の出力はmain()関数の戻り値です。シェルの出力にはでてこないのですが、doctestだと戻り値を取得するようです。
doctest.testmod()にもverbose=Trueのオプションを付けました。
pq@pq-VirtualBox:~/eclipse-workspace/Proj1$ python3 cli.py
Trying:
main(("/home",))
Expecting:
/home
0
ok
4 items had no tests:
__main__
__main__.CLIError
__main__.CLIError.__init__
__main__.CLIError.__repr__
1 items passed all tests:
1 tests in __main__.main
1 tests in 5 items.
1 passed and 0 failed.
Test passed.
/home
しかしdoctestでコマンドオプションをテストする方法はわかりませんでした。Python3用に書き換えたModule: CLI(agarose)
#!/usr/bin/python3
# -*- coding: utf-8 -*-
'''
cli -- shortdesc
cli is a description
It defines classes_and_methods
@author: user_name
@copyright: 2019 organization_name. All rights reserved.
@license: license
@contact: user_email
@deffield updated: Updated
'''
import sys
import os
from argparse import ArgumentParser
from argparse import RawDescriptionHelpFormatter
__all__ = []
__version__ = 0.1
__date__ = '2019-06-07'
__updated__ = '2019-06-07'
DEBUG = 0
TESTRUN = 0
PROFILE = 1
class CLIError(Exception):
'''Generic exception to raise and log different fatal errors.'''
def __init__(self, message):
super().__init__(message)
self.message = "E: {}".format(message)
def __repr__(self):
return self.message
def main(argv=None):
'''Command line options.
>>> main(("/home",))
/home
0
'''
if argv is None:
argv = sys.argv
else:
sys.argv.extend(argv)
program_name = os.path.basename(sys.argv[0])
program_version = "v{}".format(__version__)
program_build_date = __updated__
program_version_message = '%(prog)s {} ({})'.format(program_version, program_build_date)
program_shortdesc = __import__('__main__').__doc__.split("\n")[1]
program_license = '''{}
Created by user_name on {}.
Copyright 2019 organization_name. All rights reserved.
Licensed under the Apache License 2.0
http://www.apache.org/licenses/LICENSE-2.0
Distributed on an "AS IS" basis without warranties
or conditions of any kind, either express or implied.
USAGE
'''.format(program_shortdesc, __date__)
try:
parser = ArgumentParser(description=program_license, formatter_class=RawDescriptionHelpFormatter)
parser.add_argument("-r", "--recursive", dest="recurse", action="store_true", help="recurse into subfolders [default: %(default)s]")
parser.add_argument("-v", "--verbose", dest="verbose", action="count", default=0, help="set verbosity level [default: %(default)s]")
parser.add_argument("-i", "--include", dest="include", help="only include paths matching this regex pattern. Note: exclude is given preference over include. [default: %(default)s]", metavar="RE" )
parser.add_argument("-e", "--exclude", dest="exclude", help="exclude paths matching this regex pattern. [default: %(default)s]", metavar="RE" )
parser.add_argument('-V', '--version', action='version', version=program_version_message)
parser.add_argument(dest="paths", help="paths to folder(s) with source file(s) [default: %(default)s]", metavar="path", nargs='+')
args = parser.parse_args()
paths = args.paths
verbose = args.verbose
recurse = args.recurse
inpat = args.include
expat = args.exclude
if verbose > 0:
print("Verbose mode on")
print("Count: {}".format(verbose))
if recurse:
print("Recursive mode on")
else:
print("Recursive mode off")
if inpat and expat and inpat == expat:
raise CLIError("include and exclude pattern are equal! Nothing will be processed.")
for inpath in paths:
print(inpath)
return 0
except KeyboardInterrupt: # Ctrl + C。
return 0
except Exception as e:
if DEBUG or TESTRUN:
raise(e)
indent = len(program_name) * " "
sys.stderr.write("{}: {}\n".format(program_name, repr(e)))
sys.stderr.write("{} for help use --help\n".format(indent))
return 2
if __name__ == "__main__":
if DEBUG:
sys.argv.append("-h")
sys.argv.append("-v")
sys.argv.append("-r")
if TESTRUN:
import doctest
doctest.testmod(verbose=True)
if PROFILE:
import cProfile, pstats
pr = cProfile.Profile()
pr.run('main()')
with open("profile_stats.txt", "w") as f:
stats = pstats.Stats(pr, stream=f).strip_dirs().sort_stats('cumulative')
stats.print_stats()
stats.print_callers()
stats.print_callees()
sys.exit(0)
sys.exit(main())



0 件のコメント:
コメントを投稿