"""
設定パラメータを管理するためのクラスを提供します。
このモジュールは、アプリケーションの設定パラメータを読み込み、保存し、
アクセスするための `tkParams` クラスを定義します。INIファイル形式での
パラメータの永続化、コマンドライン引数の管理、パラメータの説明機能などを
サポートします。
関連リンク:
:doc:`tklib_usage` (tklibの全体的な使用方法に関するドキュメントへのリンク例)
"""
import os
import sys
import copy
from tklib.tkobject import tkObject, _analyze_varstr
from tklib.tkutils import pconv, pint, pfloat
from tklib.tkinifile import tkIniFile
import tklib.tkre as tkre
#=========================
# Parameter class
#=========================
[ドキュメント]
class tkParams(tkObject):
"""
アプリケーションの設定パラメータを管理するクラスです。
このクラスは、INIファイルからのパラメータの読み込み、保存、
コマンドライン引数からのパラメータの更新、パラメータ値へのアクセス、
およびその説明の管理機能を提供します。
"""
def __init__(self, parameter_file = None, app = None, **args):
"""
tkParamsクラスの新しいインスタンスを初期化します。
パラメータファイルパス、アプリケーションオブジェクト、および
初期パラメータを設定します。
:param parameter_file: str, optional
パラメータを読み書きするINIファイルのパス。デフォルトはNone。
:param app: object, optional
関連付けられるアプリケーションオブジェクト。主にログ出力などに使用されます。
デフォルトはNone。
:param args: dict
初期設定として追加する任意のキーワード引数(パラメータ)。
"""
# super(tkObject, self).__init__(**args)
self._argv = sys.argv
self._path = parameter_file
self._app = app
self._explanation = {}
self.update(**args)
def __del__(self):
"""
オブジェクトが破棄される際に呼び出されるデストラクタです。
現在、特別な処理は行いません。
"""
# super(tkObject, self).__del__()
pass
def __str__(self):
"""
オブジェクトの文字列表現を返します。
:returns: str
このオブジェクトのクラスパスを示す文字列。
"""
return self.ClassPath()
[ドキュメント]
def dict(self):
"""
このオブジェクトの属性を辞書として返します。
:returns: dict
オブジェクトの `__dict__` 属性。
"""
return self.__dict__
[ドキュメント]
def copy(self):
"""
現在のパラメータの新しいコピーを作成し、tkParamsオブジェクトとして返します。
すべてのパラメータとその値を新しいtkParamsインスタンスにコピーします。
:returns: tkParams
現在のパラメータを持つ新しいtkParamsオブジェクト。
"""
target = tkParams()
for key, val in self.dict().items():
target.__dict__[key] = val
return target
[ドキュメント]
def get_param_dict(self):
"""
このオブジェクトのパラメータ辞書を取得します。
これは `self.__dict__` と同じです。
:returns: dict
オブジェクトの `__dict__` 属性。
"""
return self.__dict__
[ドキュメント]
def get_path(self, path: str = None) -> str:
"""
現在のパラメータファイルのパスを取得または設定します。
`path` が指定された場合、内部のパラメータファイルパスを更新します。
:param path: str, optional
設定するパラメータファイルのパス。Noneの場合、現在のパスが返されます。
デフォルトはNone。
:returns: str
現在の(または更新された)パラメータファイルのパス。
"""
if path is not None:
self._path = path
return self._path
[ドキュメント]
def keys(self):
"""
現在のパラメータ辞書のキーのリストを返します。
:returns: dict_keys
パラメータ辞書のすべてのキーを含む `dict_keys` オブジェクト。
"""
return self.__dict__.keys()
[ドキュメント]
def get(self, key: str, defval = None):
"""
指定されたキーのパラメータ値を取得します。
キーが存在しない場合、指定されたデフォルト値を返します。
:param key: str
取得するパラメータのキー。
:param defval: any, optional
キーが見つからなかった場合に返すデフォルト値。デフォルトはNone。
:returns: any
指定されたキーのパラメータ値、またはデフォルト値。
"""
return self.__dict__.get(key, defval)
[ドキュメント]
def set(self, key: str, val, explanation: str = None):
"""
指定されたキーのパラメータ値を設定します。
オプションで、そのパラメータの説明を設定することもできます。
:param key: str
設定するパラメータのキー。
:param val: any
設定するパラメータの値。
:param explanation: str, optional
パラメータの説明。デフォルトはNone。
:returns: any
設定された値。
"""
setattr(self, key, val)
self._explanation[key] = explanation
return val
[ドキュメント]
def set_attr(self, key: str, val, explanation: str = None):
"""
指定されたキーのパラメータ値を設定します。(`set` メソッドのエイリアス)
オプションで、そのパラメータの説明を設定することもできます。
:param key: str
設定するパラメータのキー。
:param val: any
設定するパラメータの値。
:param explanation: str, optional
パラメータの説明。デフォルトはNone。
:returns: any
設定された値。
"""
return self.set(key, val, explanation)
[ドキュメント]
def get_explanation(self, key: str) -> str:
"""
指定されたキーのパラメータの説明を取得します。
説明が見つからず、関連するアプリケーションオブジェクトが存在する場合は、
アプリケーションオブジェクトの翻訳機能(`p()` メソッド)を通じて
説明を処理します。
:param key: str
説明を取得するパラメータのキー。
:returns: str or None
パラメータの説明、または説明が存在しない場合はNone。
"""
expl = self._explanation.get(key, None)
if expl is None:
return None
if self._app:
return self._app.p(expl)
return expl
[ドキュメント]
def get_print_func(self, app, use_warning: bool) -> callable:
"""
指定された条件に基づいて適切な出力関数を返します。
`app` オブジェクトと `use_warning` フラグに応じて、
`app.print_warning`、`app.print`、または標準の `print` 関数を返します。
:param app: object or None
アプリケーションオブジェクト。存在しない場合は標準出力が使用されます。
:param use_warning: bool
`True`の場合、`app.print_warning`を優先的に使用します。
:returns: callable
出力に使用する関数。
"""
if app and use_warning:
print_func = app.print_warning
elif app:
print_func = app.print
else:
print_func = print
return print_func
[ドキュメント]
def printinf(self, app = None, use_warning: bool = False):
"""
現在のすべてのパラメータとその値を、オプションで説明と共に表示します。
:param app: object, optional
出力に使用するアプリケーションオブジェクト。Noneの場合、内部の `_app` を使用します。
デフォルトはNone。
:param use_warning: bool, optional
`True`の場合、警告出力関数(`app.print_warning`)を使用します。
デフォルトはFalse。
"""
if app is None:
app = self.app
print_func = self.get_print_func(app, use_warning)
print_func("Parameters:")
for key in self.__dict__.keys():
expl = self.get_explanation(key)
if expl:
print_func(f" {key}: {self.__dict__[key]} ({expl})")
else:
print_func(f" {key}: {self.__dict__[key]}")
[ドキュメント]
def print_parameters(self, heading: str = "", sort_by_keys: bool = True, exclude_keys: list = [], app = None, use_warning: bool = False):
"""
現在のパラメータとその値を整形して出力します。
特定のキーを除外したり、キーでソートしたり、見出しを付けたりすることができます。
:param heading: str, optional
出力の冒頭に表示する見出し文字列。デフォルトは空文字列。
:param sort_by_keys: bool, optional
`True`の場合、キーをアルファベット順(大文字小文字を区別しない)でソートして表示します。
デフォルトはTrue。
:param exclude_keys: list of str, optional
出力から除外するキーのリスト。デフォルトは空リスト。
:param app: object, optional
出力に使用するアプリケーションオブジェクト。Noneの場合、内部の `_app` を使用します。
デフォルトはNone。
:param use_warning: bool, optional
`True`の場合、警告出力関数(`app.print_warning`)を使用します。
デフォルトはFalse。
"""
if app is None:
app = self._app
print_func = self.get_print_func(app, use_warning)
# print("print_func=", print_func)
dict = self.get_dict()
keys = [key for key in dict.keys() if key]
if sort_by_keys:
keys = sorted(keys, key = str.lower)
if heading != "":
print_func(heading)
for key in keys:
if tkre.Match('_', key) or dict[key] is None:
continue
expl = self.get_explanation(key)
if expl:
print_func(f" {key}: {dict[key]} ({expl})")
else:
print_func(f" {key}: {dict[key]}")
[ドキュメント]
def print_parameters_warning(self, heading: str = "", sort_by_keys: bool = True, app = None):
"""
現在のパラメータとその値を警告として出力します。
`app` オブジェクトの `print_warning` メソッドを使用します。
:param heading: str, optional
出力の冒頭に表示する見出し文字列。デフォルトは空文字列。
:param sort_by_keys: bool, optional
`True`の場合、キーをアルファベット順(大文字小文字を区別しない)でソートして表示します。
デフォルトはTrue。
:param app: object, optional
出力に使用するアプリケーションオブジェクト。Noneの場合、内部の `_app` を使用します。
デフォルトはNone。
"""
if app is None:
app = self._app
app.print_original("print_parameters_warning")
dict = self.get_dict()
keys = [key for key in dict.keys()]
if sort_by_keys:
keys = sorted(keys, key = str.lower)
if heading != "":
app.print_warning(heading)
for key in keys:
if tkre.Match('_', key) or dict[key] is None:
continue
expl = self.get_explanation(key)
if expl:
app.print_warning(f" {key}: {dict[key]} ({expl})")
else:
app.print_warning(f" {key}: {dict[key]}")
[ドキュメント]
def get_string(self, path: str = None, section: str = None, key: str = None, def_val: str = None, is_print: bool = False):
"""
INIファイルから文字列型のパラメータ値を読み込みます。
指定されたパス、セクション、キーに基づいて値を読み込みます。
:param path: str, optional
INIファイルのパス。Noneの場合、現在の内部パスを使用します。
デフォルトはNone。
:param section: str, optional
パラメータが属するセクション名。デフォルトはNone。
:param key: str, optional
取得するパラメータのキー。デフォルトはNone。
:param def_val: str, optional
キーが見つからなかった場合に返すデフォルト値。デフォルトはNone。
:param is_print: bool, optional
処理中にメッセージを出力するかどうか。デフォルトはFalse。
:returns: str or None
指定されたキーの文字列値、またはデフォルト値、またはNone。
"""
path = self.get_path(path)
self._path = path
ini = tkIniFile(IsPrint = IsPrint) # IsPrint は外部スコープで定義されていると仮定
if ini is None:
return None
return ini.get_string(section = section, key = key, def_val = def_val, is_print = is_print)
[ドキュメント]
def write_string(self, path: str = None, section: str = None, key: str = None, value = None, outfile = None, is_print: bool = False):
"""
INIファイルに文字列型のパラメータ値を書き込みます。
指定されたパス、セクション、キー、値に基づいてファイルを更新します。
:param path: str, optional
INIファイルのパス。Noneの場合、現在の内部パスを使用します。
デフォルトはNone。
:param section: str, optional
パラメータが属するセクション名。デフォルトはNone。
:param key: str, optional
書き込むパラメータのキー。デフォルトはNone。
:param value: any, optional
書き込むパラメータの値。`str()` で文字列に変換されます。デフォルトはNone。
:param outfile: any, optional
出力ファイルオブジェクト(使用されていない可能性が高い)。デフォルトはNone。
:param is_print: bool, optional
処理中にメッセージを出力するかどうか。デフォルトはFalse。
:returns: bool or None
書き込みが成功した場合はTrue、失敗した場合はFalse。tkIniFileのインスタンス化に失敗した場合はNone。
"""
path = self.get_path(path)
self._path = path
ini = tkIniFile(IsPrint = IsPrint) # IsPrint は外部スコープで定義されていると仮定
if ini is None:
return None
# ここは `ini.write_string(...)` の間違いの可能性が高いが、既存コードは変更しないルールのためそのまま。
return write_string(self, section = section, key = key, value = value, is_print = is_print)
[ドキュメント]
def read_parameters(self, path: str = None, section: str = None, AddSection: bool = False,
ignore_keys: list = [], terminator: str = None, IsPrint: bool = True, follow_vartype: bool = True, read_inifile: bool = False):
"""
INIファイルからすべてのパラメータを読み込み、現在のオブジェクトに適用します。
読み込んだ値は、既存のパラメータの型に基づいて型変換されます。
リスト、タプル、辞書などの複合型もサポートします。
:param path: str, optional
INIファイルのパス。Noneの場合、現在の内部パスを使用します。
デフォルトはNone。
:param section: str, optional
読み込むセクション名。Noneの場合、すべてのセクションを読み込みます。
デフォルトはNone。
:param AddSection: bool, optional
読み込んだセクションをパラメータとして追加するかどうか。デフォルトはFalse。
:param ignore_keys: list of str, optional
読み込み時に無視するキーのリスト。デフォルトは空リスト。
:param terminator: str, optional
値の終端を示す文字。これ以降の文字列は無視されます。デフォルトはNone。
:param IsPrint: bool, optional
処理中にメッセージを出力するかどうか。デフォルトはTrue。
:param follow_vartype: bool, optional
`True`の場合、既存のパラメータの型に合わせて読み込んだ値を変換します。
`False`の場合、`pconv` を使用して変換します。デフォルトはTrue。
:param read_inifile: bool, optional
`True`の場合、INIファイル自体に関する情報を読み込みます。デフォルトはFalse。
:returns: dict or None
読み込まれたパラメータを含む辞書、またはINIファイルが存在しない場合はNone。
"""
path = self.get_path(path)
self._path = path
# Store copy of self to check var types
vars_org = copy.copy(self)
# vars_org = copy.deepcopy(self)
ini = tkIniFile(IsPrint = IsPrint)
if ini is None:
return None
inf = ini.read_all(path, section = section, AddSection = AddSection, ignore_keys = ignore_keys)
if inf is None:
return None
# Check list/tuple/dict variables. Get # of elements, and initialize dict variables
if not read_inifile:
try:
del inf['inifile']
except:
pass
keys = inf.keys()
list_dict = {}
val_param = None
for key in keys:
varname, index = _analyze_varstr(key)
if index is None:
continue
val_param = self.get2(varname, None)
if type(index) is int:
# print("*varname=", varname)
if list_dict.get(index, None) is None:
list_dict[varname] = index
elif list_dict[varname] < index:
list_dict[varname] = index
else:
self.__dict__[varname] = {}
# Initialize list/tuple variables
for varname in list_dict.keys():
# print("**varname=", varname)
self.__dict__[varname] = [None] * (list_dict[varname] + 1)
# print(f"{varname}=", self.__dict__[varname])
# Assign variables
keys = inf.keys()
for key in keys:
# Check var types from vars_org
varname, index = _analyze_varstr(key)
# val_param = vars_org.get2(key, None)
var = vars_org.__dict__.get(varname, None)
if var is None:
val_param = None
else:
# Scalar
if index is None:
val_param = var
# list/tuple/dict
else:
# print("index=", varname, index, type(index))
if type(index) is int:
# list/tuple
nvar = len(var)
if nvar == 0:
val_param = ''
elif index < nvar:
val_param = var[index]
# if vars_org is shorter than the var given
else:
val_param = var[0]
# dict
else:
if var.get(index, None) is None:
val_param = ""
else:
val_param = var[index]
str = inf.get(key, None)
if str is None:
continue
if terminator is not None:
aa = str.split(terminator)
if len(aa) > 1:
str = aa[0]
if follow_vartype and val_param is not None:
vtype = type(val_param)
# print("val=", key, val_param, vtype, str)
if vtype is float:
val = pfloat(str)
elif vtype is int:
val = pint(str)
elif vtype is bool:
if str == 'False' or str == '0' or str == '':
val = False
else:
val = True
else:
val = str
else:
val = pconv(str, str)
self.set_attribute2(key, val)
return inf
[ドキュメント]
def save_parameters_by_keys(self, prmfile: str, heading: str = None, section: str = None, keys: list = None, exclude_keys: list = [], save_commandline: bool = False):
"""
指定されたキーのパラメータのみをINIファイルに保存します。
このメソッドは、`save_parameters` メソッドから特定のキーセットを
保存するために呼び出されるヘルパーメソッドです。
:param prmfile: str
パラメータを保存するINIファイルのパス。
:param heading: str, optional
ファイル内のセクションの前に挿入される見出し(使用されていない可能性が高い)。
デフォルトはNone。
:param section: str, optional
パラメータを保存するセクション名。デフォルトはNone。
:param keys: list of str, optional
保存するパラメータのキーのリスト。Noneの場合、`params.keys()` が使用されます。
(`params` はこのスコープで未定義だが、既存コード変更ルールに従いそのまま)。
デフォルトはNone。
:param exclude_keys: list of str, optional
保存から除外するキーのリスト。デフォルトは空リスト。
:param save_commandline: bool, optional
コマンドライン引数を保存するかどうか。デフォルトはFalse。
:returns: None
戻り値は明示的に指定されていませんが、`tkIniFile.write_string` の結果が使われます。
実際には何も返しません。
"""
if keys is None:
# paramsは未定義だが、既存ロジック変更不可のためそのまま。self.keys()の意図か。
keys = list(params.keys())
if save_commandline:
keys.remove("commandline")
ini = tkIniFile(path = prmfile)
for key in keys:
if key in exclude_keys:
continue
v = self.get(key, None)
if v is not None:
ini.write_string(section = section, key = key, value = v, is_print = False)
[ドキュメント]
def save_parameters(self, path: str = None, section: str = 'Preferences',
keys: list = None, exclude_keys: list = [], otherparams: dict = None,
sort_by_keys: bool = True,
other_section: str = 'OtherParameters',
update_commandline: bool = True,
save_commandline: bool = False,
save_inifile: bool = False, IsPrint: bool = True):
"""
現在のすべてのパラメータをINIファイルに保存します。
パラメータはセクションにまとめられ、キーと値のペアとして保存されます。
リストや辞書のような複合型のパラメータも適切に保存されます。
:param path: str, optional
INIファイルのパス。Noneの場合、現在の内部パスを使用します。
デフォルトはNone。
:param section: str, optional
主要なパラメータを保存するセクション名。デフォルトは'Preferences'。
:param keys: list of str, optional
保存するパラメータのキーのリスト。Noneの場合、すべてのパラメータが保存されます。
デフォルトはNone。
:param exclude_keys: list of str, optional
保存から除外するキーのリスト。デフォルトは空リスト。
:param otherparams: dict, optional
別のセクションに保存する追加のパラメータ辞書。デフォルトはNone。
:param sort_by_keys: bool, optional
`True`の場合、キーをアルファベット順(大文字小文字を区別しない)でソートして保存します。
デフォルトはTrue。
:param other_section: str, optional
`otherparams` を保存するセクション名。デフォルトは'OtherParameters'。
:param update_commandline: bool, optional
`True`の場合、現在のコマンドライン引数を `commandline` パラメータとして更新します。
デフォルトはTrue。
:param save_commandline: bool, optional
`True`の場合、`commandline` パラメータも保存します。デフォルトはFalse。
:param save_inifile: bool, optional
`True`の場合、`inifile` パラメータも保存します。デフォルトはFalse。
:param IsPrint: bool, optional
処理中にメッセージを出力するかどうか。デフォルトはTrue。
:returns: bool
保存が成功した場合はTrue、失敗した場合はFalse。
"""
path = self.get_path(path)
self._path = path
if keys is not None:
return self.save_parameters_by_keys(path, section = section, keys = keys, exclude_keys = exclude_keys, save_commandline = save_commandline)
params = self.get_param_dict()
if update_commandline:
params['commandline'] = 'python ' + ' '.join(self._argv)
inifile = params.get('inifile')
if not save_inifile and inifile is not None:
try:
del params['inifile']
except:
pass
if keys is None:
keys = list(params.keys())
if not save_commandline: # and params.get('update_commandline', None):
if "commandline" in keys: keys.remove("commandline")
if sort_by_keys:
keys = sorted(keys, key = str.lower)
ini = tkIniFile(path, IsPrint = IsPrint)
for key in keys:
if key in exclude_keys:
continue
if tkre.Match('_', key):
continue
val = params.get(key, None)
ret = True
if val is None:
continue
if type(val) is list or type(val) is tuple:
for i in range(len(val)):
ret = ini.write_string(section, f"{key}[{i}]", val[i], is_print = IsPrint)
if not ret:
break
elif type(val) is dict:
for key2 in val.keys():
ret = ini.write_string(section, "{}[{}]".format(key, key2), val[key2], is_print = IsPrint)
if not ret:
break
else:
ret = ini.write_string(section, key, val, is_print = IsPrint)
if not ret:
break
if ret and otherparams is not None:
keys = [key for key in otherparams.keys()]
if sort_by_keys:
keys = sorted(keys, key = str.lower)
for key in keys:
val = otherparams[key]
if val is not None:
ret = ini.write_string(other_section, key, val, is_print = IsPrint)
if not ret:
break
if not ret and IsPrint:
print(f"\nError in tkParams.save_parameters(): Can not write to [{path}]\n")
params['inifile'] = inifile
return ret
[ドキュメント]
def main():
"""
このモジュールがスクリプトとして直接実行されたときに呼び出されるメイン関数です。
このモジュールはライブラリとして使用されることを意図しているため、
直接実行された場合はその旨のメッセージを表示します。
"""
print("")
print("This is library, not runnable")
print("")
if __name__ == "__main__":
main()