0
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?

pathlib.Path.is_file() は長いファイルパスで OS ごとに挙動が異なる

0
Last updated at Posted at 2026-03-11

この記事の結論

Python の pathlib.Path.is_file() によるファイル存在判定は、OS ごとに「存在しない長いファイルパス」への挙動が以下のように異なる (「個別に短いパス」の意味は後述)。
なお、Windows では長いパスを有効にしているものとする。

Windows Linux macOS
ファイルパスが ~ 約 1024 バイト
上:左から存在しない「個別に短いパス」が取れる
下:左から存在しない「〃」が取れない
False (1)
False (2)
False (1)
例外 (4)
False (1)
例外 (4)
ファイルパスが 約 1024 ~ 4096 バイト
上:左から存在しない「個別に短いパス」が取れる
下:左から存在しない「〃」が取れない
False (1)
False (2)
False (1)
例外 (4)
例外 (5)
ファイルパスが 約 4096 ~ 32767 バイト
上:左から存在しない「個別に短いパス」が取れる
下:左から存在しない「〃」が取れない
False (1)
False (2)
例外 (5) 例外 (5)
ファイルパスが 約 32767 バイト ~ False (3) 例外 (5) 例外 (5)

上記の表のセル内の各番号で発生する事象は以下である。

  • (1) os.stat()ENOENT (No such file or directory) となるが、捕捉される。
  • (2) os.stat()_WINERROR_INVALID_NAME (ファイル名不正) となるが、捕捉される。
  • (3) os.stat()ValueError: stat: path too long for Windows だが、捕捉される。
  • (4) os.stat()ENAMETOOLONG (ファイル/ディレクトリ長超過) で、捕捉されない。
  • (5) os.stat()ENAMETOOLONG (フルパス最大長超過) で、捕捉されない。

これらの差は、pathlib が ENAMETOOLONG (名前が長すぎる) は捕捉しない一方で、Windows の _WINERROR_INVALID_NAMEValueError: stat: path too long for Windows は捕捉すること、OS ごとに許容されるパスの最大長が異なる (macOS 1024 バイト、Linux 4096 バイト、Windows は長いパスを有効にすれば 32767 バイト) ことに起因する。


…なので、クロスプラットフォームで動作させるプロジェクト (特にファイル存在判定に長い文字列が渡されうるプロジェクト) ではファイル存在確認に注意する。例えばファイル名の形式を定めておき pathlib.Path.is_file() の前にチェックするか、OSError を捕捉する。

どの OS でも長い文字列を扱えるファイル存在確認の例
import pathlib

def is_file(path):
    try:
        return pathlib.Path(path).is_file()
    except OSError:
        pass
    return False

ret = is_file('a' * 1100)
print(ret)

なお、ここで「個別に短いパス」とは、個別のディレクトリ/ファイル名は約 255 バイト以内になっているパスのことをいう (下記)。要は、左から短い名前を解決している間に不存在が発見されれば、ENAMETOOLONG より先に ENOENT (No such file or directory) が出てくれる (そして pathlib が捕捉してくれる)。

  • 左から存在しない「個別に短いパス」が取れる例 (実際に存在不存在):
    • {英数250字}/{英数250字}/{英数250字}/{英数500字}
    • {英数250字}/{英数250字}/{英数250字}/{英数500字}
    • {英数250字}/{英数250字}/{英数250字}/{英数500字}
  • 左から存在しない「個別に短いパス」が取れない例 (実際に存在不存在):
    • {英数250字}/{英数250字}/{英数250字}/{英数500字}

参考文献


OS ごとの os.stat() の実行結果

pathlib.Path.is_file()os.stat() で情報取得するので、以下の結果を確認する。

python -c "import os; os.stat('a' * 1100 + '.txt')"
python -c "import os; os.stat('a/' + 'a' * 1100 + '.txt')"

結果、どの OS でも存在しないファイルの情報取得は例外になるが、例外の内容が異なる。macOS では 1100 文字で既に許容されるパス最大長を超過しているために、親ディレクトリが不存在でも File name too long になっている。

Windows Linux macOS
os.stat('a' * 1100 + '.txt') (存在しない名前が長いファイル) OSError: [WinError 123] ファイル名、ディレクトリ名、またはボリューム ラベルの構文が間違っています。 OSError: [Errno 36] File name too long OSError: [Errno 63] File name too long
os.stat('a/' + 'a' * 1100 + '.txt') (存在しない名前の短いサブディレクトリ以下にある名前が長いファイル) FileNotFoundError: [WinError 3] 指定されたパスが見つかりません。 FileNotFoundError: [Errno 2] No such file or directory OSError: [Errno 63] File name too long

ちなみに、'a' * 5000 にすると Linux も親ディレクトリによらず File name too long になる。さらに 'a' * 50000 にすると Windows は親ディレクトリによらず ValueError: stat: path too long for Windows となる (ValueError になるのが他 OS と異なる)。

OS ごとの pathlib.Path.is_file() の実行結果

次に、肝心の pathlib.Path.is_file() の結果を確認する。

python -c "import pathlib; print(pathlib.Path('a' * 1100 + '.txt').is_file())"
python -c "import pathlib; print(pathlib.Path('a/' + 'a' * 1100 + '.txt').is_file())"

pathlib.Path.is_file() はファイル名が長すぎる例外を捕捉しないため、この例外は貫通している。が、Windows でファイル名が長すぎる場合に送出される _WINERROR_INVALID_NAME は捕捉するので Windows においてはファイル名が長くても例外とならない。

Windows Linux macOS
pathlib.Path('a' * 1100 + '.txt').is_file() (存在しない名前が長いファイル) False OSError: [Errno 36] File name too long OSError: [Errno 63] File name too long
pathlib.Path('a/' + 'a' * 1100 + '.txt').is_file() (存在しない名前の短いサブディレクトリ以下にある名前が長いファイル) False False OSError: [Errno 63] File name too long

この記事の背景

  • 私の実験プロジェクトでは、実験設定を TOML ファイルパスでも TOML 解釈できる文字列でも渡せるようにしていた (先にファイルパスとみなして存在確認し、存在しなければ TOML パースを試みる)。これは Windows 機と Linux 機で問題なく動作していた。が、最近 Mac 機でも実験しようとしたら一部のテストがエラーになった。
    • 原因は TOML 文字列をファイルパスとみなして存在確認するとき、Mac では期待通り False が返らずに例外が送出されるためである。実験設定 TOML 文字列は英数字 2000 文字程度のため、Mac においてはパス最大長を超過してただちに File name too long となり、これは pathlib に捕捉されない。
    • Linux でエラーとなっていなかった原因は、 Linux ではパス最大長には到達していなかった & 実験設定 TOML 文字列にはスラッシュが含まれるため、最初のスラッシュまでが有効バイト数の親ディレクトリとみなされ FileNotFoundError となって捕捉されていたためである。
    • Windows でエラーとなっていなかった原因は、実験設定 TOML 文字列は Windows ではファイル名に使用できない文字 (ダブルクオート等) を含んでおり _WINERROR_INVALID_NAME となっていたが、これも捕捉されていたためである。
  • 暫定対応として、ファイル存在確認より TOML パースを先に試みるようにした。
0
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
0
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?