Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistent behaviour in os.path.islink for symbolic links to directories #110807

Closed
igordertigor opened this issue Oct 13, 2023 · 5 comments
Closed
Labels
type-bug An unexpected behavior, bug, or error

Comments

@igordertigor
Copy link

igordertigor commented Oct 13, 2023

Bug report

Bug description:

I am finding inconsistent behavior for os.path.islink for symbolic links to directories. Here is a minimal example:

Setup (bash on Ubuntu focal):

mkdir directory
touch directory/file
ln -s directory directory_link
ln -s directory/file file_link

I would expect os.path.link to return False for directory and directory/file but to return True for directory_link and file_link. However, os.path.link("directory_link") == False (the others are as expected).

It might be that the term "existing directory entry" in the documentation means that os.path.link would always be False for links to directories, however that would be inconsistent with the documentation os.path.isdir, which says "both islink() and isdir() can be true for the same path".

The described behaviour for os.path.link differs from the behaviour of pathlib: pathlib.Path("directory_link").is_symlink() == True.

I can see two possible solutions here:

  1. os.path.link("directory_link") returns True. This appears to be consistent with both pathlib and the bash-expression [[ -L "directory_link" ]] and I feel that it's preferable.
  2. The documentation should be less ambiguous and maybe state clearly that os.path.link is only ever true for files. In addition, this would probably also mean adjusting the documentation for os.path.isdir.

I'm happy to help with either.

CPython versions tested on:

3.11

Operating systems tested on:

Linux

@igordertigor igordertigor added the type-bug An unexpected behavior, bug, or error label Oct 13, 2023
@jwilk
Copy link

jwilk commented Oct 16, 2023

I can't reproduce it here:

$ mkdir directory
$ touch directory/file
$ ln -s directory directory_link
$ ln -s directory/file file_link
$ python3.11
Python 3.11.2 (main, Mar 13 2023, 12:18:29) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os.path
>>> os.path.islink("directory_link")
True

@igordertigor
Copy link
Author

This is getting even more confusing. If I run python interactively as @jwilk reports, I can't reproduce it either. However, when writing a file with the following content:

import os.path

print(f'Directory: {os.path.islink("directory/") = }')
print(f'Directory link: {os.path.islink("directory_link/") = }')
print(f'File: {os.path.islink("directory/file") = }')
print(f'File link: {os.path.islink("file_link") = }')

and running that file with python3.11 test_os.py, I get:

Directory: os.path.islink("directory/") = False
Directory link: os.path.islink("directory_link/") = False
File: os.path.islink("directory/file") = False
File link: os.path.islink("file_link") = True

This is python 3.11.4, but again, I get the exact same behaviour as @jwilk but only in an interactive session, not in a script.

@jwilk
Copy link

jwilk commented Oct 17, 2023

I think this is the difference you're seeing:

>>> os.path.islink("directory_link")
True
>>> os.path.islink("directory_link/")
False

This is as expected, because directory_link/ is not a symlink; it's a real directory that happens to have a symlink as one of the path components.
You get the same results with the stat command:

$ stat directory_link | head -n2
  File: directory_link -> directory
  Size: 9         	Blocks: 0          IO Block: 4096   symbolic link
$ stat directory_link/ | head -n2
  File: directory_link/
  Size: 4096      	Blocks: 8          IO Block: 4096   directory

@jwilk
Copy link

jwilk commented Oct 17, 2023

Now, there's also:

>>> pathlib.Path('directory_link').is_symlink()
True
>>> pathlib.Path('directory_link/').is_symlink()
True

but that's a bug in pathlib: #65238

@igordertigor
Copy link
Author

Thank you @jwilk , that make sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

2 participants