-
Notifications
You must be signed in to change notification settings - Fork 767
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
False positive "is abstract" flagging when using abstract properties #157
Comments
We've discussed this issue previously. It was a conscious decision not to allow an abstract property to be considered properly "overridden" by a simple instance variable. They have different behaviors. A property is an object and is not settable or deletable (unless it also has a setter and deleter). That's very different from a simple instance variable. A type checker should flag this difference. If you define an abstract property, Pyright requires that you override it with a non-abstract property. Incidentally, you can use the |
Okay. I can understand this decision, thank you for explaining. And I also checked However, I am now faced with a conundrum: for Pylance not to report an error, I need to use deprecated Python function. Because, according to Python docs, And Using Which makes even less sense in this context. Not recognizing I would like to use Pylance typechecking in my daily workflow. It is really powerful and on-point, and I like it being somehow more strict than No because this behavior from Pylance is wrong. But because it contradicts the Python programming rules. Which is kind of not desirable in a Language Server for a Python language. |
Ah, I didn't realize that @abstractproperty was deprecated. Interesting. Yeah, then disregard that suggestion. Pyright does suppress the return type validation in a method that is decorated with @AbstractMethod. When you say "contradicts the Python programming rules", what are you referring to? |
Well, maybe "contradicts the Python programming rules" is a too strong of a statement, sorry. I referred to your suggestion of using I also mean that using combination of And when Pylance told me that I am wrong, I was a bit upset. |
The "proper" way to indicate that child classes should have a variable defined is to use a Protocol (as defined in PEP 544). This requires Python 3.8, which may not be feasible for you. Is that an option? |
Should really read that PEP, thank you. |
There is other side of the issue: the issuer reported by Pylance is misleading at best. Provided snippet IS a valid Python, and it is the only (at least the only I know of) way to force a presence of a variable in subclasses (prior to 3.8). Is it good or bad code is not for me to decide, but it works and forces my intention on interpreter level. Pylance reports it as an invalid Python. Moreover, if I remove the Having abstract property being overridden by a class variable IS NOT the same as not having it defined at all. The first one leads to a runnable code, and the other leads to the There should be distinction between the two. I also looked through Either way, the I consider the current Pylance behavior (treating the abstract property overriden with a class variable as not implemented at all) not correct. Moreover, I think that there is no issue with overriding abstract properties with class variables. Pylance should at least treat such case differently. Something along the lines
still would be upsetting, but at least it will not be misleading. |
Thanks for your well reasoned and well researched response. A few thoughts... First, I don't automatically equate "raises a runtime exception" with "valid Python". I can provide many examples where a runtime exception is valid, and I can provide many other examples where code doesn't raise an exception but is still worthy of being flagged as incorrect. Most type violations fall into this latter category, since Python doesn't perform any runtime type checks and therefore won't raise exceptions in those cases. Second, the behavior of mypy is interesting to consider, but there are many places where Pylance/Pyright deviates from mypy — most of them intended and for good reasons. This is a case where I think an incorrect decision was made in mypy. A property is different from a variable in important ways, and it's the job of a type checker to point out inconsistencies like this. There are several points that I agree with you on, and I think if I address these, it will satisfy your use case. First, I agree that the diagnostic message provided by Pylance/Pyright in this case is misleading. Second, the mismatch between a property and a variable should be controllable via a diagnostic rule other than How does that sound to you? |
I agree that reporting property override with a variable as On the other hand, I still believe that described case is a bit different.
class Parent:
def reverse(self, x: int) -> int:
return -x
class Child(Parent):
def reverse(self, x: str) -> str:
return x[::-1] This is obviously a violation of the With properties it is a bit more subtle. When I do class Parent:
@property
def name(self) -> str:
return "parent"
class Child(Parent):
name = "child" I am not overriding a function From my point of view, the It is kind of the same with You should also consider what happens when the situation is reverse: overriding a variable with a property. from typing import Optional
class StaticallyNamedObject:
name = "my name is StaticallyNamedObject"
class DynamicallyNamedObject:
def __init__(self, name: Optional[str] = None):
self._name = name
@property
def name(self):
if self._name:
return "my name is " + self._name
return "my name is " + self.__class__.__name__
@name.setter
def name(self, new_name: str):
self._name = new_name Pylance is totally fine with provided code snippet, even with
I don't know, the more I think about it, the more confused I get. I totally agree that properties and variables are not the same. But in terms of class interfaces they can act the same... Even though there are cases where even in terms of defined interfaces they are not the same: class LockedName:
@property
def name(self) -> str:
return "locked"
@name.setter
def name(self, new_name: str) -> None:
raise NotImplementedError
class UnlockedName(LockedName):
name = "unlocked" Where Thinking about examples like this I agree that overriding property with a variable should be reported. But not with |
This issue has been fixed in version 2020.7.4, which we've just released. You can find the changelog here: https://github.com/microsoft/pylance-release/blob/master/CHANGELOG.md#202074-29-july-2020 |
Environment data
Pylance language server
Pylance's Type Checking Mode is set to
basic
Expected behaviour
Provided code should work just fine, with no errors reported.
I use
@property
in combination with@abstractmethod
to show that child classes should have a typed property defined. I usually use it to indicate the necessary class variables, such as "name", "id", "version" etc. It is valid Python, andmypy
has no issues with this code:And it is valid type check, because if I change
name = "name"
toname = 42
,mypy
will complain:I expect
Pylance
to at least recognize this snippet as a valid Python code, and at best report an issue if there are type mismatch (asmypy
does).Actual behaviour
Pylance
identifies thePython
class as an abstract one and reports an error:The text was updated successfully, but these errors were encountered: