Introduce Black formatting, additional tests

This commit is contained in:
Paul Hallett 2019-04-30 10:02:23 +01:00
parent 05c89c19fb
commit e6ad5887ca
No known key found for this signature in database
GPG Key ID: 529C11F0C93CDF11
15 changed files with 86 additions and 62 deletions

View File

@ -1,19 +1,21 @@
language: python language: python
sudo: false sudo: required
dist: xenial
python: python:
- 2.7 - 2.7
- 3.4 - 3.4
- 3.5 - 3.5
- 3.6 - 3.6
- 3.7
install: install:
- | - |
if [ "$TEST_TYPE" = build ]; then if [ "$TEST_TYPE" = build ]; then
pip install -e .[test] pip install -e .[test]
pip install psycopg2 # Required for Django postgres fields testing pip install psycopg2==2.8.2 # Required for Django postgres fields testing
pip install django==$DJANGO_VERSION pip install django==$DJANGO_VERSION
python setup.py develop python setup.py develop
elif [ "$TEST_TYPE" = lint ]; then elif [ "$TEST_TYPE" = lint ]; then
pip install flake8 pip install flake8==3.7.7
fi fi
script: script:
- | - |
@ -45,10 +47,16 @@ matrix:
env: TEST_TYPE=build DJANGO_VERSION=2.1 env: TEST_TYPE=build DJANGO_VERSION=2.1
- python: '3.6' - python: '3.6'
env: TEST_TYPE=build DJANGO_VERSION=2.1 env: TEST_TYPE=build DJANGO_VERSION=2.1
- python: '3.6'
env: TEST_TYPE=build DJANGO_VERSION=2.2
- python: '3.7'
env: TEST_TYPE=build DJANGO_VERSION=2.2
- python: '2.7' - python: '2.7'
env: TEST_TYPE=lint env: TEST_TYPE=lint
- python: '3.6' - python: '3.6'
env: TEST_TYPE=lint env: TEST_TYPE=lint
- python: '3.7'
env: TEST_TYPE=lint
deploy: deploy:
provider: pypi provider: pypi
user: syrusakbary user: syrusakbary

View File

