Skip to content

Broken symlink fails os.path.exists(symlink) check but raises FileExists error in os.makedirs(symlink, exist_ok=True). #129626

@LeoZDong

Description

@LeoZDong

Bug report

Bug description:

Description

Suppose I have a symlink that points to original, and then original gets deleted, which makes it an invalid symlink. I get an unexpected behavior where:

os.path.exists("symlink")
>>> False
os.makedirs("symlink", exist_ok=True)
>>> FileExistsError: [Errno 17] File exists: "symlink"

Why this is important

It is not uncommon for code to rely on os.makedirs(..., exist_ok=True) to create a directory only if it exists, or to use a if not os.path.exists(...) to guard before calling os.makedirs(...). In the case when the given directory is a broken symlink, this would no longer work. In particular, raising a FileExistsError when exist_ok=True is especially confusing, and this behavior should be avoided.

Steps to reproduce

In terminal, create a broken symlink

mkdir original
ln -s original symlink
rm -r original

In Python, observe:

os.path.exists("symlink")
>>> False
os.makedirs("symlink", exist_ok=True)
>>> FileExistsError: [Errno 17] File exists: "symlink"

Python version: 3.12.8

Expected behavior

os.path.exists behavior should be consistent with os.makedirs. When given a broken symlink, either:

  1. os.path.exists(symlink) returns True (does not implicitly resolve symlink) and os.makedirs(symlink, exist_ok=True) does nothing
  2. OR: os.path.exists(symlink) returns False (implicitly resolves symlink) and os.makedirs(symlink, exist_ok=True) creates a directory at the symlink target.

1 might be the preferred behavior since implicitly resolving symlink is kind of bad. Open to other options (perhaps a special error for handling symlinks), but regardless the main thing is to fix the behavior where a FileExistsError is raised even when exist_ok=True is passed.

CPython versions tested on:

3.12

Operating systems tested on:

Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions