信頼度ランク
| 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 連携を含む。
まとめ
| チェック | 方法 |
|---|---|
| パスが空・URLかどうか | 文字列チェック(先に弾く) |
| 存在確認 | Path.exists() |
| フォルダかどうか | Path.is_dir() |
| 権限・アクセス可否 | os.scandir() を try/except |
| SharePoint 疑い | パス文字列に “SharePoint” / “OneDrive” が含まれるか |
| 開いている Explorer の取得 | PowerShell Shell.Application COM |
exists() は最初の入口にすぎない。scandir() で実際にドアを開けてみることで、権限エラーを事前に捕捉できる。