-
Notifications
You must be signed in to change notification settings - Fork 228
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
Hybrid properties don't work in the presence of future annotations #389
Comments
Doing more research on this today. As described above, So I did a quick pass at handling @convert_sqlalchemy_type.register(safe_isinstance(ForwardRef))
def convert_sqlalchemy_hybrid_property_forwardref(
type_arg: Any, column: hybrid_property | None = None, **kwargs
):
from .registry import get_global_registry
# Try to get the module namespace where the hybrid property is defined.
namespace = sys.modules[column.__module__].__dict__ if column else globals()
def forward_reference_solver():
try:
# Evaluate the type definition in the module namespace.
# (This is what inspect.get_annotations(eval_str=True) does)
return convert_sqlalchemy_type(
eval(type_arg.__forward_arg__, namespace), **kwargs
)
except:
model = registry_sqlalchemy_model_from_str(type_arg.__forward_arg__)
if not model:
raise TypeError(
"No model found in Registry for forward reference for type %s. "
"Only forward references to other SQLAlchemy Models mapped to "
"SQLAlchemyObjectTypes are allowed." % type_arg
)
return get_global_registry().get_type_for_model(model)
return forward_reference_solver I think this could be made even more efficient by attempting the With this change, all the hybrid properties in our codebase just work, with no extra fiddling or overriding. I can roll this into a PR, let me know how you want to proceed! |
Actually, we don't even need to consult the model type registry in a separate step, if we just dump the contents of the registry into the namespace as well. Then we get the added benefit of being able to define hybrid properties that return composites like |
Ok, reading this now, and it pretty much describes all the pain I'm having trying to get this to work. :) I think I may revert all our future annotations stuff until this much improved design of annotations is ready. |
Thanks for the report! Yes, this is definitely an issue. Solving it at the current state is not trivial, because we need to ensure forward refs are actually imported to check their type (hence the usage of |
I'm definitely not suggesting trying to implement the PEP-649 approach yet... but it does seem like the community is going to be moving in that direction, so I'm hesitant to dump much more work into this approach because it feels like chasing my tail. I did come up with a fairly clean way (I think?) of making things work with ForwardRefs that probably covers 95+% of use cases, but I don't know if it's reasonable or possible to make it work in all cases, due to the need to ensure that all types in the return-type annotation are imported/defined/in-scope. But I'm happy to roll what I was doing into a draft PR for you to take a look at. :) One of the annoyances I was running into is that there are unit tests that define classes locally in the test function. Those classes are difficult to find at runtime — |
I Agree Curious to see the draft, let's continue the discussion on the PR! 🙂 |
This has been driving me a bit nuts since the columns/hybrids refactor, and I haven't been able to figure out if it's something new, expected, or something I'm doing wrong. Here's the simplest scenario I could come up with to demonstrate it.
I would expect this to work, and it does. It even works if I make my
hello
property return alist[list[str]]
or something weird like that. But! If you import__future__
annotations, this scenario no longer works:This is because with
__future__
annotations,-> str
creates aForwardRef
, and forward refs (unlike normal types) have to be SQLAlchemy models (https://github.com/graphql-python/graphene-sqlalchemy/blob/master/graphene_sqlalchemy/converter.py#L610). So you have to explicitly supply hybrid field definitions, even for methods that return simple primitive types. This is a huge inconvenience, as we have many of these kinds of computed properties in our codebase.Any ideas if this is something fixable? Or are we just stuck with having to manually add field definitions everywhere?
The text was updated successfully, but these errors were encountered: