Skip to content

pyxel run and pyxel watch fail to import local modules because they append relative paths to sys.path which break after pyxel.init() calls os.chdir() #667

@gomatama5

Description

@gomatama5

概要

pyxel run および pyxel watch コマンドで、サブディレクトリを指定してスクリプトを実行した際(例: pyxel run game/start.py)、そのスクリプトと同じディレクトリにあるモジュールのインポート(例: import hoge)に失敗する問題を報告します。

これは、pyxel run が検索パスを「相対パス」のまま登録しているため、pyxel.init() によるディレクトリ移動(os.chdir)が発生すると、登録されたパスが実在しない場所を指してしまうことが原因です。

再現用ファイル構成

以下のように、game というサブディレクトリの中にメインスクリプトと自作モジュールがある構成を想定します。

project/ (ここからコマンドを実行)
└── game/
    ├── start.py  (import hoge; pyxel.init(...) を含む)
    └── hoge.py   (自作モジュール)

再現手順

  1. project/ ディレクトリ(game/ の親ディレクトリ)へ移動します。
  2. 以下のコマンドを実行します。
    pyxel run game/start.py

期待される挙動

標準の python game/start.py コマンドと同様に、隣接する hoge.py が正常にインポートされ、ゲームが起動すること。

実際の挙動

ModuleNotFoundError: No module named 'hoge' が発生し、実行に失敗します。

原因の分析

本現象は、pyxel/cli.py 内のパス処理と pyxel.init() の仕様が干渉することで発生しています。

  1. 標準の python コマンドの挙動との違い
    標準の python game/start.py を実行した場合、Pythonインタプリタは自動的にスクリプトがあるディレクトリ(game/)の 絶対パスsys.path[0] に挿入します。そのため、実行中に os.chdir() が呼ばれてもインポートの参照先は影響を受けません。

  2. pyxel run による相対パスの登録
    pyxel/cli.pyrun_python_script 関数内では、sys.path.append(os.path.dirname(python_script_file)) と処理されています。このため、コマンドライン引数に基づいた 相対パス(例: "game")sys.path の末尾に追加されます。

  3. os.chdir() による参照切れ
    start.py 内で pyxel.init() が呼ばれると、カレントディレクトリがスクリプトの場所(project/game/)へ移動します。この移動後も sys.path には起動時の相対パス("game")が残っているため、Pythonは project/game/game/ を探しに行ってしまい、インポートに失敗します。
    pyxel play は一時フォルダを絶対パスで扱うためこの問題は発生しませんが、runwatch はユーザーの入力を直接利用するため発生します。

修正案の提案

pyxel/cli.py 内の run_python_script 等において、追加するディレクトリを 絶対パス に変換し、かつ標準の挙動に合わせて sys.path の先頭(index 0) に挿入することを提案します。

# 修正案 (pyxel/cli.py)
# os.path.abspath を使用して、ディレクトリ移動の影響を受けないようにする
script_dir = os.path.abspath(os.path.dirname(python_script_file))
sys.path.insert(0, script_dir)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions