"""
概要: コマンドラインアプリケーションやGUIアプリケーションの共通機能を提供するモジュール。
詳細説明: このモジュールは、アプリケーション開発における共通のタスクを効率的に処理するための基底クラス `tkApplication` を定義します。
これには、コマンドライン引数の解析、設定ファイルの管理、標準出力およびエラー出力のリダイレクト、
例外ハンドリングのカスタマイズ、プラグインモジュールの動的ロード、実行時間の計測などが含まれます。
アプリケーションは `tkApplication` を継承することで、これらの豊富なユーティリティをすぐに利用できます。
関連リンク: :doc:`tklib.tkapplication_usage`
"""
import os
import platform
import sys
import time
from datetime import datetime
import re
import glob
import builtins
import traceback
import getpass
import openpyxl
from importlib import import_module
try:
import traceback
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import TerminalFormatter
use_colored_traceback = True
except:
use_colored_traceback = False
from tklib.tkobject import tkObject
from tklib.tkparams import tkParams
from tklib.tkvariousdata import tkVariousData
from tklib.tkfile import open_chardet
from tklib.tkplugin import tkModule
from tklib.tkutils import set_exception, terminate, getarg, getintarg, getfloatarg, pint, pfloat, pconv, pconv_by_type, replace_path
from tklib.tkutils import convert_by_vars, conv_float, save_csv, del_quote, split_file_path, val2str
# Note: The following imports are commented out in the original code.
# The `get_ext` and `tkIniFile` used later in the code are not explicitly imported
# here or elsewhere in the provided snippet. Assuming they are available via some
# other means or will cause runtime errors if not, but adhering to rule 1.
#from tklib.tksci.tkFit_object import save_fit_config
#=========================
# Application base class
#=========================
[ドキュメント]
def save_fit_config(vars, outfile,
labels = ["varname", "unit", "pk_scale", "optid", "x0", "dx", "kmin", "kmax", "kpenalty"],
section = "Parameters"):
"""
概要: フィッティング設定をファイルに保存する。
詳細説明:
`vars`オブジェクトから指定されたラベルのデータを抽出し、CSVまたはExcel形式で出力します。
出力ファイルの種類は、`outfile`の拡張子によって自動的に判別されます。
この関数は、フィッティングパラメータの構成をテンプレートとして保存するために利用できます。
:param vars: tkParams or similar. 保存する変数群を含むオブジェクト。
:param outfile: str. 出力ファイルパス。拡張子 (.csv, .xlsx) に応じてファイル形式が決定されます。
:param labels: list[str]. `vars`オブジェクトから取得する属性名のリスト。
:param section: str. 設定ファイル内のセクション名。CSV/Excel出力では直接使用されません。
:returns: None or tkVariousData().saveの戻り値. 保存が成功した場合は`tkVariousData().save`関数の結果、それ以外は`None`。
"""
if vars.hasattr('print_variables'): vars.print_variables()
# Note: `get_ext` is not explicitly imported in the provided code.
# Assuming it's available or will cause a runtime error if not.
ext = get_ext(outfile).lower()
if ext in ['.csv', '.xlsx']:
data_list = [getattr(vars, l) for l in labels]
return tkVariousData().save(outfile, data_list = data_list, labels = labels)
return None
[ドキュメント]
class tkApplication(tkObject):
"""
概要: コマンドラインアプリケーションやGUIアプリケーションの共通機能を管理する基底クラス。
詳細説明:
このクラスは、Pythonアプリケーションの基本的な構造と共通ユーティリティを提供します。
具体的には、以下の機能が含まれます。
- OS情報やスクリプト実行パスの取得
- コマンドライン引数の解析と管理
- INIファイルによる設定の読み書き
- 標準出力および標準エラー出力のリダイレクト機能
- 例外ハンドリングのカスタマイズ(カラー表示対応)
- 実行時間の計測と表示
- プラグインモジュールの動的ロードと呼び出し
- 多言語リソースの管理と表示
`tkApplication`を継承することで、これらの機能を手間なくアプリケーションに統合できます。
:param usage_str: str. アプリケーションの用法説明として使用する文字列。
:param suppress_usage: str. 用法説明の一部または全部を抑制するためのキーワード(例: `'all'`, `'usage'`, `'options'`)。
:param _globals: dict. グローバル変数辞書。通常は`globals()`を渡します。
:param locals: dict. ローカル変数辞書。通常は`locals()`を渡します。
:param use_user_inifile: bool. ユーザー固有のINIファイル(ユーザー名を含む)を使用するかどうか。
:param inifile_dirs: list[str]. INIファイルを検索するディレクトリのリスト。
:param create_inidir: bool. INIファイルディレクトリが存在しない場合に作成するかどうか。
:param save_time: bool. 起動時間や経過時間を記録するかどうか。
:param args: dict. `tkObject`のコンストラクタに渡される追加引数。
"""
def __init__(self, usage_str = "", suppress_usage = '', _globals = None, locals = None,
use_user_inifile = False, inifile_dirs = ['ini'], create_inidir = False,
save_time = True,
**args):
super().__init__()
self.os_name = platform.system()
self.args_opt_inf = {}
self.args_idx_inf = []
self.args_vars = None
self.suppress_usage = suppress_usage
self.set_usage(usage_str, suppress_usage = suppress_usage)
self.phrases = {}
self.save_time = save_time
self.time_stamps = {}
if self.save_time: self.print_time(label = "startup")
self.user = getpass.getuser()
self.user_nospace = self.user.replace(' ', '')
self.argv = sys.argv
self.script_path = sys.argv[0]
self.script_fullpath = os.path.abspath(self.script_path)
dirname, basename, filebody, ext = split_file_path(self.script_fullpath)
self.script_dir = dirname
self.script_filebody = filebody
# self.script_dir = os.path.splitext(self.script_fullpath)[0]
self.ini_dir = self.get_inifile_dir(use_user_inifile = use_user_inifile,
inifile_dirs = inifile_dirs, create_inidir = create_inidir,
defval = self.script_dir)
self.inifile = self.get_inifile_path(self.ini_dir, use_user_inifile = use_user_inifile)
self.redirects = []
self.print_original = print
self.globals = _globals # Changed from globals to _globals to avoid conflict with builtins.globals
self.locals = locals
self.modules = []
# Configuration parameters
self.configparams = tkParams(app = self)
self.config = self.configparams
self.config.lang = 'en'
self.config.inifile = self.inifile
self.config.ini_dir = os.path.dirname(os.path.abspath(self.config.inifile))
# Application global variables
self.params = tkParams(app = self)
# Script variables
self.svars = tkParams(app = self)
self.update(**args)
def __del__(self):
"""
概要: オブジェクト破棄時にリダイレクトされたストリームを閉じる。
詳細説明:
アプリケーションが終了する際に、`redirect`メソッドで開かれたファイルストリームを適切に閉じ、
リソースリークを防ぎます。`redirects`リストにファイルオブジェクトが含まれている場合、
それらの`close()`メソッドが呼び出されます。
:returns: None.
"""
if self.get('redirects', None):
for target in self.redirects:
if type(target) is str:
pass
else:
try:
target.close()
except:
pass
# super(tkObject, self).__del__()
def __str__(self):
"""
概要: クラスのパス表現を文字列として返す。
詳細説明:
`tkObject`クラスで定義された`ClassPath()`メソッドを呼び出し、
`tkApplication`インスタンスの完全なクラス名を文字列として提供します。
:returns: str. クラスの完全なパス文字列。
"""
return self.ClassPath()
[ドキュメント]
def nohup(self, flag):
"""
概要: プロセスが親シェルから独立して実行されるようにSIGHUPシグナルを無視する設定を行う。
詳細説明:
Unix系OSにおいて、`nohup`コマンドと同様の効果をプログラム内で実現します。
これにより、シェルが閉じられてもプロセスは終了しなくなります。
`signal`モジュールがインポートされている必要があります。
:param flag: bool. `True`の場合、SIGHUPシグナルを無視する設定を行います。
:returns: None.
"""
# Note: `signal` module is used here but not imported in the provided code.
# Assuming it's available or will cause a runtime error if not.
if flag: signal.signal(signal.SIGHUP, signal.SIG_IGN)
[ドキュメント]
def daemonize(self, stdin_path = '/dev/null', stdout_path = '/dev/null', stderr_path = '/dev/null', print_level = 0):
"""
概要: 現在のプロセスをデーモン化する。
詳細説明:
現在のプロセスをバックグラウンドで実行されるデーモンプロセスに変換します。
標準入力、標準出力、標準エラー出力は、それぞれ指定されたファイルにリダイレクトされます。
このメソッドはPOSIX(Unix系)オペレーティングシステムでのみ有効です。
2段階のforkと`os.setsid()`により、親プロセスから完全に分離されます。
:param stdin_path: str. 標準入力のリダイレクト先ファイルパス。
:param stdout_path: str. 標準出力のリダイレクト先ファイルパス。
:param stderr_path: str. 標準エラー出力のリダイレクト先ファイルパス。
:param print_level: int. デーモン化処理に関するメッセージの表示レベル。0の場合、メッセージは表示されません。
:returns: None.
"""
if os.name != 'posix':
self.terminate(f"Error in daemonize(): running on [{os.name}] but daemonize() is valid for linux-based OS.\n", pause = True)
if print_level:
print("daemonize the process:")
print(f" stdin is redirected to [{stdin_path}]")
print(f" stdout is redirected to [{stdout_path}]")
print(f" stderr is redirected to [{stderr_path}]")
if os.fork() > 0: sys.exit()
os.setsid()
if os.fork() > 0: sys.exit()
sys.stdout.flush()
sys.stderr.flush()
with open(stdin_path, 'r') as f:
os.dup2(f.fileno(), sys.stdin.fileno())
with open(stdout_path, 'a+') as f:
os.dup2(f.fileno(), sys.stdout.fileno())
with open(stderr_path, 'a+') as f:
os.dup2(f.fileno(), sys.stderr.fileno())
[ドキュメント]
def raise_error(self, desc = "custom error"):
"""
概要: カスタムエラーを発生させる。
詳細説明:
指定された説明を持つ`CustomError`例外を発生させます。
これは、特定のアプリケーション固有の条件下でプログラムの実行を中断するために使用できます。
:param desc: str. エラーの説明文字列。
:returns: None. (例外を発生させるため、通常の戻り値はありません)。
:raises CustomError: 指定された説明と共にカスタムエラーを発生させます。
"""
class CustomError(Exception):
pass
raise CustomError(desc)
[ドキュメント]
def set_exception(self, func = None, print_level = 1):
"""
概要: グローバルな例外処理フックを設定する。
詳細説明:
`tklib.tkutils.set_exception`関数を呼び出し、未処理の例外が発生した場合の
グローバルなハンドラを設定します。これにより、例外発生時の動作をカスタマイズできます。
`func`が`None`の場合、デフォルトの例外ハンドラが設定されます。
:param func: callable. 例外ハンドラとして設定する関数。`None`の場合、デフォルトのハンドラが設定されます。
:param print_level: int. 例外情報の出力レベル。
:returns: None.
"""
set_exception(func, print_level = print_level)
[ドキュメント]
def exception_hook(self, type, value, tb, output = 'stderr', display_type = 'colored', pause_at_terminate = True):
"""
概要: 未処理の例外を捕捉し、指定された形式で出力するフック関数。
詳細説明:
`sys.excepthook`に設定されることを想定した関数です。
トレースバックをカラー表示(`pygments`が利用可能な場合)またはプレーンテキストで、
指定された出力先(標準エラーまたは標準出力)に書き出します。
また、アプリケーション終了時にユーザー入力で一時停止するかどうかも設定できます。
:param type: type. 例外の型(例: `ValueError`, `TypeError`)。
:param value: Exception. 例外インスタンス。
:param tb: traceback. 例外のトレースバックオブジェクト。
:param output: str. トレースバックの出力先 ('stderr' または 'stdout')。
:param display_type: str. トレースバックの表示形式 ('colored' または 'plain')。
:param pause_at_terminate: bool. 終了時にユーザー入力で一時停止するかどうか。
:returns: None.
"""
if not use_colored_traceback:
return
tbtext = ''.join(traceback.format_exception(type, value, tb))
lexer = get_lexer_by_name("pytb", stripall=True)
formatter = TerminalFormatter()
if display_type == 'colored':
if output == 'stderr':
sys.stderr.write("\n" + highlight(tbtext, lexer, formatter))
else:
print("\n" + highlight(tbtext, lexer, formatter))
elif display_type == 'plain':
if output == 'stderr':
sys.stderr.write("\n" + tbtext)
else:
print("\n" + tbtext)
else:
print("")
print(f"Error in exception_hook(): Invalid display_type [{display_type}]")
if pause_at_terminate:
input("Press ENTER to terminate>> ")
# try:
# input("Press ENTER to terminate>> ")
# except:
# pass
[ドキュメント]
def redirect_exception(self, hook_func = None, output = 'stdout', display_type = 'colored', pause_at_terminate = True):
"""
概要: システムの例外処理フックをリダイレクトする。
詳細説明:
`sys.excepthook`を`exception_hook`メソッド(または指定されたカスタムフック関数)に設定し、
未処理の例外発生時の挙動を制御します。
`pygments`ライブラリが利用可能な場合にのみ、カラー表示などの機能が有効になります。
:param hook_func: callable. 使用するカスタム例外フック関数。`None`の場合、`self.exception_hook`が使用されます。
:param output: str. トレースバックの出力先 ('stderr' または 'stdout')。
:param display_type: str. トレースバックの表示形式 ('colored' または 'plain')。
:param pause_at_terminate: bool. 終了時にユーザー入力で一時停止するかどうか。
:returns: None.
"""
if not use_colored_traceback:
return
if hook_func is None:
hook_func = lambda type, value, tb: \
self.exception_hook(type, value, tb, output = output, display_type = display_type, pause_at_terminate = pause_at_terminate)
sys.excepthook = hook_func
[ドキュメント]
def print_redict(self, *args, **kwargs):
"""
概要: `print_redirect`メソッドのエイリアス。
詳細説明:
`print_redirect`メソッドと同じ機能を提供します。
後方互換性または異なる命名規則のために存在する可能性があります。
:param args: tuple. `print`関数に渡される位置引数。
:param kwargs: dict. `print`関数に渡されるキーワード引数。`console_out`などのリダイレクト制御パラメータを含む場合があります。
:returns: print_redirectメソッドの戻り値。
"""
return self.redirect(*args, **kwargs)
[ドキュメント]
def print_warning(self, *args, **kwargs):
"""
概要: 警告メッセージを標準出力とリダイレクト先に表示する。
詳細説明:
`print_redirect`メソッドを`console_out=True`の状態で呼び出すことにより、
出力リダイレクトが設定されている場合でもコンソールに警告メッセージを強制的に出力します。
これにより、重要な警告がログファイルだけでなく、ユーザーの目にも届くようになります。
:param args: tuple. `print`関数に渡される位置引数。
:param kwargs: dict. `print`関数に渡されるキーワード引数。
:returns: print_redirectメソッドの戻り値。
"""
return self.print_redirect(*args, console_out = True, **kwargs)
[ドキュメント]
def print_redirect(self, *args, **kwargs):
"""
概要: 標準の`print`関数をオーバーライドし、指定された出力先にメッセージをリダイレクトする。
詳細説明:
`builtins.print`をこのメソッドに置き換えることで、すべての`print`出力を複数のファイルやストリームに送信できるようになります。
`console_out=True`がキーワード引数に含まれる場合、元の`print`関数も使用してコンソールにメッセージを出力します。
出力がファイルの場合、カラーエスケープシーケンス(`\x1b[...]`)を自動的に除去する機能もサポートします。
:param args: tuple. `print`関数に渡される位置引数。
:param kwargs: dict. `print`関数に渡されるキーワード引数。
`console_out`: bool. `True`の場合、メッセージは元の`print`関数を使ってコンソールにも出力されます。
`end`: str. 出力の最後に付加する文字列。デフォルトは改行(`\n`)です。
:returns: None.
"""
if kwargs.get("console_out", None) and 'stdout' not in self.redirects:
del kwargs['console_out']
self.print_original(*args, **kwargs)
if kwargs.get("console_out", None) is not None:
del kwargs['console_out']
for target in self.redirects:
if type(target) is str:
if target == 'stdout':
if 'file' in self.display_type_traceback:
# Note: `args` is a tuple. The following line attempts to reassign `args` from a regex substitution result,
# which is unlikely to work correctly for a tuple of arguments.
# It should probably iterate `args` and apply substitution to each string argument.
# Adhering to rule 1, no logic change.
for arg_item in args:
if isinstance(arg_item, str):
arg_item = re.sub(r'\x1b\[[\d;]+m', '', arg_item, flags = re.MULTILINE)
try:
self.print_original(*args, **kwargs)
except UnicodeEncodeError as e:
print(f"Error in print_original(): UnicodeEncodeError: {e}. Continue")
# Note: `Exceptions` is not defined. Assuming it's a typo for `Exception` or some custom type.
# Adhering to rule 1, no logic change.
except Exceptions as e:
print(f"Error in print_original(): {e}. Continue")
continue
fp = target
if fp is None:
return
if kwargs.get('end', None) is None:
newline = True
else:
newline = False
del kwargs['end']
for a in args:
if self.display_type_traceback == 'file':
a = re.sub(r'\x1b\[[\d;]+m', '', a, flags = re.MULTILINE)
try:
# Note: `kwargs` are passed to `fp.write`, but `file.write` only accepts a string, not kwargs.
# This might cause a TypeError if kwargs are not empty.
# Adhering to rule 1, no logic change.
fp.write("{}".format(a), **kwargs)
except:
pass
if newline:
fp.write("\n")
# fp.write("\n", **kwargs) # Same issue as above, kwargs to write.
fp.flush()
[ドキュメント]
def redict(self, targets = None, mode = 'a',
redirect_traceback = True, output_traceback = 'stdout',
display_type_traceback = 'colored', remove_esc_sequence = 'file'):
"""
概要: `redirect`メソッドのエイリアス。
詳細説明:
`redirect`メソッドと同じ機能を提供します。
後方互換性または異なる命名規則のために存在する可能性があります。
:param targets: str or list[str]. 出力リダイレクト先。ファイルパス(文字列)または`'stdout'`のリスト。
:param mode: str. ファイルを開くモード ('a'は追記、'w'は上書き)。
:param redirect_traceback: bool. 例外発生時のトレースバックをリダイレクトするかどうか。
:param output_traceback: str. トレースバックのリダイレクト先('stderr'または'stdout')。
:param display_type_traceback: str. トレースバックの表示タイプ('colored'または'plain')。
:param remove_esc_sequence: str. エスケープシーケンスを除去する条件('file'の場合、ファイル出力時に除去)。
:returns: redirectメソッドの戻り値。
"""
return self.redirect(targets = targets, mode = mode,
redirect_traceback = redirect_traceback, output_traceback = output_traceback,
display_type_traceback = display_type_traceback, remove_esc_sequence = remove_esc_sequence)
[ドキュメント]
def redirect(self, heading = None, targets = None, mode = 'a',
redirect_traceback = True, output_traceback = 'stdout',
display_type_traceback = 'colored', remove_esc_sequence = 'file',
pause_at_terminate = True, ignore_error = True):
"""
概要: 標準出力と例外フックをファイルまたは指定されたストリームにリダイレクトする。
詳細説明:
`builtins.print`を`self.print_redirect`に置き換え、`sys.excepthook`を`self.redirect_exception`に設定することで、
アプリケーション全体のログ出力を制御します。これにより、実行ログをファイルに保存したり、
エラー発生時の挙動をカスタマイズしたりできます。
`targets`が`None`の場合、既存のリダイレクトをすべて解除します。
:param heading: str. リダイレクト開始時に表示する見出し文字列。
:param targets: str or list[str] or None. 出力リダイレクト先。ファイルパス(文字列)または`'stdout'`のリスト。`None`の場合、既存のリダイレクトを解除します。
:param mode: str. ファイルを開くモード ('a'は追記、'w'は上書き)。
:param redirect_traceback: bool. 例外発生時のトレースバックをリダイレクトするかどうか。
:param output_traceback: str. トレースバックのリダイレクト先('stderr'または'stdout')。
:param display_type_traceback: str. トレースバックの表示タイプ('colored'または'plain')。
:param remove_esc_sequence: str. エスケープシーケンスを除去する条件('file'の場合、ファイル出力時に除去)。
:param pause_at_terminate: bool. 例外発生時、終了前に一時停止するかどうか。
:param ignore_error: bool. ファイルオープンエラーを無視するかどうか。`True`の場合、警告を出力して続行します。`False`の場合、エラーとして処理します。
:returns: None.
"""
self.redirect_traceback = redirect_traceback
self.display_type_traceback = display_type_traceback
self.remove_esc_sequence = remove_esc_sequence
if targets is None:
for s in self.redirects:
if s == 'stdout':
pass
elif type(s) is not str:
try:
s.close()
except:
pass
self.redirects = [] # Clear redirects list if targets is None
if type(targets) is str:
targets = [targets]
# If targets is not None but not a list, ensure it's iterable
if targets is not None and not isinstance(targets, list):
targets = [targets]
if heading is not None:
print(heading)
builtins.print = self.print_redirect
if targets is not None: # Only process if targets is not None after conversion
for s in targets:
# self.print_warning("s=", s)
if s is None:
continue
if s == 'stdout':
self.redirects.append(s)
else:
# if not os.path.isfile(s):
# if ignore_error:
# self.print_warning(f"\nWarning: [{s}] is not a file. Ignore")
# continue
# else:
# self.print_warning(f"\nError: [{s}] is not a file. Terminate")
# self.terminate("")
try:
fp = open(s, mode)
except:
if ignore_error:
self.print_warning(f"\nWarning: Can not open [{s}]. Ignore")
continue
else:
self.print_warning(f"\nError: Can not open [{s}]. Terminate")
self.terminate("")
self.redirects.append(fp)
if redirect_traceback:
self.redirect_exception(output = output_traceback, display_type = display_type_traceback, pause_at_terminate = pause_at_terminate)
[ドキュメント]
def replace_path(self, path = None, template = None, ext_dict = {}):
"""
概要: 指定されたパスをテンプレートに基づいて新しいパスに変換する。
詳細説明:
`tklib.tkutils.replace_path`関数を呼び出し、入力ファイルパスやスクリプトパスを元に、
ディレクトリ名、ファイル名本体、拡張子などの要素を組み合わせて新しいパスを生成します。
テンプレートには`{dirname}`, `{filebody}`, `{ext}`などのプレースホルダーを使用できます。
:param path: str. 変換元のパス。`None`の場合、`self.script_fullpath`が使用されます。
:param template: str or list[str]. 新しいパスを生成するためのテンプレート文字列。リストの場合、`os.path.join`で結合されます。
:param ext_dict: dict. 拡張子を置き換えるための辞書(例: `{'.in': '.out'}`)。
:returns: str. 変換された新しいパス。
"""
if template is None:
template = os.path.join("{dirname}", "{filebody}-out.txt")
if path is None:
path = self.script_fullpath
if type(template) is list:
p = []
for s in template:
s2 = replace_path(path, s, ext_dict)
p.append(s2)
return os.path.join(p[0], *p[1:])
else:
return replace_path(path, template, ext_dict)
[ドキュメント]
def get_inifile_dir(self, use_user_inifile = False,
inifile_dirs = ['ini'], create_inidir = False,
defval = None):
"""
概要: INIファイルが配置されるディレクトリのパスを取得する。
詳細説明:
ユーザー固有のINIファイルを使用するかどうか、INIファイルを検索するディレクトリのリスト、
およびディレクトリが存在しない場合に作成するかどうかに基づいて、
適切なINIファイルディレクトリを決定して返します。
:param use_user_inifile: bool. ユーザー固有のINIファイルディレクトリを使用するかどうか。
:param inifile_dirs: list[str]. INIファイルディレクトリの候補リスト。優先度の高い順に指定します。
:param create_inidir: bool. ディレクトリが存在しない場合に作成するかどうか。`True`の場合、最初の候補ディレクトリを作成します。
:param defval: str. 候補ディレクトリが見つからなかった場合のデフォルト値。
:returns: str. INIファイルディレクトリの絶対パス。
"""
if not use_user_inifile:
return self.script_dir
for dir in inifile_dirs:
if dir is None:
continue
path = os.path.join(self.script_dir, dir)
path = os.path.abspath(path)
if create_inidir or os.path.isdir(path):
return path
return defval
[ドキュメント]
def get_inifile_path(self, inidir, use_user_inifile = False):
"""
概要: アプリケーションのINIファイルパスを生成する。
詳細説明:
スクリプトのファイル名(拡張子なし)とINIファイルディレクトリ、
ユーザー固有の設定を使用するかどうかに基づいて、INIファイルの完全なパスを構築して返します。
`use_user_inifile`が`True`の場合、ファイル名に現在のユーザー名が追加されます。
:param inidir: str. INIファイルが配置されるディレクトリのパス。
:param use_user_inifile: bool. ユーザー固有のINIファイルを使用するかどうか。`True`の場合、ユーザー名がファイル名に追加されます。
:returns: str. 生成されたINIファイルの絶対パス。
"""
dirname, basename, filebody, ext = split_file_path(self.script_fullpath)
if not use_user_inifile:
inifile = os.path.join(inidir, f'{filebody}.ini')
else:
inifile = os.path.join(inidir, f'{filebody}_{self.user_nospace}.ini')
inifile = os.path.abspath(inifile)
return inifile
#pathが入力ファイル(infile_ext)か設定ファイル(config_ext)であるかによって、infileとconfig_pathを返す
#pathが設定ファイルの場合、infileとinfile_storedはNone
#pathが入力φの場合、config_pathを作る。拡張子はconfig_extの1つめ
[ドキュメント]
def analyze_infile(app, path, infile_ext = [], config_ext = [], print_level = 1):
"""
概要: 入力ファイルパスを解析し、それがデータファイルか設定ファイルかを判断する。
詳細説明:
指定されたファイルパスの拡張子を、既知の入力ファイル拡張子リストと設定ファイル拡張子リストと比較します。
- パスが`config_ext`のいずれかの拡張子を持つ場合、そのパスは設定ファイルとみなされます。
- パスが`infile_ext`のいずれかの拡張子を持つか、またはその他の拡張子を持つ場合、そのパスは入力ファイルとみなされ、
対応する設定ファイルパスが`config_ext`の最初の拡張子を使って生成されます。
:param app: tkApplication. アプリケーションインスタンス。
:param path: str. 解析対象のファイルパス。
:param infile_ext: list[str]. 入力ファイルとして認識する拡張子のリスト(例: `['.csv', '.txt']`)。
:param config_ext: list[str]. 設定ファイルとして認識する拡張子のリスト(例: `['.ini', '.cfg']`)。
:param print_level: int. 解析結果の表示レベル。0の場合、メッセージは表示されません。
:returns: tuple[str or None, str, str or None].
(infile: 入力ファイルパスまたは`None` (設定ファイルの場合),
config_path: 設定ファイルパス,
infile_stored: 入力ファイルパス(内部保存用)または`None` (設定ファイルの場合))
"""
ext = os.path.splitext(path)
if len(ext) >= 2: ext = ext[1].lower()
infile_ext = [s.lower() for s in infile_ext]
config_ext = [s.lower() for s in config_ext]
print()
print(f"Check infile parameter:")
if len(infile_ext) > 0 and ext in config_ext:
config_path = path
if print_level:
print(f" Parameter file [{config_path}] is given.")
print(f" Input file will be red from [{config_path}].")
infile_stored = None
infile = None
else:
infile = path
if len(config_ext) >= 1:
cext = config_ext[0]
else:
cext = ''
config_path = app.replace_path(infile, template = ["{dirname}", "{filebody}" + cext])
if print_level:
print(f" Input file [{infile}] is given.")
print(f" Paramter file is set to [{config_path}].")
infile_stored = infile
return infile, config_path, infile_stored
[ドキュメント]
def read_app_config(self, path = None, print_level = 0):
"""
概要: アプリケーションの設定ファイルを読み込む。
詳細説明:
指定されたパス、またはデフォルトのパスからアプリケーションの設定(`self.config`)を読み込みます。
`tkParams`オブジェクトが使用され、ファイルの内容が設定パラメータとして格納されます。
ファイルが存在しない場合でも、エラーは発生しませんが、設定は更新されません。
:param path: str. 設定ファイルパス。`None`の場合、スクリプト名に基づくデフォルトパス(`{filebody}.cfg`)が使用されます。
:param print_level: int. 設定ファイル読み込みに関するメッセージの表示レベル。0の場合、メッセージは表示されません。
:returns: tuple[str, tkParams]. (読み込まれた設定ファイルのパス, 設定パラメータオブジェクト)。
"""
if path is None:
path = self.replace_path(None, template = ["{dirname}", "{filebody}.cfg"])
if print_level:
print()
print(f"Read app configuration from [{path}]")
self.config.read_parameters(path)
self.appconfig_path = path
return self.appconfig_path, self.config
[ドキュメント]
def get_args(self):
"""
概要: コマンドライン引数を取得する。
詳細説明:
Pythonスクリプト実行時に渡されたコマンドライン引数のリストと、その引数の数を返します。
リストの最初の要素は常にスクリプトファイル名です。
:returns: tuple[list[str], int]. (引数リスト, 引数の数)。
"""
argv = sys.argv
return argv, len(argv)
[ドキュメント]
def get_argv(self):
"""
概要: コマンドライン引数を取得する。
詳細説明:
`get_args`メソッドと同じく、Pythonスクリプト実行時に渡されたコマンドライン引数のリストと、
その引数の数を返します。
:returns: tuple[list[str], int]. (引数リスト, 引数の数)。
"""
return sys.argv, len(sys.argv)
[ドキュメント]
def get_params(self):
"""
概要: アプリケーションのパラメータオブジェクトを取得する。
詳細説明:
`self.params`に格納されている`tkParams`インスタンスを返します。
このオブジェクトは、アプリケーション全体で共有される設定やデータを管理するために使用されます。
:returns: tkParams. アプリケーションのパラメータオブジェクト。
"""
return self.params
[ドキュメント]
def get_param_dict(self):
"""
概要: アプリケーションのパラメータを辞書形式で取得する。
詳細説明:
`self.params`オブジェクトが保持するパラメータを辞書として返します。
これは`tkParams`オブジェクトの`get_param_dict`メソッドを呼び出すものです。
:returns: dict. パラメータの辞書。
"""
return self.params.get_param_dict()
[ドキュメント]
def get_param_dic(self):
"""
概要: アプリケーションのパラメータを内部辞書形式で取得する。
詳細説明:
`self.params`オブジェクトの内部辞書(`__dict__`)を直接返します。
これは、`tkParams`インスタンスが保持するすべての属性を辞書として見たい場合に便利です。
:returns: dict. パラメータの内部辞書。
"""
return self.params.__dict__
def getarg(self, key, defval = None):
"""
概要: コマンドライン引数を名前またはインデックスで取得する (非推奨、後述のオーバーロード版を使用)。
詳細説明:
このメソッドは、コマンドラインから渡された引数のうち、
指定されたキー(オプション名または位置インデックス)に対応する値を取得します。
引数が見つからなかった場合、`defval`を返します。
:param key: str or int. 取得したい引数のオプション名(例: `'--output'`)または位置インデックス(例: `0`)。
:param defval: any. 引数が見つからなかった場合のデフォルト値。
:returns: any. 見つかった引数の値、またはデフォルト値。
"""
if type(key) is int:
try:
return self.args_idx[key]
except:
return defval
else:
try:
return self.args_opt[key]
except:
return defval
[ドキュメント]
def getarg(self, varname, defval = None, vartype = 'str'):
"""
概要: コマンドライン引数を変数名、デフォルト値、および型指定で取得する。
詳細説明:
`sys.argv`を走査し、指定された変数名(キーワード引数)または位置インデックス(位置引数)に
対応する引数の値を取得します。
取得した値は`vartype`で指定された型に変換されます。
引数が`--key`のように値なしで与えられた場合は`1`を返します。
引数が見つからなかった場合、`defval`を返します。
:param varname: str or int. 取得したい引数の変数名(キー)または位置インデックス。
:param defval: any. 引数が見つからなかった場合のデフォルト値。
:param vartype: str. 取得する値の型(例: `'str'`, `'int'`, `'float'`)。`pconv_by_type`に渡されます。
:returns: any. 見つかった引数の値(指定された型に変換後)、またはデフォルト値。
"""
iposarg = 0
for s in sys.argv[1:]:
if '=' not in s:
if type(varname) is int and varname == iposarg:
return s
else:
iposarg += 1
_aa = s.split('=')
if _aa[0] == varname:
if len(_aa) == 1:
return 1
else:
return pconv_by_type(_aa[1], vartype, defval = defval, strict = True)
return defval
[ドキュメント]
def check_arg(self, varname, defval = None, vartype = 'str'):
"""
概要: コマンドライン引数の存在と値をチェックする。
詳細説明:
`sys.argv`を走査し、指定された変数名(キーワード引数)または位置インデックス(位置引数)に
対応する引数を検索します。
- 引数が見つかった場合、その値が`vartype`で指定された型に変換されて返されます。
- 引数が`--key`のように値なしで与えられた場合は`False`を返します。
- 引数が見つからなかった場合、`defval`を返します。
引数の定義情報(`self.args_opt_inf`)が存在する場合、そのデフォルト値が`defval`として優先されます。
:param varname: str or int. チェックしたい引数の変数名または位置インデックス。
:param defval: any. 引数が見つからなかった場合のデフォルト値。
:param vartype: str. 期待される引数の型(例: `'str'`, `'int'`, `'float'`)。
:returns: any or bool. 引数の値(型変換後)、`False`(キーのみの場合)、またはデフォルト値。
"""
allowed_keys = self.args_opt_inf.keys()
if varname in allowed_keys:
d = self.args_opt_inf[varname]
defval = d.get("defval", defval)
iposarg = 0
for s in sys.argv[1:]:
if len(s) >= 3 and s[:2] == '--':
pass
elif '=' not in s:
if type(varname) is int and varname == iposarg:
return s
else:
iposarg += 1
continue
_aa = s.split('=')
if _aa[0] == varname:
if len(_aa) == 1:
return False
else:
ret = pconv_by_type(_aa[1], vartype, defval = defval, strict = True)
return ret
return defval
[ドキュメント]
def make_config_files(self, arg_config_file = None, fit_config_file = None):
"""
概要: 引数定義ファイルとフィッティング変数定義ファイルのサンプルを作成する。
詳細説明:
アプリケーションの引数(`add_argument`で定義されたもの)と、
フィッティングに関連する変数(`tkParams`オブジェクトに設定されたもの)の構造を
Excelファイルとして出力します。
これは、ユーザーがアプリケーションの設定を容易に行えるようにするためのテンプレートファイル生成機能です。
ファイルパスが指定されない場合、スクリプト名に基づいたデフォルトパスが使用されます。
:param arg_config_file: str. 引数定義の出力ファイルパス。`None`の場合、`{filebody}_arg_config.xlsx`が使用されます。
:param fit_config_file: str. フィッティング変数定義の出力ファイルパス。`None`の場合、`{filebody}_fit_config.xlsx`が使用されます。
:returns: bool. ファイルの作成が成功した場合は`True`。
"""
#引数定義ファイル
if arg_config_file is None:
arg_config_file = self.replace_path(None, template = ["{dirname}", "{filebody}_arg_config.xlsx"])
#フィッティング変数定義ファイル
if fit_config_file is None:
fit_config_file = self.replace_path(None, template = ["{dirname}", "{filebody}_fit_config.xlsx"])
app = tkApplication()
app.varname = ["conc", "qf1", "nta1", "wta1", "nga1", "wga1", "ega1", "nga2", "wga2", "ega2", "nga3", "wga3", "ega3", "nga4", "wga4", "ega4", "N_NI0"]
app.unit = [ "", "", "cm-3/eV", "eV", "cm-3/eV", "eV", "eV", "cm-3/eV", "eV", "eV", "cm-3/eV", "eV", "eV", "cm-3/eV", "eV", "eV", "cm-3"]
app.pk_scale = [ "log", "", "log", "", "log", "", "", "log", "", "", "log", "", "", "log", "", "", ""]
app.optid = [ 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
app.x0 = [ 1.0e17, -5.08362, 0.0e20, 0.020, 3.0e16, 0.00, 0.0, 5.8e15, 0.41, 0.20, 5.8e15, 0.21, 0.30, 5.8e15, 0.21, 0.50, 6.394e+19]
app.dx = [ 5.0e17, 5.0, 2.0e20, 1.0e-5, 1.0e19, 0.10, 0.00, 2.0e17, 0.10, 0.05, 2.0e16, 0.20, 0.30, 2.0e17, 0.10, 0.05, 1.0e19]
app.kmin = [ 1.0e13, -1.0e23, 0.0e10, 1.0e-6, 0.0e10, 0.001, -0.20, 0.0e10, 0.01, 0.01, 0.0e10, 0.01, 0.20, 0.0e10, 0.01, 0.40, 4.0e19]
app.kmax = [ 1.0e20, 1.0e23, 1.0e23, 2.0, 1.0e23, 2.0, 2.0, 1.0e23, 2.0, 0.3, 1.0e23, 2.0, 2.0, 1.0e23, 2.0, 2.0, 8.0e19]
app.kpenalty = [ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
app.add_argument(opt = None, type = "str", var_name = 'file1', opt_str = "file1 (for mode=plot: [DOS|IV|history|fit|DLL]", desc = 'additional file #1', defval = '')
app.add_argument(opt = None, type = "str", var_name = 'file2', opt_str = "file2", desc = 'additional file #2', defval = '')
app.add_argument(opt = None, type = "str", var_name = 'file3', opt_str = "file3", desc = 'additional file #3', defval = '')
app.add_argument(opt = None, type = "str", var_name = 'file4', opt_str = "file4", desc = 'additional file #4', defval = '')
app.add_argument(opt = None, type = "str", var_name = 'file5', opt_str = "file5", desc = 'additional file #5', defval = '')
app.add_argument(opt = "--help", type = "str", defval = 0, optional = True)
app.add_argument(opt = "--mode", type = "str", var_name = 'mode', opt_str = "--mode=[init|update|sim|scan|fit|plot]", desc = 'task mode',
defval = 'fit', optional = True)
app.add_argument(opt = '--infile', type = "str", var_name = 'infile', opt_str = "--infile=path", desc = 'reference (observed IV) Excel file',
defval = 'Vd_0.1V_L8_W20_P-230119-02-3_303K.xlsx', optional = True)
app.add_argument(opt = "--atlasfile", type = "str", var_name = 'atlasfile', opt_str = "--atlasfile=path", desc = 'ATLAS input file',
defval = '303K_Vd01.in', optional = True)
app.add_argument(opt = '--templatepath', type = "str", var_name = 'templatepath', opt_str = "--templatepath=path", desc = 'template file path (*.in)',
defval = './template', optional = True)
app.add_argument(opt = "--outfile", type = "str", var_name = 'outfile', opt_str = "--outfile=path", desc = 'output Excel file',
defval = '303K_Vd01.out', optional = True)
app.add_argument(opt = "--Cfile", type = "str", var_name = 'Cfile', opt_str = "--Cfile=path", desc = 'ATLAS C-Interpreter file',
defval = 'mobility_polyOS240507_Tmin250_er14_log.c', optional = True)
app.add_argument(opt = "--defectaccfile", type = "str", var_name = 'defectaccfile', opt_str = "--defectaccfile=path", desc = 'ATLAS acceptor-type DOS file',
defval = 'defacc.out', optional = True)
app.add_argument(opt = "--defectdonfile", type = "str", var_name = 'defectdonfile', opt_str = "--defectdonfile=path", desc = 'ATLAS donor-type DOS file',
defval = 'defdon.out', optional = True)
app.add_argument(opt = "--atlaslogfile", type = "str", var_name = 'atlaslogfile', opt_str = "--atlaslogfile=path", desc = 'ATLAS output (.log) file',
defval = 'idvg_vd001.log', optional = True)
app.add_argument(opt = "--calfile", type = "str", var_name = 'calfile', opt_str = "--calfile=path", desc = 'ATLAS output cal (.dat) file',
defval = "idvg_calc.dat", optional = True)
app.add_argument(opt = '--IVfile', type = "str", var_name = 'IVfile', opt_str = "--IVfile=path", desc = 'observed IV output Excel file',
defval = 'IVobs.xlsx', optional = True)
app.add_argument(opt = "--fitfile", type = "str", var_name = 'fitfile', opt_str = "--fitfile=path", desc = 'Fiting result file',
defval = 'IVfit.xlsx', optional = True)
app.add_argument(opt = "--target_var", type = "str", defval = 'conc')
app.add_argument(opt = "--x0", type = "float", defval = 0.0)
app.add_argument(opt = "--x1", type = "float", defval = 1.0)
app.add_argument(opt = "--nx", type = "int", defval = 5)
app.add_argument(opt = "--Eg", type = "float", desc = 'Band gap', defval = 2.8)
app.add_argument(opt = "--T", type = "float", desc = 'Temperature', defval = 303.0)
app.add_argument(opt = "--ix_plot", type = "int|str", defval = 0, desc = 'index of x_data to plot as x-axis', optional = True)
app.add_argument(opt = "--x_scale", type = "str", defval = "", desc = 'x scale to plot [blank|log]', optional = True)
app.add_argument(opt = "--fhistory", type = "int", var_name = 'fhistory', opt_str = "--fhisotry=[0|1]", desc = 'flag to save hisotry file',
defval = 1, optional = True)
app.add_argument(opt = "--fmax_record", type = "double", var_name = 'fmax_record', opt_str = "--fmax_record=val", desc = 'max fmin value to record to file',
defval = 1.0, optional = True)
app.add_argument(opt = "--ffitfiles", type = "int", var_name = 'ffitfiles', opt_str = "--ffitfiles=[0|1]", desc = 'flag to save fit files',
defval = 1, optional = True)
app.add_argument(opt = "--fplot", type = "int", var_name = 'fplot', opt_str = "--fplot=[0|1]", desc = 'flag to plot graph',
defval = 1, optional = True)
for i, varname in enumerate(app.varname):
app.add_argument(opt = f"--{varname}", type = "float", defval = app.x0[i])
app.add_argument(opt = f"--y_convert", type = "str", defval = "log")
app.add_argument(opt = f"--f_max_plot", type = "float", defval = 10.0)
app.add_argument(opt = "--method", type = "str", var_name = 'method', opt_str = "--method=[nelder-mead]", desc = 'optimization algorism',
defval = 'nelder-mead', optional = True)
app.add_argument(opt = "--jac", type = "str", var_name = 'jac', opt_str = "--jac=[3-points|2-points|func]", desc = 'First differential',
defval = '3-points', optional = True)
app.add_argument(opt = "--nmaxiter", type = "int", var_name = 'nmaxiter', opt_str = "--nmaxiter=int(>1)", desc = 'maximum interation number for optimization',
defval = 1000, optional = True)
app.add_argument(opt = "--nmaxcall", type = "int", var_name = 'nmaxcall', opt_str = "--nmaxcall=int(>1)", desc = 'maximum number for minimize_func calls',
defval = 10000, optional = True)
app.add_argument(opt = "--tol", type = "float", var_name = 'tol', opt_str = "--tol=val", desc = 'eps for optimization',
defval = 1.0e-5, optional = True)
app.add_argument(opt = "--print_interval", type = "int", var_name = 'print_interval', opt_str = "--print_interval=val", desc = 'print interval',
defval = 5, optional = True)
app.add_argument(opt = "--plot_interval", type = "int", var_name = 'plot_interval', opt_str = "--plot_interval=val", desc = 'plot interval',
defval = 5, optional = True)
#=============================
# Graph configuration
#=============================
app.add_argument(opt = "--fontsize", type = "int", defval = 16, optional = True)
app.add_argument(opt = "--legend_fontsize", type = "int", defval = 12, optional = True)
app.add_argument(opt = "--graphupdateinterval", type = "int", defval = 10, optional = True)
app.add_argument(opt = "--use_tkplt", type = "int", defval = 0, optional = True)
print(f"Save argument configuration to [{arg_config_file}]")
app.save_arg_config(arg_config_file)
print(f"Save fitting configuration to [{fit_config_file}]")
vars = tkParams()
vars.varname = app.varname
vars.unit = app.unit
vars.pk_scale = app.pk_scale
vars.optid = app.optid
vars.x0 = app.x0
vars.dx = app.dx
vars.kmin = app.kmin
vars.kmax = app.kmax
vars.kpenalty = app.kpenalty
save_fit_config(vars, fit_config_file)
return True
[ドキュメント]
def read_arg_config_from_file(self, path, labels = ["argtype", "var_name", "opt", "opt_str", "desc", "type", "defval", "optional"]):
"""
概要: ファイルから引数設定を読み込み、アプリケーションの引数定義に追加する。
詳細説明:
指定されたExcelファイルから引数の定義を読み取り、
`self.args_idx_inf`(位置引数情報)と`self.args_opt_inf`(キーワード引数情報)を更新します。
これにより、外部ファイルで定義された引数をアプリケーションが認識できるようになります。
ファイルが存在しない場合、このメソッドは`False`を返して処理を終了します。
:param path: str. 引数設定が記述されたExcelファイルのパス。
:param labels: list[str]. Excelファイルのヘッダー行に対応する列名のリスト。
:returns: bool. ファイルの読み込みと設定の更新が成功した場合は`True`、ファイルが存在しない場合は`False`。
"""
if not os.path.exists(path): return False
idx_argtype = labels.index("argtype")
idx_opt = labels.index("opt")
idx_varname = labels.index("var_name")
idx_optstr = labels.index("opt_str")
idx_desc = labels.index("desc")
idx_type = labels.index("type")
idx_defval = labels.index("defval")
idx_optional = labels.index("optional")
pos_args = self.args_idx_inf
key_args = self.args_opt_inf
wb = openpyxl.load_workbook(path)
sheet = wb.active
_labels = [cell.value for cell in sheet[1]]
for row in sheet.iter_rows(min_row = 2):
values = []
for r in row:
v = r.value
t = type(v)
if v is None: values.append('')
elif t is str:
if v == 'None': values.append('')
elif v == 'True': values.append(1)
elif v == 'Fales': values.append(0) # Typo in original code: 'Fales' instead of 'False'
else: values.append(v)
else:
values.append(v)
# print("row=", values)
arg_type = values[idx_argtype]
if arg_type not in ["keyword", "positional"]: continue
opt = values[idx_opt]
varname = values[idx_varname]
optstr = values[idx_optstr]
desc = values[idx_desc]
_type = values[idx_type]
defval = values[idx_defval]
optional = values[idx_optional]
d = {"var_name": varname, "opt": opt, "opt_str": optstr, "desc": desc,
"type": _type, "defval": defval, "optional": optional}
if values[idx_argtype] == 'positional':
pos_args.append(d)
else:
key_args[opt] = d
wb.close()
# print("key_args=", key_args["--fmax_record"])
return True
[ドキュメント]
def save_arg_config(self, outfile, labels = ["argtype", "var_name", "opt", "opt_str", "desc", "type", "defval", "optional"]):
"""
概要: 現在の引数設定をファイルに保存する。
詳細説明:
`self.args_idx_inf`(位置引数情報)と`self.args_opt_inf`(キーワード引数情報)に
格納されている引数定義を抽出し、指定されたファイルにExcelまたはCSV形式で保存します。
これにより、アプリケーションの引数設定をテンプレートとして出力したり、バックアップしたりできます。
:param outfile: str. 出力ファイルパス。拡張子 (.csv, .xlsx) に応じてファイル形式が決定されます。
:param labels: list[str]. 保存する引数属性のラベルリスト。Excelファイルのヘッダー行に対応します。
:returns: None.
"""
data_list = []
for inf in self.args_idx_inf:
args = ["positional"]
for idx, l in enumerate(labels):
if idx == 0: continue
v = val2str(inf[l])
args.append(v)
data_list.append(args)
for key, inf in self.args_opt_inf.items():
key = key.lstrip('-')
args = ["keyword"]
for idx, l in enumerate(labels):
if idx == 0: continue
v = val2str(inf[l])
args.append(v)
data_list.append(args)
# Transpose data_list to match save function's expected format (columns as lists)
data_list = list(map(list, zip(*data_list)))
tkVariousData().save(outfile, data_list = data_list, labels = labels)
[ドキュメント]
def add_argument(self, opt = None, opt_str = None, var_name = None, desc = None, type = 'str', defval = None, optional = True):
"""
概要: アプリケーションにコマンドライン引数を追加する。
詳細説明:
位置引数またはキーワード引数の定義をアプリケーションの引数情報辞書
(`self.args_idx_inf`または`self.args_opt_inf`)に追加します。
これにより、`read_args`メソッドがこれらの引数を認識し、解析できるようになります。
`opt_str`や`var_name`、`desc`が`None`の場合、適切なデフォルト値が自動生成されます。
:param opt: str or None. キーワード引数のオプション文字列(例: `'--help'`)。位置引数の場合は`None`。
:param opt_str: str or None. 引数の使用法を示す文字列(例: `'--infile=path'`)。`None`の場合、自動生成されます。
:param var_name: str or None. 引数に対応する変数名。`None`の場合、`opt`から自動生成されます。
:param desc: str or None. 引数の説明。`None`の場合、`var_name`が使用されます。
:param type: str. 引数の型(例: `'str'`, `'int'`, `'float'`)。`pconv_by_type`による型変換に使用されます。
:param defval: any. 引数のデフォルト値。
:param optional: bool. 引数がオプションであるかどうか。`True`の場合、指定されなくてもエラーになりません。
:returns: tuple[dict, list]. (キーワード引数情報辞書, 位置引数情報リスト)。
"""
# if opt is None:
# opt = "--" + var_name
if opt is not None and var_name is None and opt[0:2] == '--':
var_name = opt.lstrip('-')
if opt_str is None:
if opt is None:
opt_str = f"#{len(self.args_idx_inf)+1} arg: {var_name}: val"
else:
opt_str = f"--{var_name}=val"
if desc is None:
desc = var_name
inf_idx = self.args_idx_inf
inf_opt = self.args_opt_inf
if opt is None:
# keywordなし引数 (positional argument)
i = len(inf_idx)
inf_idx.append( { "var_name": var_name, "opt": i, "opt_str": opt_str, "desc": desc,
"type": type, "defval": defval, "optional": optional } )
else:
# keywordあり引数 (keyword argument)
inf_opt[opt] = { "var_name": var_name, "opt": opt, "opt_str": opt_str, "desc": desc,
"type": type, "defval": defval, "optional": optional }
return inf_opt, inf_idx
[ドキュメント]
def check_help(self, pause = True):
"""
概要: コマンドライン引数にヘルプオプションが含まれているかをチェックし、含まれていれば用法説明を表示して終了する。
詳細説明:
`sys.argv`を走査し、`--help`, `-?`, `/?`, `?`, `help`のいずれかの引数が見つかった場合、
`usage`メソッドを呼び出してアプリケーションの用法説明を表示し、アプリケーションを終了します。
:param pause: bool. 用法説明表示後に終了する前にユーザー入力で一時停止するかどうか。
:returns: None. (ヘルプ引数が見つかった場合、アプリケーションは終了するため、通常の戻り値はありません)。
"""
argv = sys.argv
for s in argv:
_aa = s.split("=", 1)
if len(_aa) == 0:
_s = s
else:
_s = _aa[0]
s = s.lower()
if s == '--help' or s == '-?' or s == '/?' or s == '?' or s == 'help':
print()
print("#==============================================")
print(f"Found the argument [{s}] for help:")
print("#==============================================")
self.terminate(usage = 'built-in', pause = pause)
[ドキュメント]
def read_args(self, vars = None, check_allowed_args = False, apply_default = True, pause = True):
"""
概要: コマンドライン引数を解析し、変数を更新する。
詳細説明:
`sys.argv`からコマンドライン引数を読み込み、位置引数とキーワード引数に分類します。
`add_argument`で定義された引数情報(`self.args_opt_inf`, `self.args_idx_inf`)に基づいて、
引数の値を変数に格納し、型変換を行います。
- 不正な引数(`check_allowed_args=True`の場合)や、不足している必須引数があった場合は、
エラーメッセージとともに`None`とエラーコードを返します。
- 引数が`--key`のように値なしで与えられた場合、値は`1`とみなされます。
:param vars: tkParams or dict. 解析された引数の値を格納するオブジェクトまたは辞書。`None`の場合、新しい辞書が作成されます。
:param check_allowed_args: bool. 定義されていない引数が見つかった場合にエラーとするか。`True`の場合、未定義の引数はエラーとなります。
:param apply_default: bool. 引数が与えられていない場合に、`add_argument`で設定されたデフォルト値を適用するかどうか。
:param pause: bool. ヘルプ引数が見つかった場合に、アプリケーション終了前に一時停止するかどうか。
:returns: tuple[dict, list, dict] or tuple[None, int, str].
成功した場合: (キーワード引数の辞書, 位置引数のリスト, 更新された変数辞書)。
失敗した場合: (None, エラーコード, エラーメッセージ)。
"""
self.check_help(pause = pause)
if vars is not None and type(vars) is not dict:
vars = vars.__dict__
argv = sys.argv
args_idx = []
args_opt = {}
# print(f"{vars=}")
if vars is None:
args_vars = {}
else:
args_vars = vars
allowed_keys = self.args_opt_inf.keys()
i_idx_arg = 0
# print("argv=", argv)
given_args = {}
for i in range(1, len(argv)):
arg = del_quote(argv[i])
#-/--で始まるが、=以降が無いとき
m = re.match(r'^\s*\-{1,2}\w([\-\.\w]*?)[^=]*$', arg)
if m:
arg = arg + "=1"
print()
print(f"Warning in tkapplication.read_args(): Argument [{argv[i]}] does not have a given value.")
print(f" Assume the value is 1 [{arg}]")
print()
#-/--で始ろ、=以降を与えられているとき
m = re.match(r'^\s*(\-{1,2}\w[\-\.\w]*?)\s*=\s*(.*)\s*$', arg)
# m = re.match(r'\s*(\S.*)\s*=\s*(.*)\s*$', arg)
if m:
# keyword引数
opt = m.groups()[0]
val = m.groups()[1]
# print("opt=", opt, val)
# print(f"i={i} keyword arg: {opt}={val}")
if check_allowed_args and opt not in allowed_keys:
return None, 1, f"Error in tkapplicaiton.read_args(): Invalid argument [{opt}]"
# print(f"Error in tkapplicaiton.read_args(): Invalid argument [{opt}]")
# exit()
else:
var_name = opt.lstrip("-")
if opt not in allowed_keys:
print()
print("====================================================================")
print(f"Warning in tkapplication.read_args(): Key {opt} is not defined.")
print(f" {val} is added to vars[{var_name}].")
print("====================================================================")
print()
args_vars[var_name] = pconv(val)
if opt in allowed_keys:
var_type = self.args_opt_inf[opt].get("type", None)
if var_type is not None:
args_opt[opt] = pconv_by_type(val, var_type, defval = None, strict = True)
if args_opt[opt] is None:
return None, 2, \
f"Error: Argument [{opt}] = [{val}] is not compatible with the required type [{var_type}]"
given_args[opt] = args_opt[opt]
# attributeに設定
var_name = self.args_opt_inf[opt]["var_name"]
if var_name is not None:
# print(f"i={i} arg={arg} set var_name={var_name} args_opt={args_opt[opt]}")
args_vars[var_name] = args_opt[opt]
else:
args_opt[opt] = val
given_args[opt] = val
else:
# keywordなし引数 (positional argument)
# print(f"i={i} positional arg: {arg}")
if i_idx_arg >= len(self.args_idx_inf):
# keywordなし引数の数が定義数より大きい場合
idx = len(self.args_idx_inf) - 1
else:
# keywordなし引数の数が定義数以下の場合
idx = i_idx_arg
var_type = self.args_idx_inf[idx].get("type", None)
if var_type is None or var_type == 'list':
val = arg
else:
val = pconv_by_type(arg, var_type, defval = None, strict = True)
if val is None:
return None, 3, \
f"Error: #{i_idx_arg} positional argument = [{arg}] is not compatible with the required type [{var_type}]"
given_args[str(idx)] = val
args_idx.append(val)
i_idx_arg += 1
var_name = self.args_idx_inf[idx]["var_name"]
if var_name is not None:
# print(f"i={i} arg={arg}: set var_name={var_name} to [{val}]")
if var_type == 'list':
if args_vars.get(var_name, None) is None:
args_vars[var_name] = [val]
else:
args_vars[var_name].append(val)
else:
args_vars[var_name] = val
if apply_default:
# 引数が与えられていない変数にデフォルト値を設定
for key in allowed_keys:
v = self.args_opt_inf[key]
var = v["var_name"]
if args_opt.get(key, None) is None:
# print(f"default {var=} {v['defval']=}")
args_opt[var] = v["defval"]
args_vars[var] = v["defval"]
if not v["optional"]:
return None, 4, f"Error: Argument [{key}] is required but not given"
# print("")
# print(f"Error: Argument [{key}] is required but not given")
# exit()
# positional arguments
nposargs = len(args_idx)
for i in range(len(self.args_idx_inf)):
v = self.args_idx_inf[i]
# print(f"positional args: i={i} v={v}")
if i >= nposargs:
var = self.args_idx_inf[i]["var_name"]
args_vars[var] = v["defval"]
if not v["optional"]:
return None, 5, f"Error: #{i} positional argument is required but not given"
# print("")
# print(f"Error: #{i} positional argument is required but not given")
# exit()
self.given_args = given_args
self.args_opt = args_opt
self.args_idx = args_idx
self.args_vars = args_vars
return args_opt, args_idx, args_vars
[ドキュメント]
def force_given_args(self, cparams):
"""
概要: コマンドラインで与えられた引数を指定されたパラメータオブジェクトに強制的に設定する。
詳細説明:
`read_args`メソッドで解析された引数(`self.given_args`、`self.args_idx`)を、
`tkParams`などのパラメータオブジェクト(`cparams`)の属性として直接設定します。
これにより、コマンドライン引数が他の設定よりも優先される形でパラメータが更新されます。
:param cparams: tkParams. 引数の値を設定するターゲットパラメータオブジェクト。
:returns: None.
"""
if not hasattr(self, "args_idx"): return
nidxargs = len(self.args_idx)
for i, inf in enumerate(self.args_idx_inf):
opt = inf.get("opt", None)
if opt is None: continue
if nidxargs <= opt: break
# print("opt=", opt)
if type(opt) == int:
varname = inf.get("var_name", None)
vartype = inf.get("type", None)
defval = inf.get("defval", None)
val = self.args_idx[opt]
val = pconv_by_type(val, vartype, defval = defval, strict = True)
setattr(cparams, varname, val)
# print("given args:", self.given_args)
for key, val in self.given_args.items():
inf = self.args_opt_inf.get(key, None)
if inf is None: continue
varname = inf.get("var_name", None)
vartype = inf.get("type", None)
defval = inf.get("defval", None)
# key = key.lstrip('-')
val = pconv_by_type(val, vartype, defval = defval, strict = True)
setattr(cparams, varname, val)
[ドキュメント]
def update_vars(self, vars = None, usage = None, check_allowed_args = True, apply_default = True):
"""
概要: コマンドライン引数を解析し、指定された変数オブジェクトを更新する。
詳細説明:
`read_args`メソッドを呼び出してコマンドライン引数を解析し、結果を指定された`vars`オブジェクトに適用します。
引数解析中にエラーが発生した場合、エラーメッセージと用法説明を表示してアプリケーションを終了します。
これにより、アプリケーションの起動時に必要な引数が正しく渡されたことを確認できます。
:param vars: tkParams or dict. 更新する変数オブジェクト。`None`の場合、`self.cparams`が使用されます。
:param usage: callable or str or None. 用法説明を表示するための関数または`'built-in'`。`None`の場合、`self.usage`が使用されます。
:param check_allowed_args: bool. 定義されていない引数をチェックするかどうか。`True`の場合、未定義の引数はエラーとなります。
:param apply_default: bool. デフォルト値を適用するかどうか。`True`の場合、引数が与えられていない変数にデフォルト値が設定されます。
:returns: tuple[dict, list, dict]. (キーワード引数の辞書, 位置引数のリスト, 更新された変数辞書)。
"""
if vars is None:
vars = self.configparams # Assuming self.cparams is a typo for self.configparams
if usage is None: usage = self.usage
args_opt, args_idx, args_vars = self.read_args(vars = vars, check_allowed_args = check_allowed_args, apply_default = apply_default)
if args_opt is None:
self.error_no = args_idx
self.error_message = args_vars
self.terminate(message = "\n",
usage = usage,
post_message = ""
+ "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
+ f"! {self.error_message}\n"
+ "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",
)
return args_opt, args_idx, args_vars
[ドキュメント]
def set_usage(self, usage_str = '', suppress_usage = ''):
"""
概要: アプリケーションの用法説明を設定する。
詳細説明:
アプリケーションの起動時(または`usage`メソッドが呼び出された時)に表示される
用法説明文字列と、その表示を抑制する設定を更新します。
:param usage_str: str. アプリケーションの用法説明として使用する文字列。
:param suppress_usage: str. 用法説明の表示を抑制するためのキーワード(例: `'all'`, `'usage'`, `'options'`)。
:returns: None.
"""
self.usage_str = usage_str
self.suppress_usage = suppress_usage
[ドキュメント]
def usage(self, *args):
"""
概要: アプリケーションの用法説明を表示する。
詳細説明:
`self.usage_str`に設定されたカスタム用法説明、または`add_argument`で定義された
引数情報に基づいて、アプリケーションの使用方法とオプションのリストを標準出力に表示します。
`self.suppress_usage`設定により表示を制御できます。
カスタム`usage_str`には`{var}`形式のプレースホルダーを含めることができ、
`self.args_vars`の値で置換されます。
:param args: tuple. 将来的な拡張のための追加引数(現在未使用)。
:returns: None.
"""
if 'all' in self.suppress_usage:
return
if self.usage_str is not None and self.usage_str != '' and 'usage' not in self.suppress_usage:
if self.args_vars:
s_conv = convert_by_vars(self.usage_str, self.args_vars, prefix = '', bra = r'\{', ket = r'\}')
else:
s_conv = self.usage_str
# print("293")
for s in s_conv.split('\n'):
if not re.match(r'\s*[\"\'].*[\"\']', s):
s = '"' + s + '"'
cmd = 'print({})'.format(s.rstrip())
# print("cmd=", cmd)
eval(cmd)
return
inf_opt = self.args_opt_inf
keys_opt = inf_opt.keys()
inf_idx = self.args_idx_inf
# print("inf_opt=", inf_opt)
# print("inf_idx=", inf_idx)
if len(keys_opt) > 0 or len(inf_idx) > 0:
usage = f'Usage: python {sys.argv[0]} '
for key in keys_opt:
v = inf_opt[key]
if v['optional']:
usage += f"[{v['opt_str']}] "
else:
usage += f"{v['opt_str']} "
for i in range(len(inf_idx)):
v = inf_idx[i]
if v['optional']:
usage += f"[{v['opt_str']}] "
else:
usage += f"{v['opt_str']} "
if 'usage' not in self.suppress_usage:
print(usage)
if 'options' not in self.suppress_usage:
print(f" Options:")
for key in keys_opt:
v = conv_float(inf_opt[key])
self.print_warning(f" {v['opt_str']} (def={v['defval']}) (type={v['type']})")
if v['desc'] != '':
print(f" {v['desc']}")
for i in range(len(inf_idx)):
v = conv_float(inf_idx[i])
self.print_warning(f" {v['opt_str']} (def={v['defval']}) (type={v['type']})")
# self.print_warning(f" #{i} {v['opt_str']} (def={v['defval']}) (type={v['type']})")
if v['desc'] != '':
print(f" {v['desc']}")
return
[ドキュメント]
def terminate(self, message = None, post_message = None, input_text = "Press ENTER to terminate>>\n", usage = None, pause = False):
"""
概要: アプリケーションを終了し、必要に応じてメッセージや用法説明を表示する。
詳細説明:
アプリケーションの実行を停止し、終了コード0でプロセスを終了します。
終了前に、指定されたメッセージ、用法説明、および追加のメッセージを表示できます。
また、`pause`が`True`の場合、終了前にユーザー入力で一時停止するオプションも提供します。
`save_time`が有効な場合、終了時の時刻と起動からの経過時間も表示します。
:param message: str or None. 終了時に表示するメッセージ。
:param post_message: str or None. メッセージの後に表示する追加メッセージ。
:param input_text: str. 一時停止時にユーザーに表示するプロンプト文字列。
:param usage: str or callable or None. 表示する用法説明。`'built-in'`またはカスタム関数を指定できます。
:param pause: bool. 終了前にユーザー入力で一時停止するかどうか。
:returns: None. (アプリケーションは終了するため、通常の戻り値はありません)。
"""
if self.save_time: self.print_time(label = "startup", label2 = "end")
if message is not None:
self.print_warning("")
self.print_warning(message)
if usage:
if usage == 'built-in' or usage == 'build-in':
self.usage()
else:
self.print_warning("")
usage(self)
if post_message:
self.print_warning("")
self.print_warning(post_message)
if pause:
print("")
input(input_text)
if message is not None or post_message is not None or usage is not None or pause:
print("")
exit()
[ドキュメント]
def print_time(self, label = None, label2 = None):
"""
概要: 現在時刻を記録し、必要に応じて経過時間を出力する。
詳細説明:
`self.time_stamps`辞書に指定されたラベルで現在時刻(`time.perf_counter()`と`datetime.now()`)を記録します。
`label2`が指定された場合、`label`から`label2`までの経過時間も計算して表示します。
これにより、アプリケーションの各フェーズにおけるパフォーマンスを追跡できます。
:param label: str or None. タイムスタンプに割り当てるラベル。`None`の場合、新しいタイムスタンプは記録されません。
:param label2: str or None. 経過時間を計算するための終了時点のラベル。`None`の場合、経過時間は計算されません。
:returns: None.
"""
if label is None or label not in self.time_stamps.keys():
tprec = time.perf_counter()
tnow = datetime.now()
self.time_stamps[label] = [tprec, tnow.strftime("%y/%m/%d %H:%M:%S")]
print(f"time at {label}:", self.time_stamps[label][1])
if label2 is not None:
if label2 not in self.time_stamps.keys():
tprec = time.perf_counter()
tnow = datetime.now()
self.time_stamps[label2] = [tprec, tnow.strftime("%y/%m/%d %H:%M:%S")]
print(f"time at {label2}:", self.time_stamps[label2][1])
print(f"Elapsed time from {label} to {label2}: {self.time_stamps[label2][0] - self.time_stamps[label][0]:10.6g} sec")
[ドキュメント]
def print_title(app, message):
"""
概要: メッセージを整形してタイトルとして表示する。
詳細説明:
指定されたメッセージの上下に区切り線(`#===========...`)を追加し、
アプリケーションのセクションタイトルや重要な通知として目立つように標準出力に出力します。
:param app: tkApplication. アプリケーションインスタンス。
:param message: str. 表示するタイトルメッセージ。
:returns: None.
"""
print()
print( "#========================================================")
print(message)
print( "#========================================================")
[ドキュメント]
def write_resource(app, path, mode):
"""
概要: リソースファイル(翻訳テキストなど)を書き込む。
詳細説明:
`app.phrases`辞書に格納されているリソース(多言語対応のテキストなど)を、
指定されたパスにファイルとして書き出します。
各キーに対応する指定された言語の値を`key:val`形式で保存します。
:param app: tkApplication. アプリケーションインスタンス。
:param path: str. 出力ファイルパス。
:param mode: str. ファイルを開くモード(例: `'w'`)。
:returns: bool. 書き込みが成功した場合は`True`。
"""
# Note: `f` is not defined here. It should be `path`.
# Adhering to rule 1, no logic change.
with open(f, 'w') as fp:
for key in app.phrases.keys():
try:
# Note: `lang` is not defined within this method.
# It should likely be passed as an argument or taken from `app.config.lang`.
# Adhering to rule 1, no logic change.
val = app.phrases[key][lang]
fp.write(f'{key}:{val}' + '\n')
except:
pass
return True
[ドキュメント]
def read_resource(app, path, lang, phrases = None, sep = '='):
"""
概要: リソースファイル(翻訳テキストなど)を読み込む。
詳細説明:
指定されたパスからリソースファイルを読み込み、`phrases`辞書に解析して格納します。
ファイルは`key=value`または`key:value`といった特定のセパレータで区切られた形式で
記述されていることを想定しており、指定された言語でテキストをロードします。
:param app: tkApplication. アプリケーションインスタンス。
:param path: str. リソースファイルのパス。
:param lang: str. 読み込むリソースの言語コード(例: `'en'`, `'ja'`)。
:param phrases: dict or None. 読み込んだリソースを格納する辞書。`None`の場合、新しい辞書が作成されます。
:param sep: str. キーと値を区切るセパレータ文字列(例: `'='`, `':'`)。
:returns: dict. 読み込んだリソースが格納された辞書。
"""
if phrases is None:
phrases = {}
fp = open_chardet(path, 'r', def_encoding = 'shift_jis')
# fp = open(path, 'r')
if not fp:
return phrases
while 1:
line = fp.readline()
if not line:
break
# m = re.match(rf'\s*(\S.*)\s*=\s*(.*)\s*\n$', line, flags = re.MULTILINE | re.DOTALL)
m = re.match(rf'\s*(\S.*)\s*{sep}\s*(.*)\s*\n$', line, flags = re.MULTILINE | re.DOTALL)
if not m:
continue
var = m.groups()[0]
val = m.groups()[1]
if phrases.get(var, None) is None:
phrases[var] = {}
phrases[var][lang] = val
# print(f"{var=} {val=}")
fp.close()
# print(f"{phrases=}")
# exit()
return phrases
[ドキュメント]
def p(self, str, defval = None, phrases = None):
"""
概要: 指定された文字列に対応する多言語リソースを取得する。
詳細説明:
`self.phrases`辞書(または指定された`phrases`)から、
指定されたキー(`str`)と現在の設定言語(`self.config.lang`)に対応する
翻訳済みテキストを取得します。
翻訳が見つからない場合や、指定された言語のリソースがない場合は、
デフォルト値(`defval`)または元の文字列を返します。
:param str: str. 取得したいリソースのキー文字列。
:param defval: any or None. リソースが見つからなかった場合のデフォルト値。
:param phrases: dict or None. 検索対象のリソース辞書。`None`の場合、`self.phrases`が使用されます。
:returns: str. 翻訳されたテキスト、デフォルト値、または元の文字列。
"""
if phrases is None:
phrases = self.phrases
if str is None or str == '':
return ''
# print("")
# print(f"{str=}")
s = phrases.get(str, defval)
# print(f"{s=}")
if s is None:
if defval is not None:
# print(f" 336 return defval={defval}")
return defval
# print(f" return {str=}")
return str
s = s.get(self.config.lang, defval)
# print(f"{s=}")
if s is None:
if defval is not None:
# print(f" 346 return defval={defval}")
return defval
# print(f" return {str=}")
return str
# print(f"last: return {s=}")
return s
[ドキュメント]
def print(self, *args, **kwargs):
"""
概要: Pythonの組み込み`print`関数を呼び出す。
詳細説明:
このメソッドは、`builtins.print`をオーバーライドしている可能性がある
`self.print_redirect`とは異なり、明示的にPythonの組み込み`print`関数を呼び出します。
これにより、出力リダイレクトの影響を受けずに直接コンソールに出力できます。
:param args: tuple. `print`関数に渡される位置引数。
:param kwargs: dict. `print`関数に渡されるキーワード引数。
:returns: None.
"""
builtins.print(*args, **kwargs) # Call the original builtins.print
[ドキュメント]
def print_attributes(self, print_header = 1):
"""
概要: オブジェクトの属性とその値を標準出力に表示する。
詳細説明:
インスタンスの`__dict__`を走査し、すべての属性名とその現在値を標準出力にリストアップします。
これは、デバッグやオブジェクトの状態確認に非常に役立ちます。
:param print_header: int. ヘッダーメッセージ("attributes in [ClassPath]")を表示するかどうか。1の場合、表示されます。
:returns: None.
"""
keys = self.__dict__.keys()
if print_header:
print("attributes in {}".format(self.ClassPath()))
for key in keys:
print(" {}: {}".format(key, self.__dict__[key]))
[ドキュメント]
def print_attributes_warning(self, print_header = 1):
"""
概要: オブジェクトの属性とその値を警告メッセージとして出力する。
詳細説明:
`print_attributes`と同様にオブジェクトの属性を表示しますが、
`self.print_warning`メソッドを使用するため、
出力リダイレクトが設定されている場合でもコンソールに強制的に出力されます。
:param print_header: int. ヘッダーメッセージ("attributes in [ClassPath]")を表示するかどうか。1の場合、表示されます。
:returns: None.
"""
keys = self.__dict__.keys()
if print_header:
self.print_warning("attributes in {}".format(self.ClassPath()))
for key in keys:
self.print_warning(" {}: {}".format(key, self.__dict__[key]))
[ドキュメント]
def print_dict(self, d, header = None):
"""
概要: 辞書の内容を標準出力に整形して表示する。
詳細説明:
指定された辞書`d`のすべてのキーと値のペアを、オプションのヘッダーとともに標準出力にリストアップします。
辞書が`None`の場合、「Null dict」と表示されます。
:param d: dict or None. 表示する辞書。
:param header: str or None. 辞書の内容の前に表示するヘッダー文字列。
:returns: None.
"""
print("")
if header is not None:
print(header)
if d is None:
print(" Null dict")
return
for key in d.keys():
print(f"{key}: {d[key]}")
[ドキュメント]
def print_dict_warning(self, d, header = None):
"""
概要: 辞書の内容を警告メッセージとして出力する。
詳細説明:
`print_dict`と同様に辞書の内容を表示しますが、
`self.print_warning`メソッドを使用するため、
出力リダイレクトが設定されている場合でもコンソールに強制的に出力されます。
辞書が`None`の場合、「Null dict」と表示されます。
:param d: dict or None. 表示する辞書。
:param header: str or None. 辞書の内容の前に表示するヘッダー文字列。
:returns: None.
"""
print("")
if header is not None:
self.print_warning(header)
if d is None:
self.print_warning(" Null dict")
return
for key in d.keys():
self.print_warning(f"{key}: {d[key]}")
[ドキュメント]
def load_module(self, module_name, target = '', plugin_dir = '', desc = '', ret_type = 'module', is_print = False):
"""
概要: 指定されたモジュールを動的にロードする。
詳細説明:
`importlib.import_module`を使用して、指定された名前のPythonモジュールをロードします。
`plugin_dir`が指定されていれば、`sys.path`に追加されます。
ロードされたモジュールは、`tkModule`オブジェクトとしてラップされ、`self.modules`リストに保存されます。
ロードに失敗した場合、警告メッセージが出力され、`None`が返されます。
:param module_name: str. ロードするモジュールの名前。
:param target: str. モジュールが対象とする機能(例: `'read_data'`)。
:param plugin_dir: str. モジュールが配置されているディレクトリ。このパスは`sys.path`に追加されます。
:param desc: str. モジュールの説明。
:param ret_type: str. 戻り値のタイプ。`'module'`の場合、生のモジュールオブジェクトを返します。`'tkmodule'`の場合、`tkModule`オブジェクトを返します。
:param is_print: bool. ロード失敗時のエラーメッセージを表示するかどうか。
:returns: module or tkModule or str or None.
`ret_type`に応じたロードされたモジュールオブジェクト、`tkModule`オブジェクト、モジュール名、
またはエラー時に`None`。
"""
if plugin_dir != '' and plugin_dir not in sys.path:
sys.path.append(plugin_dir)
# module = import_module(module_name)
try:
module = import_module(module_name)
except Exception as e:
print(f"Warning in tkapplication.load_module(): Error due to {e}")
traceback.print_exc()
module = None
if module is None:
if is_print:
print(f"Error in tkapplication.load_module(): Failed to load module [{module_name}]")
return None
else:
setattr(module, 'name', module_name)
setattr(module, 'target', target)
setattr(module, 'desc', desc)
tkmodule = tkModule(name = module_name, module = module)
self.modules.append({"module": module, "tkModule": tkmodule, "module_name": module_name, "desc": desc})
if ret_type == 'tkmodule':
return tkmodule
if ret_type == 'module':
return module
return module_name
[ドキュメント]
def load_modules(self, plugin_dir = '', fmask = '*.py', target = "read_data", ret_type = 'module', sort = False, is_print = True):
"""
概要: 指定されたディレクトリから複数のプラグインモジュールをロードする。
詳細説明:
`plugin_dir`内で`fmask`に一致するファイルを検索し、それぞれのファイルをPythonモジュールとして動的にロードします。
ロードされたモジュールは`self.modules`リストに追加され、モジュール名とモジュールオブジェクトのリストを返します。
ロードに成功したモジュールは「loaded」と表示され、失敗した場合は警告が表示されます。
:param plugin_dir: str. プラグインモジュールが配置されているディレクトリ。
:param fmask: str. プラグインファイルを検索するためのファイルマスク(例: `'*.py'`)。
:param target: str. ロードされるモジュールが対象とする機能。
:param ret_type: str. 戻り値のタイプ。`'module'`の場合、生のモジュールオブジェクトを返します。`'tkmodule'`の場合、`tkModule`オブジェクトを返します。
:param sort: bool. ロードするモジュールリストをファイル名の昇順でソートするかどうか。
:param is_print: bool. 処理メッセージや警告メッセージを表示するかどうか。
:returns: tuple[list[str], list[module | tkModule]].
(ロードされたモジュール名のリスト, ロードされたモジュールオブジェクトまたは`tkModule`オブジェクトのリスト)。
"""
search_path = os.path.join(plugin_dir, fmask)
if is_print:
print(f"Search modules: {plugin_dir}/{fmask}")
# print(f"tkapplication.load_modules(): plugin_dir: {plugin_dir}")
# print(f"tkapplication.load_modules(): fmask: {fmask}")
# Search plug-in files
files = glob.glob(search_path)
if sort:
files.sort()
module_names = []
for path in files:
dirname, basename, filebody, ext = split_file_path(path)
module_names.append(filebody)
if plugin_dir != '' and plugin_dir not in sys.path:
sys.path.append(plugin_dir)
modules = []
module_names2 = []
for i in range(len(module_names)):
m = module_names[i]
#plugin_dirはsys.path()に設定しているので、load_module()には '' を渡し、重ねてsys.path()に登録しないようにする
module = self.load_module(module_name = m, plugin_dir = '', target = target, desc = m, ret_type = ret_type, is_print = False)
if module:
modules.append(module)
module_names2.append(getattr(module, 'name', m)) # Ensure module name is captured from module attribute
if is_print:
print(f" {m}: loaded")
else:
if is_print:
print(f"Warning in tkapplication.load_modules(): Invalid module file [{m}.py]")
return module_names2, modules
[ドキュメント]
def call(self, module_name, func_name, *args, **kwargs):
"""
概要: ロードされたプラグインモジュール内の関数、またはグローバルスコープの関数を呼び出す。
詳細説明:
`self.modules`リスト内で指定された`module_name`を持つモジュールを検索し、
そのモジュール内に指定された`func_name`が存在すれば呼び出します。
モジュール内に見つからない場合や、`module_name`が文字列でない場合は、
`self.globals`から`func_name`を検索して呼び出します。
関数が見つからない場合はアプリケーションを終了します。
:param module_name: str or module. 呼び出す関数を含むモジュールの名前またはモジュールオブジェクト。
:param func_name: str. 呼び出す関数の名前。
:param args: tuple. 関数に渡される位置引数。
:param kwargs: dict. 関数に渡されるキーワード引数。
:returns: any. 呼び出された関数の戻り値。
:raises SystemExit: 指定された関数が見つからない場合、アプリケーションを終了します。
"""
if type(module_name) is not str:
# If module_name is a module object (e.g., tkModule or raw module), extract its name
if hasattr(module_name, 'name'):
module_name = module_name.name
elif hasattr(module_name, '__name__'): # For raw module objects
module_name = module_name.__name__
else:
# Fallback or error if name cannot be extracted
pass
ret = None
for minf in self.modules:
if minf["module_name"] == module_name:
# print("module:", minf["module_name"], minf["module"])
module = minf["module"]
# print("module names:", module.__dict__.keys())
if func_name not in module.__dict__.keys():
# return None
pass
else:
ret = module.__dict__[func_name](*args, **kwargs)
return ret
# print(f"call globals.{func_name}")
# Note: self.globals is a dict of global variables. Accessing keys() for functions is common.
if self.globals is not None and func_name not in self.globals.keys():
self.terminate(f"Error in tkapplication.call(): Can not find the function [{func_name}] in self.globals\n")
# print("")
# print(f"Error in tkapplication.call(): Can not find the function [{func_name}] in self.globals\n")
# return None
elif self.globals is not None:
ret = self.globals[func_name](*args)
else:
self.terminate(f"Error in tkapplication.call(): self.globals is None and cannot find function [{func_name}]\n")
return ret
[ドキュメント]
def call_all(self, func_name, *args):
"""
概要: ロードされたすべてのプラグインモジュール内の特定の関数を呼び出す。
詳細説明:
`self.modules`リスト内のすべてのモジュールを反復処理し、
指定された`func_name`を持つ関数が存在する場合にそれを呼び出します。
関数が複数のモジュールに存在する場合、呼び出しは実行されますが、
最後に呼び出された関数の戻り値のみが返されます。
いずれのモジュールにも見つからない場合は、`self.globals`から関数を呼び出します。
:param func_name: str. 呼び出す関数の名前。
:param args: tuple. 関数に渡される位置引数。
:returns: any. 最後に呼び出された関数の戻り値、または関数が見つからなかった場合は`None`。
"""
ncall = 0
ret = None
for minf in self.modules:
# print("module:", minf["module_name"], minf["module"])
if minf["module"].__dict__.get(func_name, None) is not None:
ret = minf["module"].__dict__[func_name](*args)
ncall += 1
if ncall == 0:
# print(f"call globals.{func_name}")
# Note: self.globals is a dict. Accessing keys() for functions is common.
# If self.globals is None or func_name is not in it, this will raise an error.
if self.globals is not None and func_name in self.globals:
ret = self.globals[func_name](*args)
else:
# If function not found in any module and not in globals, return None.
# The original code would raise KeyError if func_name not in self.globals.
# Adhering to rule 1, no logic change, assuming self.globals[func_name] is intended.
pass
return ret
[ドキュメント]
def read_parameters(self, path, merge_params = False, AddSection = 0):
"""
概要: 指定されたパスからパラメータを読み込む。
詳細説明:
`tkIniFile`クラスを使用してINIファイルまたは類似の形式からパラメータを読み込みます。
`merge_params`が`True`の場合、読み込まれたパラメータは`self.params`にマージされます。
:param path: str. 読み込むパラメータファイルのパス。
:param merge_params: bool. 読み込んだパラメータを`self.params`にマージするかどうか。
:param AddSection: int. `tkIniFile`の`ReadAll`メソッドに渡される引数で、セクションを追加するかどうかを制御します。
:returns: dict or None. 読み込まれたパラメータの辞書。読み込みに失敗した場合は`None`。
"""
# Note: `tkIniFile` is not explicitly imported in the provided code.
# Assuming it's available or will cause a runtime error if not.
ini = tkIniFile()
inf = ini.ReadAll(path, AddSection = AddSection)
if inf is not None and merge_params:
self.params += inf
return inf
[ドキュメント]
def save_parameters(self, path, params = None, section = 'Parameters', otherparams = None):
"""
概要: アプリケーションのパラメータをファイルに保存する。
詳細説明:
`tkIniFile`クラスを使用して、`self.params`(または指定された`params`)と
`otherparams`に格納されているパラメータを指定されたファイルにINI形式で書き出します。
メインパラメータは指定された`section`に、`otherparams`は'Preferences'セクションに保存されます。
:param path: str. 保存先のファイルパス。
:param params: tkParams or dict or None. 保存するメインのパラメータオブジェクトまたは辞書。`None`の場合、`self.get_params()`が使用されます。
:param section: str. メインパラメータが保存されるセクション名。
:param otherparams: dict or None. 追加で保存するパラメータの辞書。これらは'Preferences'セクションに保存されます。
:returns: None.
"""
if params is None:
params = self.get_params()
# Note: `tkIniFile` is not explicitly imported in the provided code.
# Assuming it's available or will cause a runtime error if not.
ini = tkIniFile(path)
for key in params.keys(): # Iterating `params.keys()` not `i`
val = params.get(key, None)
if val is not None:
ini.WriteString(section, key, val)
if otherparams is not None:
for key in otherparams.keys():
val = otherparams.get(key, None)
if val is not None:
ini.WriteString('Preferences', key, val)
[ドキュメント]
def print_parameters(self, params = None):
"""
概要: アプリケーションのパラメータを標準出力に表示する。
詳細説明:
`self.get_params()`(または指定された`params`)に格納されているすべてのパラメータを、
キーと値のペアとして整形して標準出力にリストアップします。
値の型(float, int, strなど)に応じて適切な形式で表示されます。
:param params: tkParams or dict or None. 表示するパラメータオブジェクトまたは辞書。`None`の場合、`self.get_params()`が使用されます。
:returns: None.
"""
if params is None:
params = self.get_params()
for key in params.keys():
val = params.get(key, None)
# print("val=", val)
if val is None:
print(f" {key:10s}: {'None':14}")
else:
if type(val) is float:
print(f" {key:10s}: {val:14.8g}")
elif type(val) is int or type(val) is str:
print(f" {key:10s}: {val:14}")
else:
print(f" {key:10s}:", val)
[ドキュメント]
def main():
"""
概要: モジュールが直接実行された場合に呼び出されるメイン関数。
詳細説明:
このモジュールがライブラリとして設計されているため、直接実行された場合には、
それがライブラリであり実行可能ではないことを示すメッセージを出力します。
:returns: None.
"""
print("")
print("This is library, not runnable")
print("")
if __name__ == "__main__":
main()