Code Monkey home page Code Monkey logo

graphql-core-legacy's Introduction

GraphQL-core 2

⚠️ This is the repository of GraphQL for Python 2 (legacy version).

The repository for the current version is available at github.com/graphql-python/graphql-core.

Build Status Coverage Status

This library is a port of GraphQL.js to Python and up-to-date with release 0.6.0.

GraphQL-core 2 supports Python version 2.7, 3.5, 3.6, 3.7 and 3.8.

GraphQL.js is the JavaScript reference implementation for GraphQL, a query language for APIs created by Facebook.

See also the GraphQL documentation at graphql.org and graphql.org/graphql-js/graphql/.

For questions regarding GraphQL, ask Stack Overflow.

Getting Started

An overview of the GraphQL language is available in the README for the Specification for GraphQL.

The overview describes a simple set of GraphQL examples that exist as tests in this repository. A good way to get started is to walk through that README and the corresponding tests in parallel.

Using GraphQL-core 2

Install from pip:

pip install "graphql-core<3"

GraphQL-core provides two important capabilities: building a type schema, and serving queries against that type schema.

First, build a GraphQL type schema which maps to your code base.

from graphql import (
    graphql,
    GraphQLSchema,
    GraphQLObjectType,
    GraphQLField,
    GraphQLString
)

schema = GraphQLSchema(
  query=GraphQLObjectType(
    name='RootQueryType',
    fields={
      'hello': GraphQLField(
        type=GraphQLString,
        resolver=lambda *_: 'world'
      )
    }
  )
)

This defines a simple schema with one type and one field, that resolves to a fixed value. The resolver function can return a value, a promise, or an array of promises. A more complex example is included in the top level tests directory.

Then, serve the result of a query against that type schema.

query = '{ hello }'

result = graphql(schema, query)

# Prints
# {'hello': 'world'} (as OrderedDict)

print result.data

This runs a query fetching the one field defined. The graphql function will first ensure the query is syntactically and semantically valid before executing it, reporting errors otherwise.

query = '{ boyhowdy }'

result = graphql(schema, query)

# Prints
# [GraphQLError('Cannot query field "boyhowdy" on type "RootQueryType".',)]

print result.errors

Executors

The graphql query is executed, by default, synchronously (using SyncExecutor). However the following executors are available if we want to resolve our fields in parallel:

  • graphql.execution.executors.asyncio.AsyncioExecutor: This executor executes the resolvers in the Python asyncio event loop.
  • graphql.execution.executors.gevent.GeventExecutor: This executor executes the resolvers in the Gevent event loop.
  • graphql.execution.executors.process.ProcessExecutor: This executor executes each resolver as a process.
  • graphql.execution.executors.thread.ThreadExecutor: This executor executes each resolver in a Thread.
  • graphql.execution.executors.sync.SyncExecutor: This executor executes each resolver synchronusly (default).

Usage

You can specify the executor to use via the executor keyword argument in the grapqhl.execution.execute function.

from graphql import parse
from graphql.execution import execute
from graphql.execution.executors.sync import SyncExecutor

ast = parse('{ hello }')

result = execute(schema, ast, executor=SyncExecutor())

print result.data

Contributing

After cloning this repo, create a virtualenv and ensure dependencies are installed by running:

virtualenv venv
source venv/bin/activate
pip install -e ".[test]"

Well-written tests and maintaining good test coverage is important to this project. While developing, run new and existing tests with:

pytest PATH/TO/MY/DIR/test_test.py # Single file
pytest PATH/TO/MY/DIR/ # All tests in directory

Add the -s flag if you have introduced breakpoints into the code for debugging. Add the -v ("verbose") flag to get more detailed test output. For even more detailed output, use -vv. Check out the pytest documentation for more options and test running controls.

GraphQL-core 2 supports several versions of Python. To make sure that changes do not break compatibility with any of those versions, we use tox to create virtualenvs for each Python version and run tests with that version. To run against all python versions defined in the tox.ini config file, just run:

tox

If you wish to run against a specific version defined in the tox.ini file:

tox -e py36

Tox can only use whatever versions of python are installed on your system. When you create a pull request, Travis will also be running the same tests and report the results, so there is no need for potential contributors to try to install every single version of python on their own system ahead of time. We appreciate opening issues and pull requests to make GraphQL-core even more stable & useful!

Main Contributors

License

MIT License

graphql-core-legacy's People