@ -31,6 +31,19 @@ After developing, the full test suite can be evaluated by running:
make tests make tests
``` ```
## Opening Pull Requests
Please fork the project and open a pull request against the master branch.
This will trigger a series of test and lint checks.
We advise that you format and run lint locally before doing this to save time:
```sh
make format
make lint
```
## Documentation ## Documentation
The [documentation](http://docs.graphene-python.org/projects/django/en/latest/) is generated using the excellent [Sphinx](http://www.sphinx-doc.org/) and a custom theme. The [documentation](http://docs.graphene-python.org/projects/django/en/latest/) is generated using the excellent [Sphinx](http://www.sphinx-doc.org/) and a custom theme.

View File

@ -1,5 +1,11 @@
dev-setup: dev-setup:
pip install -e ".[test]" pip install -e ".[dev]"
tests: tests:
py.test graphene_django --cov=graphene_django -vv py.test graphene_django --cov=graphene_django -vv
format:
black graphene_django
lint:
flake8 graphene_django

View File

@ -96,4 +96,4 @@ To learn more check out the following [examples](examples/):
## Contributing ## Contributing
See [CONTRIBUTING.md](contributing.md) See [CONTRIBUTING.md](CONTRIBUTING.md)

View File

@ -5,7 +5,11 @@ class MissingType(object):
try: try:
# Postgres fields are only available in Django with psycopg2 installed # Postgres fields are only available in Django with psycopg2 installed
# and we cannot have psycopg2 on PyPy # and we cannot have psycopg2 on PyPy
from django.contrib.postgres.fields import (ArrayField, HStoreField, from django.contrib.postgres.fields import (
JSONField, RangeField) ArrayField,
HStoreField,
JSONField,
RangeField,
)
except ImportError: except ImportError:
ArrayField, HStoreField, JSONField, RangeField = (MissingType,) * 4 ArrayField, HStoreField, JSONField, RangeField = (MissingType,) * 4

View File

@ -3,9 +3,7 @@ from graphene import Boolean, Float, ObjectType, String
class DjangoDebugSQL(ObjectType): class DjangoDebugSQL(ObjectType):
class Meta: class Meta:
description = ( description = "Represents a single database query made to a Django managed DB."
"Represents a single database query made to a Django managed DB."
)
vendor = String( vendor = String(
required=True, required=True,
@ -14,37 +12,26 @@ class DjangoDebugSQL(ObjectType):
), ),
) )
alias = String( alias = String(
required=True, required=True, description="The Django database alias (e.g. 'default')."
description="The Django database alias (e.g. 'default').",
) )
sql = String(description="The actual SQL sent to this database.") sql = String(description="The actual SQL sent to this database.")
duration = Float( duration = Float(
required=True, required=True, description="Duration of this database query in seconds."
description="Duration of this database query in seconds.",
) )
raw_sql = String( raw_sql = String(
required=True, required=True, description="The raw SQL of this query, without params."
description="The raw SQL of this query, without params.",
) )
params = String( params = String(
required=True, required=True, description="JSON encoded database query parameters."
description="JSON encoded database query parameters.",
)
start_time = Float(
required=True,
description="Start time of this database query.",
)
stop_time = Float(
required=True,
description="Stop time of this database query.",
) )
start_time = Float(required=True, description="Start time of this database query.")
stop_time = Float(required=True, description="Stop time of this database query.")
is_slow = Boolean( is_slow = Boolean(
required=True, required=True,
description="Whether this database query took more than 10 seconds.", description="Whether this database query took more than 10 seconds.",
) )
is_select = Boolean( is_select = Boolean(
required=True, required=True, description="Whether this database query was a SELECT."
description="Whether this database query was a SELECT.",
) )
# Postgres # Postgres

View File

@ -7,7 +7,4 @@ class DjangoDebug(ObjectType):
class Meta: class Meta:
description = "Debugging information for the current query." description = "Debugging information for the current query."
sql = List( sql = List(DjangoDebugSQL, description="Executed SQL queries for this API query.")
DjangoDebugSQL,
description="Executed SQL queries for this API query.",
)

View File

@ -45,8 +45,7 @@ class GrapheneFilterSetMixin(BaseFilterSet):
FILTER_DEFAULTS = dict( FILTER_DEFAULTS = dict(
itertools.chain( itertools.chain(
FILTER_FOR_DBFIELD_DEFAULTS.items(), FILTER_FOR_DBFIELD_DEFAULTS.items(), GRAPHENE_FILTER_SET_OVERRIDES.items()
GRAPHENE_FILTER_SET_OVERRIDES.items()
) )
) )
@ -59,7 +58,6 @@ if VERSION[0] < 2:
from django.utils.text import capfirst from django.utils.text import capfirst
class GrapheneFilterSetMixinPython2(GrapheneFilterSetMixin): class GrapheneFilterSetMixinPython2(GrapheneFilterSetMixin):
@classmethod @classmethod
def filter_for_reverse_field(cls, f, name): def filter_for_reverse_field(cls, f, name):
"""Handles retrieving filters for reverse relationships """Handles retrieving filters for reverse relationships

View File

