Skip to content

Commit

Permalink
[REF-2302] When a Var points to a model, prefer access to model field…
Browse files Browse the repository at this point in the history
…s. (#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 python/cpython#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
  • Loading branch information
masenf committed Mar 27, 2024
1 parent b788890 commit b23859a
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 4 deletions.
2 changes: 1 addition & 1 deletion reflex/utils/types.py
Expand Up @@ -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:
Expand Down
34 changes: 31 additions & 3 deletions reflex/vars.py
Expand Up @@ -677,6 +677,33 @@ def __getitem__(self, i: Any) -> 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.
Expand Down Expand Up @@ -1891,8 +1918,9 @@ def _deps(
"""
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):
Expand Down Expand Up @@ -1976,7 +2004,7 @@ def _determine_var_type(self) -> Type:
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
Expand Down

0 comments on commit b23859a

Please sign in to comment.