Contributors

akira-dev avatar alecaivazis avatar bcb avatar blazewicz avatar caselit avatar caseywebdev avatar cito avatar ckarnell avatar cpennington avatar dan98765 avatar dittos avatar ekampf avatar faassen avatar femesq avatar globegitter avatar jaemk avatar jhgg avatar jkimbo avatar jnak avatar kingdarboja avatar mcamac avatar mwilliamson avatar nickbruun avatar obi1kenobi avatar pmlandwehr avatar projectcheshire avatar rapthead avatar syrusakbary avatar valdergallo avatar woodb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphql-core-legacy's Issues

errors during `Schema`'s `schema` property method extremely difficult to debug

Errors in @property def schema (and probably any other @property in that class) do not yield useful tracebacks; instead, python "helpfully" falls back to __getattr__ and then fails in there, which is extremely unhelpful. This appears to be a specific case of encode/django-rest-framework#2108

Would it be possible to ditch the __getattr_ use, or alternatively the @propertys?

Traceback of what I'm hitting:

<ipython-input-2-8b3585dcab10> in <module>()
----> 1 res2 = schema.execute("query { subject_user {user_id} }", request_context=dict(_subject_user_id=1))

/usr/local/encap/python-2.7.7/lib/python2.7/site-packages/graphene/core/schema.pyc in execute(self, request, root, args, **kwargs)
    120
    121     def execute(self, request='', root=None, args=None, **kwargs):
--> 122         kwargs = dict(kwargs, request=request, root=root, args=args, schema=self.schema)
    123         with self.plugins.context_execution(**kwargs) as execute_kwargs:
    124             return self.executor.execute(**execute_kwargs)

/usr/local/encap/python-2.7.7/lib/python2.7/site-packages/graphene/core/schema.pyc in __getattr__(self, name)
     47         if name in self.plugins:
     48             return getattr(self.plugins, name)
---> 49         return super(Schema, self).__getattr__(name)
     50
     51     def T(self, _type):

AttributeError: 'super' object has no attribute '__getattr__'

AsyncIO Executor

I've run into a bug with the AsyncIO Executor when trying to run a query from an application where the event is running.

I think it's because the executor's implemention of wait_until_complete uses run_until_complete which starts up the event loop and waits. Since the event loop is already running, it threw an exception when I tried to start it a second time.

Does not throw error on empty Mutation

Having a schema like this:

class Mutation(graphene.ObjectType):
    pass

class Customer(graphene.ObjectType):
    test = graphene.Int()

schema = graphene.Schema(
        query=Customer,
        mutation=Mutation,
    )

In 0.x on running the introspection query I get an error message:

  File "third_party/python/graphene/core/schema.py", line 133, in introspect
    return graphql(self.schema, introspection_query).data
  File "third_party/python/graphene/core/schema.py", line 81, in schema
    subscription=self.T(self.subscription))
  File "third_party/python/graphene/core/schema.py", line 19, in __init__
    super(GraphQLSchema, self).__init__(*args, **kwargs)
  File "third_party/python/graphql/type/schema.py", line 58, in __init__
    self._type_map = self._build_type_map(types)
  File "third_party/python/graphql/type/schema.py", line 107, in _build_type_map
    type_map = reduce(type_map_reducer, types, OrderedDict())
  File "third_party/python/graphql/type/schema.py", line 151, in type_map_reducer
    field_map = type.get_fields()
  File "third_party/python/graphql/type/definition.py", line 193, in get_fields
    self._field_map = define_field_map(self, self._fields)
  File "third_party/python/graphql/type/definition.py", line 211, in define_field_map
    ).format(type)
AssertionError: Mutation fields must be a mapping (dict / OrderedDict) with field names as keys or a function which returns such a mapping

Where-as in 1.0 I do not and it creates a schema.json that relay can not handle. Not super high priority but I though I'd log it. Can hopefully provide a failing test and maybe even fix it.

Parse equality is whitespace dependent

@syrusakbary - I'm trying to test the generation of graphql queries by using graphql.parse and experiencing some weird behavior:

import graphql
print(graphql.parse("""
    query {
        user(id: 1) { foo {
                bar(arg:"2") {
                    id
                }
            }
        }
    }
""") == graphql.parse("""
    query {
        user(id: 1) { foo { bar(arg:"2") {
                    id
                }
            }
        }
    }
"""))
# prints false

