-
-
Notifications
You must be signed in to change notification settings - Fork 542
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
self parameter is None in resolver methods #654
Comments
Another quick note, Actually taking an instance of import strawberry
@strawberry.type
class Query:
@strawberry.field
def test_field(self) -> str:
return "Hello"
@strawberry.field
def test_field2(self) -> str:
print(self)
return str(self)
@strawberry.field
def test_field3(self, test_param: str) -> str:
print(self)
print(test_param)
return str(self)
q = Query()
assert q.test_field2() == 'None' # <= AssertionError! So `self` isn't None! This leads me to believe that the resolver engine doesn't actually use instances of classes while executing resolver methods. If I had to take one stab at a possible fix, it would be to design expecting instances into q = Query()
#m = Mutation()
#s = Subscription()
schema = strawberry.Schema(query=q) (I'll make note that if you run this example now, it seems like That's just an idea making many assumptions, and I don't know enough of how strawberry works to make a valid argument here. What are your thoughts? |
@AadamZ5 yep this is a known issue and it's because the resolver functions are actually static methods. It was discussed briefly here: #515 You can "fix" it by setting the result = schema.execute(query, root_value=Query()) (for the framework integrations you can override the I'm in favour or defaulting the root value to always be an instance of the |
I wonder if that might be a problem or not due to potential side effects 🤔 |
As long as it's a shared instance, and not a newly generated one for each individual query. I think having the Right now, I'm not using any sort of framework. I am simply playing with the |
@AadamZ5 what do you mean by that? In my mind the Query instance would be created each time
Ah yeah it's not possible to customise the root value when using the dev server. You will have to setup a custom view inside Flask to be able to do that. Also I realised that defaulting the root value to an instance of Query will only work for queries and is not possible for Mutations (or Subscriptions though I'd have to check that). Thats because the GraphQL execute method doesn't differentiate between queries and mutations |
@jkimbo I was planning to use instance variables on the
This brings me to another topic I'd like to discuss. Do you have a roadmap for when we can expect more usage examples? For instance, I see some code for ASGI implementation with uvicorn and such, but no examples for Flask (although I am new to Flask as well...), and I was really hoping to eventually see examples of using the library without any big frameworks too. I believe this could be done using
With this info, my opinion is that all of the root-level objects (Query, Mutation, Subscription) should be structured and behave the same. If the other two root-level objects can't actually receive an instance of themselves, then none of them should, for consistency. In other words, I believe you should lean into people defining every resolver method as static. I believe I discussed this on #515. Loving this library so far! I hope I'm not being a nuisance, or asking some un-useful questions! I'm trying to get involved more but I don't have a bunch of professional experience, so bear with me if something I ask is... seemingly obvious. Thanks! |
That's annoying, so basically there would always one root value? I wonder if can do that (and if we should). From what I understood the GraphQL spec don't specify what the very first root value should be, maybe we can see if there's any other framework that do things differently?
@AadamZ5 maybe you can use
This is something we discussed in another issue related to subscriptions, we made a repo for adding examples, but we haven't started yet, feel free to suggest examples, I've added some ideas here: strawberry-graphql/examples#1
I think that makes sense, I'm personally only worried that having something like this: @strawberry.type
class A:
@strawberry.field
def field_a() -> str:
return "Example" Might look weird to beginners as they would expect a self argument, but maybe I'm worrying too much.
No worries at all, feel free to ask any question you have in mind (here or on discord)! |
As far as I'm aware yes, there will only be one root value. The root value can be anything you want it to be but I think it's fair for Strawberry to default it to something if it makes sense within the context of the library. I don't think other frameworks suffer from the same problem because they treat resolvers as static methods or they don't have the same python convention of
Because we have the
You're not being a nuisance at all! It's great to question these assumptions and it's always helpful to hear the problems that new users have. It's easy to miss these things once you've been working on it for a while. Another option would be to change how the root types are constructed to avoid having to define a type at all. For example: import strawberry
@strawberry.field
def get_current_user(info) -> User:
return User(username="jkimbo")
@strawberry.field
def get_user_by_username(username: str) -> User:
return User(username=username)
@strawberry.mutation
def add_user(info, username: str) -> User:
return User(username=username)
schema = strawberry.Schema(
queries=[get_current_user, get_user_by_username],
mutations=[add_user]
) This way it's not surprising that |
I'd avoid this, query, mutation and subscription are special types, but not that special enough to treat them different I think 🤔 Maybe I can try and see if I can find a way to pass the correct self to queries, mutations and subscriptions, even when sending multiple operations at once, if that's possible. What do you think? |
I agree with this statement too. The query, mutation, and subscription types are important, and shouldn't just be a list IMO. The extra functionality of a class (even as a static one) might be important to a lot of users.
If the root-level query objects (query, subscription, mutation) get an instance of themselves in One thing I think you should consider as the project evolves (beyond what it has already): Is this library going to build GraphQL in Python, or make Python effortlessly turn into GraphQL? For example, the My two cents: The library's decorator implementation feels magical, and it's wonderfully easy to get most of the setup and typing done. I was delighted when I found a more modern code-first approach. The resolving side is where I hit a learning curve. The idea of resolvers being (or working like) instance-bound functions was fresh in my head, which is how this issue was created. |
@patrick91 did you managed to achieve this? I think I might have been mistaken when I said that you can execute a mutation and a query at the same time. According to the spec a document can define multiple operations (query or mutation) but when executing you need to define which one to run: https://spec.graphql.org/June2018/#sec-Executing-Requests I still think that the root types are different enough to justify treating them differently. Even if we manage to automatically instantiate the right Query or Mutation instance, as @AadamZ5 points out, we would need to provide a factory method to allow people to customise how the instances get created. I think it's cleaner to sidestep the whole problem and just create schemas with a list of queries, mutations and subscriptions.
@AadamZ5 in my head Strawberry is a library to build GraphQL in Python. That doesn't mean it can't be "pythonic" though and we should strive to make it as "pythonic" as possible. |
I think it might ok to close this issue, we have documented how to access self/parent here: https://strawberry.rocks/docs/guides/accessing-parent-data#accessing-parents-data-in-a-method-resolver |
In an example like this,
I noticed while trying to use instance attributes that
self
isn't actually passed into the method. Are the Query, Mutation, and Subscription class instances actually passed back into the methods?A couple things to note here,
test_field2
resolver method.test_field3
resolver method.__init__
. Does overriding the initializer function break anything?Personally I can work around this using dependency injection, but I wanted to bring it up. The reason being, is because I noticed that on your documentation, the examples you provide (like in the README, or on the website home page) also contain the
self
parameter in the resolver functions, although not actually utilized in the examples.Reproduction is simple, just try one of your examples, modified to utilize the
self
parameter, or the example I've copied above, and run it with:I'm not sure when the problem first started occurring, I've only been playing with this library for a couple days.
I'm using the latest version of strawberry from PyPI. My python environment is running python 3.8. I'm running it on WSL 2.
I haven't had time to inspect the source code, but I will try if I get some free time. School starts again tomorrow... 📚🥱
Thanks!
Upvote & Fund
The text was updated successfully, but these errors were encountered: