mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Do not count `__slots__` as a protocol member

Open sobolevn opened this issue 4 years ago • 11 comments

Refs https://github.com/python/mypy/pull/9314 Closes https://github.com/python/mypy/issues/11884

sobolevn avatar Jan 02 '22 13:01 sobolevn

It's not so clear-cut, but would it be worth special-casing __class_getitem__ in the same way? It might avoid regressions like this (cc. @hauntsaninja)

AlexWaygood avatar Jan 02 '22 13:01 AlexWaygood

It's not so clear-cut, but would it be worth special-casing class_getitem in the same way?

Can you please open a new issue for it? Probably we need to discuss / research all special cases separatelly. But, it looks like a good candidate 👍

sobolevn avatar Jan 02 '22 13:01 sobolevn

It's not so clear-cut, but would it be worth special-casing class_getitem in the same way?

Can you please open a new issue for it? Probably we need to discuss / research all special cases separatelly. But, it looks like a good candidate 👍

Sure!

AlexWaygood avatar Jan 02 '22 13:01 AlexWaygood

New issue filed here: https://github.com/python/mypy/issues/11886

AlexWaygood avatar Jan 02 '22 13:01 AlexWaygood

Now I am also thinking: what if I want to have a function / interface / contract / whatever that only works with slotted types? How can I express this?

For example:

def pretty_print_slots(klass_with_slots: ???) -> None:
   ...

I guess what Eric said in https://github.com/python/mypy/issues/11886 really makes sense. So, proably we can ignore protocols __slots__ = () definition as a "performance hack", we need to treat __slots__: str | Iterable[str] as a part of the protocol.

Do you agree?

sobolevn avatar Jan 02 '22 16:01 sobolevn

That sounds like a decent solution too, tried it out a bit and found some weird behavior in current mypy somewhat related to this, just bringing this up because it's a bit funny:

This fails:

import typing

@typing.runtime_checkable
class Foo(typing.Protocol):
    __slots__ = ()


class Bar(Foo):
    ...

print(issubclass(Bar, Foo))  # this fails

and this works:

import typing

@typing.runtime_checkable
class Foo(typing.Protocol):
    __slots__: str | typing.Iterable[str]  = ()


class Bar(Foo):
    ...

print(issubclass(Bar, Foo))  # this does not ?

(Anyways, that'll get fixed by this too, it's just some weird behavior...)

Basically yes, I agree that would make sense to me -- though I'm sure any solution would be nice.

A5rocks avatar Jan 02 '22 17:01 A5rocks

We are blocked by https://github.com/python/mypy/issues/11891

sobolevn avatar Jan 03 '22 11:01 sobolevn

We are blocked by #11891

I wonder if it's even helpful for typeshed to define __slots__ in the stub for object (as it does currently), or if it should be removed. Given that __slots__ are so special, it doesn't seem like it does much for the type-checker.

AlexWaygood avatar Jan 03 '22 11:01 AlexWaygood

@AlexWaygood well, technically, object does not have __slots__.

>>> object.__slots__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'object' has no attribute '__slots__'. Did you mean: '__class__'?
>>> object().__slots__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute '__slots__'. Did you mean: '__class__'?

My vote is to remove them (my opinion is pretty simple: typeshed should be as close to source as possible).

sobolevn avatar Jan 03 '22 11:01 sobolevn

According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉

github-actions[bot] avatar May 07 '22 23:05 github-actions[bot]

Diff from mypy_primer, showing the effect of this PR on open source code:

discord.py (https://github.com/Rapptz/discord.py)
- discord/ext/commands/core.py:2333: error: Argument 1 to "is_owner" of "BotBase" has incompatible type "Union[discord.user.User, Member]"; expected "discord.abc.User"

github-actions[bot] avatar Jul 28 '22 07:07 github-actions[bot]

What's the status of this?

Instead of hard-coding __slots__, shouldn't this check a collection of what to exclude so that what's excluded can be easily updated? From the discussion on #11886, apparently Python uses this collection to decide what to exclude from Protocol. Perhaps follow exactly what Python excludes rather than debating which ones should be special-cased in Mypy? I assume if this route is taken then Mypy would need a collection for each Python version with differing exclusions.

Maybe I should try making a separate PR with my suggestions?

HexDecimal avatar Jun 21 '23 15:06 HexDecimal

@HexDecimal I've deleted my fork by accident. You can re-submit this if you need this feature :)

sobolevn avatar Jun 21 '23 15:06 sobolevn

I'll go ahead and attempt my own PR. I mostly want to fetch your test to work with.

HexDecimal avatar Jun 21 '23 15:06 HexDecimal

Great, ping me to review your PR :)

sobolevn avatar Jun 21 '23 16:06 sobolevn