The only difference between these two queries is the position of the bar node but the individual results look identical:

print(graphql.parse("""
    query {
        user(id: 1) { foo {
                bar(arg:"2") {
                    id
                }
            }
        }
    }
"""))
# prints Document(definitions=[OperationDefinition(operation='query', name=None, variable_definitions=[], directives=[], selection_set=SelectionSet(selections=[Field(alias=None, name=Name(value='user'), arguments=[Argument(name=Name(value='id'), value=IntValue(value='1'))], directives=[], selection_set=SelectionSet(selections=[Field(alias=None, name=Name(value='foo'), arguments=[], directives=[], selection_set=SelectionSet(selections=[Field(alias=None, name=Name(value='bar'), arguments=[Argument(name=Name(value='arg'), value=StringValue(value='2'))], directives=[], selection_set=SelectionSet(selections=[Field(alias=None, name=Name(value='id'), arguments=[], directives=[], selection_set=None)]))]))]))]))])

print(graphql.parse("""
    query {
        user(id: 1) { foo { bar(arg:"2") {
                    id
                }
            }
        }
    }
"""))
# prints Document(definitions=[OperationDefinition(operation='query', name=None, variable_definitions=[], directives=[], selection_set=SelectionSet(selections=[Field(alias=None, name=Name(value='user'), arguments=[Argument(name=Name(value='id'), value=IntValue(value='1'))], directives=[], selection_set=SelectionSet(selections=[Field(alias=None, name=Name(value='foo'), arguments=[], directives=[], selection_set=SelectionSet(selections=[Field(alias=None, name=Name(value='bar'), arguments=[Argument(name=Name(value='arg'), value=StringValue(value='2'))], directives=[], selection_set=SelectionSet(selections=[Field(alias=None, name=Name(value='id'), arguments=[], directives=[], selection_set=None)]))]))]))]))])

Error message when you return a non-iterable for a `List` in a `resolve_` function should mention the name of the resolve function

Particularly this code:

https://github.com/graphql-python/graphql-core/blob/450620de817480e5f36b64b7eb055a2be12abea0/graphql/core/execution/executor.py#L250

Since graphql-core doesn't bubble up exceptions for most problems with the schema - here included - it's entirely nonobvious what types are involved and more importantly, what resolve_* function is the cause of this error. It makes it really hard to debug - turning a 5 minute problem into a "hack your own debugging code in" ~30 minute problem... if you have the mind to try that. Resolving a bug like this shouldn't take that kind of surgery to debug.

"Dirty" stdout from version on pypi

Hi!
Not sure if it is the right place to report, but the latest version on pypi is not clean. It still has
print(field_def.args.items())
at line 36 of known_argument_names.py (and probably more alike), which brings a lot of garbage in stdout.

Latest version here does not have this issue.

Implement outstanding tests

This issue is just a check-list of outstanding tests I need to implement.

If you plan to help, please comment before starting work on any of these tests so we don't do the same work at the same time. If there's a name by the checkbox, that person is working on implementing those tests.

Type Tests

Executor Tests

Language Tests

Utility Tests

Validation Tests

Starwars

v0.6.1

Not sure how much time I have the next few weeks but wanted to get the ball rolling on v0.6.0+. I think it is probably best to target the sub-relsease rather than straight to 0.7.0 making it easier chunks.

In any case, here is the list of commits: graphql/graphql-js@v0.6.0...v0.6.1

As soon as I find some time I'll get started on it.

Execute should return an awaitable with AsyncioExecutor

The current graphql.execution.execute.execute implementation forces AsyncioExecutor to use loop.run_until_complete in wait_until_finished. This is a bit ugly.

It would be better to let the executor decide what is returned from the execute method. It can be given a function to call after the data is ready as an argument.

With this AsyncioExecutor could return an awaitable and execute would be used as one would expect

result = await execute(schema, ast, executor=AsyncioExecutor())

It could even have an argument to get the new way to keep compatibility with existing uses.

This would also make TwistedExecutor much easier to implement.

Option for strict key ordering in results.

Related to #18, sometimes users might want their results to be in the same order as they are in the fields. In this case, I am thinking that it should be an optional argument (default False) to the Executor.

In the default, it will use regular dict objects to store results. But if say enforce_strict_ordering=True is passed to the Executor, then we will use OrderedDict instead.

Use AsyncioExecutor from within a coroutine.

I'd like to be able to resolve a graphql query with async def resolvers from inside a coroutine.

