sgqlc icon indicating copy to clipboard operation
sgqlc copied to clipboard

Cannot un-pickle a pickled Operation instance 🥒

Open chartpath opened this issue 6 years ago • 6 comments

Hey, I'm using sgqlc to introspect a schema and then generate queries from it and it is going well. However, I'm trying to parallelize tasks that contain an operation, which requires pickling it inside the task.

Here is the traceback that arises when trying to deserialize the (successfully) serialized op:

Traceback (most recent call last):
  File "/Users/myuser/myproj/venv/lib/python3.7/site-packages/sgqlc/types/__init__.py", line 657, in __getattr__
    return self.__kinds[key]  # .type, .scalar, etc...
  File "/Users/myuser/myproj/venv/lib/python3.7/site-packages/sgqlc/types/__init__.py", line 657, in __getattr__
    return self.__kinds[key]  # .type, .scalar, etc...
  File "/Users/myuser/myproj/venv/lib/python3.7/site-packages/sgqlc/types/__init__.py", line 657, in __getattr__
    return self.__kinds[key]  # .type, .scalar, etc...
  [Previous line repeated 486 more times]
RecursionError: maximum recursion depth exceeded while calling a Python object

I can reproduce this like so (using the generated python schema from sgqlc):

import my_python_schema
from cloudpickle import dumps, loads

schema_query = my_python_schema.Query
op = Operation(schema_query)

pickled_op = dumps(op) # works
unpickled_op = loads(pickled_op) # blows up with traceback above

Are there any options I'm missing that could maybe pre-flatten the types or specify the max depth?

Thanks for the sweet project 🙂

chartpath avatar Jul 31 '19 23:07 chartpath

I'm not sure it will be possible to autommatically serialize/load these types as they abuse some python protocols to make it easy to use (__getattr__, __setattr__ and the likes).

I'll think a bit about it when I have some time (currently busy with work), but what you want to parallelize is to generate the GraphQL DSL (text), the server execution or the parse of response data?

If you just want to parallelize the server execution, then you can convert the operation to bytes on the main thread/process and send that to the server -- HTTPEndpoint.__call__() accepts that as well. In that case you can return the JSON to the main process/thread and from there you can "add" the operation to get the Python objects.

barbieri avatar Aug 01 '19 04:08 barbieri

Hi, did the above comment help?

Do you still need support with this? Otherwise please close the issue :-)

barbieri avatar Aug 17 '19 22:08 barbieri

Is there any development on that ? Is it possible to create an Operation instance from a bytes representation ?

ar-ms avatar Dec 22 '19 17:12 ar-ms

no, I couldn't look into this.

barbieri avatar Dec 23 '19 13:12 barbieri

I know this is quite a late follow up, but is there any ability to deserialize raw graphql? This would allow for "copying" by serializing and deserializing.

I am also looking into pagination and am not able to avoid modifying the original operation. variables seem to be difficult especially when there are multiple selections in the query.

rpmcginty avatar Mar 21 '24 19:03 rpmcginty

@rpmcginty not the classes, but you should really look into variables rather than this. Actually, in most of other frameworks you're not allowed to create queries on the fly, for instance TypeScript/Apollo requires the exact DSL so it will parse and generate the matching output type definitions. Java and Swift is also the same.

We work with GraphQL since 2017 and we always used variables for actual code. The only place where we don't use it is while playing in a playground console. If you match that with some directives, such as @skip(if: ) and @include(if: ) you can even control the body shape (ie: detail x simple queries). Fragments are also very helpful in that sense.

Pagination and filtering is really one case to use such things. But since we can't do nested/grouped variables, indeed the naming may become cumbersome if you have like 20 variables, then you need to use some convention, like a prefix with the "group" (ex: user_id, user_name, ... element_id, element_name...).

Another case to do variables is mutations, usually you include all the mutation arguments as query variables and also some extra to control result selection (maybe required if your query needs parameters, like lang or currency).

barbieri avatar Mar 22 '24 11:03 barbieri