pylint-dev / pylint

It's not just a linter that annoys you!
https://pylint.readthedocs.io/en/latest/
GNU General Public License v2.0
5.31k stars 1.14k forks source link

Negation and None #5085

Open socketpair opened 3 years ago

socketpair commented 3 years ago

Bug description

Pylint incorrectly tracks types:

class Xxx:
    def __init__(self):
        self.__val = None

    @property
    def get_value(self) -> int:
        if self.__val is None:
            self.__val = 42
        return self.__val

    def method(self):
        return 'qwe'[:-self.get_value]

Configuration

No response

Command used

pylint a.py

Pylint output

a.py:12:22: E1130: bad operand type for unary -: NoneType (invalid-unary-operand-type)

Expected behavior

no errors

Pylint version

pylint 2.9.6
astroid 2.6.6
Python 3.9.7 (default, Aug 30 2021, 00:00:00) 
[GCC 11.2.1 20210728 (Red Hat 11.2.1-1)]

OS / Environment

Fedora 34

Additional dependencies

No response

saroad2 commented 2 years ago

I also encountered this bug with the following example:

from pathlib import Path
from typing import Optional, List

def clear(paths_list: List[Path], limit: Optional[int] = None):
    if limit is not None and limit < len(paths_list):
        paths_list = paths_list[-limit:]
    for path in paths_list:
        path.unlink()

When running Pylint over the code above it raises:

my\path.py:7:32: E1130: bad operand type for unary -: NoneType (invalid-unary-operand-type)
sam-s commented 1 year ago

Here is an even smaller test case:

l = [1,2,3]
x = None
if isinstance(x, int):
    l = l[-x:]

that gives

 bad operand type for unary -: NoneType (invalid-unary-operand-type)

on

pylint 2.17.5
astroid 2.15.6
Python 3.11.5 (tags/v3.11.5:cce6ba9, Aug 24 2023, 14:38:34) [MSC v.1936 64 bit (AMD64)]
socketpair commented 7 months ago
val: int | None = None

def get_value() -> int:
    if val is None:
        return 42
    return val

print(get_value() + 1)   # no errors here (as expected)
print(-get_value())   # BUMM! error! WHY ?
print(None + 1)  # NO ERROR HERE ?! WUT ?
pylint 3.0.4
astroid 3.0.3
Python 3.12.2 (main, Feb 21 2024, 00:00:00) [GCC 13.2.1 20231205 (Red Hat 13.2.1-6)]
socketpair commented 7 months ago

@Pierre-Sassoulas @sam-s @saroad2

Do you have any ideas ?

Pierre-Sassoulas commented 6 months ago

The is None case was introduced in astroid 2.13 https://github.com/pylint-dev/astroid/pull/1189, isinstance still needs works in astroid. I won't have enough time to dig into the specifics about how the inference works in your specific case @socketpair .

socketpair commented 6 months ago

@Pierre-Sassoulas I have minified:

val = None
if val is None:
    val = 42
print(-val)

E1130: bad operand type for unary -: NoneType (invalid-unary-operand-type)

Still requires digging ?

For original post, seems, whole system ignores typing for functions and re-calculates possibly types on its own way. Because the function is said to return int. But int the sensitive place, Pylint thinks it may return None (agree that's another bug)