#!/usr/bin/env python3
"""
ファイル: list_files_recursive.py
作成者: ChatGPT
変更修正者:robotcreation-dialy.com
説明:
指定されたフォルダに含まれるすべてのファイルパス(サブフォルダも含む)のリストを返す単一の関数 `list_files_recursive(folder_path)` を提供します。返されるパスは絶対パスで、アルファベット順に並べられています。
スクリプトを直接実行した際に、現在の作業ディレクトリで関数を実行する小さなテストハーネスが含まれています。
"""
import os
from typing import List
from langchain_core.tools import tool
from pydantic import BaseModel, Field
import json
g_file_description = {}
g_data_file_path = ""
def _list_files_recursive(folder_path: str) -> List[str]:
"""
*folder_path* 配下のすべてのファイルパスのリストを再帰的に返します。
パラメータ
----------
folder_path : str
スキャンするディレクトリへのパス。相対パスまたは絶対パスで指定します。
パスが存在しないかディレクトリでない場合は、ValueError がスローされます。
戻り値
-------
List[str]
ソートされた絶対ファイルパスのリスト。ディレクトリは除外されます。
注記
-----
* 隠しファイル(ドットで始まるもの)も含まれます。
* ファイルを指すシンボリックリンクも含まれます。ディレクトリを指すリンクは os.walk によって辿られます。
* この関数は、大規模なディレクトリツリーに効率的な `os.walk` を使用します。
"""
global g_file_description
global g_data_file_path
folder_path = folder_path.replace("\\", "/")
if not os.path.isdir(folder_path):
raise ValueError(f"'{folder_path}' is not a valid directory")
abs_root = os.path.abspath(folder_path)
file_list: List[str] = []
for root, dirs, files in os.walk(abs_root):
for name in files:
file_path = os.path.join(root, name)
file_list.append(file_path)
file_list.sort()
g_file_description = {}
for file_path in file_list:
file_path = file_path.replace("\\", "/")
file_path = file_path.replace(folder_path, "")
_set_file_description(file_path)
if os.path.exists(g_data_file_path):
_load_file_descriptions()
else:
_save_file_descriptions()
return file_list
def _create_folder(folder_path):
"""指定されたフォルダが存在しない場合、フォルダを作成します。
Args:
folder_path: 作成するフォルダのパス。
"""
folder_path = folder_path.replace("\\", "/")
if not os.path.exists(folder_path):
os.makedirs(folder_path)
if folder_path.endswith("/"):
return folder_path
else:
return folder_path + "/"
def _get_parent_directory(file_path):
"""__file__からファイル名を除いてさらに親フォルダのパスを得る関数。
Args:
file_path: ファイルのパス。
Returns:
親フォルダのパス。ファイルが存在しない場合はNoneを返す。
"""
if not os.path.exists(file_path):
return None
parent_dir = os.path.dirname(file_path)
grandparent_dir = os.path.dirname(parent_dir)
return grandparent_dir
def _get_directory(file_path):
"""__file__からファイル名を除いてさらに親フォルダのパスを得る関数。
Args:
file_path: ファイルのパス。
Returns:
親フォルダのパス。ファイルが存在しない場合はNoneを返す。
"""
if not os.path.exists(file_path):
return None
parent_dir = os.path.dirname(file_path)
return parent_dir
def _set_data_file_path(file_path):
"""データファイルのパスを設定します。
Args:
file_path: データファイルのパス。
"""
global g_data_file_path
g_data_file_path = file_path
def _print_file_descriptions():
"""現在のファイルの説明をコンソールに出力します。
"""
global g_file_description
for file_path, description in g_file_description.items():
print(f"{file_path}: {description}")
def _set_file_description(file_path, purpose_of_creation = "",description=""):
"""指定されたファイルの説明を作成します。
Args:
file_path: 説明を作成するファイルのパス。
purpose_of_creation: ファイル作成目的
description: ファイル内容の説明
"""
global g_file_description
g_file_description[file_path] = {"purpose_of_creation":purpose_of_creation,"description":description}
def _save_file_descriptions():
"""ファイルの説明を指定されたパスに保存します。
Args:
output_path: 説明を保存するファイルのパス。
"""
global g_file_description
global g_data_file_path
with open(g_data_file_path, "w", encoding="utf-8") as f:
json.dump(g_file_description, f, indent=4, ensure_ascii=False)
def _load_file_descriptions():
"""指定されたパスからファイルの説明を読み込みます。
Args:
input_path: 説明を読み込むファイルのパス。
"""
global g_file_description
global g_data_file_path
if not os.path.exists(g_data_file_path):
return
with open(g_data_file_path, "r", encoding="utf-8") as f:
g_file_description = json.load(f)
_clean_up_file_descriptions()
def _get_all_file_descriptions() -> str:
"""現在のすべてのファイルの説明を取得します。
Returns:
すべてのファイルの説明を含む辞書。
"""
global g_file_description
# g_file_descriptionをJSON文字列として返す
result = json.dumps(g_file_description, indent=4, ensure_ascii=False)
return result
def _clean_up_file_descriptions():
"""
存在しないファイルを説明リストから削除します。
"""
global g_file_description
to_delete = []
for file_path in g_file_description.keys():
if not os.path.exists(file_path):
to_delete.append(file_path)
for file_path in to_delete:
del g_file_description[file_path]
def _get_and_create_all_file_descriptions(folder_path: str) -> List[str]:
"""
*folder_path* 配下のすべてのファイルパスのリストを再帰的に返します。
パラメータ
----------
folder_path : str
スキャンするディレクトリへのパス。相対パスまたは絶対パスで指定します。
パスが存在しないかディレクトリでない場合は、ValueError がスローされます。
戻り値
-------
List[str]
ソートされた絶対ファイルパスのリスト。ディレクトリは除外されます。
注記
-----
* 隠しファイル(ドットで始まるもの)も含まれます。
* ファイルを指すシンボリックリンクも含まれます。ディレクトリを指すリンクは os.walk によって辿られます。
* この関数は、大規模なディレクトリツリーに効率的な `os.walk` を使用します。
"""
_create_folder(folder_path)
file_path = os.path.join(folder_path, "progress_file_list.json")
_set_data_file_path(file_path)
if not os.path.exists(file_path):
_list_files_recursive(folder_path)
else:
_load_file_descriptions()
_clean_up_file_descriptions()
return _get_all_file_descriptions()
# --------------------------------------------------------------------------- #
# Test harness – runs when the script is executed directly
# --------------------------------------------------------------------------- #
class SetFileDescription(BaseModel):
file_path: str = Field()
description: str = Field(default="")
@tool(args_schema=SetFileDescription)
def set_file_description(file_path, purpose_of_creation = "",description=""):
"""
指定されたファイルの説明を作成します。
Args:
file_path: 説明を作成するファイルのパス。
purpose_of_creation: ファイル作成目的
description: ファイル内容の説明
"""
_set_file_description(file_path, purpose_of_creation,description)
@tool
def save_file_descriptions():
"""
ファイルの説明を指定されたパスに保存します。
Args:
output_path: 説明を保存するファイルのパス。
"""
_save_file_descriptions()
@tool
def load_file_descriptions():
"""
指定されたパスからファイルの説明を読み込みます。
Args:
input_path: 説明を読み込むファイルのパス。
"""
_load_file_descriptions()
def _get_file_description(file_path):
"""
指定されたファイルの説明を取得します。
Args:
file_path: 説明を取得するファイルのパス。
Returns:
ファイルの説明。説明が存在しない場合は空文字列を返す。
"""
global g_file_description
return g_file_description.get(file_path, "")
@tool
def get_all_file_descriptions() -> str:
"""
現在のすべてのファイルの説明を取得します。
Returns:
すべてのファイルの説明を含む辞書。
"""
return _get_all_file_descriptions()
if __name__ == "__main__":
import sys
# Use the directory from which the script is run if no argument is given
target_dir = _get_directory(__file__)
try:
files = _list_files_recursive(target_dir)
print(f"Found {len(files)} file(s) under '{target_dir}':")
target_dir = target_dir.replace("/", "\\")
print(target_dir)
for f in files:
f = f.replace(target_dir, "")
print(f)
print(target_dir)
except ValueError as e:
print(f"Error: {e}")
sys.exit(1)