expose livehtml autobuild in Makefile + Add API autodoc (#971)

* expose livehtml autobuild in Makefile

* add API documentation for schema

* document graphene core API

* fixes black lint

* Update graphene/types/union.py

Co-Authored-By: Jonathan Kim <jkimbo@gmail.com>

* Update graphene/types/argument.py

Co-Authored-By: Jonathan Kim <jkimbo@gmail.com>

* Update graphene/types/field.py

Co-Authored-By: Jonathan Kim <jkimbo@gmail.com>

* Update graphene/types/inputfield.py

Co-Authored-By: Jonathan Kim <jkimbo@gmail.com>

* add note about non-functional `interfaces` meta argument in mutation

* update with other virtual environment configuration

* pin autobuild

* format argument example code

* format enum input object and interface examples

* format enum mutation union examples

* revise documentation with imports, capitalization
This commit is contained in:
David Anderson 2019-06-09 18:36:06 -04:00 committed by Jonathan Kim
parent 40229b8a73
commit da1359ecca
23 changed files with 579 additions and 25 deletions

2
.gitignore vendored
View File

@ -11,6 +11,8 @@ __pycache__/
# Distribution / packaging
.Python
env/
venv/
.venv/
build/
develop-eggs/
dist/

View File

@ -3,9 +3,17 @@ help:
@echo "Please use \`make <target>' where <target> is one of"
@grep -E '^\.PHONY: [a-zA-Z_-]+ .*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = "(: |##)"}; {printf "\033[36m%-30s\033[0m %s\n", $$2, $$3}'
.PHONY: install-dev ## Install development dependencies
install-dev:
pip install -e ".[test]"
test:
py.test graphene
.PHONY: docs ## Generate docs
docs:
@cd docs &&\
pip install -r requirements.txt &&\
make html &&\
cd -
docs: install-dev
cd docs && make install && make html
.PHONY: docs-live ## Generate docs with live reloading
docs-live: install-dev
cd docs && make install && make livehtml

View File

@ -19,7 +19,11 @@ help:
@echo "Please use \`make <target>' where <target> is one of"
@grep -E '^\.PHONY: [a-zA-Z_-]+ .*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = "(: |##)"}; {printf "\033[36m%-30s\033[0m %s\n", $$2, $$3}'
.PHONY: clean
.PHONY: install ## to install all documentation related requirements
install:
pip install -r requirements.txt
.PHONY: clean ## to remove all built documentation
clean:
rm -rf $(BUILDDIR)/*
@ -199,6 +203,6 @@ dummy:
@echo
@echo "Build finished. Dummy builder generates no files."
.PHONY: livehtml
.PHONY: livehtml ## to build and serve live-reloading documentation
livehtml:
sphinx-autobuild -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
sphinx-autobuild -b html --watch ../graphene $(ALLSPHINXOPTS) $(BUILDDIR)/html

0
docs/_static/.gitkeep vendored Normal file
View File

106
docs/api/index.rst Normal file
View File

@ -0,0 +1,106 @@
API Reference
=============
Schema
------
.. autoclass:: graphene.types.schema.Schema
:members:
.. Uncomment sections / types as API documentation is fleshed out
.. in each class
Object types
------------
.. autoclass:: graphene.ObjectType
.. autoclass:: graphene.InputObjectType
.. autoclass:: graphene.Mutation
:members:
Fields (Mounted Types)
----------------------
.. autoclass:: graphene.Field
.. autoclass:: graphene.Argument
.. autoclass:: graphene.InputField
Fields (Unmounted Types)
------------------------
.. autoclass:: graphene.types.unmountedtype.UnmountedType
GraphQL Scalars
---------------
.. autoclass:: graphene.Int()
.. autoclass:: graphene.Float()
.. autoclass:: graphene.String()
.. autoclass:: graphene.Boolean()
.. autoclass:: graphene.ID()
Graphene Scalars
----------------
.. autoclass:: graphene.Date()
.. autoclass:: graphene.DateTime()
.. autoclass:: graphene.Time()
.. autoclass:: graphene.Decimal()
.. autoclass:: graphene.UUID()
.. autoclass:: graphene.JSONString()
Enum
----
.. autoclass:: graphene.Enum()
Structures
----------
.. autoclass:: graphene.List
.. autoclass:: graphene.NonNull
Type Extension
--------------
.. autoclass:: graphene.Interface()
.. autoclass:: graphene.Union()
Execution Metadata
------------------
.. autoclass:: graphene.ResolveInfo
.. autoclass:: graphene.Context
.. autoclass:: graphql.execution.base.ExecutionResult
.. Relay
.. -----
.. .. autoclass:: graphene.Node
.. .. autoclass:: graphene.GlobalID
.. .. autoclass:: graphene.ClientIDMutation
.. .. autoclass:: graphene.Connection
.. .. autoclass:: graphene.ConnectionField
.. .. autoclass:: graphene.PageInfo

View File

@ -22,9 +22,10 @@ on_rtd = os.environ.get("READTHEDOCS", None) == "True"
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
import os
import sys
sys.path.insert(0, os.path.abspath(".."))
# -- General configuration ------------------------------------------------
@ -41,6 +42,7 @@ extensions = [
"sphinx.ext.todo",
"sphinx.ext.coverage",
"sphinx.ext.viewcode",
"sphinx.ext.napoleon",
]
if not on_rtd:
extensions += ["sphinx.ext.githubpages"]

View File

@ -11,9 +11,10 @@ Contents:
execution/index
relay/index
testing/index
api/index
Integrations
-----
------------
* `Graphene-Django <http://docs.graphene-python.org/projects/django/en/latest/>`_ (`source <https://github.com/graphql-python/graphene-django/>`_)
* `Graphene-SQLAlchemy <http://docs.graphene-python.org/projects/sqlalchemy/en/latest/>`_ (`source <https://github.com/graphql-python/graphene-sqlalchemy/>`_)

View File

@ -1,4 +1,5 @@
# Required library
Sphinx==1.5.3
sphinx-autobuild==0.7.1
# Docs template
http://graphene-python.org/sphinx_graphene_theme.zip

View File

@ -8,6 +8,37 @@ from .utils import get_type
class Argument(MountedType):
"""
Makes an Argument available on a Field in the GraphQL schema.
Arguments will be parsed and provided to resolver methods for fields as keyword arguments.
All ``arg`` and ``**extra_args`` for a ``graphene.Field`` are implicitly mounted as Argument
using the below parameters.
.. code:: python
from graphene import String, Boolean, Argument
age = String(
# Boolean implicitly mounted as Argument
dog_years=Boolean(description="convert to dog years"),
# Boolean explicitly mounted as Argument
decades=Argument(Boolean, default_value=False),
)
args:
type (class for a graphene.UnmountedType): must be a class (not an instance) of an
unmounted graphene type (ex. scalar or object) which is used for the type of this
argument in the GraphQL schema.
required (bool): indicates this argument as not null in the graphql scehma. Same behavior
as graphene.NonNull. Default False.
name (str): the name of the GraphQL argument. Defaults to parameter name.
description (str): the description of the GraphQL argument in the schema.
default_value (Any): The value to be provided if the user does not set this argument in
the operation.
"""
def __init__(
self,
type,

View File

@ -1,4 +1,25 @@
class Context(object):
"""
Context can be used to make a convenient container for attributes to provide
for execution for resolvers of a GraphQL operation like a query.
.. code:: python
from graphene import Context
context = Context(loaders=build_dataloaders(), request=my_web_request)
schema.execute('{ hello(name: "world") }', context=context)
def resolve_hello(parent, info, name):
info.context.request # value set in Context
info.context.loaders # value set in Context
# ...
args:
**params (Dict[str, Any]): values to make available on Context instance as attributes.
"""
def __init__(self, **params):
for key, value in params.items():
setattr(self, key, value)

View File

@ -67,6 +67,30 @@ class EnumMeta(SubclassWithMeta_Meta):
class Enum(six.with_metaclass(EnumMeta, UnmountedType, BaseType)):
"""
Enum type definition
Defines a static set of values that can be provided as a Field, Argument or InputField.
.. code:: python
from graphene import Enum
class NameFormat(Enum):
FIRST_LAST = "first_last"
LAST_FIRST = "last_first"
Meta:
enum (optional, Enum): Python enum to use as a base for GraphQL Enum.
name (optional, str): Name of the GraphQL type (must be unique in schema). Defaults to class
name.
description (optional, str): Description of the GraphQL type in the schema. Defaults to class
docstring.
deprecation_reason (optional, str): Setting this value indicates that the enum is
depreciated and may provide instruction or reason on how for clients to proceed.
"""
@classmethod
def __init_subclass_with_meta__(cls, enum=None, _meta=None, **options):
if not _meta:

View File

@ -19,6 +19,47 @@ def source_resolver(source, root, info, **args):
class Field(MountedType):
"""
Makes a field available on an ObjectType in the GraphQL schema. Any type can be mounted as a
Field:
- Object Type
- Scalar Type
- Enum
- Interface
- Union
All class attributes of ``graphene.ObjectType`` are implicitly mounted as Field using the below
arguments.
.. code:: python
class Person(ObjectType):
first_name = graphene.String(required=True) # implicitly mounted as Field
last_name = graphene.Field(String, description='Surname') # explicitly mounted as Field
args:
type (class for a graphene.UnmountedType): must be a class (not an instance) of an
unmounted graphene type (ex. scalar or object) which is used for the type of this
field in the GraphQL schema.
args (optional, Dict[str, graphene.Argument]): arguments that can be input to the field.
Prefer to use **extra_args.
resolver (optional, Callable): A function to get the value for a Field from the parent
value object. If not set, the default resolver method for the schema is used.
source (optional, str): attribute name to resolve for this field from the parent value
object. Alternative to resolver (cannot set both source and resolver).
deprecation_reason (optional, str): Setting this value indicates that the field is
depreciated and may provide instruction or reason on how for clients to proceed.
required (optional, bool): indicates this field as not null in the graphql scehma. Same behavior as
graphene.NonNull. Default False.
name (optional, str): the name of the GraphQL field (must be unique in a type). Defaults to attribute
name.
description (optional, str): the description of the GraphQL field in the schema.
default_value (optional, Any): Default value to resolve if none set from schema.
**extra_args (optional, Dict[str, Union[graphene.Argument, graphene.UnmountedType]): any
additional arguments to mount on the field.
"""
def __init__(
self,
type,

View File

@ -4,6 +4,46 @@ from .utils import get_type
class InputField(MountedType):
"""
Makes a field available on an ObjectType in the GraphQL schema. Any type can be mounted as a
Input Field except Interface and Union:
- Object Type
- Scalar Type
- Enum
Input object types also can't have arguments on their input fields, unlike regular ``graphene.Field``.
All class attributes of ``graphene.InputObjectType`` are implicitly mounted as InputField
using the below arguments.
.. code:: python
from graphene import InputObjectType, String, InputField
class Person(InputObjectType):
# implicitly mounted as Input Field
first_name = String(required=True)
# explicitly mounted as Input Field
last_name = InputField(String, description="Surname")
args:
type (class for a graphene.UnmountedType): Must be a class (not an instance) of an
unmounted graphene type (ex. scalar or object) which is used for the type of this
field in the GraphQL schema.
name (optional, str): Name of the GraphQL input field (must be unique in a type).
Defaults to attribute name.
default_value (optional, Any): Default value to use as input if none set in user operation (
query, mutation, etc.).
deprecation_reason (optional, str): Setting this value indicates that the field is
depreciated and may provide instruction or reason on how for clients to proceed.
description (optional, str): Description of the GraphQL field in the schema.
required (optional, bool): Indicates this input field as not null in the graphql scehma.
Raises a validation error if argument not provided. Same behavior as graphene.NonNull.
Default False.
**extra_args (optional, Dict): Not used.
"""
def __init__(
self,
type,

View File

@ -36,7 +36,33 @@ class InputObjectType(UnmountedType, BaseType):
An input object defines a structured collection of fields which may be
supplied to a field argument.
Using `NonNull` will ensure that a value must be provided by the query
Using ``graphene.NonNull`` will ensure that a input value must be provided by the query.
All class attributes of ``graphene.InputObjectType`` are implicitly mounted as InputField
using the below Meta class options.
.. code:: python
from graphene import InputObjectType, String, InputField
class Person(InputObjectType):
# implicitly mounted as Input Field
first_name = String(required=True)
# explicitly mounted as Input Field
last_name = InputField(String, description="Surname")
The fields on an input object type can themselves refer to input object types, but you can't
mix input and output types in your schema.
Meta class options (optional):
name (str): the name of the GraphQL type (must be unique in schema). Defaults to class
name.
description (str): the description of the GraphQL type in the schema. Defaults to class
docstring.
container (class): A class reference for a value object that allows for
attribute initialization and access. Default InputObjectTypeContainer.
fields (Dict[str, graphene.InputField]): Dictionary of field name to InputField. Not
recommended to use (prefer class attributes).
"""
@classmethod

View File

@ -22,6 +22,28 @@ class Interface(BaseType):
is used to describe what types are possible, what fields are in common across
all types, as well as a function to determine which type is actually used
when the field is resolved.
.. code:: python
from graphene import Interface, String
class HasAddress(Interface):
class Meta:
description = "Address fields"
address1 = String()
address2 = String()
If a field returns an Interface Type, the ambiguous type of the object can be determined using
``resolve_type`` on Interface and an ObjectType with ``Meta.possible_types`` or ``is_type_of``.
Meta:
name (str): Name of the GraphQL type (must be unique in schema). Defaults to class
name.
description (str): Description of the GraphQL type in the schema. Defaults to class
docstring.
fields (Dict[str, graphene.Field]): Dictionary of field name to Field. Not recommended to
use (prefer class attributes).
"""
@classmethod

View File

@ -8,7 +8,12 @@ from .scalars import Scalar
class JSONString(Scalar):
"""JSON String"""
"""
Allows use of a JSON String for input / output from the GraphQL schema.
Use of this type is *not recommended* as you lose the benefits of having a defined, static
schema (one of the key benefits of GraphQL).
"""
@staticmethod
def serialize(dt):

View File

@ -22,7 +22,47 @@ class MutationOptions(ObjectTypeOptions):
class Mutation(ObjectType):
"""
Mutation Type Definition
Object Type Definition (mutation field)
Mutation is a convenience type that helps us build a Field which takes Arguments and returns a
mutation Output ObjectType.
.. code:: python
from graphene import Mutation, ObjectType, String, Boolean, Field
class CreatePerson(Mutation):
class Arguments:
name = String()
ok = Boolean()
person = Field(Person)
def mutate(parent, info, name):
person = Person(name=name)
ok = True
return CreatePerson(person=person, ok=ok)
class Mutation(ObjectType):
create_person = CreatePerson.Field()
Meta class options (optional):
output (graphene.ObjectType): Or ``Output`` inner class with attributes on Mutation class.
Or attributes from Mutation class. Fields which can be returned from this mutation
field.
resolver (Callable resolver method): Or ``mutate`` method on Mutation class. Perform data
change and return output.
arguments (Dict[str, graphene.Argument]): Or ``Arguments`` inner class with attributes on
Mutation class. Arguments to use for the mutation Field.
name (str): Name of the GraphQL type (must be unique in schema). Defaults to class
name.
description (str): Description of the GraphQL type in the schema. Defaults to class
docstring.
interfaces (Iterable[graphene.Interface]): NOT IMPLEMENTED (use ``output`` to define a
payload implementing interfaces). GraphQL interfaces to extend with the payload
object. All fields from interface will be included in this object's schema.
fields (Dict[str, graphene.Field]): Dictionary of field name to Field. Not recommended to
use (prefer class attributes or ``Meta.output``).
"""
@classmethod
@ -80,6 +120,7 @@ class Mutation(ObjectType):
def Field(
cls, name=None, description=None, deprecation_reason=None, required=False
):
""" Mount instance of mutation Field. """
return Field(
cls._meta.output,
args=cls._meta.arguments,

View File

@ -22,6 +22,70 @@ class ObjectType(BaseType):
Almost all of the GraphQL types you define will be object types. Object types
have a name, but most importantly describe their fields.
The name of the type defined by an _ObjectType_ defaults to the class name. The type
description defaults to the class docstring. This can be overriden by adding attributes
to a Meta inner class.
The class attributes of an _ObjectType_ are mounted as instances of ``graphene.Field``.
Methods starting with ``resolve_<field_name>`` are bound as resolvers of the matching Field
name. If no resolver is provided, the default resolver is used.
Ambiguous types with Interface and Union can be determined through``is_type_of`` method and
``Meta.possible_types`` attribute.
.. code:: python
from graphene import ObjectType, String, Field
class Person(ObjectType):
class Meta:
description = 'A human'
# implicitly mounted as Field
first_name = String()
# explicitly mounted as Field
last_name = Field(String)
def resolve_last_name(parent, info):
return last_name
ObjectType must be mounted using ``graphene.Field``.
.. code:: python
from graphene import ObjectType, Field
class Query(ObjectType):
person = Field(Person, description="My favorite person")
Meta class options (optional):
name (str): Name of the GraphQL type (must be unique in schema). Defaults to class
name.
description (str): Description of the GraphQL type in the schema. Defaults to class
docstring.
interfaces (Iterable[graphene.Interface]): GraphQL interfaces to extend with this object.
all fields from interface will be included in this object's schema.
possible_types (Iterable[class]): Used to test parent value object via isintance to see if
this type can be used to resolve an ambigous type (interface, union).
default_resolver (any Callable resolver): Override the default resolver for this
type. Defaults to graphene default resolver which returns an attribute or dictionary
key with the same name as the field.
fields (Dict[str, graphene.Field]): Dictionary of field name to Field. Not recommended to
use (prefer class attributes).
An _ObjectType_ can be used as a simple value object by creating an instance of the class.
.. code:: python
p = Person(first_name='Bob', last_name='Roberts')
assert p.first_name == 'Bob'
Args:
*args (List[Any]): Positional values to use for Field values of value object
**kwargs (Dict[str: Any]): Keyword arguments to use for Field values of value object
"""
@classmethod

View File

@ -27,10 +27,26 @@ def assert_valid_root_type(_type):
class Schema(GraphQLSchema):
"""
Schema Definition
Graphene Schema can execute operations (query, mutation, subscription) against the defined
types.
A Schema is created by supplying the root types of each type of operation,
query and mutation (optional).
For advanced purposes, the schema can be used to lookup type definitions and answer questions
about the types through introspection.
Args:
query (ObjectType): Root query *ObjectType*. Describes entry point for fields to *read*
data in your Schema.
mutation (ObjectType, optional): Root mutation *ObjectType*. Describes entry point for
fields to *create, update or delete* data in your API.
subscription (ObjectType, optional): Root subscription *ObjectType*. Describes entry point
for fields to receive continuous updates.
directives (List[GraphQLDirective], optional): List of custom directives to include in
GraphQL schema. Defaults to only include directives definved by GraphQL spec (@include
and @skip) [GraphQLIncludeDirective, GraphQLSkipDirective].
types (List[GraphQLType], optional): List of any types to include in schema that
may not be introspected through root types.
auto_camelcase (bool): Fieldnames will be transformed in Schema's TypeMap from snake_case
to camelCase (preferred by GraphQL standard). Default True.
"""
def __init__(
@ -99,6 +115,32 @@ class Schema(GraphQLSchema):
raise Exception("{} is not a valid GraphQL type.".format(_type))
def execute(self, *args, **kwargs):
"""
Use the `graphql` function from `graphql-core` to provide the result for a query string.
Most of the time this method will be called by one of the Graphene :ref:`Integrations`
via a web request.
Args:
request_string (str or Document): GraphQL request (query, mutation or subscription) in
string or parsed AST form from `graphql-core`.
root (Any, optional): Value to use as the parent value object when resolving root
types.
context (Any, optional): Value to be made avaiable to all resolvers via
`info.context`. Can be used to share authorization, dataloaders or other
information needed to resolve an operation.
variables (dict, optional): If variables are used in the request string, they can be
provided in dictionary form mapping the variable name to the variable value.
operation_name (str, optional): If mutiple operations are provided in the
request_string, an operation name must be provided for the result to be provided.
middleware (List[SupportsGraphQLMiddleware]): Supply request level middleware as
defined in `graphql-core`.
backend (GraphQLCoreBackend, optional): Override the default GraphQLCoreBackend.
**execute_options (Any): Depends on backend selected. Default backend has several
options such as: validate, allow_subscriptions, return_promise, executor.
Returns:
:obj:`ExecutionResult` containing any data and errors for the operation.
"""
return graphql(self, *args, **kwargs)
def introspect(self):

View File

@ -39,6 +39,14 @@ class List(Structure):
A list is a kind of type marker, a wrapping type which points to another
type. Lists are often created within the context of defining the fields of
an object type.
List indicates that many values will be returned (or input) for this field.
.. code:: python
from graphene import List, String
field_name = List(String, description="There will be many values")
"""
def __str__(self):
@ -63,6 +71,16 @@ class NonNull(Structure):
usually the id field of a database row will never be null.
Note: the enforcement of non-nullability occurs within the executor.
NonNull can also be indicated on all Mounted types with the keyword argument ``required``.
.. code:: python
from graphene import NonNull, String
field_name = NonNull(String, description='This field will not be null')
another_field = String(required=True, description='This is equivalent to the above')
"""
def __init__(self, *args, **kwargs):

View File

@ -19,6 +19,34 @@ class Union(UnmountedType, BaseType):
When a field can return one of a heterogeneous set of types, a Union type
is used to describe what types are possible as well as providing a function
to determine which type is actually used when the field is resolved.
The schema in this example can take a search text and return any of the GraphQL object types
indicated: Human, Droid or Startship.
Ambigous return types can be resolved on each ObjectType through ``Meta.possible_types``
attribute or ``is_type_of`` method. Or by implementing ``resolve_type`` class method on the
Union.
.. code:: python
from graphene import Union, ObjectType, List
class SearchResult(Union):
class Meta:
types = (Human, Droid, Starship)
class Query(ObjectType):
search = List(SearchResult.Field(
search_text=String(description='Value to search for'))
)
Meta:
types (Iterable[graphene.ObjectType]): Required. Collection of types that may be returned
by this Union for the graphQL schema.
name (optional, str): the name of the GraphQL type (must be unique in schema). Defaults to class
name.
description (optional, str): the description of the GraphQL type in the schema. Defaults to class
docstring.
"""
@classmethod

View File

@ -6,13 +6,37 @@ class UnmountedType(OrderedType):
This class acts a proxy for a Graphene Type, so it can be mounted
dynamically as Field, InputField or Argument.
Instead of writing
>>> class MyObjectType(ObjectType):
>>> my_field = Field(String, description='Description here')
Instead of writing:
It let you write
>>> class MyObjectType(ObjectType):
>>> my_field = String(description='Description here')
.. code:: python
from graphene import ObjectType, Field, String
class MyObjectType(ObjectType):
my_field = Field(String, description='Description here')
It lets you write:
.. code:: python
from graphene import ObjectType, String
class MyObjectType(ObjectType):
my_field = String(description='Description here')
It is not used directly, but is inherited by other types and streamlines their use in
different context:
- Object Type
- Scalar Type
- Enum
- Interface
- Union
An unmounted type will accept arguments based upon its context (ObjectType, Field or
InputObjectType) and pass it on to the appropriate MountedType (Field, Argument or InputField).
See each Mounted type reference for more information about valid parameters.
"""
def __init__(self, *args, **kwargs):

View File

@ -8,7 +8,10 @@ from .scalars import Scalar
class UUID(Scalar):
"""UUID"""
"""
Leverages the internal Python implmeentation of UUID (uuid.UUID) to provide native UUID objects
in fields, resolvers and input.
"""
@staticmethod
def serialize(uuid):