diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..321e5ca --- /dev/null +++ b/.coveragerc @@ -0,0 +1,2 @@ +[run] +omit = */tests/* diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..568b397 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{py,rst,ini}] +indent_style = space +indent_size = 4 diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..d4ed37b --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,2 @@ +[settings] +known_third_party = aniso8601,graphql,graphql_relay,promise,pytest,pytz,pyutils,setuptools,six,snapshottest,sphinx_graphene_theme diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..739c634 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,29 @@ +repos: +- repo: git://github.com/pre-commit/pre-commit-hooks + rev: v2.1.0 + hooks: + - id: check-merge-conflict + - id: check-json + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + exclude: ^docs/.*$ + - id: pretty-format-json + args: + - --autofix + - id: trailing-whitespace + exclude: README.md +- repo: https://github.com/asottile/pyupgrade + rev: v1.12.0 + hooks: + - id: pyupgrade +- repo: https://github.com/ambv/black + rev: 18.9b0 + hooks: + - id: black + language_version: python3 +- repo: https://github.com/PyCQA/flake8 + rev: 3.7.7 + hooks: + - id: flake8 + exclude: tests/schema.py diff --git a/.travis.yml b/.travis.yml index 2d3731b..7139aad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,27 +1,32 @@ language: python -sudo: false matrix: include: - - python: pypy - env: TOX_ENV=pypy - - python: '2.7' - env: TOX_ENV=py27 - - python: '3.4' - env: TOX_ENV=py34 - - python: '3.5' - env: TOX_ENV=py35 - - python: '3.6' - env: TOX_ENV=py36,import-order,flake8,mypy + - env: TOXENV=py27 + python: 2.7 + - env: TOXENV=py34 + python: 3.4 + - env: TOXENV=py35 + python: 3.5 + - env: TOXENV=py36 + python: 3.6 + - env: TOXENV=py37 + python: 3.7 + dist: xenial + sudo: true + - env: TOXENV=pypy + python: pypy-5.7.1 + - env: TOXENV=pre-commit + python: 3.6 + - env: TOXENV=mypy + python: 3.6 +install: + - pip install coveralls tox +script: tox +after_success: coveralls cache: directories: - - $HOME/.cache/pip - - $TRAVIS_BUILD_DIR/.tox -install: -- pip install tox coveralls -script: -- tox -e $TOX_ENV -- --cov=graphql_server -after_success: -- coveralls + - $HOME/.cache/pip + - $HOME/.cache/pre-commit deploy: provider: pypi user: syrusakbary diff --git a/LICENSE b/LICENSE index be33212..fb89122 100644 --- a/LICENSE +++ b/LICENSE @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..028bb3d --- /dev/null +++ b/mypy.ini @@ -0,0 +1,5 @@ +[mypy] +ignore_missing_imports = True + +[mypy-tests.*] +ignore_errors = True diff --git a/setup.cfg b/setup.cfg index 72574d8..1f8e8f3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,8 +5,8 @@ max-line-length = 160 [isort] known_first_party=graphql_server -[pytest] -norecursedirs = venv .tox .cache +[coverage:run] +omit = */tests/* [bdist_wheel] universal=1 diff --git a/setup.py b/setup.py index 537f58e..056bcbb 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,19 @@ from setuptools import setup, find_packages required_packages = ["graphql-core>=2.1", "promise"] +tests_require = [ + "pytest", + "pytest-benchmark", + "pytest-cov", + "pytest-mock", + "snapshottest", + "coveralls", + "promise", + "six", + "mock", + "pytz", + "iso8601", +] setup( name="graphql-server-core", @@ -22,13 +35,14 @@ "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: PyPy", "License :: OSI Approved :: MIT License", ], - keywords="api graphql protocol rest", + keywords="api graphql protocol rest python graphene", packages=find_packages(exclude=["tests"]), install_requires=required_packages, - tests_require=["pytest>=2.7.3"], + tests_require=tests_require, include_package_data=True, zip_safe=False, platforms="any", diff --git a/tests/schema.py b/tests/schema.py index 7ea803f..d9b4577 100644 --- a/tests/schema.py +++ b/tests/schema.py @@ -1,4 +1,9 @@ -from graphql.type.definition import GraphQLArgument, GraphQLField, GraphQLNonNull, GraphQLObjectType +from graphql.type.definition import ( + GraphQLArgument, + GraphQLField, + GraphQLNonNull, + GraphQLObjectType, +) from graphql.type.scalars import GraphQLString from graphql.type.schema import GraphQLSchema @@ -8,31 +13,29 @@ def resolve_raises(*_): QueryRootType = GraphQLObjectType( - name='QueryRoot', + name="QueryRoot", fields={ - 'thrower': GraphQLField(GraphQLNonNull(GraphQLString), resolver=resolve_raises), - 'request': GraphQLField(GraphQLNonNull(GraphQLString), - resolver=lambda obj, info: context.args.get('q')), - 'context': GraphQLField(GraphQLNonNull(GraphQLString), - resolver=lambda obj, info: context), - 'test': GraphQLField( + "thrower": GraphQLField(GraphQLNonNull(GraphQLString), resolver=resolve_raises), + "request": GraphQLField( + GraphQLNonNull(GraphQLString), + resolver=lambda obj, info: context.args.get("q"), + ), # noqa: F821 + "context": GraphQLField( + GraphQLNonNull(GraphQLString), resolver=lambda obj, info: context + ), # noqa + "test": GraphQLField( type=GraphQLString, - args={ - 'who': GraphQLArgument(GraphQLString) - }, - resolver=lambda obj, info, who='World': 'Hello %s' % who - ) - } + args={"who": GraphQLArgument(GraphQLString)}, + resolver=lambda obj, info, who="World": "Hello %s" % who, + ), + }, ) MutationRootType = GraphQLObjectType( - name='MutationRoot', + name="MutationRoot", fields={ - 'writeTest': GraphQLField( - type=QueryRootType, - resolver=lambda *_: QueryRootType - ) - } + "writeTest": GraphQLField(type=QueryRootType, resolver=lambda *_: QueryRootType) + }, ) schema = GraphQLSchema(QueryRootType, MutationRootType) diff --git a/tests/test_base.py b/tests/test_base.py index 52ad6e5..feb1b21 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -1,8 +1,11 @@ from pytest import raises import json - -from graphql.execution import ExecutionResult -from graphql_server import run_http_query, GraphQLParams, HttpQueryError, default_format_error +from graphql_server import ( + run_http_query, + GraphQLParams, + HttpQueryError, + default_format_error, +) from .schema import schema @@ -21,82 +24,83 @@ def executions_to_dict(execution_results): def test_allows_get_with_query_param(): - query = '{test}' - results, params = run_http_query(schema, 'get', {}, query_data=dict(query=query)) + query = "{test}" + results, params = run_http_query(schema, "get", {}, query_data=dict(query=query)) - assert executions_to_dict(results) == [{ - 'data': {'test': "Hello World"}, - }] - assert params == [GraphQLParams(query=query,variables=None,operation_name=None)] + assert executions_to_dict(results) == [{"data": {"test": "Hello World"}}] + assert params == [GraphQLParams(query=query, variables=None, operation_name=None)] def test_allows_get_with_variable_values(): - query = '{test}' - results, params = run_http_query(schema, 'get', {}, query_data=dict( - query='query helloWho($who: String){ test(who: $who) }', - variables=json.dumps({'who': "Dolly"}) - )) + results, params = run_http_query( + schema, + "get", + {}, + query_data=dict( + query="query helloWho($who: String){ test(who: $who) }", + variables=json.dumps({"who": "Dolly"}), + ), + ) - assert executions_to_dict(results) == [{ - 'data': {'test': "Hello Dolly"}, - }] + assert executions_to_dict(results) == [{"data": {"test": "Hello Dolly"}}] def test_allows_get_with_operation_name(): - results, params = run_http_query(schema, 'get', {}, query_data=dict( - query=''' + results, params = run_http_query( + schema, + "get", + {}, + query_data=dict( + query=""" query helloYou { test(who: "You"), ...shared } query helloWorld { test(who: "World"), ...shared } query helloDolly { test(who: "Dolly"), ...shared } fragment shared on QueryRoot { shared: test(who: "Everyone") } - ''', - operationName='helloWorld' - )) + """, + operationName="helloWorld", + ), + ) - assert executions_to_dict(results) == [{ - 'data': { - 'test': 'Hello World', - 'shared': 'Hello Everyone' - }, - }] + assert executions_to_dict(results) == [ + {"data": {"test": "Hello World", "shared": "Hello Everyone"}} + ] def test_reports_validation_errors(): - results, params = run_http_query(schema, 'get', {}, query_data=dict( - query='{ test, unknownOne, unknownTwo }' - )) - - assert executions_to_dict(results) == [{ - 'errors': [ - { - 'message': 'Cannot query field "unknownOne" on type "QueryRoot".', - 'locations': [{'line': 1, 'column': 9}] - }, - { - 'message': 'Cannot query field "unknownTwo" on type "QueryRoot".', - 'locations': [{'line': 1, 'column': 21}] - } - ] - }] + results, params = run_http_query( + schema, "get", {}, query_data=dict(query="{ test, unknownOne, unknownTwo }") + ) + + err_1 = 'Cannot query field "unknownOne" on type "QueryRoot".' + err_2 = 'Cannot query field "unknownTwo" on type "QueryRoot".' + + assert executions_to_dict(results) == [ + { + "errors": [ + {"message": err_1, "locations": [{"line": 1, "column": 9}]}, + {"message": err_2, "locations": [{"line": 1, "column": 21}]}, + ] + } + ] def test_errors_when_missing_operation_name(): - results, params = run_http_query(schema, 'get', {}, query_data=dict( - query=''' + results, params = run_http_query( + schema, + "get", + {}, + query_data=dict( + query=""" query TestQuery { test } mutation TestMutation { writeTest { test } } - ''' - )) + """ + ), + ) + msg = "Must provide operation name if query contains multiple operations." - assert executions_to_dict(results) == [{ - 'errors': [ - { - 'message': 'Must provide operation name if query contains multiple operations.' - } - ] - }] + assert executions_to_dict(results) == [{"errors": [{"message": msg}]}] # def test_errors_when_sending_a_mutation_via_get(): @@ -109,7 +113,8 @@ def test_errors_when_missing_operation_name(): # assert executions_to_dict(results) == [{ # 'errors': [ # { -# 'message': 'Can only perform a mutation operation from a POST request.' +# 'message': 'Can only perform a mutation +# operation from a POST request.' # } # ] # }] @@ -402,16 +407,15 @@ def test_errors_when_missing_operation_name(): def test_handles_unsupported_http_methods(): with raises(HttpQueryError) as exc_info: - run_http_query(schema, 'put', None) + run_http_query(schema, "put", None) assert exc_info.value == HttpQueryError( 405, - 'GraphQL only supports GET and POST requests.', - headers={ - 'Allow': 'GET, POST' - } + "GraphQL only supports GET and POST requests.", + headers={"Allow": "GET, POST"}, ) + # def test_passes_request_into_request_context(client): # response = client.get(url_string(query='{request}', q='testing')) @@ -486,8 +490,8 @@ def test_handles_unsupported_http_methods(): # # 'id': 1, # 'data': {'test': "Hello Dolly"} # }] - - + + # @pytest.mark.parametrize('app', [create_app(batch=True)]) # def test_batch_allows_post_with_operation_name(client): # response = client.post( diff --git a/tox.ini b/tox.ini index 5c220bc..cb95189 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = flake8,import-order,py35,py27,py33,py34,pypy +envlist = flake8,py27,py34,py35,py36,py37,pre-commit,pypy,mypy skipsdist = true [testenv] @@ -10,12 +10,26 @@ deps = graphql-core>=2.1 pytest-cov commands = - py{py,27,34,35,36}: py.test tests {posargs} + py{py,27,34,35,36}: py.test --cov=graphql_server tests {posargs} + +[testenv:pre-commit] +basepython=python3.6 +deps = + pre-commit>0.12.0 +setenv = + LC_CTYPE=en_US.UTF-8 + LC_ALL=C.UTF-8 + LANG=C.UTF-8 + + +commands = + pre-commit {posargs:run --all-files} [testenv:flake8] basepython=python3.6 deps = flake8 commands = + pip install -e . flake8 graphql_server [testenv:mypy] @@ -24,10 +38,4 @@ deps = mypy commands = mypy graphql_server --ignore-missing-imports -[testenv:import-order] -basepython=python3.6 -deps = - isort - graphql-core>=2.1 -commands = - isort --check-only graphql_server/ -rc +[pytest]