Skip to content

Commit

Permalink
Create v2 that is compatible with Core v2 (#21)
Browse files Browse the repository at this point in the history
* Create v2 that is compatible with Core v2

* Bump version to 2.0
* Support GraphQL-core >= 2.0, < 3
  (use the newer signature for resolvers)
* Drop support for Python 3.3
* Drop redundant README.rst
* Clean-up and PEP-8
* Use .flake8 instead of setup.cfg
* Use "setup.py test" for testing, as explained in README
* Make this run with pytest 4. This will be changed in v3,
  where we use pytest 5 and run it directly.
* Only check relevant files with flake8
* Minor fixes in the README
  • Loading branch information
Cito authored Jul 14, 2019
1 parent 08c5995 commit b6bd31c
Show file tree
Hide file tree
Showing 27 changed files with 349 additions and 630 deletions.
4 changes: 4 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[flake8]
ignore = E203,W503,E704
exclude = .git,.mypy_cache,.pytest_cache,.tox,.venv,__pycache__,build,dist,docs
max-line-length = 88
39 changes: 20 additions & 19 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
# Created by https://www.gitignore.io

### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
Expand All @@ -22,40 +13,50 @@ lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
*.cover
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log

# Sphinx documentation
docs/_build/

# PyBuilder
target/

.python-version

.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

.mypy_cache/

.idea/
16 changes: 8 additions & 8 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ language: python
dist: xenial

python:
- 2.7
- 3.5
- 3.6
- 3.7
- pypy3
- "2.7"
- "3.5"
- "3.6"
- "3.7"
- "pypy3"
install:
- pip install pytest pytest-cov flake8
- pip install .
- pip install "flake8>=3.7,<4"
script:
- py.test --cov=graphql_relay
# - flake8
- python setup.py test -a "--cov=graphql_relay"
- flake8 setup.py src tests
after_success:
- pip install coveralls
- coveralls
54 changes: 25 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ that documentation and the corresponding tests in this library together.
Install Relay Library for GraphQL Python

```sh
pip install graphql-core --pre # Last version of graphql-core
pip install "graphql-core>=2,<3" # use version 2.x of graphql-core
pip install graphql-relay
```

Expand All @@ -54,13 +54,13 @@ returning those types.
they return a connection type.
- `connection_definitions` returns a `connection_type` and its associated
`edgeType`, given a name and a node type.
- `connection_from_list` is a helper method that takes an array and the
- `connection_from_list` is a helper method that takes a list and the
arguments from `connection_args`, does pagination and filtering, and returns
an object in the shape expected by a `connection_type`'s `resolver` function.
- `connection_from_promised_list` is similar to `connection_from_list`, but
it takes a promise that resolves to an array, and returns a promise that
resolves to the expected shape by `connection_type`.
- `cursor_for_object_in_connection` is a helper method that takes an array and a
- `cursor_for_object_in_connection` is a helper method that takes a list and a
member object, and returns a cursor for use in the mutation payload.

An example usage of these methods from the [test schema](tests/starwars/schema.py):
Expand All @@ -69,32 +69,31 @@ An example usage of these methods from the [test schema](tests/starwars/schema.p
ship_edge, ship_connection = connection_definitions('Ship', shipType)

factionType = GraphQLObjectType(
name= 'Faction',
description= 'A faction in the Star Wars saga',
name='Faction',
description='A faction in the Star Wars saga',
fields= lambda: {
'id': global_id_field('Faction'),
'name': GraphQLField(
GraphQLString,
description='The name of the faction.',
),
'ships': GraphQLField(
shipConnection,
description= 'The ships used by the faction.',
args= connection_args,
resolver= lambda faction, args, *_: connection_from_list(
map(getShip, faction.ships),
args
ship_connection,
description='The ships used by the faction.',
args=connection_args,
resolver=lambda faction, _info, **args: connection_from_list(
[getShip(ship) for ship in faction.ships], args
),
)
},
interfaces= [node_interface]
interfaces=[node_interface]
)
```

This shows adding a `ships` field to the `Faction` object that is a connection.
It uses `connection_definitions({name: 'Ship', nodeType: shipType})` to create
the connection type, adds `connection_args` as arguments on this function, and
then implements the resolver function by passing the array of ships and the
then implements the resolver function by passing the list of ships and the
arguments to `connection_from_list`.

### Object Identification
Expand All @@ -108,7 +107,7 @@ this, it takes a function to resolve an ID to an object, and to determine
the type of a given object.
- `to_global_id` takes a type name and an ID specific to that type name,
and returns a "global ID" that is unique among all types.
- `from_global_id` takes the "global ID" created by `toGlobalID`, and retuns
- `from_global_id` takes the "global ID" created by `to_global_id`, and returns
the type name and ID used to create it.
- `global_id_field` creates the configuration for an `id` field on a node.
- `plural_identifying_root_field` creates a field that accepts a list of
Expand All @@ -118,17 +117,16 @@ objects.
An example usage of these methods from the [test schema](tests/starwars/schema.py):

```python
def get_node(global_id, context, info):
resolvedGlobalId = from_global_id(global_id)
_type, _id = resolvedGlobalId.type, resolvedGlobalId.id
if _type == 'Faction':
return getFaction(_id)
elif _type == 'Ship':
return getShip(_id)
def get_node(global_id, _info):
type_, id_ = from_global_id(global_id)
if type_ == 'Faction':
return getFaction(id_)
elif type_ == 'Ship':
return getShip(id_)
else:
return None

def get_node_type(obj, context, info):
def get_node_type(obj, _info):
if isinstance(obj, Faction):
return factionType
else:
Expand Down Expand Up @@ -177,11 +175,9 @@ class IntroduceShipMutation(object):
def __init__(self, shipId, factionId, clientMutationId=None):
self.shipId = shipId
self.factionId = factionId
self.clientMutationId = None
self.clientMutationId = clientMutationId

def mutate_and_get_payload(data, *_):
shipName = data.get('shipName')
factionId = data.get('factionId')
def mutate_and_get_payload(_info, shipName, factionId, **_input):
newShip = createShip(shipName, factionId)
return IntroduceShipMutation(
shipId=newShip.id,
Expand All @@ -201,19 +197,19 @@ shipMutation = mutation_with_client_mutation_id(
output_fields= {
'ship': GraphQLField(
shipType,
resolver= lambda payload, *_: getShip(payload.shipId)
resolver=lambda payload, _info: getShip(payload.shipId)
),
'faction': GraphQLField(
factionType,
resolver= lambda payload, *_: getFaction(payload.factionId)
resolver=lambda payload, _info: getFaction(payload.factionId)
)
},
mutate_and_get_payload=mutate_and_get_payload
)

mutationType = GraphQLObjectType(
'Mutation',
fields= lambda: {
fields=lambda: {
'introduceShip': shipMutation
}
)
Expand Down
Loading

0 comments on commit b6bd31c

Please sign in to comment.