@ -13,7 +13,7 @@ class MyForm(forms.Form):
class PetForm(forms.ModelForm): class PetForm(forms.ModelForm):
class Meta: class Meta:
model = Pet model = Pet
fields = '__all__' fields = "__all__"
def test_needs_form_class(): def test_needs_form_class():
@ -66,7 +66,7 @@ class ModelFormMutationTests(TestCase):
class PetMutation(DjangoModelFormMutation): class PetMutation(DjangoModelFormMutation):
class Meta: class Meta:
form_class = PetForm form_class = PetForm
exclude_fields = ['id'] exclude_fields = ["id"]
self.assertEqual(PetMutation._meta.model, Pet) self.assertEqual(PetMutation._meta.model, Pet)
self.assertEqual(PetMutation._meta.return_field_name, "pet") self.assertEqual(PetMutation._meta.return_field_name, "pet")
@ -102,7 +102,9 @@ class ModelFormMutationTests(TestCase):
pet = Pet.objects.create(name="Axel", age=10) pet = Pet.objects.create(name="Axel", age=10)
result = PetMutation.mutate_and_get_payload(None, None, id=pet.pk, name="Mia", age=10) result = PetMutation.mutate_and_get_payload(
None, None, id=pet.pk, name="Mia", age=10
)
self.assertEqual(Pet.objects.count(), 1) self.assertEqual(Pet.objects.count(), 1)
pet.refresh_from_db() pet.refresh_from_db()
@ -132,7 +134,6 @@ class ModelFormMutationTests(TestCase):
# A pet was not created # A pet was not created
self.assertEqual(Pet.objects.count(), 0) self.assertEqual(Pet.objects.count(), 0)
fields_w_error = [e.field for e in result.errors] fields_w_error = [e.field for e in result.errors]
self.assertEqual(len(result.errors), 2) self.assertEqual(len(result.errors), 2)
self.assertIn("name", fields_w_error) self.assertIn("name", fields_w_error)

View File

@ -64,7 +64,7 @@ class Command(CommandArguments):
indent = options.get("indent") indent = options.get("indent")
schema_dict = {"data": schema.introspect()} schema_dict = {"data": schema.introspect()}
if out == '-': if out == "-":
self.stdout.write(json.dumps(schema_dict, indent=indent, sort_keys=True)) self.stdout.write(json.dumps(schema_dict, indent=indent, sort_keys=True))
else: else:
self.save_file(out, schema_dict, indent) self.save_file(out, schema_dict, indent)

View File

@ -11,7 +11,7 @@ class GraphQLTestCase(TestCase):
""" """
# URL to graphql endpoint # URL to graphql endpoint
GRAPHQL_URL = '/graphql/' GRAPHQL_URL = "/graphql/"
# Here you need to set your graphql schema for the tests # Here you need to set your graphql schema for the tests
GRAPHQL_SCHEMA = None GRAPHQL_SCHEMA = None
@ -20,7 +20,9 @@ class GraphQLTestCase(TestCase):
super(GraphQLTestCase, cls).setUpClass() super(GraphQLTestCase, cls).setUpClass()
if not cls.GRAPHQL_SCHEMA: if not cls.GRAPHQL_SCHEMA:
raise AttributeError('Variable GRAPHQL_SCHEMA not defined in GraphQLTestCase.') raise AttributeError(
"Variable GRAPHQL_SCHEMA not defined in GraphQLTestCase."
)
cls._client = Client(cls.GRAPHQL_SCHEMA) cls._client = Client(cls.GRAPHQL_SCHEMA)
@ -37,14 +39,15 @@ class GraphQLTestCase(TestCase):
Returns: Returns:
Response object from client Response object from client
""" """
body = {'query': query} body = {"query": query}
if op_name: if op_name:
body['operation_name'] = op_name body["operation_name"] = op_name
if input_data: if input_data:
body['variables'] = {'input': input_data} body["variables"] = {"input": input_data}
resp = self._client.post(self.GRAPHQL_URL, json.dumps(body), resp = self._client.post(
content_type='application/json') self.GRAPHQL_URL, json.dumps(body), content_type="application/json"
)
return resp return resp
def assertResponseNoErrors(self, resp): def assertResponseNoErrors(self, resp):
@ -55,7 +58,7 @@ class GraphQLTestCase(TestCase):
""" """
content = json.loads(resp.content) content = json.loads(resp.content)
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
self.assertNotIn('errors', list(content.keys())) self.assertNotIn("errors", list(content.keys()))
def assertResponseHasErrors(self, resp): def assertResponseHasErrors(self, resp):
""" """
@ -63,4 +66,4 @@ class GraphQLTestCase(TestCase):
:resp HttpResponse: Response :resp HttpResponse: Response
""" """
content = json.loads(resp.content) content = json.loads(resp.content)
self.assertIn('errors', list(content.keys())) self.assertIn("errors", list(content.keys()))

