python:S930 FP on pathlib.Path method calls

SonarQube Server, Enterprise Edition, v10.8.1 (101195)

A false positive is found when methods on objects from python’s pathlib are called.

It looks like they’re being treated as calls from the os.path module instead, so Sonarqube incorrectly raises issues about missing function argument.

Code example:

from pathlib import Path

path = Path("/tmp")
print(path.is_dir())

This triggers rule python:S930, with the message:

Add 1 missing arguments; 'is_dir' expects 1 positional arguments.

os.path.isdir requires one positional argument, but pathlib.Path.is_dir does not.

This also affects other methods on the pathlib.Path object that have the same name as os.path functions, e.g. the same rule is triggered for the code:

from pathlib import Path

path = Path("/tmp")
print(path.glob("*"))

This triggers the same false positive, with the message:

Add 1 missing arguments; 'glob' expects 2 positional arguments.

Hello @dsc,

Thank you for reporting this issue.

After having a look, I cannot reproduce it with the given reproducer in the latest version of the analyzer, nor in the version embedde in SonarQube Server 10.8.1. I find that the analyzer correctly resolves pathlib.Path as well as its methods is_dir and glob.

It could be possible that the resolution is incorrect if os.path is also in the scope, though. Could you double check your reproducer and make sure it actually raises the issue? If it doesn’t, could you adapt it in a way that the false positive actually triggers?

Cheers,
Guillaume

I had a check, and os.path isn’t in scope as far as I can see.

I also tried renaming path to path2 to avoid clashes, but that didn’t change anything.

I did some more testing, and it’s not picked up in all cases, here’s a fuller example that’s closer to the code that’s triggering the issue (if there’s a way I can run the reproducer locally, I’d be happy to test using that too):

from pathlib import Path

class FileHandler:
    @classmethod
    def list_files(cls, path2: Path | str):
        path2 = path2 if isinstance(path2, Path) else Path(path2)

        # The line below flags python:S930.
        if not path2.is_dir():
            raise ValueError(f"Path {path2} is not a directory.")

        # The line below flags python:S930.
        return path2.glob("*.yml")

# This line does NOT flag python:S930 however.
p = Path("foo").is_dir()

After a bit more testing, it looks like it might be related to type hints.

If I remove it from the code above, then nothing is flagged:

class FileHandler:
    @classmethod
    def list_files(cls, path2):
        path2 = path2 if isinstance(path2, Path) else Path(path2)

        # The line below does NOT flag python:S930 when the type hint is removed.
        if not path2.is_dir():
            raise ValueError(f"Path {path2} is not a directory.")

        # The line below does NOT python:S930 when the type hint is removed.
        return path2.glob("*.yml")

Hello @dsc,

Thank you for the updated reproducer, I managed to reproduce the problem now.
Indeed, the combination of @classmethod and the type hint cause an issue with the type inference of the calls of path2.

I created a ticket to tackle this, which we aim to fix in the following weeks.

Cheers,
Guillaume

1 Like