Replies: 3 comments
-
|
Pyright is working as intended here, so I don't consider this a bug. This is also not something that we'd be likely to entertain as an enhancement request. Static type checkers need to do significant special-casing for the builtin Also, a property is not the same as a simple attribute, so overriding a property with a simple attribute or vice versa in a subclass is not type safe. Here's the type safe variant that works with static type checkers: class TestClass:
@property
def field(self) -> list[str]:
return self._field
@field.setter
def field(self, value: list[str]):
self._field = value
c = TestClass()
c.field = ["hello", "tester"]
for s in c.field:
print(s.upper())
class TestClass2:
@property
def field(self):
return self._field
@field.setter
def field(self, value: list[str]):
self._field = value
d = TestClass2()
d.field = ["greetings", "tester"]
for s2 in d.field:
print(s2.upper()) |
Beta Was this translation helpful? Give feedback.
-
|
Hi - circling back here because I think there is an inconsistency in the handling of properties, as shown in this slightly different example. class TestClass1:
myproperty: list[str] = property(lambda s: s._myproperty)
class TestClass2:
myproperty: list[str] = property(lambda s: s._myproperty)
@myproperty.setter
def myproperty(self, value: list[str]):
self._myproperty = valueThe only difference in these classes is the addition of the setter on an existing property. In the first case, Pyright recognizes |
Beta Was this translation helpful? Give feedback.
-
|
I managed to 'solve' this by writing a custom descriptor class which relies on Python >=3.11 class Fixed(Generic[S, T]):
__slots__ = ["create", "created", "instances", "lock", "name"]
def __init__(
self,
obj: type[T] | Callable[[FixedCreate[S]], T] | str,
/,
*,
created: Callable[[FixedCreated[S, T]]] | None = None,
) -> None:
if isinstance(obj, str):
self.create = lambda _: import_item(obj)()
elif inspect.isclass(obj):
self.create = lambda _: obj()
elif callable(obj):
self.create = obj
else:
msg = f"{obj=} is invalid! Use a lambda instead eg: lambda _: {obj}" # pyright: ignore[reportUnreachable]
raise TypeError(msg)
self.created = created
self.instances = {}
self.lock = Lock()
def __set_name__(self, owner_cls: type[S], name: str) -> None:
self.name = name
def __get__(self, obj: S, objtype: type[S] | None = None) -> T:
try:
return self.instances[id(obj)]
except KeyError:
if obj is None:
return self # pyright: ignore[reportReturnType]
with self.lock:
try:
return self.instances[id(obj)]
except KeyError:
key = id(obj)
instance: T = self.create({"name": self.name, "owner": obj}) # pyright: ignore[reportAssignmentType]
self.instances[key] = instance
weakref.finalize(obj, self.instances.pop, key)
if self.created:
try:
self.created({"owner": obj, "obj": instance, "name": self.name})
except Exception:
if log := getattr(obj, "log", None):
msg = f"Callback `created` failed for {obj.__class__}.{self.name}"
log.exception(msg, extra={"obj": self.created})
return instance
def __set__(self, obj: S, value: Self) -> Never:
# Note: above we use `Self` for the `value` type hint to give a useful typing error
msg = f"Setting `Fixed` parameter {obj.__class__.__name__}.{self.name} is forbidden!"
raise AttributeError(msg)
The full code is here If you want to try it you can install the library. pip install async-kernelfrom async-kernel.common import Fixed
class MyClass:
a: Fixed[Self, dict] = Fixed(dict)
b: Fixed[Self, int] = Fixed(lambda c: id(c["owner"].a))
c: Fixed[Any, list[str]] = Fixed(list, created=lambda c: c["obj"].append(c["name"])) |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I'm posting this here before filing an issue to be sure this is actually a Pyright issue and that others agree it is a big and not an enhancement request. The issue is that when I declare a class attribute with a type hint and set it equal to a descriptor class instance inline (rather than using a decorator to create the descriptor), and then define a setter, vs code/pyright see the attribute on the instance as a method (specifically, the setter method). Here is an example using the builtin property descriptor:
In the first instance, where the property is created inline using a lambda as the getter, upright only sees the setter method, and when I then try to iterate the
fieldattribute (or use it in any other way where I want its type to be recognized), pyright doesn't see it as an utterable and doesn't recognize the type of its elements. The result is, I don't get intellisense help on the methods of the element (in the example, the string'suppermethod). In the second example, where the property is created using a descriptor to define the getter, it works as expected. Thefieldattribute is recognized as alistofstrand its elements are recognized as strings with all the built-in string methods.Obviously, this is a contrived example--there would be no reason to define a generic property using a lambda. My actual descriptors are more complex, but the problem is the same.
So is this a Pyright issue? Is it a bug, or am I looking for an enhancement?
Beta Was this translation helpful? Give feedback.
All reactions