SJ blog
tools
A

信頼度ランク

S 公式ソース確認済み
A 成功実績多数・失敗例少数
B 賛否両論
C 動作未確認・セキュリティリスク高
Z 個人所感

Python で Windows フォルダパスを堅牢に扱う ── pathlib・os.scandir・SharePoint対応

os.path.exists() だけでは不十分な理由と、SharePoint/OneDrive 同期フォルダ・ネットワークパス・権限エラーに対応したフォルダ診断の実装パターンを解説する。

一言結論

exists() は「存在するが入れない」を検出できない。os.scandir() を使った権限確認、SharePoint/OneDrive パスの判別、エラーコードによる診断結果の構造化が堅牢なフォルダ操作の基本。

Windows 環境で Python からフォルダを扱うとき、os.path.exists()pathlib.Path.exists() だけを信じると痛い目に遭う。SharePoint の同期フォルダ、ネットワークドライブ、権限付きフォルダは「存在はする、でも入れない」という状態になりうる。

この記事では、フォルダを開く前に実施すべき診断処理の考え方と実装パターンを整理する。


1. exists() だけでは足りないケース

from pathlib import Path

p = Path(r"C:\Users\example\OneDrive\仕事用")
if p.exists():
    os.startfile(str(p))  # ← PermissionError になることがある

Path.exists() は「OS がそのパスを認識しているか」を返す。しかし以下のケースでは True を返しながら実際にはアクセスできないことがある。

  • SharePoint / OneDrive 同期中: メタデータだけ存在し、実データがローカルに降りていない
  • ネットワークドライブ: マウントはされているが権限がない
  • プレースホルダファイル: Windows の「オンデマンド同期」でアイコンは出るが実体がない

2. os.scandir() で権限まで確認する

os.scandir() はディレクトリを開いてイテレータを返す。このとき実際にアクセスを試みるため、権限エラーを事前に捕捉できる。

import os
from pathlib import Path

def can_access(path_str: str) -> bool:
    try:
        with os.scandir(path_str):
            pass
        return True
    except PermissionError:
        return False
    except OSError:
        return False

os.listdir() でも同様だが、scandir() の方がイテレータを即座に閉じられる点で効率がよい。


3. エラーを「コード+メッセージ」で構造化する

フォルダ診断の結果を単純な bool で返すと、呼び出し側が「なぜダメだったか」を判断できない。@dataclass でエラーコードと表示用メッセージを持たせると、UI 表示やログ出力を分離できる。

from dataclasses import dataclass

@dataclass
class FolderCheckResult:
    path: str
    available: bool
    code: str          # 機械が読む識別子
    message: str       # 人間が読む説明
    detail: str = ""   # 例外の詳細(ログ用)

コード例:

  • OK — 使用可能
  • NOT_FOUND — 存在しない
  • SHAREPOINT_OR_SYNC_SUSPECT — SharePoint/OneDrive の同期問題の疑い
  • PERMISSION_DENIED — 権限なし
  • OS_ERROR — Windows レベルのエラー

4. SharePoint / OneDrive パスの判別

SharePoint が同期するローカルパスは C:\Users\...\OneDrive - 会社名\C:\Users\...\SharePoint のような形になる。https:// の URL をそのままパスとして渡しても Explorer では開けない。

def check_path(path_str: str) -> FolderCheckResult:
    if path_str.lower().startswith(("http://", "https://")):
        return FolderCheckResult(
            path=path_str,
            available=False,
            code="URL_NOT_SUPPORTED",
            message="URLはフォルダパスとして使えません。同期済みのローカルパスを使ってください。"
        )
    
    p = Path(path_str)
    if not p.exists():
        code = "NOT_FOUND"
        msg = "フォルダが存在しません。"
        if "SharePoint" in path_str or "OneDrive" in path_str:
            code = "SHAREPOINT_OR_SYNC_SUSPECT"
            msg = "SharePoint/OneDriveの未同期またはパス変更の可能性があります。"
        return FolderCheckResult(path=path_str, available=False, code=code, message=msg)
    # ...

5. PowerShell COM で開いている Explorer ウィンドウを取得する

Python から現在開いている Explorer のフォルダパス一覧を取得するには、PowerShell の Shell.Application COM オブジェクトを使う。

import subprocess, json

ps_script = r'''
$windows = (New-Object -ComObject Shell.Application).Windows()
$result = @()
foreach ($w in $windows) {
    try {
        if ($w.FullName -like "*explorer.exe") {
            $path = $w.Document.Folder.Self.Path
            if ($path -and (Test-Path $path)) { $result += $path }
        }
    } catch {}
}
$result | Sort-Object -Unique | ConvertTo-Json -Compress
'''

result = subprocess.run(
    ["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", ps_script],
    capture_output=True, text=True, encoding="utf-8", timeout=10
)
paths = json.loads(result.stdout.strip() or "[]")

注意点:Windows 11 のタブ機能では、非アクティブなタブのパスはこの方法では取得できない。取得できるのはウィンドウ単位のアクティブタブのパスのみ。


実装コード

上記のパターンをすべて組み合わせた実装。フォルダ診断・JSON 設定管理・Explorer 連携を含む。

explorer_mysets.py

まとめ

チェック方法
パスが空・URLかどうか文字列チェック(先に弾く)
存在確認Path.exists()
フォルダかどうかPath.is_dir()
権限・アクセス可否os.scandir() を try/except
SharePoint 疑いパス文字列に “SharePoint” / “OneDrive” が含まれるか
開いている Explorer の取得PowerShell Shell.Application COM

exists() は最初の入口にすぎない。scandir() で実際にドアを開けてみることで、権限エラーを事前に捕捉できる。