View File

@ -10,14 +10,18 @@ def test_generate_file_on_call_graphql_schema(savefile_mock, settings):
assert "Successfully dumped GraphQL schema to schema.json" in out.getvalue() assert "Successfully dumped GraphQL schema to schema.json" in out.getvalue()
@patch('json.dump') @patch("json.dump")
def test_files_are_canonical(dump_mock): def test_files_are_canonical(dump_mock):
open_mock = mock_open() open_mock = mock_open()
with patch('graphene_django.management.commands.graphql_schema.open', open_mock): with patch("graphene_django.management.commands.graphql_schema.open", open_mock):
management.call_command('graphql_schema', schema='') management.call_command("graphql_schema", schema="")
open_mock.assert_called_once() open_mock.assert_called_once()
dump_mock.assert_called_once() dump_mock.assert_called_once()
assert dump_mock.call_args[1]["sort_keys"], "json.mock() should be used to sort the output" assert dump_mock.call_args[1][
assert dump_mock.call_args[1]["indent"] > 0, "output should be pretty-printed by default" "sort_keys"
], "json.mock() should be used to sort the output"
assert (
dump_mock.call_args[1]["indent"] > 0
), "output should be pretty-printed by default"

View File

@ -241,8 +241,7 @@ def test_should_manytoone_convert_connectionorlist():
class Meta: class Meta:
model = Article model = Article
graphene_field = convert_django_field(Reporter.articles.rel, graphene_field = convert_django_field(Reporter.articles.rel, A._meta.registry)
A._meta.registry)
assert isinstance(graphene_field, graphene.Dynamic) assert isinstance(graphene_field, graphene.Dynamic)
dynamic_field = graphene_field.get_type() dynamic_field = graphene_field.get_type()
assert isinstance(dynamic_field, graphene.Field) assert isinstance(dynamic_field, graphene.Field)
@ -255,8 +254,7 @@ def test_should_onetoone_reverse_convert_model():
class Meta: class Meta:
model = FilmDetails model = FilmDetails
graphene_field = convert_django_field(Film.details.related, graphene_field = convert_django_field(Film.details.related, A._meta.registry)
A._meta.registry)
assert isinstance(graphene_field, graphene.Dynamic) assert isinstance(graphene_field, graphene.Dynamic)
dynamic_field = graphene_field.get_type() dynamic_field = graphene_field.get_type()
assert isinstance(dynamic_field, graphene.Field) assert isinstance(dynamic_field, graphene.Field)

View File

@ -126,8 +126,7 @@ class GraphQLView(View):
if show_graphiql: if show_graphiql:
return self.render_graphiql( return self.render_graphiql(
request, request, graphiql_version=self.graphiql_version
graphiql_version=self.graphiql_version,
) )
if self.batch: if self.batch:

View File

@ -24,6 +24,12 @@ tests_require = [
"pytest-django>=3.3.2", "pytest-django>=3.3.2",
] + rest_framework_require ] + rest_framework_require
dev_requires = [
"black==19.3b0",
"flake8==3.7.7",
] + tests_require
setup( setup(
name="graphene-django", name="graphene-django",
version=version, version=version,
@ -58,7 +64,7 @@ setup(
setup_requires=["pytest-runner"], setup_requires=["pytest-runner"],
tests_require=tests_require, tests_require=tests_require,
rest_framework_require=rest_framework_require, rest_framework_require=rest_framework_require,
extras_require={"test": tests_require, "rest_framework": rest_framework_require}, extras_require={"test": tests_require, "rest_framework": rest_framework_require, "dev": dev_requires},
include_package_data=True, include_package_data=True,
zip_safe=False, zip_safe=False,
platforms="any", platforms="any",