This used to work with the old AsyncioExecutionMiddleware (I was using version 0.4.12.1) but the new AsyncExecutor seems like it's meant to be run from outside a coroutine and internally calls loop.run_until_complete itself but my request handler is already a coroutine so I'd like to just call
result = await execute(...)

GraphQLArgument with default_value None

In wsgi_graphql at https://github.com/faassen/wsgi_graphql I have tests in the test suite when I update to graphql-core. These are caused by the handling of default_value. If you omit default_value (which I do in test_wsgi_graphql.TestSchema) it is set to None. This now causes error messages in graphql-core, which
weren't there in graphqllib before, and it doesn't seem to be right in general: the express-graphql tests also omit the default value and this works fine.

You can make the tests pass by putting in a default_value of False explicitly:

                args={
                    'who': GraphQLArgument(
                        type=GraphQLString,
                        default_value=False
                    )
                },

Improve Documentation of ResolveInfo

As far as I can tell there's no documentation anywhere about what it's fields mean and how to use it, except for maybe 1 example of "how to get requsted field names" buried in another issue. As a fairly core part of the graphql library it needs documentation.

Release new version

Having big mutations and queries it would be really useful having a new version of the library that includes graphql-python/graphql-core@cf10db6

I actually spent some time implementing it myself just to now realise it has already been implemented without a release. So yeah, that would be a quite nice free win to have :) Thanks!

Implement Remaining Utilities

In https://github.com/graphql/graphql-js/tree/master/src/utilities

Porting Guide

Remaining utilities.

  • TypeInfo
  • astFromValue #1
  • buildASTSchema
  • buildClientSchema
  • introspectionQuery
  • isValidJSValue
  • isValidLiteralValue
  • schemaPrinter #15
  • typeFromAST
  • valueFromAST
  • getOperationAST

Improve Error Handling

Initial discussion started in #31 -

graphql-core's exception handling is lacking. I'd like to implement a way where we can forward resolver errors to some place where they can be appropriately captured with their traceback in-tact.

It should handle sync and async resolver errors just the same.

Best way to capture errors

Do you have a good solution to hook in some sort of error reporting? I'm thinkin maybe some way to hook into GraphQLLocatedError when it is generated, but I dont know how to do that without forking the repo... python n00b

