PyDevのテンプレートModule: CLI(agarose)

2019-06-07

KDE neonを使おう

t f B! P L
EclipseのPyDevのテンプレートのModule: CLI(agarose)を使ってみます。

テンプレート自体は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())
 

ブログ検索 by Blogger

Translate

最近のコメント

Created by Calendar Gadget

QooQ