テンプレート自体は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 exitDEBUG = 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 件のコメント:
コメントを投稿