diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..1e98e5c --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,2 @@ +[settings] +known_third_party = graphql, setuptools diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..227da33 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,28 @@ +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: 19.3b0 + hooks: + - id: black + language_version: python3 +- repo: https://github.com/PyCQA/flake8 + rev: 3.7.7 + hooks: + - id: flake8 diff --git a/.travis.yml b/.travis.yml index 2569cf7..ece381a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,21 +2,11 @@ language: python matrix: include: - python: '3.7' - env: TOX_ENV=black,flake8,mypy,py37 + env: TOX_ENV=flake8,mypy,py37,pre-commit dist: xenial sudo: true # required workaround for https://github.com/travis-ci/travis-ci/issues/9815 - python: '3.6' env: TOX_ENV=py36 - - python: '3.5' - env: TOX_ENV=py35 - - python: '3.4' - env: TOX_ENV=py34 - - python: '2.7' - env: TOX_ENV=py27 - - python: 'pypy3.5' - env: TOX_ENV=pypy3 - - python: 'pypy' - env: TOX_ENV=pypy cache: directories: - $HOME/.cache/pip @@ -24,7 +14,7 @@ cache: install: - pip install tox codecov script: -- tox -e $TOX_ENV -- --cov-report term-missing --cov=graphql_server +- tox -e $TOX_ENV -- --cov-report term-missing after_success: - codecov deploy: 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/codecov.yml b/codecov.yml index c393a12..c155caa 100644 --- a/codecov.yml +++ b/codecov.yml @@ -7,4 +7,4 @@ coverage: status: project: default: - target: auto \ No newline at end of file + target: auto diff --git a/graphql_server/__init__.py b/graphql_server/__init__.py index e7457f7..420e728 100644 --- a/graphql_server/__init__.py +++ b/graphql_server/__init__.py @@ -10,27 +10,16 @@ import json from collections import namedtuple - -import six -from graphql import get_default_backend +#from graphql import get_default_backend from graphql.error import format_error as default_format_error from graphql.execution import ExecutionResult -from graphql.type import GraphQLSchema +from graphql import parse, graphql as graphql_, get_operation_root_type, DocumentNode, OperationDefinitionNode, GraphQLSchema from .error import HttpQueryError -try: # pragma: no cover (Python >= 3.3) - from collections.abc import MutableMapping -except ImportError: # pragma: no cover (Python < 3.3) - # noinspection PyUnresolvedReferences,PyProtectedMember - from collections import MutableMapping +from collections import MutableMapping -# Necessary for static type checking -# noinspection PyUnreachableCode -if False: # pragma: no cover - # flake8: noqa - from typing import Any, Callable, Dict, List, Optional, Type, Union - from graphql import GraphQLBackend +from typing import Any, Callable, Dict, List, Optional, Type, Union __all__ = [ @@ -57,15 +46,19 @@ # The public helper functions +def get_operation_node(doc: DocumentNode) -> OperationDefinitionNode: + operation_node = doc.definitions[0] + assert isinstance(operation_node, OperationDefinitionNode) + return operation_node def run_http_query( - schema, # type: GraphQLSchema - request_method, # type: str - data, # type: Union[Dict, List[Dict]] - query_data=None, # type: Optional[Dict] - batch_enabled=False, # type: bool - catch=False, # type: bool - **execute_options # type: Any + schema: GraphQLSchema, + request_method: str, + data: Union[Dict, List[Dict]], + query_data: Optional[Dict] = None, + batch_enabled: bool = False, + catch: bool = False, + **execute_options: Any ): """Execute GraphQL coming from an HTTP query against a given schema. @@ -83,7 +76,7 @@ def run_http_query( and the list of parameters that have been used for execution as second item. """ if not isinstance(schema, GraphQLSchema): - raise TypeError("Expected a GraphQL schema, but received {!r}.".format(schema)) + raise TypeError(f"Expected a GraphQL schema, but received {repr(schema)}.") if request_method not in ("get", "post"): raise HttpQueryError( 405, @@ -104,7 +97,7 @@ def run_http_query( if not is_batch: if not isinstance(data, (dict, MutableMapping)): raise HttpQueryError( - 400, "GraphQL params should be a dict. Received {!r}.".format(data) + 400, f"GraphQL params should be a dict. Received {repr(data)}." ) data = [data] elif not batch_enabled: @@ -223,7 +216,7 @@ def load_json_variables(variables): deserialized from JSON to a dictionary first if necessary. In case of invalid JSON input, an HttpQueryError will be raised. """ - if variables and isinstance(variables, six.string_types): + if variables and isinstance(variables, str): try: return json.loads(variables) except Exception: @@ -235,7 +228,7 @@ def execute_graphql_request( schema, # type: GraphQLSchema params, # type: RequestParams allow_only_query=False, # type: bool - backend=None, # type: GraphQLBackend + #backend=None, # type: GraphQLBackend **kwargs # type: Any ): """Execute a GraphQL request and return an ExecutionResult. @@ -250,26 +243,21 @@ def execute_graphql_request( if not params.query: raise HttpQueryError(400, "Must provide query string.") - try: - if not backend: - backend = get_default_backend() - document = backend.document_from_string(schema, params.query) - except Exception as e: - return ExecutionResult(errors=[e], invalid=True) - if allow_only_query: - operation_type = document.get_operation_type(params.operation_name) - if operation_type and operation_type != "query": + document = parse(params.query) + + op_node = get_operation_node(document) + query_type = schema.query_type + operation_type = get_operation_root_type(schema, op_node) + if operation_type and operation_type != query_type: raise HttpQueryError( 405, - "Can only perform a {} operation from a POST request.".format( - operation_type - ), + f"Can only perform a {operation_type} operation from a POST request.", headers={"Allow": "POST"}, ) try: - return document.execute( + return graphql_( operation_name=params.operation_name, variables=params.variables, **kwargs ) except Exception as e: diff --git a/setup.cfg b/setup.cfg index 8de4f2d..9efd355 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [flake8] exclude = tests,scripts,setup.py,docs -max-line-length = 160 +max-line-length = 120 [isort] known_first_party=graphql_server diff --git a/setup.py b/setup.py index 64892dc..eff37aa 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -required_packages = ["graphql-core>=2.1,<3", "promise"] +required_packages = ["graphql-core-next~=1.0.5", "promise"] setup( name="graphql-server-core", @@ -16,12 +16,6 @@ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: PyPy", diff --git a/tox.ini b/tox.ini index 6560fc2..5e12193 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = black,flake8,mypy,py37,py36,py35,py34,py33,py27,pypy3,pypy +envlist = flake8,mypy,py37,py36,pre-commit skipsdist = true [testenv] @@ -7,16 +7,20 @@ setenv = PYTHONPATH = {toxinidir} deps = pytest>=3.0,<4 - graphql-core>=2.1,<3 + graphql-core-next~=1.0.5 pytest-cov>=2.7 commands = - py{py,27,33,34,35,36,37}: py.test tests {posargs} + py{36,37}: py.test tests {posargs} -[testenv:black] +[testenv:pre-commit] basepython=python3.7 -deps = black +deps = + pre-commit>0.12.0 +setenv = + LC_CTYPE=en_US.UTF-8 commands = - black --check graphql_server tests + pre-commit run --all-files + [testenv:flake8] basepython=python3.7 @@ -24,17 +28,8 @@ deps = flake8 commands = flake8 graphql_server tests -[testenv:isort] -basepython=python3.7 -deps = - isort - graphql-core>=2.1 -commands = - isort -rc graphql_server/ tests/ - [testenv:mypy] basepython=python3.7 deps = mypy commands = mypy graphql_server tests --ignore-missing-imports -