From 17363af66827f37f308f0b8ddff022698608a2ab Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Fri, 13 Oct 2023 17:04:55 +0800 Subject: [PATCH 1/5] chore: update to latest versions of framework (#120) * chore: update to flask 3 * chore: update to sanic 23 * fix: remove use of deprecated cgi module * chore: remove Python 3.7 from CI EOL since 2023-06-27 https://devguide.python.org/versions/ * chore: remove unused context * chore: update versions of dev tools * chore: make 3.11 the default python version in CI --- .github/workflows/deploy.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/tests.yml | 8 ++------ graphql_server/sanic/graphqlview.py | 4 ++-- setup.py | 15 +++++++-------- tests/flask/test_graphqlview.py | 19 ++++++++++++++++--- tox.ini | 17 ++++++++--------- 7 files changed, 37 insertions(+), 30 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 271642c..454da04 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -14,7 +14,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: "3.11" - name: Build wheel and source tarball run: | pip install wheel diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 454ab1b..0f7453c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,7 +11,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: "3.11" - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7e58bb5..ceecb95 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,11 +8,9 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11"] os: [ubuntu-latest, windows-latest] exclude: - - os: windows-latest - python-version: "3.7" - os: windows-latest python-version: "3.8" - os: windows-latest @@ -32,8 +30,6 @@ jobs: pip install tox tox-gh-actions - name: Test with tox run: tox - env: - TOXENV: ${{ matrix.toxenv }} coverage: runs-on: ubuntu-latest @@ -43,7 +39,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: "3.11" - name: Install test dependencies run: | python -m pip install --upgrade pip diff --git a/graphql_server/sanic/graphqlview.py b/graphql_server/sanic/graphqlview.py index e8af82d..50e9922 100644 --- a/graphql_server/sanic/graphqlview.py +++ b/graphql_server/sanic/graphqlview.py @@ -1,6 +1,5 @@ import asyncio import copy -from cgi import parse_header from collections.abc import MutableMapping from functools import partial from typing import List @@ -8,6 +7,7 @@ from graphql import GraphQLError, specified_rules from graphql.pyutils import is_awaitable from graphql.type.schema import GraphQLSchema +from sanic.headers import parse_content_header from sanic.response import HTTPResponse, html from sanic.views import HTTPMethodView @@ -213,7 +213,7 @@ def get_mime_type(request): if "content-type" not in request.headers: return None - mime_type, _ = parse_header(request.headers["content-type"]) + mime_type, _ = parse_content_header(request.headers["content-type"]) return mime_type def should_display_graphiql(self, request): diff --git a/setup.py b/setup.py index bf3f24f..ee568ab 100644 --- a/setup.py +++ b/setup.py @@ -12,23 +12,23 @@ "pytest-asyncio>=0.20,<1", "pytest-cov>=4,<5", "Jinja2>=3.1,<4", - "sanic-testing>=22.3,<23", + "sanic-testing>=22.3,<24", ] dev_requires = [ - "flake8>=5,<6", + "flake8>=6,<7", "isort>=5,<6", - "black>=22.12,<22.13", - "mypy>=0.991,<1", + "black>=23.9,<23.10", + "mypy>=1.6,<1.7", "check-manifest>=0.47,<1", ] + tests_requires install_flask_requires = [ - "flask>=1,<3", + "flask>=1,<4", ] install_sanic_requires = [ - "sanic>=21.12,<23", + "sanic>=21.12,<24", ] install_webob_requires = [ @@ -71,11 +71,10 @@ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "License :: OSI Approved :: MIT License", ], keywords="api graphql protocol rest", diff --git a/tests/flask/test_graphqlview.py b/tests/flask/test_graphqlview.py index a222381..34ddcb9 100644 --- a/tests/flask/test_graphqlview.py +++ b/tests/flask/test_graphqlview.py @@ -1,5 +1,4 @@ import json -from io import StringIO from urllib.parse import urlencode import pytest @@ -518,10 +517,24 @@ def test_allow_empty_custom_context(app, client): def test_post_multipart_data(app, client): query = "mutation TestMutation { writeTest { test } }" + + data = ( + "------flaskgraphql\r\n" + 'Content-Disposition: form-data; name="query"\r\n' + "\r\n" + query + "\r\n" + "------flaskgraphql--\r\n" + "Content-Type: text/plain; charset=utf-8\r\n" + 'Content-Disposition: form-data; name="file"; filename="text1.txt";' + " filename*=utf-8''text1.txt\r\n" + "\r\n" + "\r\n" + "------flaskgraphql--\r\n" + ) + response = client.post( url_string(app), - data={"query": query, "file": (StringIO(), "text1.txt")}, - content_type="multipart/form-data", + data=data, + content_type="multipart/form-data; boundary=----flaskgraphql", ) assert response.status_code == 200 diff --git a/tox.ini b/tox.ini index 1c534fd..6627604 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,11 @@ [tox] envlist = black,flake8,import-order,mypy,manifest, - py{37,38,39,310,311} + py{38,39,310,311} ; requires = tox-conda [gh-actions] python = - 3.7: py37 3.8: py38 3.9: py39 3.10: py310 @@ -23,35 +22,35 @@ whitelist_externals = python commands = pip install -U setuptools - py{37,38,39,311}: pytest tests {posargs} - py{310}: pytest tests --cov-report=term-missing --cov=graphql_server {posargs} + py{38,39,310}: pytest tests {posargs} + py{311}: pytest tests --cov-report=term-missing --cov=graphql_server {posargs} [testenv:black] -basepython = python3.10 +basepython = python3.11 deps = -e.[dev] commands = black --check graphql_server tests [testenv:flake8] -basepython = python3.10 +basepython = python3.11 deps = -e.[dev] commands = flake8 setup.py graphql_server tests [testenv:import-order] -basepython = python3.10 +basepython = python3.11 deps = -e.[dev] commands = isort graphql_server/ tests/ [testenv:mypy] -basepython = python3.10 +basepython = python3.11 deps = -e.[dev] commands = mypy graphql_server tests --ignore-missing-imports [testenv:manifest] -basepython = python3.10 +basepython = python3.11 deps = -e.[dev] commands = check-manifest -v From 8af13c026a6a5d3c235be4e6a5cdda5fc32ab67a Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Mon, 16 Oct 2023 17:12:04 +0800 Subject: [PATCH 2/5] release: v3.0.0b7 (#122) --- graphql_server/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphql_server/version.py b/graphql_server/version.py index a769173..0be0bd8 100644 --- a/graphql_server/version.py +++ b/graphql_server/version.py @@ -4,7 +4,7 @@ __all__ = ["version", "version_info"] -version = "3.0.0b6" +version = "3.0.0b7" _re_version = re.compile(r"(\d+)\.(\d+)\.(\d+)(\D*)(\d*)") From 91802788b04b759508eace354b2847bbcf1bc4d5 Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Mon, 16 Oct 2023 19:57:20 +0800 Subject: [PATCH 3/5] chore: use pre-commit and ruff (#116) * chore: use pre-commit with ruff * chore: fix warning: Invalid # noqa directive * fix: B023 Function definition does not bind loop variable `ex`. * chore: add check-manifest pre-commit * chore: remove flake8 and isort config from setup.cfg * chore: remove black, flake8, ... from dependencies and tox * chore: add flake8-comprehensions * chore: remove pointless noqa * chore: keep type annotation syntax compatible with python 3.8 --- .github/workflows/lint.yml | 2 +- .pre-commit-config.yaml | 30 ++++++ .ruff.toml | 35 ++++++ CONTRIBUTING.md | 12 +-- MANIFEST.in | 2 + codecov.yml | 2 +- graphql_server/aiohttp/graphqlview.py | 14 +-- graphql_server/error.py | 2 +- graphql_server/flask/graphqlview.py | 12 +-- graphql_server/quart/graphqlview.py | 14 +-- graphql_server/sanic/graphqlview.py | 14 +-- graphql_server/webob/graphqlview.py | 12 +-- setup.cfg | 13 --- setup.py | 4 - tests/aiohttp/test_graphqlview.py | 76 +++++++------- tests/flask/test_graphqlview.py | 16 +-- tests/quart/test_graphqlview.py | 16 +-- tests/sanic/test_graphqlview.py | 18 ++-- tests/test_asyncio.py | 2 +- tests/test_helpers.py | 2 +- tests/test_query.py | 146 +++++++++++++------------- tests/webob/app.py | 2 +- tests/webob/test_graphiqlview.py | 6 +- tests/webob/test_graphqlview.py | 34 +++--- tox.ini | 28 +---- 25 files changed, 270 insertions(+), 244 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 .ruff.toml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0f7453c..58601c5 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -19,4 +19,4 @@ jobs: - name: Run lint and static type checks run: tox env: - TOXENV: flake8,black,import-order,mypy,manifest + TOXENV: pre-commit,mypy diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..0fab074 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,30 @@ +default_language_version: + python: python3.10 +exclude: LICENSE +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.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 +- repo: https://github.com/mgedmin/check-manifest + rev: "0.49" + hooks: + - id: check-manifest +- repo: https://github.com/psf/black + rev: 23.7.0 + hooks: + - id: black +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.0.283 + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix, --show-fixes] diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 0000000..db5e5b6 --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,35 @@ +select = [ + "E", # pycodestyle + "W", # pycodestyle + "F", # pyflake + "I", # isort + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "UP", # pyupgrade +] + +ignore = [ + "E501", # line-too-long + "B904", # check for raise statements in exception handlers that lack a from clause +] + +exclude = [ + "**/docs", +] + +target-version = "py38" + +[per-file-ignores] +# Ignore unused imports (F401) in these files +"__init__.py" = ["F401"] + +[isort] +known-first-party = ["graphql_server"] +force-wrap-aliases = true +combine-as-imports = true + +[pyupgrade] +# this keeps annotation syntaxes like Union[X, Y] instead of X | Y +# to not break Python 3.8 +# https://beta.ruff.rs/docs/settings/#pyupgrade-keep-runtime-typing +keep-runtime-typing = true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 98f59f0..79d237b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,8 +31,7 @@ Activate the virtualenv and install dependencies by running: python pip install -e ".[test]" ``` -If you are using Linux or MacOS, you can make use of Makefile command -`make dev-setup`, which is a shortcut for the above python command. +If you are using Linux or MacOS, you can make use of Makefile command `make dev-setup`, which is a shortcut for the above python command. ### Development on Conda @@ -60,8 +59,7 @@ After developing, the full test suite can be evaluated by running: pytest tests --cov=graphql-server -vv ``` -If you are using Linux or MacOS, you can make use of Makefile command -`make tests`, which is a shortcut for the above python command. +If you are using Linux or MacOS, you can make use of Makefile command `make tests`, which is a shortcut for the above python command. You can also test on several python environments by using tox. @@ -73,8 +71,7 @@ Install tox: pip install tox ``` -Run `tox` on your virtualenv (do not forget to activate it!) -and that's it! +Run `tox` on your virtualenv (do not forget to activate it!) and that's it! ### Running tox on Conda @@ -89,5 +86,4 @@ This install tox underneath so no need to install it before. Then uncomment the `requires = tox-conda` line on `tox.ini` file. -Run `tox` and you will see all the environments being created -and all passing tests. :rocket: +Run `tox` and you will see all the environments being created and all passing tests. :rocket: diff --git a/MANIFEST.in b/MANIFEST.in index a6c003d..67b0e2f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,6 +6,8 @@ include CONTRIBUTING.md include codecov.yml include tox.ini +include .pre-commit-config.yaml +include .ruff.toml recursive-include docs *.md *.svg 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/aiohttp/graphqlview.py b/graphql_server/aiohttp/graphqlview.py index 4087946..ea23037 100644 --- a/graphql_server/aiohttp/graphqlview.py +++ b/graphql_server/aiohttp/graphqlview.py @@ -56,7 +56,7 @@ class GraphQLView: encode = staticmethod(json_encode) def __init__(self, **kwargs): - super(GraphQLView, self).__init__() + super().__init__() for key, value in kwargs.items(): if hasattr(self, key): setattr(self, key, value) @@ -177,7 +177,7 @@ async def __call__(self, request): *( ex if ex is not None and is_awaitable(ex) - else wrap_in_async(lambda: ex)() + else wrap_in_async(lambda x: x)(ex) for ex in execution_results ) ) @@ -188,15 +188,15 @@ async def __call__(self, request): exec_res, is_batch=isinstance(data, list), format_error=self.format_error, - encode=partial(self.encode, pretty=is_pretty), # noqa: ignore + encode=partial(self.encode, pretty=is_pretty), ) if is_graphiql: graphiql_data = GraphiQLData( result=result, - query=getattr(all_params[0], "query"), - variables=getattr(all_params[0], "variables"), - operation_name=getattr(all_params[0], "operation_name"), + query=all_params[0].query, + variables=all_params[0].variables, + operation_name=all_params[0].operation_name, subscription_url=self.subscriptions, headers=self.headers, ) @@ -225,7 +225,7 @@ async def __call__(self, request): except HttpQueryError as err: parsed_error = GraphQLError(err.message) return web.Response( - body=self.encode(dict(errors=[self.format_error(parsed_error)])), + body=self.encode({"errors": [self.format_error(parsed_error)]}), status=err.status_code, headers=err.headers, content_type="application/json", diff --git a/graphql_server/error.py b/graphql_server/error.py index b0ca74a..497f121 100644 --- a/graphql_server/error.py +++ b/graphql_server/error.py @@ -16,7 +16,7 @@ def __init__(self, status_code, message=None, is_graphql_error=False, headers=No self.message = message self.is_graphql_error = is_graphql_error self.headers = headers - super(HttpQueryError, self).__init__(message) + super().__init__(message) def __eq__(self, other): """Check whether this HTTP query error is equal to another one.""" diff --git a/graphql_server/flask/graphqlview.py b/graphql_server/flask/graphqlview.py index 7440f82..d59124d 100644 --- a/graphql_server/flask/graphqlview.py +++ b/graphql_server/flask/graphqlview.py @@ -53,7 +53,7 @@ class GraphQLView(View): encode = staticmethod(json_encode) def __init__(self, **kwargs): - super(GraphQLView, self).__init__() + super().__init__() for key, value in kwargs.items(): if hasattr(self, key): setattr(self, key, value) @@ -120,15 +120,15 @@ def dispatch_request(self): execution_results, is_batch=isinstance(data, list), format_error=self.format_error, - encode=partial(self.encode, pretty=pretty), # noqa + encode=partial(self.encode, pretty=pretty), ) if show_graphiql: graphiql_data = GraphiQLData( result=result, - query=getattr(all_params[0], "query"), - variables=getattr(all_params[0], "variables"), - operation_name=getattr(all_params[0], "operation_name"), + query=all_params[0].query, + variables=all_params[0].variables, + operation_name=all_params[0].operation_name, subscription_url=self.subscriptions, headers=self.headers, ) @@ -153,7 +153,7 @@ def dispatch_request(self): except HttpQueryError as e: parsed_error = GraphQLError(e.message) return Response( - self.encode(dict(errors=[self.format_error(parsed_error)])), + self.encode({"errors": [self.format_error(parsed_error)]}), status=e.status_code, headers=e.headers, content_type="application/json", diff --git a/graphql_server/quart/graphqlview.py b/graphql_server/quart/graphqlview.py index 7dd479f..8885e5e 100644 --- a/graphql_server/quart/graphqlview.py +++ b/graphql_server/quart/graphqlview.py @@ -57,7 +57,7 @@ class GraphQLView(View): encode = staticmethod(json_encode) def __init__(self, **kwargs): - super(GraphQLView, self).__init__() + super().__init__() for key, value in kwargs.items(): if hasattr(self, key): setattr(self, key, value) @@ -125,7 +125,7 @@ async def dispatch_request(self): *( ex if ex is not None and is_awaitable(ex) - else wrap_in_async(lambda: ex)() + else wrap_in_async(lambda x: x)(ex) for ex in execution_results ) ) @@ -136,15 +136,15 @@ async def dispatch_request(self): exec_res, is_batch=isinstance(data, list), format_error=self.format_error, - encode=partial(self.encode, pretty=pretty), # noqa + encode=partial(self.encode, pretty=pretty), ) if show_graphiql: graphiql_data = GraphiQLData( result=result, - query=getattr(all_params[0], "query"), - variables=getattr(all_params[0], "variables"), - operation_name=getattr(all_params[0], "operation_name"), + query=all_params[0].query, + variables=all_params[0].variables, + operation_name=all_params[0].operation_name, subscription_url=self.subscriptions, headers=self.headers, ) @@ -169,7 +169,7 @@ async def dispatch_request(self): except HttpQueryError as e: parsed_error = GraphQLError(e.message) return Response( - self.encode(dict(errors=[self.format_error(parsed_error)])), + self.encode({"errors": [self.format_error(parsed_error)]}), status=e.status_code, headers=e.headers, content_type="application/json", diff --git a/graphql_server/sanic/graphqlview.py b/graphql_server/sanic/graphqlview.py index 50e9922..37e0e0c 100644 --- a/graphql_server/sanic/graphqlview.py +++ b/graphql_server/sanic/graphqlview.py @@ -58,7 +58,7 @@ class GraphQLView(HTTPMethodView): encode = staticmethod(json_encode) def __init__(self, **kwargs): - super(GraphQLView, self).__init__() + super().__init__() for key, value in kwargs.items(): if hasattr(self, key): setattr(self, key, value) @@ -130,7 +130,7 @@ async def __handle_request(self, request, *args, **kwargs): *( ex if ex is not None and is_awaitable(ex) - else wrap_in_async(lambda: ex)() + else wrap_in_async(lambda x: x)(ex) for ex in execution_results ) ) @@ -141,15 +141,15 @@ async def __handle_request(self, request, *args, **kwargs): exec_res, is_batch=isinstance(data, list), format_error=self.format_error, - encode=partial(self.encode, pretty=pretty), # noqa: ignore + encode=partial(self.encode, pretty=pretty), ) if show_graphiql: graphiql_data = GraphiQLData( result=result, - query=getattr(all_params[0], "query"), - variables=getattr(all_params[0], "variables"), - operation_name=getattr(all_params[0], "operation_name"), + query=all_params[0].query, + variables=all_params[0].variables, + operation_name=all_params[0].operation_name, subscription_url=self.subscriptions, headers=self.headers, ) @@ -181,7 +181,7 @@ async def __handle_request(self, request, *args, **kwargs): except HttpQueryError as e: parsed_error = GraphQLError(e.message) return HTTPResponse( - self.encode(dict(errors=[self.format_error(parsed_error)])), + self.encode({"errors": [self.format_error(parsed_error)]}), status=e.status_code, headers=e.headers, content_type="application/json", diff --git a/graphql_server/webob/graphqlview.py b/graphql_server/webob/graphqlview.py index e3d5a42..78ff48a 100644 --- a/graphql_server/webob/graphqlview.py +++ b/graphql_server/webob/graphqlview.py @@ -52,7 +52,7 @@ class GraphQLView: encode = staticmethod(json_encode) def __init__(self, **kwargs): - super(GraphQLView, self).__init__() + super().__init__() for key, value in kwargs.items(): if hasattr(self, key): setattr(self, key, value) @@ -122,15 +122,15 @@ def dispatch_request(self, request): execution_results, is_batch=isinstance(data, list), format_error=self.format_error, - encode=partial(self.encode, pretty=pretty), # noqa + encode=partial(self.encode, pretty=pretty), ) if show_graphiql: graphiql_data = GraphiQLData( result=result, - query=getattr(all_params[0], "query"), - variables=getattr(all_params[0], "variables"), - operation_name=getattr(all_params[0], "operation_name"), + query=all_params[0].query, + variables=all_params[0].variables, + operation_name=all_params[0].operation_name, subscription_url=self.subscriptions, headers=self.headers, ) @@ -165,7 +165,7 @@ def dispatch_request(self, request): except HttpQueryError as e: parsed_error = GraphQLError(e.message) return Response( - self.encode(dict(errors=[self.format_error(parsed_error)])), + self.encode({"errors": [self.format_error(parsed_error)]}), status=e.status_code, charset=self.charset, headers=e.headers or {}, diff --git a/setup.cfg b/setup.cfg index 615a251..9888367 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,16 +1,3 @@ -[flake8] -exclude = docs -max-line-length = 88 -ignore = E203, E501, W503 - -[isort] -known_first_party=graphql_server -profile=black -multi_line_output=3 -include_trailing_comma=True -force_grid_wrap=0 -use_parentheses=True - [tool:pytest] norecursedirs = venv .venv .tox .git .cache .mypy_cache .pytest_cache markers = asyncio diff --git a/setup.py b/setup.py index ee568ab..9048304 100644 --- a/setup.py +++ b/setup.py @@ -16,11 +16,7 @@ ] dev_requires = [ - "flake8>=6,<7", - "isort>=5,<6", - "black>=23.9,<23.10", "mypy>=1.6,<1.7", - "check-manifest>=0.47,<1", ] + tests_requires install_flask_requires = [ diff --git a/tests/aiohttp/test_graphqlview.py b/tests/aiohttp/test_graphqlview.py index e5de233..3009426 100644 --- a/tests/aiohttp/test_graphqlview.py +++ b/tests/aiohttp/test_graphqlview.py @@ -179,7 +179,7 @@ async def test_allows_mutation_to_exist_within_a_get(client): async def test_allows_post_with_json_encoding(client): response = await client.post( "/graphql", - data=json.dumps(dict(query="{test}")), + data=json.dumps({"query": "{test}"}), headers={"content-type": "application/json"}, ) @@ -192,9 +192,9 @@ async def test_allows_sending_a_mutation_via_post(client): response = await client.post( "/graphql", data=json.dumps( - dict( - query="mutation TestMutation { writeTest { test } }", - ) + { + "query": "mutation TestMutation { writeTest { test } }", + } ), headers={"content-type": "application/json"}, ) @@ -222,10 +222,10 @@ async def test_supports_post_json_query_with_string_variables(client): response = await client.post( "/graphql", data=json.dumps( - dict( - query="query helloWho($who: String){ test(who: $who) }", - variables=json.dumps({"who": "Dolly"}), - ) + { + "query": "query helloWho($who: String){ test(who: $who) }", + "variables": json.dumps({"who": "Dolly"}), + } ), headers={"content-type": "application/json"}, ) @@ -239,10 +239,10 @@ async def test_supports_post_json_query_with_json_variables(client): response = await client.post( "/graphql", data=json.dumps( - dict( - query="query helloWho($who: String){ test(who: $who) }", - variables={"who": "Dolly"}, - ) + { + "query": "query helloWho($who: String){ test(who: $who) }", + "variables": {"who": "Dolly"}, + } ), headers={"content-type": "application/json"}, ) @@ -256,10 +256,10 @@ async def test_supports_post_url_encoded_query_with_string_variables(client): response = await client.post( "/graphql", data=urlencode( - dict( - query="query helloWho($who: String){ test(who: $who) }", - variables=json.dumps({"who": "Dolly"}), - ), + { + "query": "query helloWho($who: String){ test(who: $who) }", + "variables": json.dumps({"who": "Dolly"}), + }, ), headers={"content-type": "application/x-www-form-urlencoded"}, ) @@ -273,9 +273,9 @@ async def test_supports_post_json_quey_with_get_variable_values(client): response = await client.post( url_string(variables=json.dumps({"who": "Dolly"})), data=json.dumps( - dict( - query="query helloWho($who: String){ test(who: $who) }", - ) + { + "query": "query helloWho($who: String){ test(who: $who) }", + } ), headers={"content-type": "application/json"}, ) @@ -289,9 +289,9 @@ async def test_post_url_encoded_query_with_get_variable_values(client): response = await client.post( url_string(variables=json.dumps({"who": "Dolly"})), data=urlencode( - dict( - query="query helloWho($who: String){ test(who: $who) }", - ) + { + "query": "query helloWho($who: String){ test(who: $who) }", + } ), headers={"content-type": "application/x-www-form-urlencoded"}, ) @@ -317,8 +317,8 @@ async def test_allows_post_with_operation_name(client): response = await client.post( "/graphql", data=json.dumps( - dict( - query=""" + { + "query": """ query helloYou { test(who: "You"), ...shared } query helloWorld { test(who: "World"), ...shared } query helloDolly { test(who: "Dolly"), ...shared } @@ -326,8 +326,8 @@ async def test_allows_post_with_operation_name(client): shared: test(who: "Everyone") } """, - operationName="helloWorld", - ) + "operationName": "helloWorld", + } ), headers={"content-type": "application/json"}, ) @@ -599,7 +599,7 @@ async def test_post_multipart_data(client): async def test_batch_allows_post_with_json_encoding(app, client): response = await client.post( "/graphql", - data=json.dumps([dict(id=1, query="{test}")]), + data=json.dumps([{"id": 1, "query": "{test}"}]), headers={"content-type": "application/json"}, ) @@ -614,11 +614,11 @@ async def test_batch_supports_post_json_query_with_json_variables(app, client): "/graphql", data=json.dumps( [ - dict( - id=1, - query="query helloWho($who: String){ test(who: $who) }", - variables={"who": "Dolly"}, - ) + { + "id": 1, + "query": "query helloWho($who: String){ test(who: $who) }", + "variables": {"who": "Dolly"}, + } ] ), headers={"content-type": "application/json"}, @@ -635,9 +635,9 @@ async def test_batch_allows_post_with_operation_name(app, client): "/graphql", data=json.dumps( [ - dict( - id=1, - query=""" + { + "id": 1, + "query": """ query helloYou { test(who: "You"), ...shared } query helloWorld { test(who: "World"), ...shared } query helloDolly { test(who: "Dolly"), ...shared } @@ -645,8 +645,8 @@ async def test_batch_allows_post_with_operation_name(app, client): shared: test(who: "Everyone") } """, - operationName="helloWorld", - ) + "operationName": "helloWorld", + } ] ), headers={"content-type": "application/json"}, @@ -694,7 +694,7 @@ async def test_preflight_incorrect_request(client): async def test_custom_execution_context_class(client): response = await client.post( "/graphql", - data=json.dumps(dict(query="{test}")), + data=json.dumps({"query": "{test}"}), headers={"content-type": "application/json"}, ) diff --git a/tests/flask/test_graphqlview.py b/tests/flask/test_graphqlview.py index 34ddcb9..3a6b87c 100644 --- a/tests/flask/test_graphqlview.py +++ b/tests/flask/test_graphqlview.py @@ -191,7 +191,7 @@ def test_allows_sending_a_mutation_via_post(app, client): def test_allows_post_with_url_encoding(app, client): response = client.post( url_string(app), - data=urlencode(dict(query="{test}")), + data=urlencode({"query": "{test}"}), content_type="application/x-www-form-urlencoded", ) @@ -231,10 +231,10 @@ def test_supports_post_url_encoded_query_with_string_variables(app, client): response = client.post( url_string(app), data=urlencode( - dict( - query="query helloWho($who: String){ test(who: $who) }", - variables=json.dumps({"who": "Dolly"}), - ) + { + "query": "query helloWho($who: String){ test(who: $who) }", + "variables": json.dumps({"who": "Dolly"}), + } ), content_type="application/x-www-form-urlencoded", ) @@ -260,9 +260,9 @@ def test_post_url_encoded_query_with_get_variable_values(app, client): response = client.post( url_string(app, variables=json.dumps({"who": "Dolly"})), data=urlencode( - dict( - query="query helloWho($who: String){ test(who: $who) }", - ) + { + "query": "query helloWho($who: String){ test(who: $who) }", + } ), content_type="application/x-www-form-urlencoded", ) diff --git a/tests/quart/test_graphqlview.py b/tests/quart/test_graphqlview.py index 35a5494..17d12cc 100644 --- a/tests/quart/test_graphqlview.py +++ b/tests/quart/test_graphqlview.py @@ -245,7 +245,7 @@ async def test_allows_post_with_url_encoding(app: Quart, client: TestClientProto app, client, method="POST", - data=urlencode(dict(query="{test}")), + data=urlencode({"query": "{test}"}), headers=Headers({"Content-Type": "application/x-www-form-urlencoded"}), ) @@ -303,10 +303,10 @@ async def test_supports_post_url_encoded_query_with_string_variables( client, method="POST", data=urlencode( - dict( - query="query helloWho($who: String){ test(who: $who) }", - variables=json.dumps({"who": "Dolly"}), - ) + { + "query": "query helloWho($who: String){ test(who: $who) }", + "variables": json.dumps({"who": "Dolly"}), + } ), headers=Headers({"Content-Type": "application/x-www-form-urlencoded"}), ) @@ -345,9 +345,9 @@ async def test_post_url_encoded_query_with_get_variable_values( client, method="POST", data=urlencode( - dict( - query="query helloWho($who: String){ test(who: $who) }", - ) + { + "query": "query helloWho($who: String){ test(who: $who) }", + } ), headers=Headers({"Content-Type": "application/x-www-form-urlencoded"}), variables=json.dumps({"who": "Dolly"}), diff --git a/tests/sanic/test_graphqlview.py b/tests/sanic/test_graphqlview.py index 277eefd..24f2a92 100644 --- a/tests/sanic/test_graphqlview.py +++ b/tests/sanic/test_graphqlview.py @@ -224,10 +224,10 @@ def test_supports_post_url_encoded_query_with_string_variables(app): _, response = app.test_client.post( uri=url_string(), content=urlencode( - dict( - query="query helloWho($who: String){ test(who: $who) }", - variables=json.dumps({"who": "Dolly"}), - ) + { + "query": "query helloWho($who: String){ test(who: $who) }", + "variables": json.dumps({"who": "Dolly"}), + } ), headers={"content-type": "application/x-www-form-urlencoded"}, ) @@ -253,9 +253,9 @@ def test_post_url_encoded_query_with_get_variable_values(app): _, response = app.test_client.post( uri=url_string(variables=json.dumps({"who": "Dolly"})), content=urlencode( - dict( - query="query helloWho($who: String){ test(who: $who) }", - ) + { + "query": "query helloWho($who: String){ test(who: $who) }", + } ), headers={"content-type": "application/x-www-form-urlencoded"}, ) @@ -434,9 +434,9 @@ def test_handles_poorly_formed_variables(app): def test_handles_unsupported_http_methods(app): _, response = app.test_client.put(uri=url_string(query="{test}")) assert response.status == 405 - allowed_methods = set( + allowed_methods = { method.strip() for method in response.headers["Allow"].split(",") - ) + } assert allowed_methods in [{"GET", "POST"}, {"HEAD", "GET", "POST", "OPTIONS"}] assert response_json(response) == { "errors": [ diff --git a/tests/test_asyncio.py b/tests/test_asyncio.py index 2ba2298..a0845e4 100644 --- a/tests/test_asyncio.py +++ b/tests/test_asyncio.py @@ -47,7 +47,7 @@ def test_get_responses_using_asyncio_executor(): async def get_results(): result_promises, params = run_http_query( - schema, "get", {}, dict(query=query), run_sync=False + schema, "get", {}, {"query": query}, run_sync=False ) res = [await result for result in result_promises] return res, params diff --git a/tests/test_helpers.py b/tests/test_helpers.py index ad62f62..30a0b2e 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -139,7 +139,7 @@ def test_encode_execution_results_with_format_error(): def format_error(error): return { "msg": error.message, - "loc": "{}:{}".format(error.locations[0].line, error.locations[0].column), + "loc": f"{error.locations[0].line}:{error.locations[0].column}", "pth": "/".join(error.path), } diff --git a/tests/test_query.py b/tests/test_query.py index 1ae3526..e0994db 100644 --- a/tests/test_query.py +++ b/tests/test_query.py @@ -39,7 +39,7 @@ def test_server_results(): def test_validate_schema(): query = "{test}" - results, params = run_http_query(invalid_schema, "get", {}, dict(query=query)) + results, params = run_http_query(invalid_schema, "get", {}, {"query": query}) assert as_dicts(results) == [ { "data": None, @@ -54,7 +54,7 @@ def test_validate_schema(): def test_allows_get_with_query_param(): query = "{test}" - results, params = run_http_query(schema, "get", {}, dict(query=query)) + results, params = run_http_query(schema, "get", {}, {"query": query}) assert as_dicts(results) == [{"data": {"test": "Hello World"}, "errors": None}] assert params == [GraphQLParams(query=query, variables=None, operation_name=None)] @@ -65,10 +65,10 @@ def test_allows_get_with_variable_values(): schema, "get", {}, - dict( - query="query helloWho($who: String){ test(who: $who) }", - variables=json.dumps({"who": "Dolly"}), - ), + { + "query": "query helloWho($who: String){ test(who: $who) }", + "variables": json.dumps({"who": "Dolly"}), + }, ) assert as_dicts(results) == [{"data": {"test": "Hello Dolly"}, "errors": None}] @@ -79,8 +79,8 @@ def test_allows_get_with_operation_name(): schema, "get", {}, - query_data=dict( - query=""" + query_data={ + "query": """ query helloYou { test(who: "You"), ...shared } query helloWorld { test(who: "World"), ...shared } query helloDolly { test(who: "Dolly"), ...shared } @@ -88,8 +88,8 @@ def test_allows_get_with_operation_name(): shared: test(who: "Everyone") } """, - operationName="helloWorld", - ), + "operationName": "helloWorld", + }, ) assert as_dicts(results) == [ @@ -102,7 +102,7 @@ def test_allows_get_with_operation_name(): def test_reports_validation_errors(): results, params = run_http_query( - schema, "get", {}, query_data=dict(query="{ test, unknownOne, unknownTwo }") + schema, "get", {}, query_data={"query": "{ test, unknownOne, unknownTwo }"} ) assert as_dicts(results) == [ @@ -134,7 +134,7 @@ def enter_field(self, node, *_args): schema, "get", {}, - query_data=dict(query="{ test }"), + query_data={"query": "{ test }"}, validation_rules=[CustomValidationRule], ) @@ -159,7 +159,7 @@ def test_reports_max_num_of_validation_errors(): schema, "get", {}, - query_data=dict(query="{ test, unknownOne, unknownTwo }"), + query_data={"query": "{ test, unknownOne, unknownTwo }"}, max_errors=1, ) @@ -207,12 +207,12 @@ def test_errors_when_missing_operation_name(): schema, "get", {}, - query_data=dict( - query=""" + query_data={ + "query": """ query TestQuery { test } mutation TestMutation { writeTest { test } } """ - ), + }, ) assert as_dicts(results) == [ @@ -237,11 +237,11 @@ def test_errors_when_sending_a_mutation_via_get(): schema, "get", {}, - query_data=dict( - query=""" + query_data={ + "query": """ mutation TestMutation { writeTest { test } } """ - ), + }, ) assert exc_info.value == HttpQueryError( @@ -256,11 +256,11 @@ def test_catching_errors_when_sending_a_mutation_via_get(): schema, "get", {}, - query_data=dict( - query=""" + query_data={ + "query": """ mutation TestMutation { writeTest { test } } """ - ), + }, catch=True, ) @@ -273,13 +273,13 @@ def test_errors_when_selecting_a_mutation_within_a_get(): schema, "get", {}, - query_data=dict( - query=""" + query_data={ + "query": """ query TestQuery { test } mutation TestMutation { writeTest { test } } """, - operationName="TestMutation", - ), + "operationName": "TestMutation", + }, ) assert exc_info.value == HttpQueryError( @@ -294,13 +294,13 @@ def test_allows_mutation_to_exist_within_a_get(): schema, "get", {}, - query_data=dict( - query=""" + query_data={ + "query": """ query TestQuery { test } mutation TestMutation { writeTest { test } } """, - operationName="TestQuery", - ), + "operationName": "TestQuery", + }, ) assert as_dicts(results) == [{"data": {"test": "Hello World"}, "errors": None}] @@ -311,16 +311,14 @@ def test_allows_sending_a_mutation_via_post(): schema, "post", {}, - query_data=dict(query="mutation TestMutation { writeTest { test } }"), + query_data={"query": "mutation TestMutation { writeTest { test } }"}, ) assert results == [({"writeTest": {"test": "Hello World"}}, None)] def test_allows_post_with_url_encoding(): - results, params = run_http_query( - schema, "post", {}, query_data=dict(query="{test}") - ) + results, params = run_http_query(schema, "post", {}, query_data={"query": "{test}"}) assert results == [({"test": "Hello World"}, None)] @@ -330,10 +328,10 @@ def test_supports_post_json_query_with_string_variables(): schema, "post", {}, - query_data=dict( - query="query helloWho($who: String){ test(who: $who) }", - variables='{"who": "Dolly"}', - ), + query_data={ + "query": "query helloWho($who: String){ test(who: $who) }", + "variables": '{"who": "Dolly"}', + }, ) assert results == [({"test": "Hello Dolly"}, None)] @@ -357,10 +355,10 @@ def test_supports_post_url_encoded_query_with_string_variables(): schema, "post", {}, - query_data=dict( - query="query helloWho($who: String){ test(who: $who) }", - variables='{"who": "Dolly"}', - ), + query_data={ + "query": "query helloWho($who: String){ test(who: $who) }", + "variables": '{"who": "Dolly"}', + }, ) assert results == [({"test": "Hello Dolly"}, None)] @@ -370,8 +368,8 @@ def test_supports_post_json_query_with_get_variable_values(): results, params = run_http_query( schema, "post", - data=dict(query="query helloWho($who: String){ test(who: $who) }"), - query_data=dict(variables={"who": "Dolly"}), + data={"query": "query helloWho($who: String){ test(who: $who) }"}, + query_data={"variables": {"who": "Dolly"}}, ) assert results == [({"test": "Hello Dolly"}, None)] @@ -381,8 +379,8 @@ def test_post_url_encoded_query_with_get_variable_values(): results, params = run_http_query( schema, "get", - data=dict(query="query helloWho($who: String){ test(who: $who) }"), - query_data=dict(variables='{"who": "Dolly"}'), + data={"query": "query helloWho($who: String){ test(who: $who) }"}, + query_data={"variables": '{"who": "Dolly"}'}, ) assert results == [({"test": "Hello Dolly"}, None)] @@ -392,8 +390,8 @@ def test_supports_post_raw_text_query_with_get_variable_values(): results, params = run_http_query( schema, "get", - data=dict(query="query helloWho($who: String){ test(who: $who) }"), - query_data=dict(variables='{"who": "Dolly"}'), + data={"query": "query helloWho($who: String){ test(who: $who) }"}, + query_data={"variables": '{"who": "Dolly"}'}, ) assert results == [({"test": "Hello Dolly"}, None)] @@ -403,8 +401,8 @@ def test_allows_post_with_operation_name(): results, params = run_http_query( schema, "get", - data=dict( - query=""" + data={ + "query": """ query helloYou { test(who: "You"), ...shared } query helloWorld { test(who: "World"), ...shared } query helloDolly { test(who: "Dolly"), ...shared } @@ -412,8 +410,8 @@ def test_allows_post_with_operation_name(): shared: test(who: "Everyone") } """, - operationName="helloWorld", - ), + "operationName": "helloWorld", + }, ) assert results == [({"test": "Hello World", "shared": "Hello Everyone"}, None)] @@ -423,8 +421,8 @@ def test_allows_post_with_get_operation_name(): results, params = run_http_query( schema, "get", - data=dict( - query=""" + data={ + "query": """ query helloYou { test(who: "You"), ...shared } query helloWorld { test(who: "World"), ...shared } query helloDolly { test(who: "Dolly"), ...shared } @@ -432,15 +430,15 @@ def test_allows_post_with_get_operation_name(): shared: test(who: "Everyone") } """ - ), - query_data=dict(operationName="helloWorld"), + }, + query_data={"operationName": "helloWorld"}, ) assert results == [({"test": "Hello World", "shared": "Hello Everyone"}, None)] def test_supports_pretty_printing_data(): - results, params = run_http_query(schema, "get", data=dict(query="{test}")) + results, params = run_http_query(schema, "get", data={"query": "{test}"}) result = {"data": results[0].data} assert json_encode(result, pretty=True) == ( @@ -449,14 +447,14 @@ def test_supports_pretty_printing_data(): def test_not_pretty_data_by_default(): - results, params = run_http_query(schema, "get", data=dict(query="{test}")) + results, params = run_http_query(schema, "get", data={"query": "{test}"}) result = {"data": results[0].data} assert json_encode(result) == '{"data":{"test":"Hello World"}}' def test_handles_field_errors_caught_by_graphql(): - results, params = run_http_query(schema, "get", data=dict(query="{thrower}")) + results, params = run_http_query(schema, "get", data={"query": "{thrower}"}) assert results == [ (None, [{"message": "Throws!", "locations": [(1, 2)], "path": ["thrower"]}]) @@ -467,7 +465,7 @@ def test_handles_field_errors_caught_by_graphql(): def test_handles_syntax_errors_caught_by_graphql(): - results, params = run_http_query(schema, "get", data=dict(query="syntaxerror")) + results, params = run_http_query(schema, "get", data={"query": "syntaxerror"}) assert results == [ ( @@ -491,7 +489,7 @@ def test_handles_errors_caused_by_a_lack_of_query(): def test_handles_errors_caused_by_invalid_query_type(): with raises(HttpQueryError) as exc_info: - results, params = run_http_query(schema, "get", dict(query=42)) + results, params = run_http_query(schema, "get", {"query": 42}) assert exc_info.value == HttpQueryError(400, "Unexpected query type.") @@ -525,10 +523,10 @@ def test_handles_poorly_formed_variables(): schema, "get", {}, - dict( - query="query helloWho($who: String){ test(who: $who) }", - variables="who:You", - ), + { + "query": "query helloWho($who: String){ test(who: $who) }", + "variables": "who:You", + }, ) assert exc_info.value == HttpQueryError(400, "Variables are invalid JSON.") @@ -597,7 +595,7 @@ def test_passes_request_into_request_context(): schema, "get", {}, - query_data=dict(query="{request}"), + query_data={"query": "{request}"}, context_value={"q": "testing"}, ) @@ -610,7 +608,7 @@ def __str__(self): return "CUSTOM CONTEXT" results, params = run_http_query( - schema, "get", {}, query_data=dict(query="{context}"), context_value=Context() + schema, "get", {}, query_data={"query": "{context}"}, context_value=Context() ) assert results == [({"context": "CUSTOM CONTEXT"}, None)] @@ -618,7 +616,7 @@ def __str__(self): def test_post_multipart_data(): query = "mutation TestMutation { writeTest { test } }" - results, params = run_http_query(schema, "post", {}, query_data=dict(query=query)) + results, params = run_http_query(schema, "post", {}, query_data={"query": query}) assert results == [({"writeTest": {"test": "Hello World"}}, None)] @@ -642,8 +640,8 @@ def test_batch_supports_post_json_query_with_json_variables(): def test_batch_allows_post_with_operation_name(): data = [ - dict( - query=""" + { + "query": """ query helloYou { test(who: "You"), ...shared } query helloWorld { test(who: "World"), ...shared } query helloDolly { test(who: "Dolly"), ...shared } @@ -651,8 +649,8 @@ def test_batch_allows_post_with_operation_name(): shared: test(who: "Everyone") } """, - operationName="helloWorld", - ) + "operationName": "helloWorld", + } ] data = load_json_body(json_encode(data)) results, params = run_http_query(schema, "post", data, batch_enabled=True) @@ -664,8 +662,8 @@ def test_graphiql_render_umlaut(): results, params = run_http_query( schema, "get", - data=dict(query="query helloWho($who: String){ test(who: $who) }"), - query_data=dict(variables='{"who": "Björn"}'), + data={"query": "query helloWho($who: String){ test(who: $who) }"}, + query_data={"variables": '{"who": "Björn"}'}, catch=True, ) result, status_code = encode_execution_results(results) diff --git a/tests/webob/app.py b/tests/webob/app.py index 083d179..fffbc88 100644 --- a/tests/webob/app.py +++ b/tests/webob/app.py @@ -11,7 +11,7 @@ def url_string(url="/graphql", **url_params): return f"{url}?{urlencode(url_params)}" if url_params else url -class Client(object): +class Client: def __init__(self, **kwargs): self.schema = kwargs.pop("schema", None) or Schema self.settings = kwargs.pop("settings", None) or {} diff --git a/tests/webob/test_graphiqlview.py b/tests/webob/test_graphiqlview.py index b456b60..209ae1a 100644 --- a/tests/webob/test_graphiqlview.py +++ b/tests/webob/test_graphiqlview.py @@ -5,7 +5,7 @@ @pytest.mark.parametrize( - "settings", [dict(graphiql=True), dict(graphiql=True, jinja_env=Environment())] + "settings", [{"graphiql": True}, {"graphiql": True, "jinja_env": Environment()}] ) def test_graphiql_is_enabled(client): response = client.get(url_string(query="{test}"), headers={"Accept": "text/html"}) @@ -13,7 +13,7 @@ def test_graphiql_is_enabled(client): @pytest.mark.parametrize( - "settings", [dict(graphiql=True), dict(graphiql=True, jinja_env=Environment())] + "settings", [{"graphiql": True}, {"graphiql": True, "jinja_env": Environment()}] ) def test_graphiql_simple_renderer(client): response = client.get(url_string(query="{test}"), headers={"Accept": "text/html"}) @@ -29,7 +29,7 @@ def test_graphiql_simple_renderer(client): @pytest.mark.parametrize( - "settings", [dict(graphiql=True), dict(graphiql=True, jinja_env=Environment())] + "settings", [{"graphiql": True}, {"graphiql": True, "jinja_env": Environment()}] ) def test_graphiql_html_is_not_accepted(client): response = client.get(url_string(), headers={"Accept": "application/json"}) diff --git a/tests/webob/test_graphqlview.py b/tests/webob/test_graphqlview.py index b72f12a..5acacd3 100644 --- a/tests/webob/test_graphqlview.py +++ b/tests/webob/test_graphqlview.py @@ -176,7 +176,7 @@ def test_allows_sending_a_mutation_via_post(client): def test_allows_post_with_url_encoding(client): response = client.post( url_string(), - data=urlencode(dict(query="{test}")), + data=urlencode({"query": "{test}"}), content_type="application/x-www-form-urlencoded", ) @@ -216,10 +216,10 @@ def test_supports_post_url_encoded_query_with_string_variables(client): response = client.post( url_string(), data=urlencode( - dict( - query="query helloWho($who: String){ test(who: $who) }", - variables=json.dumps({"who": "Dolly"}), - ) + { + "query": "query helloWho($who: String){ test(who: $who) }", + "variables": json.dumps({"who": "Dolly"}), + } ), content_type="application/x-www-form-urlencoded", ) @@ -245,9 +245,9 @@ def test_post_url_encoded_query_with_get_variable_values(client): response = client.post( url_string(variables=json.dumps({"who": "Dolly"})), data=urlencode( - dict( - query="query helloWho($who: String){ test(who: $who) }", - ) + { + "query": "query helloWho($who: String){ test(who: $who) }", + } ), content_type="application/x-www-form-urlencoded", ) @@ -310,7 +310,7 @@ def test_allows_post_with_get_operation_name(client): } -@pytest.mark.parametrize("settings", [dict(pretty=True)]) +@pytest.mark.parametrize("settings", [{"pretty": True}]) def test_supports_pretty_printing(client, settings): response = client.get(url_string(query="{test}")) @@ -319,7 +319,7 @@ def test_supports_pretty_printing(client, settings): ) -@pytest.mark.parametrize("settings", [dict(pretty=False)]) +@pytest.mark.parametrize("settings", [{"pretty": False}]) def test_not_pretty_by_default(client, settings): response = client.get(url_string(query="{test}")) @@ -435,7 +435,7 @@ def test_passes_request_into_request_context(client): assert response_json(response) == {"data": {"request": "testing"}} -@pytest.mark.parametrize("settings", [dict(context={"session": "CUSTOM CONTEXT"})]) +@pytest.mark.parametrize("settings", [{"context": {"session": "CUSTOM CONTEXT"}}]) def test_passes_custom_context_into_context(client, settings): response = client.get(url_string(query="{context { session request }}")) @@ -448,7 +448,7 @@ def test_passes_custom_context_into_context(client, settings): assert "request" in res["data"]["context"]["request"] -@pytest.mark.parametrize("settings", [dict(context="CUSTOM CONTEXT")]) +@pytest.mark.parametrize("settings", [{"context": "CUSTOM CONTEXT"}]) def test_context_remapped_if_not_mapping(client, settings): response = client.get(url_string(query="{context { session request }}")) @@ -465,7 +465,7 @@ class CustomContext(dict): property = "A custom property" -@pytest.mark.parametrize("settings", [dict(context=CustomContext())]) +@pytest.mark.parametrize("settings", [{"context": CustomContext()}]) def test_allow_empty_custom_context(client, settings): response = client.get(url_string(query="{context { property request }}")) @@ -504,7 +504,7 @@ def test_post_multipart_data(client): assert response_json(response) == {"data": {"writeTest": {"test": "Hello World"}}} -@pytest.mark.parametrize("settings", [dict(batch=True)]) +@pytest.mark.parametrize("settings", [{"batch": True}]) def test_batch_allows_post_with_json_encoding(client, settings): response = client.post( url_string(), @@ -524,7 +524,7 @@ def test_batch_allows_post_with_json_encoding(client, settings): ] -@pytest.mark.parametrize("settings", [dict(batch=True)]) +@pytest.mark.parametrize("settings", [{"batch": True}]) def test_batch_supports_post_json_query_with_json_variables(client, settings): response = client.post( url_string(), @@ -545,7 +545,7 @@ def test_batch_supports_post_json_query_with_json_variables(client, settings): ] -@pytest.mark.parametrize("settings", [dict(batch=True)]) +@pytest.mark.parametrize("settings", [{"batch": True}]) def test_batch_allows_post_with_operation_name(client, settings): response = client.post( url_string(), @@ -574,7 +574,7 @@ def test_batch_allows_post_with_operation_name(client, settings): @pytest.mark.parametrize( - "settings", [dict(execution_context_class=RepeatExecutionContext)] + "settings", [{"execution_context_class": RepeatExecutionContext}] ) def test_custom_execution_context_class(client): response = client.post( diff --git a/tox.ini b/tox.ini index 6627604..b44f66b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - black,flake8,import-order,mypy,manifest, + pre-commit,mypy, py{38,39,310,311} ; requires = tox-conda @@ -25,32 +25,14 @@ commands = py{38,39,310}: pytest tests {posargs} py{311}: pytest tests --cov-report=term-missing --cov=graphql_server {posargs} -[testenv:black] -basepython = python3.11 -deps = -e.[dev] -commands = - black --check graphql_server tests - -[testenv:flake8] -basepython = python3.11 -deps = -e.[dev] -commands = - flake8 setup.py graphql_server tests - -[testenv:import-order] -basepython = python3.11 -deps = -e.[dev] +[testenv:pre-commit] +skip_install = true +deps = pre-commit commands = - isort graphql_server/ tests/ + pre-commit run --all-files --show-diff-on-failure {posargs} [testenv:mypy] basepython = python3.11 deps = -e.[dev] commands = mypy graphql_server tests --ignore-missing-imports - -[testenv:manifest] -basepython = python3.11 -deps = -e.[dev] -commands = - check-manifest -v From 75a1d7d4d38cbbb834b3208455cd688014326107 Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Tue, 17 Oct 2023 16:04:46 +0800 Subject: [PATCH 4/5] refactor!: Drop VersionInfo in favor of tuple (#124) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: fix and refactor version tests to handle release candidates correctly * fix: remove VersionInfo entirely keep graphql_server.version_info as a plain tuple add tests using the packaging package to make sure graphql_server.version and graphql_server.version_info are PEP 440–compliant and pointing to the same version * chore: update pre-commit hooks --- .pre-commit-config.yaml | 8 ++-- graphql_server/version.py | 51 +++++---------------- setup.py | 1 + tests/test_version.py | 96 ++++++++++++++------------------------- 4 files changed, 50 insertions(+), 106 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0fab074..c99bdd8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,9 +1,9 @@ default_language_version: - python: python3.10 + python: python3.11 exclude: LICENSE repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-merge-conflict - id: check-json @@ -20,11 +20,11 @@ repos: hooks: - id: check-manifest - repo: https://github.com/psf/black - rev: 23.7.0 + rev: 23.9.1 hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.283 + rev: v0.1.0 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix, --show-fixes] diff --git a/graphql_server/version.py b/graphql_server/version.py index 0be0bd8..138d67d 100644 --- a/graphql_server/version.py +++ b/graphql_server/version.py @@ -1,44 +1,15 @@ -import re -from typing import NamedTuple - __all__ = ["version", "version_info"] version = "3.0.0b7" - -_re_version = re.compile(r"(\d+)\.(\d+)\.(\d+)(\D*)(\d*)") - - -class VersionInfo(NamedTuple): - major: int - minor: int - micro: int - releaselevel: str - serial: int - - @classmethod - def from_str(cls, v: str) -> "VersionInfo": - groups = _re_version.match(v).groups() # type: ignore - major, minor, micro = map(int, groups[:3]) - level = (groups[3] or "")[:1] - if level == "a": - level = "alpha" - elif level == "b": - level = "beta" - elif level in ("c", "r"): - level = "candidate" - else: - level = "final" - serial = groups[4] - serial = int(serial) if serial else 0 - return cls(major, minor, micro, level, serial) - - def __str__(self) -> str: - v = f"{self.major}.{self.minor}.{self.micro}" - level = self.releaselevel - if level and level != "final": - v = f"{v}{level[:1]}{self.serial}" - return v - - -version_info = VersionInfo.from_str(version) +version_info = (3, 0, 0, "beta", 7) +# version_info has the same format as django.VERSION +# https://github.com/django/django/blob/4a5048b036fd9e965515e31fdd70b0af72655cba/django/utils/version.py#L22 +# +# examples +# "3.0.0" -> (3, 0, 0, "final", 0) +# "3.0.0rc1" -> (3, 0, 0, "rc", 1) +# "3.0.0b7" -> (3, 0, 0, "beta", 7) +# "3.0.0a2" -> (3, 0, 0, "alpha", 2) +# +# also see tests/test_version.py diff --git a/setup.py b/setup.py index 9048304..3531daa 100644 --- a/setup.py +++ b/setup.py @@ -13,6 +13,7 @@ "pytest-cov>=4,<5", "Jinja2>=3.1,<4", "sanic-testing>=22.3,<24", + "packaging==23.2", ] dev_requires = [ diff --git a/tests/test_version.py b/tests/test_version.py index a69c95e..0ac89e5 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -1,78 +1,50 @@ -import re +import packaging +from packaging.version import Version -import graphql_server -from graphql_server.version import VersionInfo, version, version_info +from graphql_server.version import version, version_info -_re_version = re.compile(r"(\d+)\.(\d+)\.(\d+)(?:([abc])(\d+))?$") +RELEASE_LEVEL = {"alpha": "a", "beta": "b", "rc": "rc", "final": None} -def test_create_version_info_from_fields(): - v = VersionInfo(1, 2, 3, "alpha", 4) - assert v.major == 1 - assert v.minor == 2 - assert v.micro == 3 - assert v.releaselevel == "alpha" - assert v.serial == 4 +parsed_version = Version(version) -def test_create_version_info_from_str(): - v = VersionInfo.from_str("1.2.3") - assert v.major == 1 - assert v.minor == 2 - assert v.micro == 3 - assert v.releaselevel == "final" - assert v.serial == 0 - v = VersionInfo.from_str("1.2.3a4") - assert v.major == 1 - assert v.minor == 2 - assert v.micro == 3 - assert v.releaselevel == "alpha" - assert v.serial == 4 - v = VersionInfo.from_str("1.2.3beta4") - assert v.major == 1 - assert v.minor == 2 - assert v.micro == 3 - assert v.releaselevel == "beta" - assert v.serial == 4 - v = VersionInfo.from_str("12.34.56rc789") - assert v.major == 12 - assert v.minor == 34 - assert v.micro == 56 - assert v.releaselevel == "candidate" - assert v.serial == 789 +def test_valid_version() -> None: + packaging.version.parse(version) -def test_serialize_as_str(): - v = VersionInfo(1, 2, 3, "final", 0) - assert str(v) == "1.2.3" - v = VersionInfo(1, 2, 3, "alpha", 4) - assert str(v) == "1.2.3a4" +def test_valid_version_info() -> None: + """version_info has to be a tuple[int, int, int, str, int]""" + assert isinstance(version_info, tuple) + assert len(version_info) == 5 + major, minor, micro, release_level, serial = version_info + assert isinstance(major, int) + assert isinstance(minor, int) + assert isinstance(micro, int) + assert isinstance(release_level, str) + assert isinstance(serial, int) -def test_base_package_has_correct_version(): - assert graphql_server.__version__ == version - assert graphql_server.version == version +def test_valid_version_release_level() -> None: + if parsed_version.pre is not None: + valid_release_levels = {v for v in RELEASE_LEVEL.values() if v is not None} + assert parsed_version.pre[0] in valid_release_levels -def test_base_package_has_correct_version_info(): - assert graphql_server.__version_info__ is version_info - assert graphql_server.version_info is version_info +def test_valid_version_info_release_level() -> None: + assert version_info[3] in RELEASE_LEVEL.keys() -def test_version_has_correct_format(): - assert isinstance(version, str) - assert _re_version.match(version) +def test_version_same_as_version_info() -> None: + assert ( + parsed_version.major, + parsed_version.minor, + parsed_version.micro, + ) == version_info[:3] -def test_version_info_has_correct_fields(): - assert isinstance(version_info, tuple) - assert str(version_info) == version - groups = _re_version.match(version).groups() # type: ignore - assert version_info.major == int(groups[0]) - assert version_info.minor == int(groups[1]) - assert version_info.micro == int(groups[2]) - if groups[3] is None: # pragma: no cover - assert groups[4] is None - else: # pragma: no cover - assert version_info.releaselevel[:1] == groups[3] - assert version_info.serial == int(groups[4]) + release_level, serial = version_info[-2:] + if parsed_version.is_prerelease: + assert (RELEASE_LEVEL[release_level], serial) == parsed_version.pre + else: + assert (release_level, serial) == ("final", 0) From 536721c7b48b77b3352ec42999550f175b3cdd11 Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Wed, 25 Oct 2023 18:27:01 +0800 Subject: [PATCH 5/5] chore: replace black with ruff formatter (#126) --- .pre-commit-config.yaml | 7 ++----- .ruff.toml | 1 - tests/aiohttp/test_graphiqlview.py | 2 +- tests/flask/test_graphiqlview.py | 2 +- tests/quart/test_graphiqlview.py | 4 ++-- tests/sanic/test_graphiqlview.py | 2 +- tests/webob/test_graphiqlview.py | 2 +- 7 files changed, 8 insertions(+), 12 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c99bdd8..bb933c2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,12 +19,9 @@ repos: rev: "0.49" hooks: - id: check-manifest -- repo: https://github.com/psf/black - rev: 23.9.1 - hooks: - - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.0 + rev: v0.1.2 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix, --show-fixes] + - id: ruff-format diff --git a/.ruff.toml b/.ruff.toml index db5e5b6..228088e 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -25,7 +25,6 @@ target-version = "py38" [isort] known-first-party = ["graphql_server"] -force-wrap-aliases = true combine-as-imports = true [pyupgrade] diff --git a/tests/aiohttp/test_graphiqlview.py b/tests/aiohttp/test_graphiqlview.py index a3bd2fd..b3f39cd 100644 --- a/tests/aiohttp/test_graphiqlview.py +++ b/tests/aiohttp/test_graphiqlview.py @@ -27,7 +27,7 @@ async def test_graphiql_is_enabled(app, client): ' "test": "Hello World"\n' " }\n" "}".replace('"', '\\"').replace("\n", "\\n") - ) + ) # fmt: skip assert pretty_response in await response.text() diff --git a/tests/flask/test_graphiqlview.py b/tests/flask/test_graphiqlview.py index bca747c..bb398f6 100644 --- a/tests/flask/test_graphiqlview.py +++ b/tests/flask/test_graphiqlview.py @@ -33,7 +33,7 @@ def test_graphiql_renders_pretty(app, client): ' "test": "Hello World"\n' " }\n" "}".replace('"', '\\"').replace("\n", "\\n") - ) + ) # fmt: skip assert pretty_response in response.data.decode("utf-8") diff --git a/tests/quart/test_graphiqlview.py b/tests/quart/test_graphiqlview.py index 17c72d6..4b78ec2 100644 --- a/tests/quart/test_graphiqlview.py +++ b/tests/quart/test_graphiqlview.py @@ -15,7 +15,7 @@ async def execute_client( client: TestClientProtocol, method: str = "GET", headers: Optional[Headers] = None, - **extra_params + **extra_params, ) -> Response: test_request_context = app.test_request_context(path="/", method=method) async with test_request_context: @@ -51,7 +51,7 @@ async def test_graphiql_renders_pretty(app: Quart, client: TestClientProtocol): ' "test": "Hello World"\n' " }\n" "}".replace('"', '\\"').replace("\n", "\\n") - ) + ) # fmt: skip result = await response.get_data(as_text=True) assert pretty_response in result diff --git a/tests/sanic/test_graphiqlview.py b/tests/sanic/test_graphiqlview.py index a9da869..b5c877b 100644 --- a/tests/sanic/test_graphiqlview.py +++ b/tests/sanic/test_graphiqlview.py @@ -26,7 +26,7 @@ def test_graphiql_is_enabled(app): ' "test": "Hello World"\n' " }\n" "}".replace('"', '\\"').replace("\n", "\\n") - ) + ) # fmt: skip assert pretty_response in response.body.decode("utf-8") diff --git a/tests/webob/test_graphiqlview.py b/tests/webob/test_graphiqlview.py index 209ae1a..3e6ead2 100644 --- a/tests/webob/test_graphiqlview.py +++ b/tests/webob/test_graphiqlview.py @@ -24,7 +24,7 @@ def test_graphiql_simple_renderer(client): ' "test": "Hello World"\n' " }\n" "}".replace('"', '\\"').replace("\n", "\\n") - ) + ) # fmt: skip assert pretty_response in response.body.decode("utf-8")