1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

pythonで複数の.pyファイルからのメイン実行ファイルを探したい

Posted at

前提

ゴールとしてアップロードされたファイルが複数あった場合にメインで実行するファイルを探したい
複数のpythonのファイルから実行対象のファイルを探すスクリプトです
人間が確認する場合は各ファイルを確認したり、ファイル名から辺りをつけたりするのが普通ですが
今回は機会的に各ファイルを確認してメイン実行ファイルを探していく処理を試していきます

ファイルサンプル

アップロードファイル

・main.py
・test1.py
・test2.py

今回の例だと「main.py」が実行ファイルとなっており
main.pyで「test1.py, test2.py」をimportしています

例としてこのようなファイルがあったとして
パッとみた時人間ならばとりあえず「main.py」が命名的に実行対象かと思う人が多いと思います
今回はサンプルですがファイル数が増えたり名称が分かりずらい場合は1つ1つのファイルを確認するのも大変になってきます

条件

今回はシンプルに大きく2つの条件でメインの実行ファイルを探してみます

①「main」の記載があればメインファイルとして扱う
②各ファイルを検索して他のファイル全てがimportされていればメインファイルとして扱う

今回の例ではmain.pyで「test1.py, test2.py」をimportしているので②の条件で検索します

実戦

search.py
import os
import ast
import copy

def find_main_file(directory):
  # ディレクトリ内に存在する「.py」ファイルを一覧で取得
  files = [f for f in os.listdir(directory) if f.endswith('.py')]
  # 検索用の「search.py」は不要なので除外
  files.remove('search.py')
  print("ディレクトリ内の「.py」一覧")
  print(files)
  
  for file_name in files:
    print(file_name + "のファイル中を検索")
    file_path = os.path.join(directory, file_name)
    
    # ファイルの中身から「__main__ 」の記述があるファイルの場合終了
    if has_main_func(file_path):
      return file_name

    # 検索中のファイルに対して 他のファイルがimportされているか
    check_files = copy.deepcopy(files)
    # 検索中のファイルは除外
    check_files.remove(file_name)

    # 各要素から.pyを取り除いた新しいリストを作成
    check_files_extension = [item[:-3] if item.endswith('.py') else item for item in check_files]

    # 検索中のファイルからform importの内容をリストで取得
    # 例:{'re', 'json', 'lxml', 'random', 'csv'}
    dependencies = get_dependencies(file_path)
    print(file_name + "のform importの内容をリストで取得")
    print(dependencies)

    if all(item in dependencies for item in check_files_extension):
      # 全てのファイルがインポートされているのでこれがメインファイル
      return file_name
          
  return None

def get_dependencies(file_path):
  with open(file_path, 'r') as file:
    tree = ast.parse(file.read(), filename=file_path)

  dependencies = set()
  for node in ast.walk(tree):
    if isinstance(node, ast.Import):
      for alias in node.names:
        dependencies.add(alias.name)
    elif isinstance(node, ast.ImportFrom):
      dependencies.add(node.module)
  return dependencies

def has_main_func(file_path):
  with open(file_path, 'r') as file:¥
    content = file.read()
    return 'if __name__ == "__main__":' in content

if __name__ == "__main__":
  # 現在のスクリプトが実行されているディレクトリを取得
  directory_path = os.path.dirname(os.path.abspath(__file__))
  main_file = find_main_file(directory_path)

  if main_file:
    print(f"メインファイルはこちらです is: {main_file}")
  else:
    print("メインファイルが見つかりません")

実行した時のログはこちらになります

ディレクトリ内の「.py」一覧
['main.py', 'test1.py', 'test2.py']
main.pyのファイル中を検索
main.pyのform importの内容をリストで取得
{'lxml', 'json', 're', 'random', 'test1', 'test2', 'csv'}
メインファイルはこちらです is: main.py

処理の内容としては検索対象のディレクトリから「.py」の拡張子のファイルを一覧で取得します(search.pyは検索用のファイルなので一覧からは除外)
ファイル一覧をループして各ファイルの中身を確認します
main」の記載がある場合はそのファイルをメインファイルとして返却
他のファイルがimportされている場合もメインファイルをして返却
とファイル一覧を全て調べて最終的にメインファイルを特定するといった流れになります

おわり

今回はシンプルな条件での検索にしましたがこれだけではフォローしきれない場合もあるかと思います
フォローしきれない場合は条件付けをもっと細かくするとか、検索でメインファイルが特定できない場合はデフォルトの固定のファイル名称をメインファイルとして扱うなど必要かもしれません

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?