converter.compare_dir_files のソースコード

"""
ディレクトリ間のファイル構成を比較するスクリプト。

このスクリプトは、特定の除外パターンに合致するファイルを無視し、
2つのディレクトリツリー間でどちらか一方にのみ存在するファイルを報告します。
主にソースコードの同期状態や、予期せぬファイルの有無を確認する際に役立ちます。

:doc:`compare_dir_files_usage`
"""
import argparse
import os
import re
from pathlib import Path


# 除外パターンの定義 (パス全体に対して判定)
exclude_patterns = [
        r"/__init__\.py$",
        r"/.*_docstring\.",
        r"/.*_\d{8}\.",
        r"/tklib/",
        r"/cms/",
        r"/jsap_crystal/",
        r"/stastical_physics/",
    ]

[ドキュメント] def compare_directories(source_dir, target_dir, extension="*.py"): """ 2つのディレクトリの内容を比較し、一方にのみ存在するファイルを報告する。 この関数は、ネストされた ``is_excluded`` 関数と ``get_filtered_files`` 関数を使用して、 除外パターンにマッチしないファイルを収集します。収集したファイルセットの差分を計算し、 結果を標準出力に表示します。パスのセパレータはPOSIX形式 ('/') に統一されます。 :param source_dir: str 比較元のディレクトリパス。 :param target_dir: str 比較先のディレクトリパス。 :param extension: str 検索するファイルの拡張子パターン (例: "*.py")。 :returns: None 結果は標準出力に表示されるため、明示的な戻り値はない。 """ source_path = Path(source_dir) target_path = Path(target_dir) def is_excluded(rel_path_str): """ 相対パスが定義済み除外パターンにマッチするかを判定する。 相対パスの先頭に '/' を付与し、``exclude_patterns`` の各正規表現と照合します。 いずれかのパターンにマッチすれば除外対象と判断します。 :param rel_path_str: str 比較する相対パス文字列 (例: "path/to/file.py")。 :returns: bool 除外パターンにマッチした場合はTrue、そうでなければFalse。 """ # マッチング用に先頭に / をつける test_path = "/" + rel_path_str return any(re.search(pattern, test_path) for pattern in exclude_patterns) def get_filtered_files(base_path): """ 指定されたベースパス以下のファイルをフィルタリングして取得する。 ``Path.rglob()`` を使用して指定された拡張子パターンにマッチするファイルを再帰的に検索します。 各ファイルの相対パスを ``is_excluded`` 関数でチェックし、 除外対象でないファイルのみをセットとして収集します。 相対パスはPOSIX形式 ('/') に統一されます。 :param base_path: Path 検索を開始するベースディレクトリの `Path` オブジェクト。 :returns: set[str] フィルタリングされたファイルの相対パスのセット。 """ file_set = set() for p in base_path.rglob(extension): # ベースディレクトリからの相対パスを取得し '/' 区切りに変換 rel_path = p.relative_to(base_path).as_posix() if not is_excluded(rel_path): file_set.add(rel_path) return file_set source_files = get_filtered_files(source_path) target_files = get_filtered_files(target_path) print(f"{'='*60}") print(f" 比較レポート (Separator: '/')") print(f"{'='*60}") print(f"・[A] Source: {source_path.absolute()}") print(f"・[B] Target: {target_path.absolute()}") print(f"{'-'*60}\n") # AにあってBにないもの only_in_source = sorted(source_files - target_files) print(f"【 状態: Source [A] にのみ存在 / Target [B] から欠落 】") if only_in_source: for f in only_in_source: print(f" [MISSING in B] -> {f}") else: print(" 該当なし") print("\n" + "-"*60 + "\n") # BにあってAにないもの only_in_target = sorted(target_files - source_files) print(f"【 状態: Target [B] にのみ存在 / Source [A] から欠落 】") if only_in_target: for f in only_in_target: print(f" [MISSING in A] -> {f}") else: print(" 該当なし")
[ドキュメント] def main(): """ コマンドライン引数を解析し、ディレクトリ比較処理を実行する。 この関数は ``argparse`` を使用して、比較元ディレクトリ、比較先ディレクトリ、 検索拡張子パターンを引数として受け取ります。 デフォルト値も設定されており、指定されたパスが有効なディレクトリであるかを確認し、 ``compare_directories`` 関数を呼び出します。 :param None: コマンドライン引数は ``argparse`` によって処理されるため、明示的な引数はない。 :returns: None 処理結果は標準出力に表示される。 """ parser = argparse.ArgumentParser(description="特定のディレクトリやファイルパターンを除外して2つのツリーを比較します。") parser.add_argument("source", nargs="?", default=r"D:\git\tkProg\tkprog_COE", help="比較元ディレクトリ(A)") parser.add_argument("target", nargs="?", default=r"D:\git\sphinx\tkProg\source", help="比較先ディレクトリ(B)") parser.add_argument("-e", "--ext", default="*.py", help="検索パターン (デフォルト: *.py)") args = parser.parse_args() if not os.path.isdir(args.source) or not os.path.isdir(args.target): print("エラー: 指定されたパスが存在しないか、ディレクトリではありません。") return compare_directories(args.source, args.target, args.ext)
if __name__ == "__main__": main()