[REF-2302] When a Var points to a model, prefer access to model fields. (#2893)

* When a Var points to a model, prefer access to model fields.

When a Var points to a model, and fields of the model share the same name as
Var operations, access to the model fields is now preferred to avoid having Var
operation names shadow model fields. Since most Var operations do not actually
work against Models, this does not really block any functionality.

* Special case for ComputedVar needing to internally access fget

Since fget is a "slot" on property, normal __getattribute__ access cannot find it.

* Workaround https://github.com/python/cpython/issues/88459

In python 3.9 and 3.10, the `isinstance(list[...], type)` returns True, but
it's not a valid class for use in issubclass
This commit is contained in:
Masen Furer 2024-03-27 16:22:46 -07:00 committed by GitHub
parent b788890696
commit b23859a45d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 32 additions and 4 deletions

View File

@ -202,7 +202,7 @@ def get_attribute_access_type(cls: GenericType, name: str) -> GenericType | None
attr.remote_attr.key, # type: ignore[attr-defined]
)
]
elif isinstance(cls, type) and issubclass(cls, Model):
elif isinstance(cls, type) and not is_generic_alias(cls) and issubclass(cls, Model):
# Check in the annotations directly (for sqlmodel.Relationship)
hints = get_type_hints(cls)
if name in hints:

View File

@ -677,6 +677,33 @@ class Var:
_var_is_string=False,
)
def __getattribute__(self, name: str) -> Any:
"""Get a var attribute.
Args:
name: The name of the attribute.
Returns:
The var attribute.
Raises:
AttributeError: If the attribute cannot be found, or if __getattr__ fallback should be used.
"""
try:
var_attribute = super().__getattribute__(name)
if not name.startswith("_"):
# Check if the attribute should be accessed through the Var instead of
# accessing one of the Var operations
type_ = types.get_attribute_access_type(
super().__getattribute__("_var_type"), name
)
if type_ is not None:
raise AttributeError(f"{name} is being accessed through the Var.")
# Return the attribute as-is.
return var_attribute
except AttributeError:
raise # fall back to __getattr__ anyway
def __getattr__(self, name: str) -> Var:
"""Get a var attribute.
@ -1891,8 +1918,9 @@ class ComputedVar(Var, property):
"""
d = set()
if obj is None:
if self.fget is not None:
obj = cast(FunctionType, self.fget)
fget = property.__getattribute__(self, "fget")
if fget is not None:
obj = cast(FunctionType, fget)
else:
return set()
with contextlib.suppress(AttributeError):
@ -1976,7 +2004,7 @@ class ComputedVar(Var, property):
Returns:
The type of the var.
"""
hints = get_type_hints(self.fget)
hints = get_type_hints(property.__getattribute__(self, "fget"))
if "return" in hints:
return hints["return"]
return Any