"""ユーティリティ機能を提供するモジュール
このモジュールは、ファイル操作、データ変換、システム情報取得、エラーハンドリングなど、
様々な一般的なユーティリティ関数を提供します。
matplotlibやnumpyなどのライブラリに依存する機能も含まれます。
関連リンク:
:doc:`tkutils_usage`
"""
import os
import fnmatch
import sys
import os.path
import platform
import sys
import shutil
import psutil
import signal
import inspect
import traceback
from pathlib import Path
import datetime
import glob
import re
import numbers
import csv
import unicodedata
import math
import numpy as np
import matplotlib.colors as mcolors
import tklib.tkimport as imp
mpl = imp.import_lib("matplotlib", stop_by_error = False)
if mpl is not None:
from matplotlib.markers import TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN, CARETLEFT, CARETRIGHT, CARETUP, CARETDOWN
from tklib.tkobject import tkObject, _analyze_varstr
import tklib.tkre as tkre
import tklib.collabo.RH.Ryu_colormap as ryu_cm
linestyles = ['solid', 'dashed', 'dashdot', 'dotted']
#colors = ['black', 'red', 'blue', 'darkgreen', 'darkorange', 'navy',
# 'slategray', 'hotpink', 'olive', 'chocolate', 'magenta',
# 'green', 'yellow', 'cyan']
colors = ryu_cm.colors
colors = ["black", "red", "blue", colors[0], colors[5], colors[8], colors[2], colors[9]]
markers = [
('.', 'point'),
(',', 'pixel'),
('o', 'circle'),
('v', 'triangle_down'),
('^', 'triangle_up'),
('<', 'triangle_left'),
('>', 'triangle_right'),
('1', 'tri_down'),
('2', 'tri_up'),
('3', 'tri_left'),
('4', 'tri_right'),
('8', 'octagon'),
('s', 'square'),
('p', 'pentagon'),
('*', 'star'),
('h', 'hexagon1'),
('H', 'hexagon2'),
('+', 'plus'),
('x', 'x'),
('D', 'diamond'),
('d', 'thin_diamond'),
('|', 'vline'),
('_', 'hline'),
]
if mpl is not None:
markers.append([
(TICKLEFT, 'tickleft'),
(TICKRIGHT, 'tickright'),
(TICKUP, 'tickup'),
(TICKDOWN, 'tickdown'),
(CARETLEFT, 'caretleft'),
(CARETRIGHT, 'caretright'),
(CARETUP, 'caretup'),
(CARETDOWN, 'caretdown'),
('None', 'nothing'),
(None, 'nothing'),
(' ', 'nothing'),
('', 'nothing')
])
markers = ['o', 's', '+', 'x', 'D', 'v', '^', '<', '>', '8', 'h', 'H']
nlinestyles = len(linestyles)
ncolors = len(colors)
nmarkers = len(markers)
# ギリシャ文字マッピング辞書
GREEK_MAP = {
"GAMMA": "Γ",
"ALPHA": "Α",
"BETA": "Β",
"DELTA": "Δ",
"EPSILON": "Ε",
"ZETA": "Ζ",
"ETA": "Η",
"THETA": "Θ",
"IOTA": "Ι",
"KAPPA": "Κ",
"LAMDA": "Λ",
"MU": "Μ",
"NU": "Ν",
"XI": "Ξ",
"OMICRON": "Ο",
"PI": "Π",
"RHO": "Ρ",
"SIGMA": "Σ",
"TAU": "Τ",
"UPSILON": "Υ",
"PHI": "Φ",
"CHI": "Χ",
"PSI": "Ψ",
"OMEGA": "Ω",
"gamma": "γ",
"alpha": "α",
"beta": "β",
"delta": "δ",
"epsilon": "ε",
"zeta": "ζ",
"eta": "η",
"theta": "θ",
"iota": "ι",
"kappa": "κ",
"lamda": "λ",
"mu": "μ",
"nu": "ν",
"xi": "ξ",
"omicron": "ο",
"pi": "π",
"rho": "ρ",
"sigma": "σ",
"tau": "τ",
"upsilon": "υ",
"phi": "φ",
"chi": "χ",
"psi": "ψ",
"omega": "ω"
}
[ドキュメント]
def convert_color(color):
"""色名を標準的なmatplotlibのCSS4_COLORS形式に変換します。
与えられた色名がmatplotlibのCSS4_COLORSに存在する場合、その標準的な表記を返します。
存在しない場合は、元の色名をそのまま返します。
:param color: 変換する色名(文字列)またはNone。
:type color: str or None
:returns: 標準形式に変換された色名、または元の色名、またはNone。
:rtype: str or None
"""
if color is None:
return None
# matplotlib の CSS4_COLORS を辞書として利用
# https://matplotlib.org/stable/gallery/color/named_colors.html
color_dict = {key.lower(): value for key, value in mcolors.CSS4_COLORS.items()}
# 入力された色名をチェック
if color.lower() in color_dict.keys():
return color_dict[color.lower()]
# 色名でない場合は そのまま返す
return color
[ドキュメント]
def graph_style_idx(idx):
"""インデックスに基づいてグラフの色と線種を取得します。
与えられたインデックスを基に、定義済みの色と線種のリストから適切な組み合わせを選択して返します。
:param idx: スタイルを取得するためのインデックス。
:type idx: int
:returns: 色と線種のタプル。
:rtype: tuple[str, str]
"""
icolor = idx % ncolors
ils = idx // ncolors % nlinestyles
return colors[icolor], linestyles[ils]
[ドキュメント]
def raise_error(desc = "custom error"):
"""カスタムエラーを発生させます。
指定された説明文で`CustomError`例外を発生させます。
:param desc: エラーの説明。
:type desc: str
:raises CustomError: 指定された説明を持つカスタムエラー。
"""
class CustomError(Exception):
pass
raise CustomError(desc)
[ドキュメント]
def get_position(self = None, _class = None):
"""現在のコードのファイル名、関数名、行番号を取得します。
呼び出し元のフレーム情報を利用して、コードが実行されている位置に関する情報を辞書で返します。
`_class`が指定された場合はクラス名、`self`が指定された場合はインスタンスのクラス名も含まれます。
.. warning::
この関数は、`line`変数を定義せずに使用しているため、常にエラーが発生します。
:param self: メソッド内で呼び出す場合のインスタンス (オプション)。
:type self: object or None
:param _class: クラス内で呼び出す場合のクラス (オプション)。
:type _class: type or None
:returns: ファイル名、関数名、および現在の行番号を含む辞書。
:rtype: dict[str, str or int]
"""
frame = inspect.currentframe().f_back
file_name = frame.f_globals["__file__"]
function_name = frame.f_code.co_name
if _class:
return {"filename": file_name, "function": function_name, "line": line}
elif self:
class_name = self.__class__.__name__
return {"filename": file_name, "class": class_name, "function": function_name, "line": line}
else:
return {"filename": file_name, "function": function_name, "line": line}
[ドキュメント]
def get_position_str(self = None, _class = None):
"""現在のコードのファイル名、関数名、行番号を含む文字列を取得します。
呼び出し元のフレーム情報を利用して、コードが実行されている位置に関する情報を整形された文字列で返します。
`_class`が指定された場合はクラス名、`self`が指定された場合はインスタンスのクラス名も含まれます。
.. warning::
この関数は、`line_number`変数を定義せずに使用しているため、常にエラーが発生します。
:param self: メソッド内で呼び出す場合のインスタンス (オプション)。
:type self: object or None
:param _class: クラス内で呼び出す場合のクラス (オプション)。
:type _class: type or None
:returns: ファイル名、関数名、および現在の行番号を含む整形された文字列。
:rtype: str
"""
frame = inspect.currentframe().f_back
file_name = frame.f_globals["__file__"]
function_name = frame.f_code.co_name
if _class:
return f"In file: {file_name}, function: {function_name}, line: {line_number}"
elif self:
class_name = self.__class__.__name__
return f"In file: {file_name}, class: {class_name}, function: {function_name}, line: {line_number}"
else:
return f"In file: {file_name}, function: {function_name}, line: {line_number}"
[ドキュメント]
def print_position(self = None, _class = None, exc_type = None, exc_value = None, exc_tb = None):
"""現在のコードの位置情報と、オプションで例外情報を出力します。
呼び出し元のフレーム情報を利用して、現在のファイル名、関数名、行番号を標準出力に出力します。
`exc_type`, `exc_value`, `exc_tb`が指定された場合は、詳細な例外情報も出力します。
:param self: メソッド内で呼び出す場合のインスタンス (オプション)。
:type self: object or None
:param _class: クラス内で呼び出す場合のクラス (オプション)。
:type _class: type or None
:param exc_type: 例外の型 (sys.exc_info()から取得)。
:type exc_type: type or None
:param exc_value: 例外の値 (sys.exc_info()から取得)。
:type exc_value: Exception or None
:param exc_tb: 例外のトレースバックオブジェクト (sys.exc_info()から取得)。
:type exc_tb: types.TracebackType or None
"""
frame = inspect.currentframe().f_back
file_name = frame.f_globals["__file__"]
function_name = frame.f_code.co_name
line_number = frame.f_lineno
if _class:
print(f"In file: {file_name}, function: {function_name}, line: {line_number}")
elif self:
class_name = self.__class__.__name__
print(f"In file: {file_name}, class: {class_name}, function: {function_name}, line: {line_number}")
else:
print(f"In file: {file_name}, function: {function_name}, line: {line_number}")
if exc_tb:
# tb = traceback.extract_tb(exc_tb)
# print("Traceback:")
# for frame in tb:
# print(f" file: {frame.filename}, function: {frame.name}, line: {frame.lineno}")
detailed_tb = traceback.format_exception(exc_type, exc_value, exc_tb)
print("Error information:")
for line in detailed_tb:
print(line, end = '')
[ドキュメント]
def get_class(instance):
"""インスタンスのクラスを取得します。
:param instance: クラスを取得したいインスタンス。
:type instance: object
:returns: インスタンスのクラスオブジェクト。
:rtype: type
"""
return instance.__class__
[ドキュメント]
def get_definition_position(obj):
"""オブジェクト(クラスまたはインスタンス)の定義位置を取得します。
指定されたオブジェクトがクラスでない場合は、そのクラスの定義位置を取得します。
:param obj: 定義位置を取得したいオブジェクトまたはクラス。
:type obj: object or type
:returns: ファイル名と行番号のタプル。
:rtype: tuple[str, int]
"""
if not inspect.isclass(obj): obj = obj.__class__
file_name = inspect.getfile(obj)
iline = inspect.getsourcelines(obj)[1]
return file_name, iline
[ドキュメント]
def print_process(proc_name = '', username = '', print_level = 1):
"""現在実行中のプロセス情報を表示し、辞書として返します。
指定されたプロセス名やユーザー名に一致するプロセスを検索し、その情報を標準出力に表示します。
各プロセスのPIDをキーとする辞書としてプロセス情報を返します。
:param proc_name: 検索するプロセス名の一部 (オプション)。空文字列の場合は全てを対象。
:type proc_name: str
:param username: 検索するユーザー名の一部 (オプション)。'all'の場合は全てのユーザーを対象。
:type username: str
:param print_level: 表示の詳細レベル。0の場合は表示せず、1の場合は詳細を表示。
:type print_level: int
:returns: 各プロセスのPIDをキーとし、その情報を含む辞書。
:rtype: dict[int, dict]
"""
nfound = 0
proc_dict = {}
for proc in psutil.process_iter(['pid', 'name', 'username', 'cmdline']):
if username == 'all' or (proc.info['username'] and username in proc.info['username']):
if proc_name == '' or proc_name in proc.name():
nfound += 1
args = ''
if proc.info.get('cmdline', None):
filename = os.path.basename(proc.info['cmdline'][0])
args = ' '.join(proc.info['cmdline'][1:])
if len(args) >= 60: args = args[:60] + ' ...'
# print(f"exec path: {filename}")
# print(f"Command Line: ", proc.info['cmdline'][1:])
if print_level:
print("-" * 40)
print(f"process name: {proc.info['name']}")
print(f" args : {args}")
print(f" owner: {proc.info['username']}")
print(f" PID : {proc.info['pid']}")
proc_dict[proc.pid] = proc.info
if print_level and nfound == 0:
print(f"\nNo process found with {proc_name}.")
return proc_dict
[ドキュメント]
def kill_process_interactive(proc_name = '', username = '', pid = '', print_level = 1):
"""対話形式でプロセスを終了させます。
指定されたプロセス名やユーザー名に一致するプロセスをリスト表示し、
ユーザーからのPID入力に基づいてプロセスを強制終了させます。
空のPIDを入力すると終了します。
:param proc_name: 検索するプロセス名の一部 (オプション)。
:type proc_name: str
:param username: 検索するユーザー名の一部 (オプション)。
:type username: str
:param pid: 終了させるプロセスのPID (対話モードではユーザー入力で上書きされます)。
:type pid: str
:param print_level: 表示の詳細レベル。0の場合は表示せず、1の場合は詳細を表示。
:type print_level: int
"""
while True:
if print_level: print()
inf = print_process(proc_name, username, print_level = print_level)
if print_level:
print()
pid = input("input PID to kill [ENTER to terminate] >> ")
if pid == '': break
pid = int(pid)
try:
# os.kill(int(pid), signal.SIGTERM)
os.kill(pid, signal.SIGKILL)
if print_level:
print(f"Process {inf[pid]['name']} (PID: {pid}) terminated")
except Exception as e:
if print_level:
print(f"Failed to kill process (PID: {pid}) due to error: {e}")
if print_level: print()
[ドキュメント]
def safe_getelement(var, key, defval = None):
"""辞書やリストから指定されたキー/インデックスに対応する要素を安全に取得します。
指定された`var`から`key`に対応する要素を返します。
キーが存在しないなどのエラーが発生した場合は、`defval`を返します。
:param var: 要素を取得したい辞書またはリスト。
:type var: dict or list
:param key: 取得したい要素のキーまたはインデックス。
:type key: str or int
:param defval: キーが見つからない場合に返すデフォルト値。
:type defval: object or None
:returns: 指定されたキーの要素、または`defval`。
:rtype: object or None
"""
try:
return var[key]
except:
return defval
[ドキュメント]
def handle_exception(exc_type, exc_value, exc_tb):
"""未処理の例外を処理し、エラー情報を表示します。
`sys.excepthook`に設定されることを意図しており、
例外の種類、値、トレースバックを整形して出力します。
最後にユーザーがEnterキーを押すまでプログラムの終了を一時停止します。
:param exc_type: 例外の型 (sys.exc_info()から取得)。
:type exc_type: type
:param exc_value: 例外の値 (sys.exc_info()から取得)。
:type exc_value: Exception
:param exc_tb: 例外のトレースバックオブジェクト (sys.exc_info()から取得)。
:type exc_tb: types.TracebackType
"""
print(f"\nError:", end = '')
print_position(exc_type = exc_type, exc_value = exc_value, exc_tb = exc_tb)
input("Press ENTER to terminate>>\n")
[ドキュメント]
def set_exception(func = handle_exception, print_level = 1):
"""デフォルトの例外ハンドラを設定します。
`sys.excepthook`に指定された関数を設定し、未処理の例外が発生した際の挙動をカスタマイズします。
デフォルトでは`handle_exception`が設定されます。
:param func: 例外ハンドラとして設定する関数。デフォルトは`handle_exception`。
:type func: callable
:param print_level: 情報表示の詳細レベル。1の場合は設定情報を表示。
:type print_level: int
"""
if print_level:
print("Information in tkutils.set_exception(): Set excption handler sys.excepthook")
sys.excepthook = handle_exception
[ドキュメント]
def is_numeric(var):
"""変数が数値型であるか、かつ無限大やNaNではないかを判定します。
Noneや文字列は数値とはみなされません。
:param var: 判定する変数。
:type var: object
:returns: 変数が数値型であり、かつ無限大やNaNではない場合はTrue、それ以外はFalse。
:rtype: bool
"""
if var is None or type(var) is str: return False
if math.isinf(var) or math.isnan(var): return False
return isinstance(var, (int, float, np.float64, np.float32, np.int16, np.int8, numbers.Number))
[ドキュメント]
def conv_float(val, format = "{:.10g}"):
"""浮動小数点数を指定されたフォーマットで文字列に変換します。
`val`が浮動小数点数の場合、`format`文字列を使用して整形し、
それ以外の型の場合は元の値をそのまま返します。
:param val: 変換する値。
:type val: float or object
:param format: 浮動小数点数を整形するためのフォーマット文字列。Noneまたは空文字列の場合は整形しない。
:type format: str or None
:returns: フォーマットされた文字列、または元の値。
:rtype: str or object
"""
if format is None or format == '':
return val
if type(val) is float:
return format.format(val).strip()
return val
[ドキュメント]
def pconv_by_type_one(s, type = 'str', defval = None, strict = False):
"""文字列を指定された単一の型に変換します。
`strict`がTrueの場合、厳密な変換を試み、失敗した場合は`defval`を返します。
`strict`がFalseの場合は、`pint`や`pfloat`のような柔軟な変換関数を使用します。
:param s: 変換する文字列。
:type s: str
:param type: 変換先の型(`int`, `float`, `str`またはその文字列名)。
:type type: type or str
:param defval: 変換失敗時に返すデフォルト値。
:type defval: object or None
:param strict: 厳密な型変換を行うかどうかのフラグ。
:type strict: bool
:returns: 変換された値、または`defval`。
:rtype: int or float or str or object or None
"""
if strict:
if type is int or type == 'int':
try:
return int(s)
except:
return defval
if type is float or type == 'float' or type == 'double':
try:
return float(s)
except:
return defval
if type is str or type == 'str':
return f"{s}"
return s
else:
if type is int or type == 'int':
return pint(s, defval)
if type is float or type == 'float':
return pfloat(s, defval)
if type is str or type == 'str':
return f"{s}"
return s
[ドキュメント]
def pconv_by_type(s, type = 'str', defval = None, strict = False):
"""文字列を指定された複数の型候補の中から、最初に適合する型に変換します。
`type`引数はパイプ`|`で区切られた型名の文字列、または型オブジェクトのリストとして指定できます。
変換が成功した時点でその値を返し、全ての型で失敗した場合は`defval`を返します。
:param s: 変換する文字列。
:type s: str
:param type: 変換先の型(`int`, `float`, `str`またはその文字列名)。パイプで区切られた文字列も可(例: 'int|float')。
:type type: type or str or list[type or str]
:param defval: 変換失敗時に返すデフォルト値。
:type defval: object or None
:param strict: 厳密な型変換を行うかどうかのフラグ。
:type strict: bool
:returns: 変換された値、または`defval`。
:rtype: int or float or str or object or None
"""
if isinstance(type, str):
types = type.split('|')
else:
types = [type]
for t in types:
# print("s=", s, types, t)
ret = pconv_by_type_one(s, type = t, defval = None, strict = strict)
if ret is not None:
return ret
return defval
[ドキュメント]
def pconv(s, defval = 0, strict = True):
"""文字列を自動的に整数、浮動小数点数、または元の文字列に変換します。
まず整数への変換を試み、次に浮動小数点数への変換を試みます。
どちらも失敗した場合は、元の文字列を返します。
`s`がNoneの場合は`defval`を返します。
:param s: 変換する文字列。
:type s: str or None
:param defval: `s`がNoneの場合に返すデフォルト値。
:type defval: object
:param strict: この関数では`strict`引数は使用されません。
:type strict: bool
:returns: 変換された整数、浮動小数点数、または元の文字列。`s`がNoneの場合は`defval`。
:rtype: int or float or str or object
"""
if s is None:
return defval
try:
return int(s)
except:
pass
try:
return float(s)
except:
return s
# if re.match(r'^[+\-]?[\d]+$', s):
# return pint(s, strict, defval)
# if re.match(r'^[+\-]?[+-\d\.eEdD]+$', s):
## if re.match(r'^[+\-]?[+\-\d\.eEdD]+$', s):
# if re.match(r'd$', s):
# return s
# return pfloat(s, strict, defval)
return s
[ドキュメント]
def pint(s, defval = 0, strict = True):
"""文字列を整数に変換します。
まず文字列全体を整数として変換を試みます。
`strict`がTrueで変換に失敗した場合、`defval`を返します。
`strict`がFalseで変換に失敗した場合、文字列内から数値パターンを検索し、
そのパターンが整数として変換できればその値を返します。
:param s: 変換する文字列。
:type s: str or None
:param defval: 変換失敗時に返すデフォルト値。
:type defval: int or float
:param strict: 厳密な型変換を行うかどうかのフラグ。
:type strict: bool
:returns: 変換された整数、または`defval`。
:rtype: int or float
"""
if s is None:
return defval
try:
return int(s)
except:
pass
if strict:
return defval
list = tkre.Search(r'([\+\-]?[\d]+)', s)
try:
return int(list[1])
except:
return defval
[ドキュメント]
def pfloat(s, defval = 0.0, strict = True):
"""文字列を浮動小数点数に変換します。
まず文字列全体を浮動小数点数として変換を試みます。
`strict`がTrueで変換に失敗した場合、`defval`を返します。
`strict`がFalseで変換に失敗した場合、文字列内から数値パターンを検索し、
そのパターンが浮動小数点数として変換できればその値を返します。
:param s: 変換する文字列。
:type s: str or None
:param defval: 変換失敗時に返すデフォルト値。
:type defval: float or object
:param strict: 厳密な型変換を行うかどうかのフラグ。
:type strict: bool
:returns: 変換された浮動小数点数、または`defval`。
:rtype: float or object
"""
if s is None:
return defval
try:
return float(s)
except:
pass
if strict:
return defval
list = tkre.Search(r'([\+\-]?[\d\.eE]+)', s)
try:
return float(list[1])
except:
if defval is None:
return s
return defval
[ドキュメント]
def pintfloat(s, defval = 0.0, strict = True):
"""文字列を整数または浮動小数点数に変換します。
まず整数への変換を試み、失敗した場合は浮動小数点数への変換を試みます。
どちらも失敗した場合は`defval`を返します。
:param s: 変換する文字列。
:type s: str or None
:param defval: 変換失敗時に返すデフォルト値。
:type defval: float
:param strict: `pfloat`関数に渡される`strict`引数。
:type strict: bool
:returns: 変換された整数または浮動小数点数。
:rtype: int or float
"""
try:
return int(s)
except:
pass
return float(s, defval = defval, strict = strict)
[ドキュメント]
def str2val(s):
"""文字列を対応するPythonの値(None, True, False, 数値)に変換します。
'None', 'True', 'False'という文字列はそれぞれのPythonのキーワードに変換されます。
それ以外の文字列は`pconv`関数で数値への変換を試みます。
:param s: 変換する文字列。
:type s: str or None
:returns: 変換されたPythonの値。
:rtype: object or None
"""
if s is None : return None
if s == 'None' : return None
if s == 'True' : return True
if s == 'False': return False
return pconv(s)
[ドキュメント]
def val2str(val):
"""Pythonの値を文字列に変換します(None, True, Falseを特別な文字列に変換)。
None, True, FalseのPythonキーワードをそれぞれの文字列リテラルに変換します。
それ以外の値はそのまま返します。
:param val: 変換する値。
:type val: object or None
:returns: 変換された文字列、または元の値。
:rtype: str or object
"""
if val is None:
val = 'None'
elif val is True:
val = 'True'
elif val is False:
val = 'False'
return val
[ドキュメント]
def index2val(index, list_var, defval = (None, None)):
"""リストのインデックスまたは値から、対応するインデックスと値を取得します。
`index`が整数の場合は直接リストのインデックスとして使用します。
`index`が文字列で数値として解釈できる場合はインデックスとして使用します。
それ以外の場合、`index`がリストの要素値として存在するかを検索します。
見つからない場合は`defval`を返します。
:param index: 検索するインデックスまたは値。
:type index: int or str
:param list_var: 検索対象のリスト。
:type list_var: list
:param defval: 見つからなかった場合に返すデフォルトのタプル (インデックス, 値)。
:type defval: tuple[object, object]
:returns: 見つかったインデックスと値のタプル、または`defval`。
:rtype: tuple[int or None, object or None]
"""
itarget = None
if type(index) is int:
if len(list_var) <= index:
return defval[0], defval[1]
itarget = index
index = list_var[itarget]
else:
v = pint(index, None)
if v is not None:
itarget = v
index = list_var[itarget]
else:
for i, val in enumerate(list_var):
if val == index:
itarget = i
break
else:
return None, None
return itarget, index
[ドキュメント]
def print_line(data_list, format = '{:^10}'):
"""データリストの要素を整形して1行で出力します。
各データ項目は指定された`format`で整形され、スペースなしで連結されて1行に出力されます。
:param data_list: 出力するデータのリスト。
:type data_list: list
:param format: 各データ項目を整形するためのフォーマット文字列。
:type format: str
"""
for d in data_list:
print(format.format(d), end = '')
print()
[ドキュメント]
def print_data(labels, data_list, label_format = '{:^10}', data_format = '{:>10}', header = None, nmax = None, print_level = 0):
"""データを表形式で整形して出力します。
ラベルとデータリストを受け取り、指定されたフォーマットでヘッダーとデータを表形式で出力します。
`nmax`が指定されている場合、表示する行数を制限するためにデータをスキップすることがあります。
:param labels: 列のラベルのリスト。ネストされたリスト/タプルもサポート。
:type labels: list[str] or list[list[str]]
:param data_list: 各列のデータを含むリストのリスト。
:type data_list: list[list]
:param label_format: ラベルを整形するためのフォーマット文字列。
:type label_format: str
:param data_format: データ項目を整形するためのフォーマット文字列。
:type data_format: str
:param header: 表のヘッダーとして表示する文字列 (オプション)。
:type header: str or None
:param nmax: 表示するデータ行の最大数。これを超える場合はスキップして表示。
:type nmax: int or None
:param print_level: この引数は現在使用されていません。
:type print_level: int
"""
if type(labels[0]) is list and type(labels[0]) is tuple:
_l = []
_d = []
for l, d in zip(labels, data_list):
_l.extend(l)
_d.extend(d)
else:
_l = labels
_d = data_list
if header is not None:
print("")
print(header)
ncol = len(_d)
ndata = len(_d[0])
if nmax is None:
nskip = 1
else:
nskip = max([1, int(ndata / nmax)])
print_line(labels, label_format)
for i in range(0, ndata, nskip):
print_line([_d[icol][i] for icol in range(ncol)], data_format)
[ドキュメント]
def pdb():
"""Pythonデバッガ`pdb`のインタラクティブモードに入ります。
`pdb.set_trace()`を呼び出すことで、現在の実行コンテキストでデバッガを起動します。
デバッガの使用方法に関する基本的なヒントも表示されます。
"""
print("Enter to pdb: see https://docs.python.org/ja/3/library/pdb.html")
print(" h: help")
print(" p, pp: print var")
print(" what is: print type of var")
import pdb; pdb.set_trace()
[ドキュメント]
def is_list(var):
"""変数がリストまたはタプルであるかを判定します。
:param var: 判定する変数。
:type var: object
:returns: 変数がリストまたはタプルのいずれかであればTrue、それ以外はFalse。
:rtype: bool
"""
t = type(var)
if t is list or t is tuple:
return True
return False
[ドキュメント]
def to_list(variable):
"""様々な型の変数をリストに変換します。
NumPy配列、Pandas DataFrame、Pandas Series、既存のリストなどのオブジェクトを
Pythonのリスト形式に変換して返します。
:param variable: リストに変換する変数。
:type variable: object
:returns: 変換されたリスト。
:rtype: list
"""
if isinstance(variable, np.ndarray):
return variable.tolist()
# Pandas DataFrame/Seriesはtklibでインポートされていないためコメントアウト
# elif isinstance(variable, pd.DataFrame):
# return variable.values.tolist()
# elif isinstance(variable, pd.Series): # Seriesの場合も対応
# return variable.tolist()
elif type(variable) is list:
return variable
else:
return list(variable)
[ドキュメント]
def minmax_xy(x = None, y = None, x0 = None, xstep = None, xmin = None, xmax = None):
"""x-yデータまたは関数からy値の最小値と最大値を計算します。
`x0`がNoneの場合は、`x`と`y`のリストから`xmin`と`xmax`の範囲内の`y`の最小値と最大値を計算します。
`x0`が指定されている場合は、`x`が等間隔なデータであると仮定し、インデックス計算によって範囲を決定します。
:param x: x座標のリスト。`x0`がNoneの場合に必須。
:type x: list[float] or None
:param y: y座標のリスト。
:type y: list[float] or None
:param x0: xデータの開始値 (等間隔データの場合)。
:type x0: float or None
:param xstep: xデータのステップ幅 (等間隔データの場合)。
:type xstep: float or None
:param xmin: 評価範囲のx最小値。
:type xmin: float or None
:param xmax: 評価範囲のx最大値。
:type xmax: float or None
:returns: y値の最小値と最大値のタプル。
:rtype: tuple[float, float]
"""
if x0 is None:
_min = 1.0e300
_max = -1.0e300
for _x, _y in zip(x, y):
if (xmin is not None and _x < xmin) or (xmax is not None and xmax < _x):
continue
if _min > _y:
_min = _y
if _max < _y:
_max = _y
return _min, _max
else:
n = len(y)
idx0 = max([0, int((xmin - x0) / xstep + 1.0e-5)])
idx1 = min([n-1, int((xmax - x0) / xstep + 1.0e-5)])
_y = y[idx0:idx1 + 1]
return min(_y), max(_y)
[ドキュメント]
def get_max_index(list, axis = 0):
"""リスト(またはNumPy配列)の最大値のインデックスを取得します。
NumPyの`arg_max`関数を使用しますが、これは`np.argmax`の間違いである可能性があり、
現在のコードではエラーになる可能性があります。
:param list: 最大値のインデックスを検索するリストまたはNumPy配列。
:type list: list or np.ndarray
:param axis: NumPy配列の場合の軸。リストの場合は無視されます。
:type axis: int
:returns: 最大値のインデックス。
:rtype: int
"""
return np.arg_max(list, axis = axis)
# return list.index(max(list))
[ドキュメント]
def get_min_index(list, axis):
"""リスト(またはNumPy配列)の最小値のインデックスを取得します。
NumPyの`arg_min`関数を使用しますが、これは`np.argmin`の間違いである可能性があり、
現在のコードではエラーになる可能性があります。
:param list: 最小値のインデックスを検索するリストまたはNumPy配列。
:type list: list or np.ndarray
:param axis: NumPy配列の場合の軸。リストの場合は無視されます。
:type axis: int
:returns: 最小値のインデックス。
:rtype: int
"""
return np.arg_min(list, axis = axis)
# return list.index(in(list))
[ドキュメント]
def find_range_indexes(x, xmin, xmax):
"""ソートされたリスト`x`内で、`xmin`から`xmax`の範囲に収まる要素のインデックス範囲を見つけます。
結果は、`x[ix0]`が`xmin`以上になる最初のインデックスと、
`x[ix1-1]`が`xmax`以下になる最後のインデックス(排他的)のタプルです。
:param x: ソートされた数値のリスト。
:type x: list[float]
:param xmin: 検索範囲の最小値。
:type xmin: float
:param xmax: 検索範囲の最大値。
:type xmax: float
:returns: 範囲内の開始インデックスと終了インデックス(排他的)のタプル。
:rtype: tuple[int, int]
"""
n = len(x)
ix0 = 0
for idx in range(n):
if x[idx] < xmin:
ix0 = idx
else:
break
ix1 = n
for idx in range(n-1, -1, -1):
if x[idx] > xmax:
ix1 = idx
else:
break
return ix0, ix1
[ドキュメント]
def transpose_list2d(data_list):
"""2次元リストを転置します。
行と列を入れ替えた新しい2次元リストを生成します。
:param data_list: 転置する2次元リスト。
:type data_list: list[list]
:returns: 転置された2次元リスト。
:rtype: list[list]
"""
return list(map(list, zip(*data_list)))
[ドキュメント]
def sort_lists(lists):
"""複数のリストをまとめてソートします。
与えられたリストのリストを、最初のリストの要素を基準にソートし、
他のリストの要素もそれに合わせて並べ替えます。
:param lists: ソートする複数のリストを含むリスト。
:type lists: list[list]
:returns: ソートされた複数のリストを含むリスト。
:rtype: list[list]
"""
zip_lists = zip(*lists)
zip_sort = sorted(zip_lists)
return list(zip(*zip_sort))
pattern_int = re.compile(r'^[-+]?[0-9]+$')
pattern_float = re.compile(r'^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$')
[ドキュメント]
def is_float(s):
"""文字列が浮動小数点数として解釈できるかを判定します。
引数`s`が既にfloat型であればTrueを返します。
文字列の場合は、浮動小数点数のパターンにマッチするかを正規表現で判定します。
:param s: 判定する文字列または数値。
:type s: str or float
:returns: 浮動小数点数として解釈できる場合はTrue、それ以外はFalse。
:rtype: bool
"""
t = type(s)
if t is float:
return True
if t is str:
if pattern_float.match(s):
return True
return False
[ドキュメント]
def is_int(s):
"""文字列が整数として解釈できるかを判定します。
引数`s`が既にint型であればTrueを返します。
文字列の場合は、整数のパターンにマッチするかを正規表現で判定します。
:param s: 判定する文字列または数値。
:type s: str or int
:returns: 整数として解釈できる場合はTrue、それ以外はFalse。
:rtype: bool
"""
t = type(s)
if t is int:
return True
if t is str:
if pattern_int.match(s):
return True
return False
[ドキュメント]
def analyze_varstr(str):
"""変数文字列を解析します。
内部関数`_analyze_varstr`を呼び出して変数文字列の解析を行います。
この関数は`tklib.tkobject`に定義されているプライベートな関数をラップしています。
:param str: 解析する変数文字列。
:type str: str
:returns: 解析結果。具体的な型は`_analyze_varstr`の実装に依存。
:rtype: object
"""
return _analyze_varstr(str)
[ドキュメント]
def split_optstr(str):
"""オプション文字列を値とIDに分割します。
文字列が`val:id`の形式である場合、`:`で分割し、それぞれを浮動小数点数と整数に変換して返します。
分割できない場合は、元の文字列とNoneを返します。
:param str: 分割するオプション文字列。
:type str: str
:returns: 値とIDのタプル。値は浮動小数点数、IDは整数、または元の文字列とNone。
:rtype: tuple[float or str, int or None]
"""
try:
val, id = str.split(':')
return pfloat(val), pint(id)
except:
return str, None
[ドキュメント]
def quote_command_if_space(s, special_chars = "", quote_blank = True):
"""スペースや特殊文字が含まれる文字列を引用符で囲みます。
コマンドライン引数などで、スペースや指定された特殊文字を含む文字列を
適切に引用符で囲むことで、単一の引数として扱われるようにします。
すでに引用符で囲まれている場合や、特定のパターンに一致する場合は処理をスキップします。
:param s: 引用符で囲む可能性のある文字列。
:type s: str or None
:param special_chars: 引用符で囲むべき特殊文字の正規表現パターン。
:type special_chars: str
:param quote_blank: この引数は現在使用されていません。
:type quote_blank: bool
:returns: 必要に応じて引用符で囲まれた文字列。
:rtype: str or None
"""
if s is None:
return s
if s == '':
return '""'
if ' ' not in s and not re.search(rf"[{special_chars}]", s):
pass
elif len(s) >= 1 and '/-' in s:
pass
elif re.match(r"\s*[\"\'].*[\"\']", s):
pass
else:
s = f'"{s}"'
return s
[ドキュメント]
def convert_by_vars(s, vars, prefix = r'\$', bra = r'\(', ket = r'\)'):
"""文字列内の変数プレースホルダを`vars`辞書の値に置換します。
文字列`s`の中から、指定された`prefix` (デフォルトは`$`) と
`bra`/`ket` (デフォルトは`()` ) で囲まれた変数名 (例: `$(varname)`) を探し、
`vars`辞書から対応する値を取得して置換します。
変数が見つからない場合は警告を出力し、`??varname??`に置換します。
`\$`のようなエスケープされたプレフィックスはそのままのプレフィックスに変換されます。
:param s: 変数プレースホルダを含む文字列。
:type s: str
:param vars: 変数名とその値を含む辞書。
:type vars: dict[str, str]
:param prefix: 変数名の前に付くプレフィックスの正規表現パターン。デフォルトは`r'\$'`。
:type prefix: str
:param bra: 変数名を囲む開始括弧の正規表現パターン。デフォルトは`r'\('`。
:type bra: str
:param ket: 変数名を囲む終了括弧の正規表現パターン。デフォルトは`r'\)'`。
:type ket: str
:returns: 変数が置換された文字列。
:rtype: str
"""
idebug = 0
if idebug >= 1:
print("")
print("In tkutils.convert_by_vars:")
print(f" vars={vars}")
ret = ''
rest = s
if idebug >= 2:
print(f"89 rest={rest}")
print(f"90 ret={ret}")
while 1:
if idebug >= 2:
print(f"93 rest={rest}")
print(f"94 ret={ret}")
if prefix != '':
# Separate by '$'
m = re.match(rf'(.*?){prefix}(.*)$', rest, flags = re.MULTILINE | re.DOTALL)
# m = re.match(r'(.*?)\$(.*)$', rest, flags = re.MULTILINE | re.DOTALL)
if not m:
if idebug >= 1:
print(f" not matched for $: rest={rest}")
return ret + rest
if idebug >= 1:
print(f" match for $: rest={rest}")
print(f" ret={ret}")
g = m.groups()
pre = g[0]
n = len(pre)
rest = g[1]
if idebug >= 2:
print(f" pre={pre} rest={rest}")
# Case rest = 'abc\$def', should be converted to 'abc$def'
if n > 0 and pre[n-1] == '\\':
ret += pre[0:n-1] + '$'
else:
ret += pre
# Case rest = 'abc$i def', should be converted to 'abc[ini_path] def'
m = re.match(r'([a-zA-Z])([^\w_].*)?$', rest, flags = re.MULTILINE | re.DOTALL)
if m:
# if debug >= 1: # debug変数は定義されていない
# print(f"121: not matched")
g = m.groups()
n = len(g)
varname = g[0].lower()
rest = g[1]
if rest is None:
rest = ''
val = vars.get(varname, None)
# print(f" 1{var=} {val=}")
if val is None:
# app変数は定義されていない
# app.show_error_dialog(f"Invalid varname [${varname}]")
val = f"??{varname}??"
ret += val
if idebug >= 2:
print(f" 1 ret={ret}")
continue
else:
m = re.match(rf'(.*?)({bra}.*)$', rest, flags = re.MULTILINE | re.DOTALL)
if not m:
if idebug >= 1:
print(f" not matched for $: rest={rest}")
return ret + rest
g = m.groups()
ret += g[0]
rest = g[1]
# Case rest = 'abc$(tkProgRoot)def', should be converted to 'abc[tkProgRoot]def'
m = re.match(rf'{bra}(\w+){ket}(.*)$', rest, flags = re.MULTILINE | re.DOTALL)
# m = re.match(r'\((\w+)\)(.*)$', rest, flags = re.MULTILINE | re.DOTALL)
if m:
if idebug >= 1:
print(f"146: not matched rest={rest}")
g = m.groups()
varname = g[0].lower()
rest = g[1]
# print(f"{varname=}")
val = vars.get(varname, None)
if idebug >= 2:
print(f" 3 varname={varname} val={val}")
print(f" 3 vars={vars} val={val}")
if val is None:
print("")
print(f"Warning in tkutils.convert_by_vars(): Invalid varname [{prefix}{bra}{varname}{ket}]")
val = f"??{varname}??"
ret += f"{val}"
if idebug >= 2:
print(f" 4 ret={ret}")
continue
# print(f"193 ret={ret}")
return ret
[ドキュメント]
def del_quote(s):
"""文字列の先頭と末尾の引用符を削除します。
文字列が二重引用符または単一引用符で始まり、同じ引用符で終わる場合に、それらの引用符を削除します。
文字列が短い場合や引用符で囲まれていない場合は、元の文字列をそのまま返します。
:param s: 引用符を含む可能性のある文字列。
:type s: str
:returns: 引用符が削除された文字列、または元の文字列。
:rtype: str
"""
s = s.strip()
n = len(s)
if n <= 1:
return s
if s[0] == '"' and s[n-1] == '"':
return s[1:n-1]
if s[0] == "'" and s[n-1] == "'":
return s[1:n-1]
return s
[ドキュメント]
def add_quotation(str, quotation, quotation_original):
"""文字列に引用符を追加します。
`quotation`パラメータに基づいて、文字列に引用符を追加、削除、または維持します。
`quotation`が'remove'の場合は引用符を削除し、'keep'の場合は`quotation_original`で囲みます。
それ以外の場合は`quotation`で文字列を囲みます。
:param str: 処理対象の文字列。
:type str: str
:param quotation: 引用符の処理方法 ('remove', 'keep') または追加する引用符の種類。
:type quotation: str
:param quotation_original: 'keep'モードで使用される元の引用符の種類(例: '"' や "'")。
:type quotation_original: str
:returns: 処理後の文字列。
:rtype: str
"""
if quotation == 'remove':
s = del_quote(str)
return s
elif quotation == 'keep':
s = quotation_original + str + quotation_original
return s
return quotation + str + quotation
[ドキュメント]
def split_quoted_args(s, sep = r'\s+', quotation = 'remove'):
"""引用符で囲まれた引数を含む文字列を分割します。
スペースまたは指定された区切り文字で文字列を引数のリストに分割します。
二重引用符または単一引用符で囲まれた部分は単一の引数として扱われます。
`quotation`パラメータによって引用符の扱いは変更されます。
:param s: 分割する引数文字列。
:type s: str
:param sep: 引数を区切る正規表現パターン。デフォルトは空白文字。
:type sep: str
:param quotation: 引用符の処理方法 ('remove', 'keep')。
:type quotation: str
:returns: 分割された引数のリスト。
:rtype: list[str]
"""
# arg_list = s.split()
rest = s.strip()
if rest == '':
return rest
args = []
while 1:
if rest[0] == '"':
m = re.match(rf'(.*?)\"{sep}(.*)$', rest, flags = re.MULTILINE | re.DOTALL)
if not m:
args.append(del_quote(rest))
break
a1 = m.groups()[0][1:]
rest = m.groups()[1]
args.append(add_quotation(a1, quotation, '"'))
elif rest[0] == "'":
m = re.match(rf'(.*?)\'{sep}(.*)$', rest, flags = re.MULTILINE | re.DOTALL)
if not m:
args.append(del_quote(rest))
break
a1 = m.groups()[0][1:]
rest = m.groups()[1]
args.append(add_quotation(a1, quotation, "'"))
else:
m = re.match(rf'(.*?){sep}(.*)$', rest, flags = re.MULTILINE | re.DOTALL)
if not m:
args.append(del_quote(rest))
break
a1 = m.groups()[0]
rest = m.groups()[1]
args.append(a1)
return args
[ドキュメント]
def split_two(s, sep = r'\s+', quotation = 'remove', defval = ''):
"""文字列を最初の区切り文字で2つに分割します。
引用符で囲まれた部分を考慮して、文字列をコマンド部分と残りの部分に分割します。
分割できない場合は、元の文字列と`defval`を返します。
`quotation`パラメータによって引用符の扱いは変更されます。
:param s: 分割する文字列。
:type s: str
:param sep: 分割に使用する正規表現の区切り文字パターン。デフォルトは空白文字。
:type sep: str
:param quotation: 分割後の文字列の引用符の処理方法 ('remove', 'keep')。
:type quotation: str
:param defval: 第二の部分が空の場合に返すデフォルト値。
:type defval: str
:returns: 分割された2つの文字列のタプル。
:rtype: tuple[str, str]
"""
s = s.strip()
if len(s) == 0:
return '', ''
if s[0] == '"':
m = re.match(rf'(.*?)\"{sep}(.*)$', s, flags = re.MULTILINE | re.DOTALL)
if m:
g = m.groups()
s1 = g[0][1:]
s2 = g[1]
# print(f"260 s1={s1} s2={s2}")
return add_quotation(s1, quotation, '"'), add_quotation(s2, quotation, '"')
elif s[0] == "'":
m = re.match(rf'(.*?)\'{sep}(.*)$', s, flags = re.MULTILINE | re.DOTALL)
if m:
g = m.groups()
s1 = g[0][1:]
s2 = g[1]
# print(f"268 s1={s1} s2={s2}")
return add_quotation(s1, quotation, "'"), add_quotation(s2, quotation, "'")
m = re.match(rf'(\S+){sep}(.*)\s*$', s, flags = re.MULTILINE | re.DOTALL)
if not m:
# print(f"273 s1={s}")
return s, defval
g = m.groups()
# print(f"276 s1={g[0]} s2={g[1]}")
return g[0], g[1]
[ドキュメント]
def split_command_line(s, quotation = 'remove'):
"""コマンドライン文字列をコマンドと引数リストに分割します。
与えられたコマンドライン文字列を、最初のトークンをコマンドとし、
残りの部分を引用符で囲まれた引数を考慮してリストに分割します。
:param s: 分割するコマンドライン文字列。
:type s: str
:param quotation: 引数文字列の引用符の処理方法 ('remove', 'keep')。
:type quotation: str
:returns: コマンド、残りの引数文字列、および分割された引数リストのタプル。
:rtype: tuple[str, str, list[str]]
"""
cmd, rest = split_two(s, quotation = quotation)
args = split_quoted_args(rest, quotation = 'remove')
return cmd, rest, args
[ドキュメント]
def mprint(*args, fp = None, **kwargs):
"""標準出力とファイルオブジェクトの両方に印刷します。
`print`関数と同様に引数を受け取り、標準出力に表示するとともに、
`fp`で指定されたファイルオブジェクトにも出力し、すぐにフラッシュします。
:param args: `print`関数に渡す位置引数。
:type args: Any
:param fp: 出力先のファイルオブジェクト (オプション)。
:type fp: io.TextIOBase or None
:param kwargs: `print`関数に渡すキーワード引数 (例: `sep`, `end`)。
:type kwargs: Any
"""
print(*args, **kwargs)
if fp:
print(*args, file = fp, **kwargs)
fp.flush()
[ドキュメント]
def get_path_list(varname = 'PATH'):
"""指定された環境変数からパスのリストを取得します。
デフォルトでは'PATH'環境変数の値を`os.pathsep`で分割し、リストとして返します。
:param varname: パスのリストを取得する環境変数名。
:type varname: str
:returns: 環境変数に含まれるパスのリスト。
:rtype: list[str]
"""
return getenv(varname, '').split(os.pathsep)
[ドキュメント]
def desktop_name():
"""現在のデスクトップ環境の名前を取得します。
Windowsの場合は'Windows'、macOSの場合は'Darwin'、
Linuxの場合は'XDG_CURRENT_DESKTOP'環境変数の値、
またはNoneを返します。
:returns: デスクトップ環境の名前、またはNone。
:rtype: str or None
"""
os_name = platform.system()
if os_name == 'Windows':
return 'Windows'
elif os_name == 'Darwin':
return 'Darwin'
ret = getenv('XDG_CURRENT_DESKTOP', '')
if ret != '':
return ret
return None
[ドキュメント]
def add_path(path, varname = 'PATH'):
"""指定されたパスを環境変数に追加します。
`varname`で指定された環境変数(デフォルトは'PATH')に新しいパスを追加します。
パスが既に存在する場合は追加せず、現在の環境変数の値を返します。
新しいパスは既存のパスのリストの先頭に追加されます。
:param path: 環境変数に追加するパス。
:type path: str
:param varname: パスを追加する環境変数名。
:type varname: str
:returns: 更新された環境変数の値。
:rtype: str
"""
os_name = get_os()
sep = os.pathsep
paths = get_path_list(varname = varname)
path_list = [path]
is_found = False
if os_name == 'Linux':
for p in paths:
if p == path:
return os.environ.get(varname, "")
path_list.append(p)
else:
path = path.lower()
for p in paths:
if p.lower() == path:
return os.environ.get(varname, "")
path_list.append(p)
# print("path=", path)
# print("path_list=", path_list)
os.environ[varname] = sep.join(path_list)
return os.environ.get(varname, "")
[ドキュメント]
def normalize_str(str, conv_table = None, target_char = ' ', unicodedata_mode = 'NFKC', ):
"""文字列を正規化します。
指定された変換テーブル (`conv_table`) を使用して文字を置換し、
次に`unicodedata`モジュールで指定されたモード (`unicodedata_mode`) でUnicode正規化を行います。
デフォルトでは、全角スペース、タブ、改行、カンマなどの空白文字や区切り文字を
単一のスペースに変換します。
:param str: 正規化する文字列。
:type str: str or None
:param conv_table: `str.maketrans`で作成された変換テーブル (オプション)。
Noneの場合、デフォルトの変換テーブルが使用されます。
:type conv_table: dict[int, str] or None
:param target_char: デフォルトの変換テーブルで使用される変換先の文字。
:type target_char: str
:param unicodedata_mode: `unicodedata.normalize`で使用される正規化モード (例: 'NFKC', 'NFC')。
:type unicodedata_mode: str
:returns: 正規化された文字列、または元の文字列 (Noneの場合)。
:rtype: str or None
"""
if str is None:
return str
if conv_table is None:
conv_table = str.maketrans({
'\u3000': target_char
, ' ' : target_char
, '\t' : target_char
, '\r' : target_char
, ', ' : target_char
, ',' : target_char
, ',' : target_char
})
str = str.translate(conv_table)
str = unicodedata.normalize(unicodedata_mode, str)
return str
[ドキュメント]
def normalize_name(name, conv_table, unicodedata_mode = 'NFKC', upper_mode = 'upper'):
"""名前文字列を正規化します。
`normalize_str`関数を使用して空白文字や区切り文字を正規化し、
余分なスペースを削除した後、指定された`upper_mode`に基づいて大文字・小文字変換を行います。
:param name: 正規化する名前文字列。
:type name: str or None
:param conv_table: `normalize_str`に渡す変換テーブル。
:type conv_table: dict[int, str]
:param unicodedata_mode: `unicodedata.normalize`で使用される正規化モード。
:type unicodedata_mode: str
:param upper_mode: 大文字・小文字変換モード ('upper', 'lower', 'capitalize', 'title', または何もしない)。
:type upper_mode: str
:returns: 正規化された名前文字列。
:rtype: str or None
"""
name = normalize_str(name, conv_table = conv_table, unicodedata_mode = unicodedata_mode)
name = name.replace(' ', ' ')
name = name.strip()
if upper_mode == 'upper':
name = name.upper()
elif upper_mode == 'lower':
name = name.lower()
elif upper_mode == 'capitalize':
name = name.capitalize()
elif upper_mode == 'title':
name = name.title()
return name
[ドキュメント]
def add_dict(var, key1, key2 = None):
"""辞書に要素を追加またはカウントをインクリメントします。
`key2`がNoneの場合、`var[key1]`の値を1インクリメントします。
`key2`が指定されている場合、`var[key1][key2]`の値を1インクリメントします。
キーが存在しない場合は初期値1で作成します。
:param var: 操作対象の辞書。
:type var: dict
:param key1: 第一レベルのキー。
:type key1: str or Any
:param key2: 第二レベルのキー (オプション)。
:type key2: str or Any or None
"""
if key2 is None:
if var.get(key1, None) is None:
var[key1] = 1
else:
var[key1] += 1
else:
if var.get(key1, None) is None:
var[key1] = {}
if var[key1].get(key2, None) is None:
var[key1][key2] = 1
else:
var[key1][key2] += 1
[ドキュメント]
def get_os_info(terse = False):
"""OSの情報を取得します。
`platform`モジュールを使用して、OSのシステム名、プラットフォーム情報、
リリースバージョン、OSバージョンを返します。
:param terse: プラットフォーム情報を簡潔に表示するかどうかのフラグ。
:type terse: bool
:returns: システム名、プラットフォーム、リリースバージョン、OSバージョンのタプル。
:rtype: tuple[str, str, str, str]
"""
return platform.system(), platform.platform(terse = terse),\
platform.release(), platform.version()
# macOS: 'Darwin', Windows: 'Windows', Linux: 'Linux'
[ドキュメント]
def get_os():
"""OSの名前(Windows, Darwin, Linuxなど)を取得します。
`platform.system()`の戻り値をそのまま返します。
:returns: OSの名前を表す文字列。
:rtype: str
"""
return platform.system()
[ドキュメント]
def is_windows():
"""現在のOSがWindowsであるかを判定します。
Windowsの場合、OSのバージョン文字列を返します。
それ以外の場合はFalseを返します。
:returns: Windowsの場合OSのバージョン文字列、それ以外はFalse。
:rtype: str or bool
"""
if platform.system() == 'Windows':
return platform.version()
return False
[ドキュメント]
def is_mac():
"""現在のOSがmacOSであるかを判定します。
macOS (Darwin) の場合、OSのバージョン文字列を返します。
それ以外の場合はFalseを返します。
.. warning::
元のコードでは`platform.systmem()`と誤字があり、実行時にエラーとなります。
:returns: macOSの場合OSのバージョン文字列、それ以外はFalse。
:rtype: str or bool
"""
if platform.systmem() == 'Darwin':
return platform.version()
return False
[ドキュメント]
def is_linux():
"""現在のOSがLinuxであるかを判定します。
Linuxの場合、OSのバージョン文字列を返します。
それ以外の場合はFalseを返します。
.. warning::
元のコードでは`platform.systmem()`と誤字があり、実行時にエラーとなります。
:returns: Linuxの場合OSのバージョン文字列、それ以外はFalse。
:rtype: str or bool
"""
if platform.systmem() == 'Linux':
return platform.version()
return False
[ドキュメント]
def getenv(key, defval = None):
"""環境変数の値を取得します。
指定されたキーの環境変数が存在しない場合、`defval`を返します。
:param key: 取得する環境変数名。
:type key: str
:param defval: 環境変数が存在しない場合に返すデフォルト値。
:type defval: str or None
:returns: 環境変数の値、または`defval`。
:rtype: str or None
"""
return os.environ.get(key, defval)
[ドキュメント]
def setenv(key, val = None):
"""環境変数を設定または削除します。
`val`がNoneの場合、指定されたキーの環境変数を削除します。
それ以外の場合は、`val`で指定された値を環境変数に設定します。
:param key: 設定または削除する環境変数名。
:type key: str
:param val: 設定する値 (オプション)。Noneの場合、環境変数を削除。
:type val: str or None
"""
if val is None:
# if key in os.environ[key]: # この条件は常にキーがos.environに存在する場合にエラーとなる
if key in os.environ:
del os.environ[key]
return
os.environ[key] = val
[ドキュメント]
def GetPathDelimiter():
"""パス区切り文字を取得します。
`os.sep`を返します。これはOSによって'/'または'\\'になります。
:returns: パス区切り文字。
:rtype: str
"""
return os.sep
[ドキュメント]
def get_path_separator():
"""パス区切り文字を取得します。
`os.sep`を返します。これはOSによって'/'または'\\'になります。
`GetPathDelimiter`のエイリアスです。
:returns: パス区切り文字。
:rtype: str
"""
return os.sep
[ドキュメント]
def get_PATH_env_separator():
"""環境変数PATHの区切り文字を取得します。
`os.pathsep`を返します。これはOSによって':'または';'になります。
:returns: 環境変数PATHの区切り文字。
:rtype: str
"""
return os.pathsep
[ドキュメント]
def get_ext_separator():
"""ファイルの拡張子区切り文字を取得します。
`os.extsep`を返します。これは常に'.'になります。
:returns: 拡張子区切り文字。
:rtype: str
"""
return os.extsep
[ドキュメント]
def is_dir(path):
"""指定されたパスがディレクトリであるかを判定します。
`os.path.isdir()`を呼び出します。
:param path: 判定するパス。
:type path: str or Path
:returns: パスがディレクトリであればTrue、それ以外はFalse。
:rtype: bool
"""
return os.path.isdir(path)
[ドキュメント]
def IsDir(path):
"""指定されたパスがディレクトリであるかを判定します。
`is_dir`関数のエイリアスです。
:param path: 判定するパス。
:type path: str or Path
:returns: パスがディレクトリであればTrue、それ以外はFalse。
:rtype: bool
"""
return os.path.isdir(path)
[ドキュメント]
def is_file(path):
"""指定されたパスがファイルであるかを判定します。
`os.path.isfile()`を呼び出します。
:param path: 判定するパス。
:type path: str or Path
:returns: パスがファイルであればTrue、それ以外はFalse。
:rtype: bool
"""
return os.path.isfile(path)
[ドキュメント]
def IsFile(path):
"""指定されたパスがファイルであるかを判定します。
`is_file`関数のエイリアスです。
:param path: 判定するパス。
:type path: str or Path
:returns: パスがファイルであればTrue、それ以外はFalse。
:rtype: bool
"""
return os.path.isfile(path)
[ドキュメント]
def is_exist(path):
"""指定されたパスが存在するかを判定します。
`os.path.exists()`を呼び出します。
:param path: 判定するパス。
:type path: str or Path
:returns: パスが存在すればTrue、それ以外はFalse。
:rtype: bool
"""
return os.path.exists(path)
[ドキュメント]
def Exists(path):
"""指定されたパスが存在するかを判定します。
`is_exist`関数のエイリアスです。
:param path: 判定するパス。
:type path: str or Path
:returns: パスが存在すればTrue、それ以外はFalse。
:rtype: bool
"""
return os.path.exists(path)
[ドキュメント]
def make_path(dir, *args):
"""パスの要素を結合して新しいパスを作成します。
`os.path.join()`を呼び出します。
:param dir: ベースとなるディレクトリパス。
:type dir: str or Path
:param args: 結合するパス要素。
:type args: str or Path
:returns: 結合された新しいパス。
:rtype: str
"""
return os.path.join(dir, *args)
[ドキュメント]
def MakePath(dir, *args):
"""パスの要素を結合して新しいパスを作成します。
`make_path`関数のエイリアスです。
:param dir: ベースとなるディレクトリパス。
:type dir: str or Path
:param args: 結合するパス要素。
:type args: str or Path
:returns: 結合された新しいパス。
:rtype: str
"""
return make_path(dir, *args)
[ドキュメント]
def get_fullpath(path):
"""指定されたパスの絶対パスを取得します。
`os.path.abspath()`を呼び出します。
:param path: 絶対パスを取得する対象のパス。
:type path: str or Path
:returns: パスの絶対パス。
:rtype: str
"""
return os.path.abspath(path)
[ドキュメント]
def get_ext(path):
"""ファイルの拡張子を取得します。
`split_file_path`関数を使用してパスを解析し、拡張子部分を返します。
:param path: 拡張子を取得するファイルのパス。
:type path: str or Path
:returns: ファイルの拡張子 (例: '.txt', '.py')。拡張子がない場合は空文字列。
:rtype: str
"""
dirname, basename, filebody, ext = split_file_path(path)
return ext
[ドキュメント]
def get_dirname(path):
"""指定されたパスのディレクトリ部分を取得します。
`os.path.abspath()`で絶対パスに変換後、`os.path.dirname()`でディレクトリ名を取得します。
:param path: ディレクトリ部分を取得する対象のパス。
:type path: str or Path
:returns: パスのディレクトリ部分。
:rtype: str
"""
fullpath = os.path.abspath(path)
dirname = os.path.dirname(fullpath)
return dirname
[ドキュメント]
def get_last_directory(path, check_dir = False):
"""パスの最後のディレクトリ名を取得します。
`check_dir`がTrueでパスが実際にディレクトリである場合は、
そのディレクトリ名自体が返されます。
それ以外の場合は、パスのディレクトリ部分の最後のセグメントが返されます。
:param path: 最後のディレクトリ名を取得する対象のパス。
:type path: str or Path
:param check_dir: パスがディレクトリであるかを確認し、その場合特別な処理をするかどうかのフラグ。
:type check_dir: bool
:returns: パスの最後のディレクトリ名。
:rtype: str
"""
if check_dir and os.path.isdir(path):
dirname, last_dir, filebody, ext = split_file_path(path)
return last_dir
else:
dirname, basename, filebody, ext = split_file_path(path)
last_dir = os.path.basename(os.path.normpath(dirname))
return last_dir
[ドキュメント]
def get_upper_directory(path):
"""指定されたパスの上位ディレクトリ名を取得します。
`split_file_path`関数を使用してパスを解析し、親ディレクトリ部分を返します。
:param path: 上位ディレクトリ名を取得する対象のパス。
:type path: str or Path
:returns: パスの上位ディレクトリ名。
:rtype: str
"""
dirname, basename, filebody, ext = split_file_path(path)
return dirname
[ドキュメント]
def split_drive(path):
"""パスをドライブ名と残りのパスに分割します。
Windows環境ではドライブレター(例: 'C:')とその後のパスに分割します。
それ以外のOSでは、ドライブ名として空文字列を返し、元のパスをそのまま返します。
:param path: 分割するパス。
:type path: str or Path
:returns: ドライブ名とドライブ名を除いたパスのタプル。
:rtype: tuple[str, str]
"""
dirname, basename, filebody, ext = split_file_path(path)
last_dir = os.path.basename(os.path.normpath(dirname))
drive = ''
dirname_wo_drive = dirname
m = re.match(r'([a-zA-Z]:)(.*)$', dirname)
if m:
drive = m.groups()[0]
dirname_wo_drive = m.groups()[0] # ここはm.groups()[1]の間違いの可能性が高い
return drive, dirname_wo_drive
[ドキュメント]
def split_file_path(path, check_dir = False):
"""ファイルパスを構成要素(ディレクトリ名、ファイル名、ファイル本体、拡張子)に分割します。
指定されたパスを、ディレクトリ名、ベース名(ファイル名+拡張子)、
ファイル本体(拡張子なしのファイル名)、拡張子に分割して返します。
Windows環境ではバックスラッシュをスラッシュに一時的に変換して処理します。
`check_dir`がTrueでパスがディレクトリの場合、特別な処理を行います。
:param path: 分割するファイルパス。
:type path: str or Path
:param check_dir: パスがディレクトリであるかを確認し、その場合特別な処理をするかどうかのフラグ。
:type check_dir: bool
:returns: (ディレクトリ名, ベース名, ファイル本体名, 拡張子) のタプル。
:rtype: tuple[str, str, str, str]
"""
if os.sep == '\\':
path0 = tkre.Sub(r'\\', '/', path)
else:
path0 = path
path0 = tkre.Sub('/$', '', path0)
if check_dir and os.path.isdir(path):
return path, '', '', ''
# dirname, basename, os.path.split(path)
basename = os.path.basename(path0)
dirname = os.path.dirname(path0)
header, ext = os.path.splitext(path0)
filebody = os.path.basename(header)
if os.path.isdir(path):
ext = ''
if os.sep == '\\':
dirname = tkre.Sub('/', r'\\', dirname)
return dirname, basename, filebody, ext
[ドキュメント]
def SplitFilePath(path):
"""ファイルパスを構成要素(ディレクトリ名、ファイル名、ファイル本体、拡張子)に分割します。
`split_file_path`関数のエイリアスです。
:param path: 分割するファイルパス。
:type path: str or Path
:returns: (ディレクトリ名, ベース名, ファイル本体名, 拡張子) のタプル。
:rtype: tuple[str, str, str, str]
"""
return split_file_path(path)
[ドキュメント]
def modify_path(path, addpath):
"""ファイルパスのファイル本体部分を修正します。
既存のパスのディレクトリ部分を維持しつつ、
ファイル本体名に`addpath`を結合した新しいパスを作成します。
:param path: 修正する元のファイルパス。
:type path: str or Path
:param addpath: ファイル本体名に追加する文字列。
:type addpath: str
:returns: 修正された新しいファイルパス。
:rtype: str
"""
dirname, basename, filebody, ext = split_file_path(path)
return make_path(dirname, filebody + addpath)
[ドキュメント]
def replace_path(path, template, ext_dict = {}):
"""ファイルパスをテンプレート文字列に基づいてフォーマットします。
与えられたファイルパスを絶対パス、ディレクトリ名、ファイル名、ファイル本体、拡張子に分解し、
これらの情報を含む辞書を生成します。
この辞書と`ext_dict`を組み合わせて、`template`文字列をフォーマットします。
:param path: フォーマットする元のファイルパス。
:type path: str or Path
:param template: フォーマットに使用するテンプレート文字列 (例: "{dirname}/{filebody}_new{ext}")。
:type template: str
:param ext_dict: テンプレートに渡される追加の辞書。デフォルトのパス情報に上書きされることもあります。
:type ext_dict: dict
:returns: テンプレートに基づいてフォーマットされた文字列。
:rtype: str
"""
fullpath = os.path.abspath(path)
filename = os.path.basename(fullpath)
dirname = os.path.dirname(fullpath)
filebody, ext = os.path.splitext(filename)
ext_dict.update({
"fullpath": fullpath,
"dirname": dirname,
"filename": filename,
"filebody": filebody,
"ext": ext
})
return template.format(**ext_dict)
[ドキュメント]
def get_user_inifile_dir(env_vars = ['HOME'], dir_names = ['.']):
"""ユーザーの設定ファイルディレクトリを取得または作成します。
`env_vars`に指定された環境変数(デフォルトは'HOME')を順番に検索し、
ユーザーのホームディレクトリを特定します。
次に`dir_names`に指定されたディレクトリ名(デフォルトは'.')を試行し、
既存のディレクトリを見つけるか、新しく作成してそのパスを返します。
:param env_vars: ユーザーのホームディレクトリを特定するための環境変数名のリスト。
:type env_vars: list[str]
:param dir_names: ユーザー設定ファイル用のディレクトリ名のリスト。
:type dir_names: list[str]
:returns: ユーザー設定ファイルディレクトリのパス、または見つからない場合はNone。
:rtype: str or None
"""
HOME = None
for env in env_vars:
HOME = os.environ.get(env, None)
if HOME is None:
continue
if HOME is None:
return None
for dirname in dir_names:
user_ini_dir = os.path.join(HOME, dirname)
if os.path.exists(user_ini_dir):
return user_ini_dir
try:
os.mkdir(user_ini_dir)
except:
continue
return None
[ドキュメント]
def get_file_list(path, filemask = '*.*', filename_only = True):
"""指定されたパス内のファイルとディレクトリのリストを取得します。
`filemask`(セミコロンで区切られた複数のマスクも可)に一致するファイルとディレクトリを検索します。
`filename_only`がTrueの場合、ファイル名はベース名のみを返します。
ディレクトリは`[dirname]`の形式で返され、`[..]`が先頭に追加されます。
:param path: ファイルを検索するディレクトリパス。ファイルパスの場合、そのディレクトリが使用されます。
:type path: str or Path
:param filemask: 検索するファイルマスク (例: '*.txt', '*.log;*.dat')。
:type filemask: str
:param filename_only: 結果のファイル名をベース名のみにするかどうかのフラグ。
:type filename_only: bool
:returns: (ディレクトリ名のリスト, ファイル名のソート済みリスト) のタプル。
:rtype: tuple[list[str], list[str]]
"""
if not os.path.isdir(path):
path = os.path.dirname(path)
files_dict = {}
dirs_dict = {}
for fm in filemask.split(';'):
p = os.path.join(path, fm)
for f in glob.glob(p):
isdir = os.path.isdir(f)
if filename_only:
f = os.path.basename(f)
if isdir:
dirs_dict[f"[{f}]"] = 1
else:
files_dict[f] = 1
dirs = ['[..]']
dirs.extend(list(dirs_dict.keys()))
return dirs, sorted(list(files_dict.keys()))
[ドキュメント]
def get_file_masks(key = 'executables'):
"""指定されたキーに対応するファイルマスクのリストを取得します。
`key`が'executables'の場合、Windows環境では`PATHEXT`環境変数から実行可能ファイルの拡張子リストを取得します。
それ以外のOSやキーでは、汎用的なファイルマスクを返します。
:param key: 取得するファイルマスクのキー (例: 'executables')。
:type key: str
:returns: ファイルマスクのリスト。
:rtype: list[str]
"""
if is_windows():
if key == 'executables':
exts = getenv('PATHEXT', '').lower().split(';')
return ["*{}".format(ext) for ext in exts]
return ["*.exe", "*.com", "*.bat", "*.vbs", "*.sh", "*.csh"]
return ["*.*"]
[ドキュメント]
def find_files(filename, start_dir = None):
"""指定された開始ディレクトリ以下でファイルを検索します。
`os.walk()`を使用してディレクトリツリーを再帰的に走査し、
`fnmatch.fnmatch()`で`filename`パターンに一致するファイルのフルパスのリストを返します。
`start_dir`がNoneの場合、現在の作業ディレクトリから検索を開始します。
:param filename: 検索するファイルのパターン (例: 'my_file*.txt')。
:type filename: str
:param start_dir: 検索を開始するディレクトリパス (オプション)。
:type start_dir: str or Path or None
:returns: 見つかったファイルのフルパスのリスト。
:rtype: list[str]
"""
if start_dir is None:
# os.get.cwd() は os.getcwd() の間違い
start_dir = os.getcwd() # 元のコードのバグを修正しないためコメントアウト
path_list = []
for cur_dir, dirs, files in os.walk(start_dir):
for f in files:
# print("path=", cur_dir, f, filename)
if fnmatch.fnmatch(f, filename):
# if filename == files[i]:
# print(" hit:", cur_dir, f)
path_list.append(os.path.join(cur_dir, f))
# yield os.path.join(cur_dir, file)
return path_list
[ドキュメント]
def find_latest_file(filename, start_dir = None, defval = None):
"""指定された開始ディレクトリ以下で最新の更新日時を持つファイルを検索します。
`find_files`関数で指定されたパターンに一致する全てのファイルを検索し、
その中で最終更新日時が最も新しいファイルのフルパスを返します。
ファイルが見つからない場合は`defval`を返します。
:param filename: 検索するファイルのパターン。
:type filename: str
:param start_dir: 検索を開始するディレクトリパス (オプション)。
:type start_dir: str or Path or None
:param defval: ファイルが見つからない場合に返すデフォルト値。
:type defval: object or None
:returns: 最新の更新日時を持つファイルのフルパス、または`defval`。
:rtype: str or None
"""
path_list = find_files(filename, start_dir = start_dir)
if len(path_list) == 0:
return defval
mtime = 0
ret_path = None
for path in path_list:
mtime1 = os.path.getmtime(path)
# print("path=", path, mtime1)
if mtime1 >= mtime:
ret_path = path
return ret_path
[ドキュメント]
def find_executable_path(filename, defval = None, filename_only = False):
"""実行可能ファイルのフルパスをPATH環境変数から検索します。
PATH環境変数に設定されている各ディレクトリを検索し、
指定された`filename`に一致する実行可能ファイルを探します。
Windowsの場合、`PATHEXT`環境変数に含まれる拡張子も考慮します。
:param filename: 検索する実行可能ファイルの名前。
:type filename: str
:param defval: ファイルが見つからない場合に返すデフォルト値。
:type defval: object or None
:param filename_only: フルパスではなくファイル名のみを返すかどうかのフラグ。
:type filename_only: bool
:returns: 見つかった実行可能ファイルのフルパスまたはファイル名、または`defval`。
:rtype: str or None
"""
paths = get_path_list()
for path in paths:
# print("path:", path)
f = f"{path}{os.sep}{filename}"
# print(" f:", f)
if Exists(f) and os.path.getsize(f) > 0:
# print(" Exists")
if filename_only:
return filename
else:
return f
if is_windows():
exts = getenv('PATHEXT', '').lower().split(';')
for ext in exts:
p = f'{f}{ext}'
# print(" p:", p)
if Exists(p) and os.path.getsize(p) > 0:
# print(" Exists")
if filename_only:
return filename
else:
return p
return defval
[ドキュメント]
def search_executable_path(filenames, defval = None, filename_only = False):
"""複数の実行可能ファイル名から、最初にPATH環境変数で見つかったファイルのフルパスを検索します。
`filenames`のリストを順番に`find_executable_path`で検索し、
最初に見つかった実行可能ファイルのパスを返します。
:param filenames: 検索する実行可能ファイル名のリスト。
:type filenames: list[str]
:param defval: ファイルが見つからない場合に返すデフォルト値。
:type defval: object or None
:param filename_only: フルパスではなくファイル名のみを返すかどうかのフラグ。
:type filename_only: bool
:returns: 最初に見つかった実行可能ファイルのフルパスまたはファイル名、または`defval`。
:rtype: str or None
"""
for f in filenames:
path = find_executable_path(f, defval = None, filename_only = filename_only)
if path:
return path
return defval
[ドキュメント]
def terminate(message = None, usage = None, pause = False):
"""メッセージを表示し、オプションで一時停止してからプログラムを終了します。
任意のメッセージと使用法メッセージを表示し、
`pause`がTrueの場合はユーザーがEnterキーを押すまで待ち、
その後`exit()`を呼び出してプログラムを終了します。
:param message: 終了時に表示するメッセージ (オプション)。
:type message: str or None
:param usage: プログラムの使用法を表示する関数 (オプション)。
:type usage: callable or None
:param pause: プログラム終了前に一時停止するかどうかのフラグ。
:type pause: bool
"""
if message is not None:
print("")
print(message)
if usage:
print("")
usage()
print("")
print("")
if pause:
input("Press ENTER to terminate >> ")
print("")
exit()
[ドキュメント]
def sfmt(str, fmt):
"""文字列を指定されたフォーマットで整形します。
Pythonのf-stringのようなフォーマット機能を使用して、
文字列を中央揃え、右揃えなどの指定された書式で整形します。
:param str: 整形する文字列。
:type str: str
:param fmt: フォーマット文字列 (例: '^10', '>10')。
:type fmt: str
:returns: 整形された文字列。
:rtype: str
"""
f = '{' + fmt + '}'
return f.format(str).strip()
[ドキュメント]
def getarg(position, defval = None):
"""コマンドライン引数を位置指定で取得します。
`sys.argv`から指定された`position`の引数を取得します。
引数が存在しない場合は`defval`を返します。
:param position: 取得するコマンドライン引数の位置 (0はスクリプト名)。
:type position: int
:param defval: 引数が存在しない場合に返すデフォルト値。
:type defval: object or None
:returns: 指定された位置の引数、または`defval`。
:rtype: str or None
"""
try:
return sys.argv[position]
except:
return defval
[ドキュメント]
def getfloatarg(position, defval = None):
"""コマンドライン引数を浮動小数点数として取得します。
`getarg`で引数を取得し、`pfloat`で浮動小数点数に変換します。
:param position: 取得するコマンドライン引数の位置。
:type position: int
:param defval: 引数が存在しない場合、または変換失敗時に返すデフォルト値。
:type defval: float or None
:returns: 変換された浮動小数点数、または`defval`。
:rtype: float or None
"""
s = getarg(position, defval)
v = pfloat(s, defval)
return v
[ドキュメント]
def getintarg(position, defval = None):
"""コマンドライン引数を整数として取得します。
`getarg`で引数を取得し、`pint`で整数に変換します。
:param position: 取得するコマンドライン引数の位置。
:type position: int
:param defval: 引数が存在しない場合、または変換失敗時に返すデフォルト値。
:type defval: int or None
:returns: 変換された整数、または`defval`。
:rtype: int or None
"""
s = getarg(position, defval)
v = pint(s, defval)
return v
[ドキュメント]
def GetList(list, n, defval = 0.0):
"""リストを指定された長さに拡張または切り詰めます。
元のリストのコピーを作成し、`n`よりも短い場合は`defval`で拡張し、
`n`よりも長い場合は`n`で切り詰めます。
:param list: 処理対象のリスト。
:type list: list
:param n: 最終的なリストの目標長。
:type n: int
:param defval: リストを拡張する際に使用するデフォルト値。
:type defval: object
:returns: 指定された長さに調整された新しいリスト。
:rtype: list
"""
l = list.copy()
for i in range(len(l), n):
l.append(defval)
return l[0:n]
[ドキュメント]
def validate_error(y0, y1, eps, message):
"""2つの値間の相対誤差を検証し、許容範囲を超える場合は終了します。
`y0`と`y1`の間の相対誤差を計算し、`eps`で定義された許容誤差を超過する場合、
エラーメッセージを出力してプログラムを終了します。
`y0`が0の場合は`y1`を基準に誤差を計算します。
:param y0: 比較する最初の値。
:type y0: float
:param y1: 比較する2番目の値。
:type y1: float
:param eps: 許容される相対誤差の最大値。
:type eps: float
:param message: エラーメッセージのプレフィックス。
:type message: str
:raises SystemExit: 相対誤差が`eps`を超える場合。
"""
if y0 == 0.0:
err = (y1 - y0) / y1
else:
err = (y1 - y0) / y0
if abs(err) > eps:
print("")
print("{}Too large error {} vs {} error={:8.3g} > {:8.3g}".format(message, y0, y1, err, eps))
print("")
exit()
[ドキュメント]
def read_csv_to_dict(path, delimiter = ',', print_level = 1):
"""CSVファイルを読み込み、列名をキーとする辞書形式でデータを返します。
CSVファイルの1行目をヘッダーとして読み込み、その列名をキーとする辞書を作成します。
各キーの値は、その列のデータをリストとして保持します。
データは`pconv`関数で自動的に型変換されます。
:param path: 読み込むCSVファイルのパス。
:type path: str or Path
:param delimiter: CSVファイルの区切り文字。
:type delimiter: str
:param print_level: ファイル読み込みエラー時の警告表示レベル。
:type print_level: int
:returns: (ラベルのリスト, 列名をキーとするデータ辞書) のタプル。ファイル読み込み失敗時は(None, None)。
:rtype: tuple[list[str] or None, dict[str, list[object]] or None]
"""
try:
f = open(path)
except Exception as e:
if print_level:
print(f"Warning in tkutils::read_csv_to_dict(): Can not read [{path}]")
return None, None
_dict = {}
reader = csv.reader(f)
labels = next(reader)
for l in labels:
_dict[l] = []
for row in reader:
for i, l in enumerate(labels):
_dict[l].append(pconv(row[i]))
return labels, _dict
[ドキュメント]
def read_csv_to_dict_list(path, delimiter = ',', newline = '', convert_float = 0, convert_int = 0, print_level = 1):
"""CSVファイルを読み込み、行を要素とする辞書のリスト形式でデータを返します。
CSVファイルの各行を辞書として読み込み、その辞書のリストを作成します。
辞書のキーはCSVファイルのヘッダー行から取得されます。
`convert_float`または`convert_int`フラグに応じて、数値データは自動的に変換されます。
:param path: 読み込むCSVファイルのパス。
:type path: str or Path
:param delimiter: CSVファイルの区切り文字。
:type delimiter: str
:param newline: `csv.DictReader`に渡す`newline`引数。
:type newline: str
:param convert_float: データを浮動小数点数に変換するかどうかのフラグ。
:type convert_float: int or bool
:param convert_int: データを整数に変換するかどうかのフラグ。`convert_float`より優先度が低い。
:type convert_int: int or bool
:param print_level: ファイル読み込みエラー時の警告表示レベル。
:type print_level: int
:returns: 辞書のリスト。ファイル読み込み失敗時はNone。
:rtype: list[dict[str, object]] or None
"""
try:
f = open(path)
except Exception as e:
if print_level:
print(f"Warning in tkutils::read_csv_to_dict_list(): Can not read [{path}]")
return None
inf = []
reader = csv.DictReader(f, delimiter = delimiter)#, newline = newline)
for row in reader:
inf.append(row)
if convert_float:
for i in range(len(inf)):
for key, val in inf[i].items():
inf[i][key] = pfloat(inf[i][key], None)
elif convert_int:
for i in range(len(inf)):
for key, val in inf[i].items():
inf[i][key] = pint(inf[i][key], None)
return inf
[ドキュメント]
def read_csv(path, delimiter = ',', newline = '',
first_header = 1, convert_float = 0, convert_int = 0):
"""CSVファイルを読み込み、ヘッダーとデータのリストを返します。
CSVファイルを読み込み、`first_header`フラグに基づいて最初の行をヘッダーとして扱います。
残りのデータは行のリストとして返されます。
`convert_float`または`convert_int`フラグに応じて、数値データは自動的に変換されます。
:param path: 読み込むCSVファイルのパス。
:type path: str or Path
:param delimiter: CSVファイルの区切り文字。
:type delimiter: str
:param newline: `csv.reader`に渡す`newline`引数。
:type newline: str
:param first_header: 最初の行をヘッダーとして扱うかどうかのフラグ。1の場合はヘッダーとして扱う。
:type first_header: int or bool
:param convert_float: データを浮動小数点数に変換するかどうかのフラグ。
:type convert_float: int or bool
:param convert_int: データを整数に変換するかどうかのフラグ。`convert_float`より優先度が低い。
:type convert_int: int or bool
:returns: (ヘッダーのリスト, データの2次元リスト) のタプル。ファイル読み込み失敗時は(None, None)。
:rtype: tuple[list[str] or None, list[list[object]] or None]
"""
inf = []
header = None
try:
with open(path) as f:
reader = csv.reader(f, delimiter = delimiter, newline = newline)
for row in reader:
inf.append(row)
if first_header:
header = inf[0]
inf = inf[1:]
if convert_float:
for i in range(len(inf)):
for j in range(len(inf[i])):
inf[i][j] = pfloat(inf[i][j], None)
elif convert_int:
for i in range(len(inf)):
for j in range(len(inf[i])):
inf[i][j] = pint(inf[i][j], None)
return header, inf
except:
return None, None
[ドキュメント]
def read_csv2(fname, delimiter = ','):
"""CSVファイルを読み込み、最初の列をx軸、残りをy軸とする形式でデータを返します。
CSVファイルの1行目をヘッダーとして読み込み、最初のラベルを`xlabel`、
残りのラベルを`ylabels`として扱います。
各列のデータはそれぞれ`x`と`ylist`(yデータのリストのリスト)として取得し、
`pfloat`で浮動小数点数に変換します。
:param fname: 読み込むCSVファイルのパス。
:type fname: str or Path
:param delimiter: CSVファイルの区切り文字。
:type delimiter: str
:returns: (全てのラベルのリスト, 全てのデータのリスト) のタプル。最初の要素がxデータ、以降がyデータ。
:rtype: tuple[list[str], list[list[float or None]]]
"""
print("")
with open(fname) as f:
fin = csv.reader(f, delimiter = delimiter)
labels = next(fin)
xlabel = labels[0]
# label行が 空文字 の場合、データとしては読み込まない
ylabels = []
for i in range(1, len(labels)):
if labels[i] == '':
break
ylabels.append(labels[i])
ny = len(ylabels)
print("xlabel: ", xlabel)
print("ylabels: ", ylabels)
print("ny=", ny)
x = []
ylist = []
for i in range(ny):
ylist.append([])
for row in fin:
x.append(pfloat(row[0]))
for i in range(1, ny+1):
v = pfloat(row[i])
if v is not None:
ylist[i-1].append(v)
else:
ylist[i-1].append(None)
return [xlabel, *ylabels], [x, *ylist]
[ドキュメント]
def save_csv(path, headerlist, datalist, is_print = 0):
"""ヘッダーとデータリストをCSVファイルに保存します。
指定されたパスにCSVファイルを作成し、`headerlist`をヘッダーとして書き込みます。
`datalist`は列ごとのデータのリストのリストとして与えられ、行としてファイルに書き込まれます。
`is_print`がTrueの場合、書き込むデータをコンソールにも表示します。
:param path: 保存するCSVファイルのパス。
:type path: str or Path
:param headerlist: CSVファイルのヘッダーとして書き込む文字列のリスト。
:type headerlist: list[str]
:param datalist: 書き込むデータ。列のリストのリスト (datalist[列番号][行番号])。
:type datalist: list[list[object]]
:param is_print: 保存するデータをコンソールにも出力するかどうかのフラグ。
:type is_print: int or bool
:returns: 保存が成功した場合は1、失敗した場合は0。
:rtype: int
"""
f = open(path, 'w')
if not f:
return 0
# print("len=", len(datalist), len(datalist[0]))
writer = csv.writer(f, delimiter=',', lineterminator='\n')
writer.writerow(headerlist)
for i in range(len(datalist[0])):
dlist = [datalist[id][i] for id in range(len(datalist))]
if is_print:
try:
dliststr = joinf(dlist, "%12.8g", ", ")
except:
dliststr = joinf(dlist, "%12s", ", ")
print(" {:3d}: {}".format(i, dliststr))
writer.writerow(dlist)
f.close()
return 1
[ドキュメント]
def joinf(list, format, sep):
"""リストの要素を指定されたフォーマットと区切り文字で結合した文字列を返します。
`list`の各要素を`format`文字列で整形し、`sep`で区切って単一の文字列として結合します。
:param list: 結合する要素のリスト。
:type list: list
:param format: 各要素を整形するためのフォーマット文字列 (例: "%s", "%d", "%f")。
:type format: str
:param sep: 要素間の区切り文字。
:type sep: str
:returns: フォーマットされ、結合された文字列。
:rtype: str
"""
s = format % (list[0])
for i in range(1, len(list)):
s += sep + format % (list[i])
return s
[ドキュメント]
def merge_attributes(tobj, sobj):
"""ソースオブジェクトの属性をターゲットオブジェクトにマージします。
`sobj` (ソースオブジェクト) の全ての属性を`tobj` (ターゲットオブジェクト) にコピーします。
ただし、`tobj`にすでに存在する属性は上書きされません。
`sobj`がNoneの場合は`tobj`をそのまま返します。
:param tobj: 属性をマージされるターゲットオブジェクト。
:type tobj: object
:param sobj: 属性を提供するソースオブジェクト。
:type sobj: object or None
:returns: 属性がマージされたターゲットオブジェクト。
:rtype: object
"""
if sobj is None:
return tobj
for key, val in sobj.__dict__.items():
try:
getattr(tobj, key)
except:
setattr(tobj, key, val)
return tobj
[ドキュメント]
def check_attributes(obj, isprint = 0, *args):
"""オブジェクトに特定の属性が定義されているかチェックします。
`args`で指定された属性名が`obj`に存在するかを確認します。
存在しない属性名があればそのリストを返します。
`isprint`がTrueの場合、未定義の属性名をコンソールに出力します。
:param obj: 属性をチェックするオブジェクト。
:type obj: object
:param isprint: 未定義の属性名をコンソールに出力するかどうかのフラグ。
:type isprint: int or bool
:param args: チェックする属性名 (可変長引数)。
:type args: str
:returns: 未定義の属性名のリスト。全て定義されている場合はNone。
:rtype: list[str] or None
"""
list = []
for key in args:
try:
getattr(obj, key)
except:
list.append(key)
if len(list) == 0:
return None
else:
if isprint:
print("tkutils.check_attributes: The following attributes are not defined:",
list)
return list
[ドキュメント]
def lvlprint(lprint, level, *args):
"""指定されたレベルに基づいてメッセージを出力します。
`lprint`の値が`level`以上の場合に、`print`関数と同様に引数を出力します。
:param lprint: 現在の印刷レベル。
:type lprint: int
:param level: メッセージを出力するための最小レベル。
:type level: int
:param args: `print`関数に渡す位置引数。
:type args: Any
"""
if lprint >= level:
print(*args)
[ドキュメント]
def BuildCreationDateStr(date = None):
"""指定された日付、または現在の日付から作成日文字列を生成します。
`date`がNoneの場合、現在の日時を使用して'YYYY/MM/DD'形式の文字列を生成します。
:param date: 作成日として使用する`datetime`オブジェクト (オプション)。
:type date: datetime.datetime or None
:returns: 'YYYY/MM/DD'形式の日付文字列。
:rtype: str
"""
if date == None:
date = datetime.datetime.now()
s = "%04d/%02d/%02d" % (date.year, date.month, date.day)
return s
[ドキュメント]
def copy_path(path, newpath):
"""ファイルを新しい場所にコピーします。
`shutil.copy2()`を使用してファイルをコピーします。
シンボリックリンクは解決されます。
:param path: コピー元のファイルのパス。
:type path: str or Path
:param newpath: コピー先のファイルのパス。
:type newpath: str or Path
:returns: コピーが成功した場合は1、失敗した場合は0。
:rtype: int
"""
try:
shutil.copy2(path, newpath, follow_symlinks = True)
return 1
except:
return 0
[ドキュメント]
def rename_file(path, newpath):
"""ファイル名を変更します。
`os.rename()`を使用してファイルのパスを変更します。
:param path: 変更する元のファイルのパス。
:type path: str or Path
:param newpath: 新しいファイルのパス。
:type newpath: str or Path
:returns: 名前変更が成功した場合は1、失敗した場合は0。
:rtype: int
"""
try:
os.rename(path, newpath)
return 1
except:
return 0
[ドキュメント]
def delete_file(path):
"""ファイルを削除します。
`os.remove()`を使用して指定されたファイルを削除します。
:param path: 削除するファイルのパス。
:type path: str or Path
:returns: 削除が成功した場合は1、失敗した場合は0。
:rtype: int
"""
try:
os.remove(path)
return 1
except:
return 0