Also: for questions like this, is there a better place to ask this? I dont see a slack channel or any place to put this type of a question :(

Unicode support

I scalars.py, this line:

def coerce_string(value):
    if isinstance(value, bool):
        return 'true' if value else 'false'
    return unicode(value)

If the input value of a mutation is a unicode string, I get a decode error. A simple fix, unicode(value) handles this for me.

Interfaces with fields that are interfaces

Apologies if this is a little convoluted - it's hard to explain!

graphql/graphql-spec#123 allows implementations of interfaces to have fields that are implementations of interfaces. Currently assert_object_implements_interface (graphql/core/type/schema.py) only allows the fields to match types exactly. An example:

class InterfaceA(graphene.Interface):
    ...

class InterfaceB(graphene.Interface):
    my_field = graphene.List(InterfaceA)
    ...

class ImplementationA(InterfaceA):
    ...

class ImplementationB(InterfaceB):
    my_field = graphene.List(ImplementationA)
    ...

To test something out I made assert_object_implements_interface a no-op then hit further issues with graphql/core/validation/rules/overlapping_fields_can_be_merged.py. A simplified part of a query (in practise the query was built up from fragments coming from different places) returning an object of type ImplementationB:

my_field
... on InterfaceB {
    my_field
}

In theory it should be able to merge these, but in practice the first my_field is known to be ImplementationA and the second only InterfaceA.

I think the first part of this issue is worth fixing and I think I could probably manage that and open a PR if you'd like.

The second part I'm less sure about and I would have a harder time fixing - let me know what you think though. It's possible to work around the second problem by making sure queries are wrapped with ... on InterfaceB when merging might happen.

List types are not correctly validated

Consider the following test case:

from graphql.language.parser import parse
from graphql.utils.build_ast_schema import build_ast_schema
from graphql.validation import validate

def test_list_literal_validation():
    schema_text = '''
        schema {
            query: RootSchemaQuery
        }

        directive @foo(list: [String!]!) on FIELD | INLINE_FRAGMENT

        type Bar {
            name: String
        }

        type RootSchemaQuery {
            Bar: Bar
        }
    '''
    schema = build_ast_schema(parse(schema_text))

    query = '''
        query Baz {
            Bar {
                name @foo(list: "not_a_list")
            }
        }
    '''
    query_ast = parse(query)

    errors = validate(schema, query_ast)
    assert errors

Note that the directive @foo requires a list argument, but is passed a string argument instead. Despite this problem, this library claims that the GraphQL query validates with no errors, failing the test. I believe this to be a bug.

Documentation & GH Pages

We need documentation!

@woodb set up sphinx docs in dittos/graphqllib#15 however not much has been written yet.

If anyone wants to help write docs, that'd be really awesome. Most of the API mirrors that of graphql-js, however, with some pretty important differences in how the Executor is ran, and how Types are defined.

@syrusakbary, I saw you were working on gh-pages for graphene. Are you working on a specific style you want graphql-python's pages to presented in? Are you also doing sphinx docs?

Implement `astToDict` Utility

Since we use slotted classes for ASTs, we need a way to convert an AST Node to a Dict.

I.e. Name(value='test') Should get converted to {"kind": "Name", "value": "test"}.

It should convert recursively.

Async/Await keywords with AyncioExecutor

I'm running into a bug using the async/await keywords and the AsyncioExecutor. Here is my test:

from graphql.execution.executors.asyncio import AsyncioExecutor
import graphene

class Query(graphene.ObjectType):

    recipes = graphene.String()

    async def resolve_recipes(self, *_):
        return 'hello'


schema = graphene.Schema(
    query=Query,
    executor=AsyncioExecutor()
)

executed = schema.execute("""
    query {
        recipes
    }
""")

print(executed.data)
print(executed.errors)

which results in the following messages logged in my terminal:

/Users/alec/bin/python/graphql/execution/executor.py:110: RuntimeWarning: coroutine 'Query.resolve_recipes' was never awaited
  result = resolve_field(exe_context, parent_type, source_value, field_asts)
OrderedDict([('recipes', '<coroutine object Query.resolve_recipes at 0x10eb0f360>')])
[]

ProcessExecutor failing

(using osx, python 3.5)

seems to be trouble with using promises and pickle together. Also decorated methods seem to have trouble which means resolvers are often a problem. Adding a comma to args fixes the "Queue is not iterable" error but the second error is much more intricate.

ERROR|2016-10-01 01:12:57,673|graphql.execution.executor >> An error occurred while resolving field Query.viewer
Traceback (most recent call last):
  File "/Users/ben/.virtualenvs/current/lib/python3.5/site-packages/graphql/execution/executor.py", line 190, in resolve_or_error
    return executor.execute(resolve_fn, source, args, context, info)
  File "/Users/ben/.virtualenvs/current/lib/python3.5/site-packages/graphql/execution/executors/process.py", line 29, in execute
    _process = Process(target=queue_process, args=(self.q))
  File "/usr/local/Cellar/python3/3.5.2_1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/multiprocessing/process.py", line 80, in __init__
    self._args = tuple(args)
TypeError: 'Queue' object is not iterable
[01/Oct/2016 01:12:57] "POST /graphql? HTTP/1.1" 200 116
Traceback (most recent call last):
  File "/usr/local/Cellar/python3/3.5.2_1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/multiprocessing/queues.py", line 241, in _feed
    obj = ForkingPickler.dumps(obj)
  File "/usr/local/Cellar/python3/3.5.2_1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/multiprocessing/reduction.py", line 51, in dumps
    cls(buf, protocol).dump(obj)
TypeError: can't pickle _thread.lock objects

Path to 0.5.0

Version 0.5.0 compatible with the GraphQL Apr 2016 spec.
Related Pull Requests: #54 #59

Features

  • Improve error handling #41
  • Add ability to add custom errors (inheriting from GraphQLError)
  • Expose all the function and classes
  • Move modules from graphql.core.* to graphql.*
  • Make tests relative to the directories
  • Isolate promise package into own package in PyPI pypromise ☺️

Breaking changes

Improve the executor to be closer to the reference implementation:

  • Uses Promise instead of Deferred as it simplifies the implementation. Use parallel resolvers automatically as default behavior when available. Related PR: #59

Commit Checklist

Implement the commits from GraphQL-js:

Add Decimal Type

We have GraphQLFloat and GraphQLInt, as Python also have the native implementation of a Decimal, would be great if we can add the type GraphQLDecimal.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.