diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 860dfed8..00000000 --- a/.coveragerc +++ /dev/null @@ -1,29 +0,0 @@ -[run] -branch = True - -[report] -exclude_lines = - # Have to re-enable the standard pragma - pragma: no cover - pragma: no cover ${PRAGMA_VERSION} - - # Don't complain about missing debug-only code: - def __repr__ - if self\.debug - - # Don't complain if tests don't hit defensive assertion code: - raise AssertionError - raise NotImplementedError - - # Don't complain if non-runnable code isn't run: - if 0: - if __name__ == .__main__.: - if current_app: - if current_app\.config\[.DEBUG.\]: - if app\.config\[.DEBUG.\] - - # Type checker - if TYPE_CHECKING: - if t.TYPE_CHECKING: - -ignore_errors = True diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..9f4e2a30 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,17 @@ +{ + "name": "cenobites/flask-jsonrpc", + "image": "mcr.microsoft.com/devcontainers/python:3", + "customizations": { + "vscode": { + "settings": { + "python.defaultInterpreterPath": "${workspaceFolder}/.venv", + "python.terminal.activateEnvInCurrentTerminal": true, + "python.terminal.launchArgs": [ + "-X", + "dev" + ] + } + } + }, + "onCreateCommand": ".devcontainer/on-create-command.sh" + } diff --git a/.devcontainer/on-create-command.sh b/.devcontainer/on-create-command.sh new file mode 100644 index 00000000..f8d96ae8 --- /dev/null +++ b/.devcontainer/on-create-command.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -e + +python3 -m venv .venv +. .venv/bin/activate +pip install -U pip +pip install -r requirements/local.txt +pip install -e . +pre-commit install --install-hooks diff --git a/.editorconfig b/.editorconfig index 21241b6d..3b64e37f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,6 +9,7 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true max_line_length = 120 +quote_type = single [Makefile] indent_style = tab diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 88363add..f9b8c475 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,18 @@ version: 2 updates: - - package-ecosystem: "pip" - directory: "/requirements" + - package-ecosystem: github-actions + directory: / schedule: - interval: "daily" + interval: monthly + groups: + github-actions: + patterns: + - '*' + - package-ecosystem: pip + directory: /requirements + schedule: + interval: daily + groups: + python-requirements: + patterns: + - '*' diff --git a/.github/workflows/codeql_analysis.yml b/.github/workflows/codeql_analysis.yml index db0245bf..45d5e727 100644 --- a/.github/workflows/codeql_analysis.yml +++ b/.github/workflows/codeql_analysis.yml @@ -32,7 +32,7 @@ jobs: if: ${{ matrix.language == 'python' }} uses: actions/setup-python@v4 with: - python-version: '3.11' + python-version: '3.12' - name: Build Application using script if: ${{ matrix.language == 'python' }} run: | diff --git a/.github/workflows/on_update.yml b/.github/workflows/on_update.yml index 6324a58a..ebd5409e 100644 --- a/.github/workflows/on_update.yml +++ b/.github/workflows/on_update.yml @@ -19,7 +19,7 @@ jobs: matrix: platform: - ubuntu-latest - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - name: Checkout source at ${{ matrix.platform }} uses: actions/checkout@v3 @@ -47,7 +47,7 @@ jobs: matrix: platform: - ubuntu-latest - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - name: Checkout source at ${{ matrix.platform }} uses: actions/checkout@v3 @@ -72,7 +72,7 @@ jobs: matrix: platform: - ubuntu-latest - python-version: ["3.11"] + python-version: ["3.12"] steps: - name: Checkout source at ${{ matrix.platform }} uses: actions/checkout@v3 @@ -87,4 +87,4 @@ jobs: python -m pip install --upgrade pip tox - name: Run tox run: | - tox -e py,py-async,style,typing,security,docs + tox -e py,py-async,style,typing,pytype,security,docs diff --git a/.github/workflows/pre_release.yml b/.github/workflows/pre_release.yml index 9cc6f8d2..1f494767 100644 --- a/.github/workflows/pre_release.yml +++ b/.github/workflows/pre_release.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - release/** permissions: contents: read @@ -17,7 +18,7 @@ jobs: matrix: platform: - ubuntu-latest - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - name: Checkout source at ${{ matrix.platform }} uses: actions/checkout@v3 @@ -49,7 +50,7 @@ jobs: matrix: platform: - ubuntu-latest - python-version: ["3.10"] + python-version: ["3.11"] steps: - name: Checkout source at ${{ matrix.platform }} uses: actions/checkout@v3 @@ -78,7 +79,7 @@ jobs: matrix: platform: - ubuntu-latest - python-version: ["3.11"] + python-version: ["3.12"] steps: - name: Checkout source at ${{ matrix.platform }} uses: actions/checkout@v3 @@ -107,7 +108,7 @@ jobs: matrix: platform: - ubuntu-latest - python-version: ["3.11"] + python-version: ["3.12"] steps: - name: Checkout source at ${{ matrix.platform }} uses: actions/checkout@v3 @@ -129,7 +130,7 @@ jobs: bandit -r src/ - name: Check dependencies for known security vulnerabilities with Safety run: | - safety check -i 52495 -i 51457 + safety check test: name: Test @@ -142,7 +143,7 @@ jobs: - ubuntu-latest - macos-latest - windows-latest - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - name: Checkout source at ${{ matrix.platform }} uses: actions/checkout@v3 @@ -163,7 +164,7 @@ jobs: run: | py.test - name: Upload coverage JUint report - if: ${{ matrix.platform == 'ubuntu-latest' && matrix.python-version == '3.11' }} + if: ${{ matrix.platform == 'ubuntu-latest' && matrix.python-version == '3.12' }} uses: actions/upload-artifact@v3 with: name: test-n-coverage-report @@ -181,7 +182,7 @@ jobs: matrix: platform: - ubuntu-latest - python-version: ["3.11"] + python-version: ["3.12"] steps: - name: Checkout source at ${{ matrix.platform }} uses: actions/checkout@v3 @@ -204,8 +205,9 @@ jobs: - name: Send coverage stats to Coveralls env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} run: | - coveralls --service=github + coveralls build: name: Build source @@ -218,7 +220,7 @@ jobs: - ubuntu-latest - macos-latest - windows-latest - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - name: Checkout source at ${{ matrix.platform }} uses: actions/checkout@v3 @@ -233,12 +235,12 @@ jobs: python -m pip install --upgrade pip setuptools build python -m build --outdir dist-${{ matrix.platform }}-${{ matrix.python-version }} - name: Tar build and wheel distributions files - if: ${{ matrix.platform == 'ubuntu-latest' && matrix.python-version == '3.11' }} + if: ${{ matrix.platform == 'ubuntu-latest' && matrix.python-version == '3.12' }} run: | mv dist-${{ matrix.platform }}-${{ matrix.python-version }} dist tar -cvf dist.tar dist - name: Upload build and wheel distributions files - if: ${{ matrix.platform == 'ubuntu-latest' && matrix.python-version == '3.11' }} + if: ${{ matrix.platform == 'ubuntu-latest' && matrix.python-version == '3.12' }} uses: actions/upload-artifact@v3 with: name: pre-release-build @@ -255,7 +257,7 @@ jobs: matrix: platform: - ubuntu-latest - python-version: ["3.11"] + python-version: ["3.12"] steps: - name: Checkout source at ${{ matrix.platform }} uses: actions/checkout@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f2c22387..78c9df8d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: matrix: platform: - ubuntu-latest - python-version: ["3.11"] + python-version: ["3.12"] steps: - name: Checkout source at ${{ matrix.platform }} uses: actions/checkout@v3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 064bb1cc..e0d7057d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,10 +2,10 @@ exclude: '.git|.tox|.venv|.vscode|__pycache__|.pytest_cache|.eggs|.mypy_cache|.p default_stages: [commit] fail_fast: true default_language_version: - python: python3.11 + python: python3.12 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-byte-order-marker - id: check-case-conflict @@ -17,37 +17,38 @@ repos: - id: mixed-line-ending - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade - rev: v3.3.1 + rev: v3.15.0 hooks: - id: pyupgrade args: ['--py37-plus'] - repo: https://github.com/psf/black - rev: 23.3.0 + rev: 23.12.1 hooks: - id: black args: ['--safe'] - repo: https://github.com/asottile/blacken-docs - rev: 1.13.0 + rev: 1.16.0 hooks: - id: blacken-docs additional_dependencies: - - black==23.3.0 + - black==23.12.1 - repo: https://github.com/PyCQA/isort - rev: 5.12.0 + rev: 5.13.2 hooks: - id: isort - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 + rev: 7.0.0 hooks: - id: flake8 additional_dependencies: - - flake8-isort==6.0.0 - - flake8-bugbear==23.3.23 - - flake8-pyi==23.3.1 + - flake8-isort==6.1.1 + - flake8-bugbear==24.1.17 + - flake8-pyi==24.1.0 - flake8-quotes==3.3.2 - flake8-implicit-str-concat==0.4.0 - args: ['--config=setup.cfg'] + - flake8-pyproject==1.2.3 + args: ['--toml-config=pyproject.toml'] - repo: https://github.com/PyCQA/pylint - rev: v2.17.1 + rev: v3.0.3 hooks: - id: pylint diff --git a/.readthedocs.yaml b/.readthedocs.yaml index a96affdb..865c6859 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,9 +1,13 @@ version: 2 +build: + os: ubuntu-22.04 + tools: + python: '3.12' python: install: - requirements: requirements/docs.txt - method: pip path: . sphinx: - builder: htmldoc + builder: dirhtml fail_on_warning: true diff --git a/Dockerfile.it b/Dockerfile.it index 064a8fc9..846eefcc 100644 --- a/Dockerfile.it +++ b/Dockerfile.it @@ -1,4 +1,4 @@ -FROM python:3.11-alpine as builder +FROM python:3.12-alpine as builder RUN apk add --no-cache --update --virtual .build-deps \ build-base \ @@ -18,7 +18,7 @@ RUN pip install pip setuptools wheel --upgrade \ && pip wheel --wheel-dir=/svc/wheels -r test.txt \ poetry-core>=1.0.0 -FROM python:3.11-alpine +FROM python:3.12-alpine ENV PYTHONUNBUFFERED=1 \ DEBUG=0 \ @@ -56,7 +56,7 @@ WORKDIR /app ARG VERSION=1 RUN echo "Version: ${VERSION}" -COPY .docker/* requirements/test.txt tests/test_apps/*.py tests/test_apps/*.ini /app/ +COPY .docker/* requirements/test.txt tests/integration/*.py tests/integration/*.ini /app/ RUN pip install pip setuptools wheel --upgrade \ && pip install --no-index --find-links=/svc/wheels -r test.txt \ diff --git a/Dockerfile.local b/Dockerfile.local index 9073fb7d..3d65acc0 100644 --- a/Dockerfile.local +++ b/Dockerfile.local @@ -1,4 +1,4 @@ -FROM python:3.11-alpine as builder +FROM python:3.12-alpine as builder RUN apk add --no-cache --update --virtual .build-deps \ build-base \ @@ -23,7 +23,7 @@ RUN pip install pip setuptools wheel --upgrade \ && [[ ${FLASK_ASYNC} = 1 ]] && PROJECT_PATH=".[async]" || PROJECT_PATH="." \ && pip wheel --wheel-dir=/svc/wheels -e ${PROJECT_PATH} -FROM python:3.11-alpine +FROM python:3.12-alpine ENV PYTHONUNBUFFERED=1 \ DEBUG=0 \ diff --git a/Dockerfile.py310.test b/Dockerfile.py310.test index f268725b..65786ed7 100644 --- a/Dockerfile.py310.test +++ b/Dockerfile.py310.test @@ -18,8 +18,7 @@ RUN set -ex \ && pip install -r requirements/base.txt \ && pip install -r requirements/test.txt \ poetry-core>=1.0.0 \ - && cat requirements/ci.txt | grep -wv ^mypy | grep -vw ^pytype > requirements/ci-updated.txt \ - && pip install -r requirements/ci-updated.txt \ + && pip install -r requirements/ci.txt \ && apk del .build-deps \ && addgroup -S kuchulu \ && adduser \ diff --git a/Dockerfile.py311.test b/Dockerfile.py311.test index 224d8998..11d5c3dd 100644 --- a/Dockerfile.py311.test +++ b/Dockerfile.py311.test @@ -18,8 +18,7 @@ RUN set -ex \ && pip install -r requirements/base.txt \ && pip install -r requirements/test.txt \ poetry-core>=1.0.0 \ - && cat requirements/ci.txt | grep -wv ^mypy | grep -vw ^pytype > requirements/ci-updated.txt \ - && pip install -r requirements/ci-updated.txt \ + && pip install -r requirements/ci.txt \ && apk del .build-deps \ && addgroup -S kuchulu \ && adduser \ diff --git a/Dockerfile.py37.test b/Dockerfile.py312.test similarity index 79% rename from Dockerfile.py37.test rename to Dockerfile.py312.test index 5ade3340..3e42c3cb 100644 --- a/Dockerfile.py37.test +++ b/Dockerfile.py312.test @@ -1,7 +1,7 @@ -FROM python:3.7-alpine +FROM python:3.12-alpine ENV PYTHONUNBUFFERED=1 \ - PRAGMA_VERSION=py3.7 \ + PRAGMA_VERSION=py3.12 \ DEBUG=0 WORKDIR /code @@ -11,7 +11,6 @@ COPY requirements/ /code/requirements/ RUN set -ex \ && apk add --no-cache --virtual .build-deps \ gcc \ - g++ \ musl-dev \ python3-dev \ git \ @@ -19,7 +18,8 @@ RUN set -ex \ && pip install -r requirements/base.txt \ && pip install -r requirements/test.txt \ poetry-core>=1.0.0 \ - && pip install -r requirements/ci.txt \ + && cat requirements/ci.txt | grep -vw ^pytype > requirements/ci-updated.txt \ + && pip install -r requirements/ci-updated.txt \ && apk del .build-deps \ && addgroup -S kuchulu \ && adduser \ diff --git a/MANIFEST.in b/MANIFEST.in index d30658f7..6ba7dd46 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -11,3 +11,4 @@ prune docs/_build graft examples graft tests global-exclude *.pyc +exclude src/flask_jsonrpc/contrib/browse/templates/.editorconfig diff --git a/README.md b/README.md index 60eb84c2..f9559643 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Some reasons you might want to use: * Simple, powerful, flexible, and pythonic API. * Support [JSON-RPC 2.0](https://www.jsonrpc.org/specification "JSON-RPC 2.0") version. -* Support Python 3.7 or later. +* Support Python 3.8 or later. * The web browsable API. * Run-time type checking functions defined with [PEP 484](https://www.python.org/dev/peps/pep-0484/ "PEP 484") argument (and return) type annotations. * Extensive documentation, and great community support. diff --git a/bin/docker-compose-test.sh b/bin/docker-compose-test.sh index 2c912f9d..8c08fbce 100755 --- a/bin/docker-compose-test.sh +++ b/bin/docker-compose-test.sh @@ -7,9 +7,6 @@ DOCKER_COMPOSE_FILE_PATH=../${DOCKER_COMPOSE_FILE_NAME} docker-compose -f ${DOCKER_COMPOSE_FILE_PATH} -p ci build --build-arg VERSION=$(date +%s) docker-compose -f ${DOCKER_COMPOSE_FILE_PATH} -p ci --compatibility up -d -DOCKER_WAIT_FOR_PY37=$(docker wait ci_python3.7_1) -docker logs ci_python3.7_1 - DOCKER_WAIT_FOR_PY38=$(docker wait ci_python3.8_1) docker logs ci_python3.8_1 @@ -22,10 +19,13 @@ docker logs ci_python3.10_1 DOCKER_WAIT_FOR_PY311=$(docker wait ci_python3.11_1) docker logs ci_python3.11_1 +DOCKER_WAIT_FOR_PY312=$(docker wait ci_python3.12_1) +docker logs ci_python3.12_1 + docker-compose -f ${DOCKER_COMPOSE_FILE_PATH} -p ci down --remove-orphans -if [ ${DOCKER_WAIT_FOR_PY37} -ne 0 ]; then echo "Test to Python 3.7 failed"; exit 1; fi if [ ${DOCKER_WAIT_FOR_PY38} -ne 0 ]; then echo "Test to Python 3.8 failed"; exit 1; fi if [ ${DOCKER_WAIT_FOR_PY39} -ne 0 ]; then echo "Test to Python 3.9 failed"; exit 1; fi if [ ${DOCKER_WAIT_FOR_PY310} -ne 0 ]; then echo "Test to Python 3.10 failed"; exit 1; fi if [ ${DOCKER_WAIT_FOR_PY311} -ne 0 ]; then echo "Test to Python 3.11 failed"; exit 1; fi +if [ ${DOCKER_WAIT_FOR_PY312} -ne 0 ]; then echo "Test to Python 3.12 failed"; exit 1; fi diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 88482bb2..c6a85d08 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -1,6 +1,17 @@ version: '3' services: + python3.12: + build: + context: . + dockerfile: Dockerfile.py312.test + environment: + - PRAGMA_VERSION=py3.12 + command: > + sh -c "flake8 src/ tests/ && + pylint src/ tests/ && + pytest" + python3.11: build: context: . @@ -44,14 +55,3 @@ services: sh -c "flake8 src/ tests/ && pylint src/ tests/ && pytest" - - python3.7: - build: - context: . - dockerfile: Dockerfile.py37.test - environment: - - PRAGMA_VERSION=py3.7 - command: > - sh -c "flake8 src/ tests/ && - pylint src/ tests/ && - pytest" diff --git a/docs/conf.py b/docs/conf.py index 6c1e6fac..af0d3a74 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,14 +1,9 @@ -from pallets_sphinx_themes import ProjectLink - -# -- Project information ----------------------------------------------------- +from pallets_sphinx_themes import ProjectLink, get_version project = 'Flask-JSONRPC' copyright = '2021, Nycholas de Oliveira e Oliveira' # pylint: disable=W0622 author = 'Nycholas de Oliveira e Oliveira' -release = '2.2.2' -version = '2.2.2' - -# -- General configuration --------------------------------------------------- +release, version = get_version('Flask-JSONRPC') master_doc = 'index' extensions = [ @@ -24,12 +19,11 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] issues_github_path = 'cenobites/flask-jsonrpc' -# -- Options for HTML output ------------------------------------------------- - html_theme = 'flask' html_theme_options = {'index_sidebar_logo': False} html_context = { 'project_links': [ + ProjectLink('Donate', 'https://github.com/sponsors/nycholas'), ProjectLink('PyPI Releases', 'https://pypi.org/project/Flask-JSONRPC/'), ProjectLink('Source Code', 'https://github.com/cenobites/flask-jsonrpc/'), ProjectLink('Issue Tracker', 'https://github.com/cenobites/flask-jsonrpc/issues/'), @@ -47,6 +41,4 @@ html_title = f'Flask-JSONRPC Documentation ({version})' html_show_sourcelink = False -# LaTeX ---------------------------------------------------------------- - latex_documents = [(master_doc, f'Flask-JSONRPC-{version}.tex', html_title, author, 'manual')] diff --git a/examples/cerberus_params_validator/example2.py b/examples/cerberus_params_validator/example2.py index 8cd4dc72..25653b94 100755 --- a/examples/cerberus_params_validator/example2.py +++ b/examples/cerberus_params_validator/example2.py @@ -59,10 +59,17 @@ class DataclassValidatorMixin: default_schema = None def validate( - self, obj: Any, schema: Optional[Dict[str, Any]] = None, update: bool = False, normalize: bool = True + self, + obj: Any, + schema: Optional[Dict[str, Any]] = None, + update: bool = False, + normalize: bool = True, ) -> bool: return super().validate( - obj.__dict__, schema=self.default_schema if schema is None else schema, update=update, normalize=normalize + obj.__dict__, + schema=self.default_schema if schema is None else schema, + update=update, + normalize=normalize, ) diff --git a/examples/cerberus_params_validator/example3.py b/examples/cerberus_params_validator/example3.py index 7ccc6fa7..5e2749fc 100755 --- a/examples/cerberus_params_validator/example3.py +++ b/examples/cerberus_params_validator/example3.py @@ -52,10 +52,17 @@ class SchemaValidatorMixin: default_schema = None def validate( - self, obj: Any, schema: Optional[Dict[str, Any]] = None, update: bool = False, normalize: bool = True + self, + obj: Any, + schema: Optional[Dict[str, Any]] = None, + update: bool = False, + normalize: bool = True, ) -> bool: return super().validate( - obj, schema=self.default_schema if schema is None else schema, update=update, normalize=normalize + obj, + schema=self.default_schema if schema is None else schema, + update=update, + normalize=normalize, ) diff --git a/examples/decorator/decorator.py b/examples/decorator/decorator.py index bb4fa7f7..0b3f55dd 100755 --- a/examples/decorator/decorator.py +++ b/examples/decorator/decorator.py @@ -77,7 +77,10 @@ def index() -> str: @check_terminal_id @jsonrpc_headers def decorators() -> dict: - return {'terminal_id': request.get_json(silent=True).get('terminal_id', 0), 'headers': str(request.headers)} + return { + 'terminal_id': request.get_json(silent=True).get('terminal_id', 0), + 'headers': str(request.headers), + } if __name__ == '__main__': diff --git a/pyproject.toml b/pyproject.toml index bdcd5aab..71fc63b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,26 +1,68 @@ [build-system] -requires = [ - 'setuptools>=42', - 'wheel' +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "Flask-JSONRPC" +version = "2.2.2" +description = "Adds JSONRPC support to Flask." +readme = {file = "README.md", content-type = "text/markdown"} +license = {file = "LICENSE"} +authors = [{name = "Nycholas Oliveira", email = "nycholas@cenobit.es"}] +maintainers = [{name = "Cenobit Technologies Inc.", email = "hi@cenobit.es"}] +keywords = ["flask", "flask-extensions", "jsonrpc"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Internet :: WWW/HTTP :: Dynamic Content", + "Topic :: Software Development :: Libraries :: Application Frameworks", +] +requires-python = ">=3.8" +dependencies = [ + "Flask>=3.0.0,<4.0", + "typeguard==2.13.3", + "typing_extensions>=4.3.0", + "typing_inspect==0.9.0", ] -build-backend = 'setuptools.build_meta' + +[project.optional-dependencies] +async = ["Flask[async]>=3.0.0,<4.0"] +dotenv = ["Flask[dotenv]>=3.0.0,<4.0"] + +[project.urls] +Donate = "https://github.com/sponsors/nycholas" +Documentation = "https://flask-jsonrpc.readthedocs.io/" +"Source Code" = "https://github.com/cenobites/flask-jsonrpc" +"Issue Tracker" = "https://github.com/cenobites/flask-jsonrpc/issues/" +Website = "https://flask-jsonrpc.readthedocs.io/" [tool.black] line-length = 120 skip-string-normalization = true -target-version = ['py310'] -include = '\.pyi?$' -exclude = ''' +target-version = ["py312"] +include = ".pyi?$" +exclude = """ ( /( - \.eggs - | \.git - | \.hg - | \.mypy_cache - | \.pytype - | \.pytest_cache - | \.tox - | \.venv + .eggs + | .git + | .hg + | .mypy_cache + | .pytype + | .pytest_cache + | .tox + | .venv | _build | buck-out | build @@ -31,4 +73,105 @@ exclude = ''' )/ | settings.py ) -''' +""" + +[tool.isort] +profile = "black" +multi_line_output = 3 +include_trailing_comma = true +force_grid_wrap = 0 +use_parentheses = true +length_sort = true +line_length = 120 +known_flask = "flask" +sections = "FUTURE,STDLIB,FLASK,THIRDPARTY,FIRSTPARTY,LOCALFOLDER" + +[tool.flake8] +select = "B,E,F,W,B9,Y0,ISC,I0" +ignore = "D203,E302,F401,F403,F405,W503" +exclude = ".git,.tox,.docker,.github,.venv,__pycache__,docs,old,build,dist,htmlcov,junit,.pytest_cache,htmldoc" +max-complexity = 10 +max-line-length = 120 + +[tool.pycodestyle] +max-line-length = 120 +exclude = ".git,.tox,.docker,.github,.venv,__pycache__,docs,old,build,dist,htmlcov,junit,.pytest_cache,htmldoc" + +[tool.pytest.ini_options] +addopts = "--pyargs --doctest-modules --junitxml=junit/test-results.xml --cov-report=html --cov-report=term --cov=flask_jsonrpc --cov-fail-under=100" +junit_family = "xunit2" +python_files = ["tests.py", "test_*.py", "*_tests.py"] +pythonpath = "src/" +testpaths = [ + "src/flask_jsonrpc", + "tests", +] +norecursedirs = [ + "tests/test_apps", + "tests/integration", +] +required_plugins = [ + "pytest-cov", + "pytest-xdist", + "pytest-sugar", + "pytest-env", +] + +[tool.coverage.run] +branch = true +source = [ + "src/flask_jsonrpc", + "tests", +] +omit = [ + "*/settings.py", + "*/fixtures.py", + "*/tests.py", + "*/test_*.py", + "*/*_tests.py", +] + +[tool.coverage.paths] +source = [ + "src", + "*/site-packages", +] + +[tool.coverage.report] +fail_under = 100 +ignore_errors = true +skip_covered = true +exclude_lines = [ + "pragma: no cover", + "pragma: no cover ${PRAGMA_VERSION}", + "def __repr__", + "if self\\.debug", + "if settings\\.DEBUG", + "if current_app\\.config\\['DEBUG'\\]", + "if app\\.config\\['DEBUG'\\]", + "raise AssertionError", + "raise NotImplementedError", + "if 0:", + "if __name__ == .__main__.:", + "if TYPE_CHECKING:", + "if t\\.TYPE_CHECKING:", +] + +[tool.mypy] +files = ["src/flask_jsonrpc"] +python_version = "3.12" +check_untyped_defs = true +ignore_errors = false +ignore_missing_imports = false +strict = true + +[[tool.mypy.overrides]] +module = [ + "dotenv.*", + "importlib_metadata", +] +ignore_missing_imports = true + +[tool.pytype] +inputs = ["src/flask_jsonrpc"] +python_version = "3.11" diff --git a/requirements/base.txt b/requirements/base.txt index cb998c59..a86072e4 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,9 +1,9 @@ # Flask # ------------------------------------------------------------------------------ -Flask>=1.0.0,<3.0 # https://github.com/pallets/flask +Flask>=3.0.0,<4.0 # https://github.com/pallets/flask # Tools # ------------------------------------------------------------------------------ typeguard==2.13.3 # https://github.com/agronholm/typeguard typing_extensions>=4.3.0 # https://github.com/python/typing/blob/master/typing_extensions/README.rst -typing_inspect==0.8.0 # https://github.com/ilevkivskyi/typing_inspect +typing_inspect==0.9.0 # https://github.com/ilevkivskyi/typing_inspect diff --git a/requirements/ci.txt b/requirements/ci.txt index a5c32f54..3d7779d2 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -1,37 +1,36 @@ # Code formatter # ------------------------------------------------------------------------------ -black==23.3.0 # https://github.com/psf/black -pyupgrade==3.3.1 # https://github.com/asottile/pyupgrade +black==23.12.1 # https://github.com/psf/black +pyupgrade==3.15.0 # https://github.com/asottile/pyupgrade # Code quality # ------------------------------------------------------------------------------ -flake8==6.0.0;python_version>"3.7" # https://github.com/PyCQA/flake8 -flake8<6.0.0;python_version<="3.7" -flake8-isort==6.0.0 # https://github.com/gforcada/flake8-isort +flake8==7.0.0 # https://github.com/PyCQA/flake8 +flake8-isort==6.1.1 # https://github.com/gforcada/flake8-isort isort>=4.2.5,<6 # https://github.com/PyCQA/isort -flake8-bugbear==23.3.23;python_version>"3.7" # https://github.com/PyCQA/flake8-bugbear -flake8-bugbear<22.10;python_version<="3.7" -flake8-pyi==23.3.1 # https://github.com/ambv/flake8-pyi +flake8-bugbear==24.1.17 # https://github.com/PyCQA/flake8-bugbear +flake8-pyi==24.1.0 # https://github.com/ambv/flake8-pyi flake8-quotes==3.3.2 # https://github.com/zheller/flake8-quotes flake8-implicit-str-concat==0.4.0 # https://github.com/keisheiled/flake8-implicit-str-concat +flake8-pyproject==1.2.3 # https://github.com/john-hen/Flake8-pyproject # Code linter # ------------------------------------------------------------------------------ -pylint==2.17.1 # https://github.com/PyCQA/pylint +pylint==3.0.3 # https://github.com/PyCQA/pylint # Type check # ------------------------------------------------------------------------------ -mypy==1.1.1;python_version>="3.11" # https://github.com/python/mypy -pytype==2022.10.13;python_version>="3.10" and python_version<"3.11" # https://github.com/google/pytype -types_setuptools==67.6.0.7 # https://github.com/python/typeshed +mypy==1.8.0;python_version>="3.11" # https://github.com/python/mypy +pytype==2024.1.5;python_version>="3.10" and python_version<"3.12" # https://github.com/google/pytype +types_setuptools==69.0.0.20240115 # https://github.com/python/typeshed typeguard==2.13.3 # https://github.com/agronholm/typeguard # Security check # ------------------------------------------------------------------------------ -bandit==1.7.5;python_version>="3.11" # https://github.com/PyCQA/bandit -safety@git+https://github.com/pyupio/safety;python_version>="3.11" # https://github.com/pyupio/safety +bandit==1.7.6;python_version>="3.11" # https://github.com/PyCQA/bandit +safety==3.0.0;python_version>="3.11" # https://github.com/pyupio/safety # Code quality # ------------------------------------------------------------------------------ -coverage==6.5.0 # https://github.com/nedbat/coveragepy -coveralls==3.3.1 # https://github.com/marketplace/coveralls +coverage==7.4.0 # https://github.com/nedbat/coveragepy +python-coveralls==2.9.3 # https://github.com/z4r/python-coveralls diff --git a/requirements/docs.txt b/requirements/docs.txt index cd693985..d36ad64c 100644 --- a/requirements/docs.txt +++ b/requirements/docs.txt @@ -1,10 +1,10 @@ # Documentation # ------------------------------------------------------------------------------ -Sphinx==6.1.3 -sphinx-tabs==3.4.1 +Sphinx==7.2.6 +sphinx-tabs==3.4.4 sphinx-issues==3.0.1 sphinxcontrib-log-cabinet==1.0.1 # Pallets # ------------------------------------------------------------------------------ -Pallets-Sphinx-Themes==2.0.3 +Pallets-Sphinx-Themes==2.1.1 diff --git a/requirements/test.txt b/requirements/test.txt index c344665a..7f4c5c4e 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -1,12 +1,12 @@ # Testing # ------------------------------------------------------------------------------ -pytest==7.2.2 # https://github.com/pytest-dev/pytest -pytest-cov==4.0.0 # https://github.com/pytest-dev/pytest-cov -pytest-xdist==3.2.1 # https://github.com/pytest-dev/pytest-xdist -pytest-sugar==0.9.6 # https://github.com/Frozenball/pytest-sugar -pytest-env==0.8.1 # https://github.com/MobileDynasty/pytest-env -pytest-selenium@git+https://github.com/pytest-dev/pytest-selenium.git;python_version>"3.7" # https://github.com/pytest-dev/pytest-selenium -mock==5.0.1 # https://github.com/testing-cabal/mock +pytest==7.4.4 # https://github.com/pytest-dev/pytest +pytest-cov==4.1.0 # https://github.com/pytest-dev/pytest-cov +pytest-xdist==3.5.0 # https://github.com/pytest-dev/pytest-xdist +pytest-sugar==0.9.7 # https://github.com/Frozenball/pytest-sugar +pytest-env==1.1.3 # https://github.com/MobileDynasty/pytest-env +pytest-selenium@git+https://github.com/pytest-dev/pytest-selenium.git # https://github.com/pytest-dev/pytest-selenium +mock==5.1.0 # https://github.com/testing-cabal/mock # Type check # ------------------------------------------------------------------------------ @@ -14,10 +14,9 @@ typeguard==2.13.3 # https://github.com/agronholm/typeguard # Code quality # ------------------------------------------------------------------------------ -coverage==6.5.0 # https://github.com/nedbat/coveragepy -coveralls==3.3.1 # https://github.com/marketplace/coveralls +coverage==7.4.0 # https://github.com/nedbat/coveragepy +python-coveralls==2.9.3 # https://github.com/z4r/python-coveralls # Tools # ------------------------------------------------------------------------------ -requests==2.28.2 # https://github.com/psf/requests -py==1.11.0 # https://github.com/pytest-dev/py +requests==2.31.0 # https://github.com/psf/requests diff --git a/setup.cfg b/setup.cfg index d03f8c66..fc5b9c36 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,40 +1,9 @@ [metadata] name = Flask-JSONRPC -version = attr: flask_jsonrpc.__version__ -description = Adds JSONRPC support to Flask. -long_description = file: README -long_description_content_type = text/plain -url = https://github.com/cenobites/flask-jsonrpc -author = Nycholas de Oliveira e Oliveira -author_email = nycholas@gmail.com -maintainer = Cenobit Technologies Inc. -maintainer_email = hi@cenobit.es -license = BSD-3-Clause -license_files = - LICENSE -classifiers = - Environment :: Web Environment - Intended Audience :: Developers - License :: OSI Approved :: BSD License - Operating System :: OS Independent - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3 :: Only - 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 - Topic :: Internet :: WWW/HTTP :: Dynamic Content - Topic :: Software Development :: Libraries :: Application Frameworks -project_urls = - Documentation = https://flask-jsonrpc.readthedocs.io/ - Source Code = https://github.com/cenobites/flask-jsonrpc - Issue Tracker = https://github.com/cenobites/flask-jsonrpc/issues/ [options] packages = find: -python_requires = >=3.7 +python_requires = >=3.8 include_package_data = True package_dir = =src @@ -45,85 +14,3 @@ where = src [aliases] test = pytest - -[tool:pytest] -addopts = --pyargs --doctest-modules --junitxml=junit/test-results.xml --cov-report=html --cov-report=term --cov=flask_jsonrpc --cov-fail-under=100 -junit_family = xunit2 -python_files = tests.py test_*.py *_tests.py -pythonpath = src/ -norecursedirs = tests/test_apps -testpaths = - tests - flask_jsonrpc -required_plugins = - pytest-cov - pytest-xdist - pytest-sugar - pytest-env - -[coverage:run] -branch = True -source = - flask_jsonrpc - tests -omit = - */settings.py - */fixtures.py - */tests.py - */test_*.py - */*_tests.py - -[coverage:paths] -source = - src - */site-packages - -[coverage:report] -fail_under = 100 -ignore_errors = True -skip_covered = True -exclude_lines = - pragma: no cover - def __repr__ - if self.debug: - if settings.DEBUG - if app.config['DEBUG'] - raise AssertionError - raise NotImplementedError - if 0: - if __name__ == .__main__.: - if TYPE_CHECKING: - if t.TYPE_CHECKING: - -[isort] -multi_line_output = 3 -include_trailing_comma = True -force_grid_wrap = 0 -use_parentheses = True -length_sort = True -line_length = 120 -known_flask = flask -sections = FUTURE,STDLIB,FLASK,THIRDPARTY,FIRSTPARTY,LOCALFOLDER - -[flake8] -select = B,E,F,W,B9,Y0,ISC,I0 -ignore = D203,E302,F401,F403,F405,W503 -exclude = .git,.tox,.docker,.github,.venv,__pycache__,docs,old,build,dist,htmlcov,junit,.pytest_cache,htmldoc -max-complexity = 10 -max-line-length = 120 - -[pycodestyle] -max-line-length = 120 -exclude = .git,.tox,.docker,.github,.venv,__pycache__,docs,old,build,dist,htmlcov,junit,.pytest_cache,htmldoc - -[mypy] -files = src/flask_jsonrpc -python_version = 3.11 -check_untyped_defs = True -ignore_errors = False -ignore_missing_imports = False -strict = True - -[pytype] -inputs = src/flask_jsonrpc -python_version = 3.10 diff --git a/setup.py b/setup.py index 716d628a..483c0cb8 100755 --- a/setup.py +++ b/setup.py @@ -27,17 +27,4 @@ # POSSIBILITY OF SUCH DAMAGE. import setuptools -# Metadata goes in setup.cfg. These are here for GitHub's dependency graph. -setuptools.setup( - name='Flask-JSONRPC', - install_requires=[ - 'Flask>=1.0.0,<3.0', - 'typeguard==2.13.3', - 'typing_extensions>=4.3.0', - 'typing_inspect==0.8.0', - ], - extras_require={ - 'async': ['Flask[async]>=1.0.0,<3.0'], - 'dotenv': ['Flask[dotenv]>=1.0.0,<3.0'], - }, -) +setuptools.setup() diff --git a/src/flask_jsonrpc/__init__.py b/src/flask_jsonrpc/__init__.py index 882babff..15c1922a 100644 --- a/src/flask_jsonrpc/__init__.py +++ b/src/flask_jsonrpc/__init__.py @@ -24,8 +24,8 @@ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +from __future__ import annotations + from .app import JSONRPC from .views import JSONRPCView from .blueprints import JSONRPCBlueprint - -__version__ = '2.2.2' diff --git a/src/flask_jsonrpc/app.py b/src/flask_jsonrpc/app.py index 4be8b880..ce1cee65 100644 --- a/src/flask_jsonrpc/app.py +++ b/src/flask_jsonrpc/app.py @@ -92,7 +92,12 @@ def init_app(self, app: Flask) -> None: if app.config['DEBUG'] or self.enable_web_browsable_api: self.init_browse_app(app) - def register(self, view_func: t.Callable[..., t.Any], name: t.Optional[str] = None, **options: t.Any) -> None: + def register( + self, + view_func: t.Callable[..., t.Any], + name: t.Optional[str] = None, + **options: t.Any, + ) -> None: self.register_view_function(view_func, name, **options) def register_blueprint( @@ -114,7 +119,8 @@ def register_blueprint( app.add_url_rule( path, view_func=jsonrpc_app.get_jsonrpc_site_api().as_view( - urn('blueprint', app.name, jsonrpc_app.name, path), jsonrpc_site=jsonrpc_app.get_jsonrpc_site() + urn('blueprint', app.name, jsonrpc_app.name, path), + jsonrpc_site=jsonrpc_app.get_jsonrpc_site(), ), ) diff --git a/src/flask_jsonrpc/contrib/browse/__init__.py b/src/flask_jsonrpc/contrib/browse/__init__.py index 92f93229..5770ab43 100644 --- a/src/flask_jsonrpc/contrib/browse/__init__.py +++ b/src/flask_jsonrpc/contrib/browse/__init__.py @@ -41,7 +41,10 @@ class JSONRPCBrowse: def __init__( - self, app: t.Optional['Flask'] = None, url_prefix: str = '/api/browse', base_url: t.Optional[str] = None + self, + app: t.Optional['Flask'] = None, + url_prefix: str = '/api/browse', + base_url: t.Optional[str] = None, ) -> None: self.app = app self.url_prefix = url_prefix @@ -64,7 +67,9 @@ def init_app(self, app: 'Flask') -> None: app.register_blueprint(browse, url_prefix=self.url_prefix) app.add_url_rule( - f'{self.url_prefix}/static/', 'urn:browse.static', view_func=app.send_static_file + f'{self.url_prefix}/static/', + 'urn:browse.static', + view_func=app.send_static_file, ) def register_jsonrpc_site(self, jsonrpc_site: 'JSONRPCSite') -> None: diff --git a/src/flask_jsonrpc/site.py b/src/flask_jsonrpc/site.py index 52d4507d..37d7a76e 100644 --- a/src/flask_jsonrpc/site.py +++ b/src/flask_jsonrpc/site.py @@ -71,9 +71,11 @@ def is_json(self) -> bool: https://github.com/pallets/werkzeug/blob/master/src/werkzeug/wrappers/json.py#L54 """ mt = request.mimetype - return mt in ('application/json', 'application/json-rpc', 'application/jsonrequest') or ( - mt.startswith('application/') and mt.endswith('+json') - ) + return mt in ( + 'application/json', + 'application/json-rpc', + 'application/jsonrequest', + ) or (mt.startswith('application/') and mt.endswith('+json')) def set_path(self, path: str) -> None: self.path = path @@ -86,7 +88,7 @@ def register(self, name: str, view_func: t.Callable[..., t.Any]) -> None: def dispatch_request( self, - ) -> t.Tuple[t.Any, int, t.Union[Headers, t.Dict[str, str], t.Tuple[str], t.List[t.Tuple[str]]]]: + ) -> t.Tuple[t.Any, int, t.Union[Headers, t.Dict[str, str], t.Tuple[str], t.List[t.Tuple[str]]],]: if not self.validate_request(): raise ParseError( data={ @@ -115,7 +117,7 @@ def to_json(self, request_data: bytes) -> t.Any: def handle_dispatch_except( self, req_json: t.Dict[str, t.Any] - ) -> t.Tuple[t.Any, int, t.Union[Headers, t.Dict[str, str], t.Tuple[str], t.List[t.Tuple[str]]]]: + ) -> t.Tuple[t.Any, int, t.Union[Headers, t.Dict[str, str], t.Tuple[str], t.List[t.Tuple[str]]],]: try: if not self.validate(req_json): raise InvalidRequestError(data={'message': f'Invalid JSON: {req_json!r}'}) @@ -140,7 +142,7 @@ def handle_dispatch_except( def batch_dispatch( self, reqs_json: t.List[t.Dict[str, t.Any]] - ) -> t.Tuple[t.List[t.Any], int, t.Union[Headers, t.Dict[str, str], t.Tuple[str], t.List[t.Tuple[str]]]]: + ) -> t.Tuple[t.List[t.Any], int, t.Union[Headers, t.Dict[str, str], t.Tuple[str], t.List[t.Tuple[str]]],]: if not reqs_json: raise InvalidRequestError(data={'message': 'Empty array'}) @@ -158,12 +160,16 @@ def batch_dispatch( def dispatch( self, req_json: t.Dict[str, t.Any] - ) -> t.Tuple[t.Any, int, t.Union[Headers, t.Dict[str, str], t.Tuple[str], t.List[t.Tuple[str]]]]: + ) -> t.Tuple[t.Any, int, t.Union[Headers, t.Dict[str, str], t.Tuple[str], t.List[t.Tuple[str]]],]: method_name = req_json['method'] params = req_json.get('params', {}) view_func = self.view_funcs.get(method_name) validate = getattr(view_func, 'jsonrpc_validate', settings.DEFAULT_JSONRPC_METHOD['VALIDATE']) - notification = getattr(view_func, 'jsonrpc_notification', settings.DEFAULT_JSONRPC_METHOD['NOTIFICATION']) + notification = getattr( + view_func, + 'jsonrpc_notification', + settings.DEFAULT_JSONRPC_METHOD['NOTIFICATION'], + ) if not view_func: raise MethodNotFoundError(data={'message': f"Method not found: {method_name}"}) @@ -208,7 +214,7 @@ def validate(self, req_json: t.Dict[str, t.Any]) -> bool: def unpack_tuple_returns( self, resp_view: t.Any - ) -> t.Tuple[t.Any, int, t.Union[Headers, t.Dict[str, str], t.Tuple[str], t.List[t.Tuple[str]]]]: + ) -> t.Tuple[t.Any, int, t.Union[Headers, t.Dict[str, str], t.Tuple[str], t.List[t.Tuple[str]]],]: # https://github.com/pallets/flask/blob/d091bb00c0358e9f30006a064f3dbb671b99aeae/src/flask/app.py#L1981 if isinstance(resp_view, tuple): len_resp_view = len(resp_view) @@ -235,11 +241,15 @@ def unpack_tuple_returns( def make_response( self, req_json: t.Dict[str, t.Any], resp_view: t.Any - ) -> t.Tuple[t.Any, int, t.Union[Headers, t.Dict[str, str], t.Tuple[str], t.List[t.Tuple[str]]]]: + ) -> t.Tuple[t.Any, int, t.Union[Headers, t.Dict[str, str], t.Tuple[str], t.List[t.Tuple[str]]],]: rv, status_code, headers = self.unpack_tuple_returns(resp_view) if self.is_notification_request(req_json): return None, 204, headers - resp = {'id': req_json.get('id'), 'jsonrpc': req_json.get('jsonrpc', JSONRPC_VERSION_DEFAULT), 'result': rv} + resp = { + 'id': req_json.get('id'), + 'jsonrpc': req_json.get('jsonrpc', JSONRPC_VERSION_DEFAULT), + 'result': rv, + } return resp, status_code, headers def is_notification_request(self, req_json: t.Dict[str, t.Any]) -> bool: diff --git a/src/flask_jsonrpc/wrappers.py b/src/flask_jsonrpc/wrappers.py index 69ca6d3b..4ab12173 100644 --- a/src/flask_jsonrpc/wrappers.py +++ b/src/flask_jsonrpc/wrappers.py @@ -67,7 +67,7 @@ def _get_function(self, fn: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: if isfunction(fn): return fn if ismethod(fn) and getattr(fn, '__func__', None): - return fn.__func__ # pytype: disable=attribute-error + return fn.__func__ # pytype: disable=attribute-error,bad-return-type raise ValueError('the view function must be either a function or a method') def get_jsonrpc_site(self) -> 'JSONRPCSite': @@ -77,7 +77,10 @@ def get_jsonrpc_site_api(self) -> t.Type['JSONRPCView']: raise NotImplementedError('.get_jsonrpc_site_api must be overridden') def register_view_function( - self, view_func: t.Callable[..., t.Any], name: t.Optional[str] = None, **options: t.Dict[str, t.Any] + self, + view_func: t.Callable[..., t.Any], + name: t.Optional[str] = None, + **options: t.Dict[str, t.Any], ) -> t.Callable[..., t.Any]: fn = self._get_function(view_func) fn_options = self._method_options(options) diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py new file mode 100644 index 00000000..9817cbce --- /dev/null +++ b/tests/integration/__init__.py @@ -0,0 +1,26 @@ +# Copyright (c) 2022-2022, Cenobit Technologies, Inc. http://cenobit.es/ +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the Cenobit Technologies nor the names of +# its contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. diff --git a/tests/test_apps/conftest.py b/tests/integration/conftest.py similarity index 100% rename from tests/test_apps/conftest.py rename to tests/integration/conftest.py diff --git a/tests/test_apps/pytest.ini b/tests/integration/pytest.ini similarity index 86% rename from tests/test_apps/pytest.ini rename to tests/integration/pytest.ini index bbb0972f..f0f5beac 100644 --- a/tests/test_apps/pytest.ini +++ b/tests/integration/pytest.ini @@ -2,7 +2,6 @@ addopts = -rsxX -q python_files = tests.py test_*.py *_tests.py pythonpath = . -norecursedirs = app async_app junit_family = xunit2 required_plugins = pytest-cov diff --git a/tests/test_apps/pytest.local.ini b/tests/integration/pytest.local.ini similarity index 93% rename from tests/test_apps/pytest.local.ini rename to tests/integration/pytest.local.ini index e67e8520..a9eef539 100644 --- a/tests/test_apps/pytest.local.ini +++ b/tests/integration/pytest.local.ini @@ -2,7 +2,6 @@ addopts = -rsxX -q python_files = tests.py test_*.py *_tests.py pythonpath = . -norecursedirs = app async_app junit_family = xunit2 required_plugins = pytest-cov diff --git a/tests/test_apps/test_app.py b/tests/integration/test_app.py similarity index 64% rename from tests/test_apps/test_app.py rename to tests/integration/test_app.py index ae708ef6..9544eb25 100644 --- a/tests/test_apps/test_app.py +++ b/tests/integration/test_app.py @@ -1,3 +1,4 @@ +# pylint: disable=C0302 # Copyright (c) 2022-2022, Cenobit Technologies, Inc. http://cenobit.es/ # All rights reserved. # @@ -40,13 +41,25 @@ def test_greeting(self): self.assertEqual(200, rv.status_code) rv = self.requests.post( - API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']} + API_URL, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['Python'], + }, ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Python'}, rv.json()) self.assertEqual(200, rv.status_code) rv = self.requests.post( - API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': {'name': 'Flask'}} + API_URL, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': {'name': 'Flask'}, + }, ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask'}, rv.json()) self.assertEqual(200, rv.status_code) @@ -62,7 +75,14 @@ def test_app_greeting_with_different_content_types(self): rv = self.requests.post( API_URL, - data=json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']}), + data=json.dumps( + { + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['Python'], + } + ), headers={'Content-Type': 'application/jsonrequest'}, ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Python'}, rv.json()) @@ -70,7 +90,14 @@ def test_app_greeting_with_different_content_types(self): rv = self.requests.post( API_URL, - data=json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': {'name': 'Flask'}}), + data=json.dumps( + { + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': {'name': 'Flask'}, + } + ), headers={'Content-Type': 'application/json'}, ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask'}, rv.json()) @@ -164,7 +191,13 @@ def test_greeting_raise_invalid_request_error(self): def test_greeting_raise_invalid_params_error(self): rv = self.requests.post( - API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': 'Wrong'} + API_URL, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': 'Wrong', + }, ) self.assertEqual( { @@ -183,7 +216,15 @@ def test_greeting_raise_invalid_params_error(self): ) self.assertEqual(400, rv.status_code) - rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': [1]}) + rv = self.requests.post( + API_URL, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': [1], + }, + ) self.assertEqual( { 'id': 1, @@ -200,7 +241,13 @@ def test_greeting_raise_invalid_params_error(self): self.assertEqual(400, rv.status_code) rv = self.requests.post( - API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': {'name': 2}} + API_URL, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': {'name': 2}, + }, ) self.assertEqual( { @@ -236,19 +283,39 @@ def test_greeting_raise_method_not_found_error(self): def test_echo(self): rv = self.requests.post( - API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': ['Python']} + API_URL, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.echo', + 'params': ['Python'], + }, ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Python'}, rv.json()) self.assertEqual(200, rv.status_code) rv = self.requests.post( - API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': {'string': 'Flask'}} + API_URL, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.echo', + 'params': {'string': 'Flask'}, + }, ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Flask'}, rv.json()) self.assertEqual(200, rv.status_code) def test_echo_raise_invalid_params_error(self): - rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': 'Wrong'}) + rv = self.requests.post( + API_URL, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.echo', + 'params': 'Wrong', + }, + ) self.assertEqual( { 'id': 1, @@ -266,7 +333,10 @@ def test_echo_raise_invalid_params_error(self): ) self.assertEqual(400, rv.status_code) - rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': [1]}) + rv = self.requests.post( + API_URL, + json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': [1]}, + ) self.assertEqual( { 'id': 1, @@ -283,7 +353,13 @@ def test_echo_raise_invalid_params_error(self): self.assertEqual(400, rv.status_code) rv = self.requests.post( - API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': {'name': 2}} + API_URL, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.echo', + 'params': {'name': 2}, + }, ) self.assertEqual( { @@ -321,24 +397,44 @@ def test_notify(self): self.assertEqual('', rv.text) self.assertEqual(204, rv.status_code) - rv = self.requests.post(API_URL, json={'jsonrpc': '2.0', 'method': 'jsonrpc.notify', 'params': ['Some string']}) + rv = self.requests.post( + API_URL, + json={ + 'jsonrpc': '2.0', + 'method': 'jsonrpc.notify', + 'params': ['Some string'], + }, + ) self.assertEqual('', rv.text) self.assertEqual(204, rv.status_code) def test_not_allow_notify(self): - rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.not_allow_notify'}) + rv = self.requests.post( + API_URL, + json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.not_allow_notify'}, + ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Not allow notify'}, rv.json()) self.assertEqual(200, rv.status_code) rv = self.requests.post( API_URL, - json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.not_allow_notify', 'params': ['Some string']}, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.not_allow_notify', + 'params': ['Some string'], + }, ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Not allow notify'}, rv.json()) self.assertEqual(200, rv.status_code) rv = self.requests.post( - API_URL, json={'jsonrpc': '2.0', 'method': 'jsonrpc.not_allow_notify', 'params': ['Some string']} + API_URL, + json={ + 'jsonrpc': '2.0', + 'method': 'jsonrpc.not_allow_notify', + 'params': ['Some string'], + }, ) self.assertEqual( { @@ -359,11 +455,22 @@ def test_not_allow_notify(self): self.assertEqual(400, rv.status_code) def test_fails(self): - rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.fails', 'params': [2]}) + rv = self.requests.post( + API_URL, + json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.fails', 'params': [2]}, + ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 2}, rv.json()) self.assertEqual(200, rv.status_code) - rv = self.requests.post(API_URL, json={'id': '1', 'jsonrpc': '2.0', 'method': 'jsonrpc.fails', 'params': [1]}) + rv = self.requests.post( + API_URL, + json={ + 'id': '1', + 'jsonrpc': '2.0', + 'method': 'jsonrpc.fails', + 'params': [1], + }, + ) self.assertEqual( { 'id': '1', @@ -388,7 +495,12 @@ def test_strange_echo(self): } rv = self.requests.post(API_URL, json=data) self.assertEqual( - {'id': 1, 'jsonrpc': '2.0', 'result': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Flask']}, rv.json() + { + 'id': 1, + 'jsonrpc': '2.0', + 'result': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Flask'], + }, + rv.json(), ) self.assertEqual(200, rv.status_code) @@ -400,7 +512,12 @@ def test_strange_echo(self): } rv = self.requests.post(API_URL, json=data) self.assertEqual( - {'id': 1, 'jsonrpc': '2.0', 'result': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Default']}, rv.json() + { + 'id': 1, + 'jsonrpc': '2.0', + 'result': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Default'], + }, + rv.json(), ) self.assertEqual(200, rv.status_code) @@ -410,27 +527,52 @@ def test_sum(self): self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 4}, rv.json()) self.assertEqual(200, rv.status_code) - data = {'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.sum', 'params': [0.5, 1.5]} + data = { + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.sum', + 'params': [0.5, 1.5], + } rv = self.requests.post(API_URL, json=data) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 2.0}, rv.json()) self.assertEqual(200, rv.status_code) def test_decorators(self): - data = {'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.decorators', 'params': ['Python']} + data = { + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.decorators', + 'params': ['Python'], + } rv = self.requests.post(API_URL, json=data) - self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Python from decorator, ;)'}, rv.json()) + self.assertEqual( + {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Python from decorator, ;)'}, + rv.json(), + ) self.assertEqual(200, rv.status_code) def test_return_status_code(self): rv = self.requests.post( - API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.returnStatusCode', 'params': ['OK']} + API_URL, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.returnStatusCode', + 'params': ['OK'], + }, ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Status Code OK'}, rv.json()) self.assertEqual(201, rv.status_code) def test_return_headers(self): rv = self.requests.post( - API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.returnHeaders', 'params': ['OK']} + API_URL, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.returnHeaders', + 'params': ['OK'], + }, ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Headers OK'}, rv.json()) self.assertEqual(200, rv.status_code) @@ -439,16 +581,29 @@ def test_return_headers(self): def test_return_status_code_and_headers(self): rv = self.requests.post( API_URL, - json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.returnStatusCodeAndHeaders', 'params': ['OK']}, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.returnStatusCodeAndHeaders', + 'params': ['OK'], + }, + ) + self.assertEqual( + {'id': 1, 'jsonrpc': '2.0', 'result': 'Status Code and Headers OK'}, + rv.json(), ) - self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Status Code and Headers OK'}, rv.json()) self.assertEqual(400, rv.status_code) self.assertEqual('1', rv.headers['X-JSONRPC']) def test_not_validate_method(self): rv = self.requests.post( API_URL, - json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.not_validate', 'params': ['OK']}, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.not_validate', + 'params': ['OK'], + }, ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Not validate: OK'}, rv.json()) self.assertEqual(200, rv.status_code) @@ -456,7 +611,12 @@ def test_not_validate_method(self): def test_no_return_method(self): rv = self.requests.post( API_URL, - json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.noReturn', 'params': []}, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.noReturn', + 'params': [], + }, ) self.assertEqual( { @@ -481,9 +641,24 @@ def test_with_rcp_batch(self): rv = self.requests.post( API_URL, json=[ - {'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']}, - {'id': 2, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Flask']}, - {'id': 3, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['JSON-RCP']}, + { + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['Python'], + }, + { + 'id': 2, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['Flask'], + }, + { + 'id': 3, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['JSON-RCP'], + }, ], ) self.assertEqual( @@ -499,10 +674,20 @@ def test_with_rcp_batch(self): rv = self.requests.post( API_URL, json=[ - {'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']}, + { + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['Python'], + }, {'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Flask']}, {'id': 3, 'jsonrpc': '2.0', 'params': ['Flask']}, - {'id': 4, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['JSON-RCP']}, + { + 'id': 4, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['JSON-RCP'], + }, ], ) self.assertEqual( @@ -533,17 +718,34 @@ def test_class(self): self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask JSON-RPC'}, rv.json()) self.assertEqual(200, rv.status_code) - rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'greeting', 'params': ['Python']}) + rv = self.requests.post( + API_URL, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'greeting', + 'params': ['Python'], + }, + ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Python'}, rv.json()) self.assertEqual(200, rv.status_code) rv = self.requests.post( - API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'hello', 'params': {'name': 'Flask'}} + API_URL, + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'hello', + 'params': {'name': 'Flask'}, + }, ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask'}, rv.json()) self.assertEqual(200, rv.status_code) - rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'echo', 'params': ['Python', 1]}) + rv = self.requests.post( + API_URL, + json={'id': 1, 'jsonrpc': '2.0', 'method': 'echo', 'params': ['Python', 1]}, + ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Python'}, rv.json()) self.assertEqual(200, rv.status_code) @@ -580,7 +782,14 @@ def test_system_describe(self): 'jsonrpc.greeting': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 'name', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 'name', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, @@ -588,8 +797,18 @@ def test_system_describe(self): 'type': 'method', 'options': {'notification': True, 'validate': True}, 'params': [ - {'name': 'string', 'type': 'String', 'required': False, 'nullable': False}, - {'name': '_some', 'type': 'Object', 'required': False, 'nullable': False}, + { + 'name': 'string', + 'type': 'String', + 'required': False, + 'nullable': False, + }, + { + 'name': '_some', + 'type': 'Object', + 'required': False, + 'nullable': False, + }, ], 'returns': {'type': 'String'}, 'description': None, @@ -597,21 +816,42 @@ def test_system_describe(self): 'jsonrpc.notify': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': '_string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': '_string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'Null'}, 'description': None, }, 'jsonrpc.not_allow_notify': { 'type': 'method', 'options': {'notification': False, 'validate': True}, - 'params': [{'name': '_string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': '_string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, 'jsonrpc.fails': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 'n', 'type': 'Number', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 'n', + 'type': 'Number', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'Number'}, 'description': None, }, @@ -619,11 +859,36 @@ def test_system_describe(self): 'type': 'method', 'options': {'notification': True, 'validate': True}, 'params': [ - {'name': 'string', 'type': 'String', 'required': False, 'nullable': False}, - {'name': 'omg', 'type': 'Object', 'required': False, 'nullable': False}, - {'name': 'wtf', 'type': 'Array', 'required': False, 'nullable': False}, - {'name': 'nowai', 'type': 'Number', 'required': False, 'nullable': False}, - {'name': 'yeswai', 'type': 'String', 'required': False, 'nullable': False}, + { + 'name': 'string', + 'type': 'String', + 'required': False, + 'nullable': False, + }, + { + 'name': 'omg', + 'type': 'Object', + 'required': False, + 'nullable': False, + }, + { + 'name': 'wtf', + 'type': 'Array', + 'required': False, + 'nullable': False, + }, + { + 'name': 'nowai', + 'type': 'Number', + 'required': False, + 'nullable': False, + }, + { + 'name': 'yeswai', + 'type': 'String', + 'required': False, + 'nullable': False, + }, ], 'returns': {'type': 'Array'}, 'description': None, @@ -632,8 +897,18 @@ def test_system_describe(self): 'type': 'method', 'options': {'notification': True, 'validate': True}, 'params': [ - {'name': 'a', 'type': 'Number', 'required': False, 'nullable': False}, - {'name': 'b', 'type': 'Number', 'required': False, 'nullable': False}, + { + 'name': 'a', + 'type': 'Number', + 'required': False, + 'nullable': False, + }, + { + 'name': 'b', + 'type': 'Number', + 'required': False, + 'nullable': False, + }, ], 'returns': {'type': 'Number'}, 'description': None, @@ -641,28 +916,56 @@ def test_system_describe(self): 'jsonrpc.decorators': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 'string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 'string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, 'jsonrpc.returnStatusCode': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 's', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 's', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'Array'}, 'description': None, }, 'jsonrpc.returnHeaders': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 's', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 's', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'Array'}, 'description': None, }, 'jsonrpc.returnStatusCodeAndHeaders': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 's', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 's', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'Array'}, 'description': None, }, @@ -676,28 +979,56 @@ def test_system_describe(self): 'jsonrpc.noReturn': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': '_string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': '_string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'Null'}, 'description': None, }, 'classapp.index': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 'name', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 'name', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, 'greeting': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 'name', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 'name', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, 'hello': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 'name', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 'name', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, @@ -705,8 +1036,18 @@ def test_system_describe(self): 'type': 'method', 'options': {'notification': True, 'validate': True}, 'params': [ - {'name': 'string', 'type': 'String', 'required': False, 'nullable': False}, - {'name': '_some', 'type': 'Object', 'required': False, 'nullable': False}, + { + 'name': 'string', + 'type': 'String', + 'required': False, + 'nullable': False, + }, + { + 'name': '_some', + 'type': 'Object', + 'required': False, + 'nullable': False, + }, ], 'returns': {'type': 'String'}, 'description': None, @@ -714,21 +1055,42 @@ def test_system_describe(self): 'notify': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': '_string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': '_string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'Null'}, 'description': None, }, 'not_allow_notify': { 'type': 'method', 'options': {'notification': False, 'validate': True}, - 'params': [{'name': '_string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': '_string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, 'fails': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 'n', 'type': 'Number', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 'n', + 'type': 'Number', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'Number'}, 'description': None, }, diff --git a/tests/test_apps/test_browse.py b/tests/integration/test_browse.py similarity index 100% rename from tests/test_apps/test_browse.py rename to tests/integration/test_browse.py diff --git a/tests/test_apps/app/__init__.py b/tests/test_apps/app/__init__.py index 7003a9d7..288d3b74 100644 --- a/tests/test_apps/app/__init__.py +++ b/tests/test_apps/app/__init__.py @@ -117,7 +117,11 @@ def fails(n: int) -> int: # pylint: disable=W0612 @jsonrpc.method('jsonrpc.strangeEcho') def strange_echo( - string: str, omg: t.Dict[str, t.Any], wtf: t.List[str], nowai: int, yeswai: str = 'Default' + string: str, + omg: t.Dict[str, t.Any], + wtf: t.List[str], + nowai: int, + yeswai: str = 'Default', ) -> t.List[t.Any]: return [string, omg, wtf, nowai, yeswai] diff --git a/tests/test_apps/async_app/__init__.py b/tests/test_apps/async_app/__init__.py index c9e02899..3d98e458 100644 --- a/tests/test_apps/async_app/__init__.py +++ b/tests/test_apps/async_app/__init__.py @@ -128,7 +128,11 @@ async def fails(n: int) -> int: # pylint: disable=W0612 @jsonrpc.method('jsonrpc.strangeEcho') async def strange_echo( - string: str, omg: t.Dict[str, t.Any], wtf: t.List[str], nowai: int, yeswai: str = 'Default' + string: str, + omg: t.Dict[str, t.Any], + wtf: t.List[str], + nowai: int, + yeswai: str = 'Default', ) -> t.List[t.Any]: await asyncio.sleep(0) return [string, omg, wtf, nowai, yeswai] @@ -160,7 +164,9 @@ async def return_headers(s: str) -> t.Tuple[str, t.Dict[str, t.Any]]: # pylint: disable=W0612 @jsonrpc.method('jsonrpc.returnStatusCodeAndHeaders') - async def return_status_code_and_headers(s: str) -> t.Tuple[str, int, t.Dict[str, t.Any]]: + async def return_status_code_and_headers( + s: str, + ) -> t.Tuple[str, int, t.Dict[str, t.Any]]: await asyncio.sleep(0) return f'Status Code and Headers {s}', 400, {'X-JSONRPC': '1'} diff --git a/tests/contrib/__init__.py b/tests/unit/__init__.py similarity index 100% rename from tests/contrib/__init__.py rename to tests/unit/__init__.py diff --git a/tests/conftest.py b/tests/unit/conftest.py similarity index 96% rename from tests/conftest.py rename to tests/unit/conftest.py index c9304799..5f7bcb71 100644 --- a/tests/conftest.py +++ b/tests/unit/conftest.py @@ -26,8 +26,8 @@ # POSSIBILITY OF SUCH DAMAGE. import pytest -from .test_apps.app import create_app -from .test_apps.async_app import create_async_app +from ..test_apps.app import create_app +from ..test_apps.async_app import create_async_app @pytest.fixture(scope='module') diff --git a/tests/unit/contrib/__init__.py b/tests/unit/contrib/__init__.py new file mode 100644 index 00000000..cd33beea --- /dev/null +++ b/tests/unit/contrib/__init__.py @@ -0,0 +1,26 @@ +# Copyright (c) 2020-2020, Cenobit Technologies, Inc. http://cenobit.es/ +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the Cenobit Technologies nor the names of +# its contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. diff --git a/tests/contrib/test_browse.py b/tests/unit/contrib/test_browse.py similarity index 81% rename from tests/contrib/test_browse.py rename to tests/unit/contrib/test_browse.py index df52ecdc..b077f621 100644 --- a/tests/contrib/test_browse.py +++ b/tests/unit/contrib/test_browse.py @@ -55,11 +55,17 @@ def fn3(s: str) -> str: assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Foo 1'} assert rv.status_code == 200 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Foo :)'} assert rv.status_code == 200 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn3', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn3', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Foo :)'} assert rv.status_code == 200 @@ -101,7 +107,14 @@ def fn3(s: str) -> str: 'name': 'app.fn2', 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 's', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 's', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, @@ -109,7 +122,14 @@ def fn3(s: str) -> str: 'name': 'app.fn3', 'type': 'method', 'options': {'notification': False, 'validate': True}, - 'params': [{'name': 's', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 's', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, @@ -184,7 +204,14 @@ def fn1(s: str) -> str: 'name': 'app.fn2', 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 's', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 's', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, } @@ -235,7 +262,14 @@ def fn2(s: str) -> str: 'name': 'app.fn2', 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 's', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 's', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, @@ -243,7 +277,14 @@ def fn2(s: str) -> str: 'name': 'app.fn3', 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 's', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 's', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, @@ -282,7 +323,14 @@ def fn2(s: str) -> str: 'name': 'app.fn1', 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 's', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 's', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, @@ -290,7 +338,14 @@ def fn2(s: str) -> str: 'name': 'app.fn2', 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 's', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 's', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, @@ -370,7 +425,14 @@ def fn1_b3(s: str) -> str: 'name': 'blue1.fn2', 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 's', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 's', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, } @@ -380,7 +442,14 @@ def fn1_b3(s: str) -> str: 'name': 'blue2.fn1', 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 's', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 's', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, @@ -388,7 +457,14 @@ def fn1_b3(s: str) -> str: 'name': 'blue2.fn2', 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 's', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 's', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, @@ -396,7 +472,14 @@ def fn1_b3(s: str) -> str: 'name': 'blue2.not_notify', 'type': 'method', 'options': {'notification': False, 'validate': True}, - 'params': [{'name': 's', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 's', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, diff --git a/tests/test_app.py b/tests/unit/test_app.py similarity index 82% rename from tests/test_app.py rename to tests/unit/test_app.py index c522b688..ba3338cc 100644 --- a/tests/test_app.py +++ b/tests/unit/test_app.py @@ -72,8 +72,15 @@ def fn4(s: str) -> str: jsonrpc.register(fn3, name='app.fn3') with app.test_client() as client: - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.index', 'params': []}) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Welcome to Flask JSON-RPC'} + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.index', 'params': []}, + ) + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': 'Welcome to Flask JSON-RPC', + } assert rv.status_code == 200 rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn0', 'params': []}) @@ -84,15 +91,24 @@ def fn4(s: str) -> str: assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Bar'} assert rv.status_code == 200 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Foo :)'} assert rv.status_code == 200 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn3', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn3', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Foo :)'} assert rv.status_code == 200 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn4', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn4', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Goo :)'} assert rv.status_code == 200 @@ -117,7 +133,11 @@ def fn4(s: str) -> str: data=json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'app.index'}), headers={'Content-Type': 'application/json-rpc'}, ) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Welcome to Flask JSON-RPC'} + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': 'Welcome to Flask JSON-RPC', + } assert rv.status_code == 200 rv = client.post( @@ -125,7 +145,11 @@ def fn4(s: str) -> str: data=json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'app.index', 'params': []}), headers={'Content-Type': 'application/jsonrequest'}, ) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Welcome to Flask JSON-RPC'} + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': 'Welcome to Flask JSON-RPC', + } assert rv.status_code == 200 rv = client.post( @@ -133,7 +157,11 @@ def fn4(s: str) -> str: data=json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'app.index', 'params': []}), headers={'Content-Type': 'application/json'}, ) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Welcome to Flask JSON-RPC'} + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': 'Welcome to Flask JSON-RPC', + } assert rv.status_code == 200 @@ -148,8 +176,15 @@ def index() -> str: return 'Welcome to Flask JSON-RPC' with app.test_client() as client: - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.index', 'params': []}) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Welcome to Flask JSON-RPC'} + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.index', 'params': []}, + ) + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': 'Welcome to Flask JSON-RPC', + } assert rv.status_code == 200 @@ -165,7 +200,10 @@ def fn1(s: str) -> str: jsonrpc.init_app(app) with app.test_client() as client: - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Foo :)'} assert rv.status_code == 200 @@ -174,26 +212,28 @@ def test_app_create_without_register_browse(): jsonrpc = JSONRPC(service_url='/api', enable_web_browsable_api=True) with pytest.raises( - RuntimeError, match='You need to init the Browse app before register the Site, see JSONRPC.init_browse_app(...)' + RuntimeError, + match='You need to init the Browse app before register the Site, see JSONRPC.init_browse_app(...)', ): jsonrpc.register_browse(jsonrpc) def test_app_create_with_method_without_annotation(): - with pytest.raises(ValueError, match='no type annotations present to: app.fn1'): - app = Flask('test_app', instance_relative_config=True) - jsonrpc = JSONRPC(app, '/api', enable_web_browsable_api=True) + app = Flask('test_app', instance_relative_config=True) + jsonrpc = JSONRPC(app, '/api', enable_web_browsable_api=True) + with pytest.raises(ValueError, match='no type annotations present to: app.fn1'): # pylint: disable=W0612 @jsonrpc.method('app.fn1') def fn1(s): return f'Foo {s}' - # pylint: disable=W0612 - @jsonrpc.method('app.fn2') - def fn2(s: str) -> str: - return f'Bar {s}' + # pylint: disable=W0612 + @jsonrpc.method('app.fn2') + def fn2(s: str) -> str: + return f'Bar {s}' + with pytest.raises(ValueError, match='no type annotations present to: app.fn3'): # pylint: disable=W0612 @jsonrpc.method('app.fn3') def fn3(s): # pylint: disable=W0612 @@ -201,25 +241,26 @@ def fn3(s): # pylint: disable=W0612 def test_app_create_with_method_without_annotation_on_params(): - with pytest.raises(ValueError, match='no type annotations present to: app.fn2'): - app = Flask('test_app', instance_relative_config=True) - jsonrpc = JSONRPC(app, '/api', enable_web_browsable_api=True) + app = Flask('test_app', instance_relative_config=True) + jsonrpc = JSONRPC(app, '/api', enable_web_browsable_api=True) - # pylint: disable=W0612 - @jsonrpc.method('app.fn4') - def fn4() -> None: - pass + # pylint: disable=W0612 + @jsonrpc.method('app.fn4') + def fn4() -> None: + pass + with pytest.raises(ValueError, match='no type annotations present to: app.fn2'): # pylint: disable=W0612 @jsonrpc.method('app.fn2') def fn2(s) -> str: return f'Foo {s}' - # pylint: disable=W0612 - @jsonrpc.method('app.fn1') - def fn1(s: str) -> str: - return f'Bar {s}' + # pylint: disable=W0612 + @jsonrpc.method('app.fn1') + def fn1(s: str) -> str: + return f'Bar {s}' + with pytest.raises(ValueError, match='no type annotations present to: app.fn3'): # pylint: disable=W0612 @jsonrpc.method('app.fn3') def fn3(s): # pylint: disable=W0612 @@ -246,7 +287,10 @@ def fn3(s: str) -> t.NoReturn: raise ValueError(f'no return: {s}') with app.test_client() as client: - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn1', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn1', 'params': [':)']}, + ) assert rv.json == { 'error': { 'code': -32602, @@ -259,11 +303,17 @@ def fn3(s: str) -> t.NoReturn: } assert rv.status_code == 400 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Bar :)'} assert rv.status_code == 200 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn3', 'params': ['OK']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn3', 'params': ['OK']}, + ) assert rv.json == { 'error': { 'code': -32000, @@ -286,7 +336,10 @@ def fn2(s: str) -> t.Tuple[str, int, int, int]: # pylint: disable=W0612 return f'Bar {s}', 1, 2, 3 with app.test_client() as client: - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn1', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn1', 'params': [':)']}, + ) assert rv.json == { 'error': { 'code': -32000, @@ -318,7 +371,10 @@ def fn1(s: str) -> str: jsonrpc.register(fn1.__new__, name='invalid') with app.test_client() as client: - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Foo :)'} assert rv.status_code == 200 @@ -361,27 +417,45 @@ def fn4_v2(s: str) -> str: jsonrpc_v2.register(fn4_v2) with app.test_client() as client: - rv = client.post('/api/v1', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}) + rv = client.post( + '/api/v1', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'v1: Foo :)'} assert rv.status_code == 200 - rv = client.post('/api/v2', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':D']}) + rv = client.post( + '/api/v2', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':D']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'v2: Foo :D'} assert rv.status_code == 200 - rv = client.post('/api/v1', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn3', 'params': [';)']}) + rv = client.post( + '/api/v1', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn3', 'params': [';)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Poo ;)'} assert rv.status_code == 200 - rv = client.post('/api/v2', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn1', 'params': ['\\oB']}) + rv = client.post( + '/api/v2', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn1', 'params': ['\\oB']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Bar \\oB'} assert rv.status_code == 200 - rv = client.post('/api/v1', json={'id': 1, 'jsonrpc': '2.0', 'method': 'fn4_v1', 'params': ['\\oB']}) + rv = client.post( + '/api/v1', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'fn4_v1', 'params': ['\\oB']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Poo \\oB'} assert rv.status_code == 200 - rv = client.post('/api/v2', json={'id': 1, 'jsonrpc': '2.0', 'method': 'fn4_v2', 'params': ['\\oB']}) + rv = client.post( + '/api/v2', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'fn4_v2', 'params': ['\\oB']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Bar \\oB'} assert rv.status_code == 200 @@ -420,19 +494,31 @@ def fn1_b3(s: str) -> str: jsonrpc.register_blueprint(app, jsonrpc_api_3, url_prefix='/b3') with app.test_client() as client: - rv = client.post('/api/b1', json={'id': 1, 'jsonrpc': '2.0', 'method': 'blue1.fn2', 'params': [':)']}) + rv = client.post( + '/api/b1', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'blue1.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'b1: Foo :)'} assert rv.status_code == 200 - rv = client.post('/api/b2', json={'id': 1, 'jsonrpc': '2.0', 'method': 'blue2.fn2', 'params': [':)']}) + rv = client.post( + '/api/b2', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'blue2.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'b2: Foo :)'} assert rv.status_code == 200 - rv = client.post('/api/b2', json={'id': 1, 'jsonrpc': '2.0', 'method': 'blue2.fn1', 'params': [':)']}) + rv = client.post( + '/api/b2', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'blue2.fn1', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'b2: Bar :)'} assert rv.status_code == 200 - rv = client.post('/api/b3', json={'id': 1, 'jsonrpc': '2.0', 'method': 'blue3.fn2', 'params': [':)']}) + rv = client.post( + '/api/b3', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'blue3.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'b3: Foo :)'} assert rv.status_code == 200 @@ -503,7 +589,10 @@ def headers_duplicate() -> t.Tuple[float, int, t.Dict[str, t.Any]]: with app.test_client() as client: idx = uuid.uuid4() - rv = client.post('/api', json={'id': idx.hex, 'jsonrpc': '2.0', 'method': 'sum', 'params': [1, 1]}) + rv = client.post( + '/api', + json={'id': idx.hex, 'jsonrpc': '2.0', 'method': 'sum', 'params': [1, 1]}, + ) assert rv.json == {'id': idx.hex, 'jsonrpc': '2.0', 'result': 2} assert rv.status_code == 200 @@ -597,7 +686,12 @@ def headers_duplicate() -> t.Tuple[float, int, t.Dict[str, t.Any]]: json=[ {'id': '1', 'jsonrpc': '2.0', 'method': 'sum', 'params': [1, 1]}, {'id': '2', 'jsonrpc': '2.0', 'method': 'subtract', 'params': [2, 2]}, - {'id': '3', 'jsonrpc': '2.0', 'method': 'get_user', 'params': {'uid': '345'}}, + { + 'id': '3', + 'jsonrpc': '2.0', + 'method': 'get_user', + 'params': {'uid': '345'}, + }, {'jsonrpc': '2.0', 'method': 'notify_sum', 'params': [[1, 2, 3, 4, 5]]}, {'id': 'h1', 'jsonrpc': '2.0', 'method': 'headers1'}, {'id': 'h2', 'jsonrpc': '2.0', 'method': 'headers2'}, diff --git a/tests/test_async_app.py b/tests/unit/test_async_app.py similarity index 82% rename from tests/test_async_app.py rename to tests/unit/test_async_app.py index 1e7d8187..80f36f3f 100644 --- a/tests/test_async_app.py +++ b/tests/unit/test_async_app.py @@ -79,8 +79,15 @@ def fn4(s: str) -> str: jsonrpc.register(fn3, name='app.fn3') with app.test_client() as client: - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.index', 'params': []}) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Welcome to Flask JSON-RPC'} + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.index', 'params': []}, + ) + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': 'Welcome to Flask JSON-RPC', + } assert rv.status_code == 200 rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn0', 'params': []}) @@ -91,15 +98,24 @@ def fn4(s: str) -> str: assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Bar'} assert rv.status_code == 200 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Foo :)'} assert rv.status_code == 200 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn3', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn3', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Foo :)'} assert rv.status_code == 200 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn4', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn4', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Goo :)'} assert rv.status_code == 200 @@ -124,7 +140,11 @@ def fn4(s: str) -> str: data=json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'app.index'}), headers={'Content-Type': 'application/json-rpc'}, ) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Welcome to Flask JSON-RPC'} + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': 'Welcome to Flask JSON-RPC', + } assert rv.status_code == 200 rv = client.post( @@ -132,7 +152,11 @@ def fn4(s: str) -> str: data=json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'app.index', 'params': []}), headers={'Content-Type': 'application/jsonrequest'}, ) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Welcome to Flask JSON-RPC'} + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': 'Welcome to Flask JSON-RPC', + } assert rv.status_code == 200 rv = client.post( @@ -140,7 +164,11 @@ def fn4(s: str) -> str: data=json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'app.index', 'params': []}), headers={'Content-Type': 'application/json'}, ) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Welcome to Flask JSON-RPC'} + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': 'Welcome to Flask JSON-RPC', + } assert rv.status_code == 200 @@ -155,8 +183,15 @@ def index() -> str: return 'Welcome to Flask JSON-RPC' with app.test_client() as client: - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.index', 'params': []}) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Welcome to Flask JSON-RPC'} + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.index', 'params': []}, + ) + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': 'Welcome to Flask JSON-RPC', + } assert rv.status_code == 200 @@ -173,28 +208,32 @@ async def fn1(s: str) -> str: jsonrpc.init_app(app) with app.test_client() as client: - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Foo :)'} assert rv.status_code == 200 def test_app_create_with_method_without_annotation(): - with pytest.raises(ValueError, match='no type annotations present to: app.fn1'): - app = Flask('test_app', instance_relative_config=True) - jsonrpc = JSONRPC(app, '/api', enable_web_browsable_api=True) + app = Flask('test_app', instance_relative_config=True) + jsonrpc = JSONRPC(app, '/api', enable_web_browsable_api=True) + with pytest.raises(ValueError, match='no type annotations present to: app.fn1'): # pylint: disable=W0612 @jsonrpc.method('app.fn1') async def fn1(s): await asyncio.sleep(0) return f'Foo {s}' - # pylint: disable=W0612 - @jsonrpc.method('app.fn2') - async def fn2(s: str) -> str: - await asyncio.sleep(0) - return f'Bar {s}' + # pylint: disable=W0612 + @jsonrpc.method('app.fn2') + async def fn2(s: str) -> str: + await asyncio.sleep(0) + return f'Bar {s}' + with pytest.raises(ValueError, match='no type annotations present to: app.fn3'): # pylint: disable=W0612 @jsonrpc.method('app.fn3') async def fn3(s): # pylint: disable=W0612 @@ -203,27 +242,28 @@ async def fn3(s): # pylint: disable=W0612 def test_app_create_with_method_without_annotation_on_params(): - with pytest.raises(ValueError, match='no type annotations present to: app.fn2'): - app = Flask('test_app', instance_relative_config=True) - jsonrpc = JSONRPC(app, '/api', enable_web_browsable_api=True) + app = Flask('test_app', instance_relative_config=True) + jsonrpc = JSONRPC(app, '/api', enable_web_browsable_api=True) - # pylint: disable=W0612 - @jsonrpc.method('app.fn4') - async def fn4(): - await asyncio.sleep(0) + # pylint: disable=W0612 + @jsonrpc.method('app.fn4') + async def fn4(): + await asyncio.sleep(0) + with pytest.raises(ValueError, match='no type annotations present to: app.fn2'): # pylint: disable=W0612 @jsonrpc.method('app.fn2') async def fn2(s) -> str: await asyncio.sleep(0) return f'Foo {s}' - # pylint: disable=W0612 - @jsonrpc.method('app.fn1') - async def fn1(s: str) -> str: - await asyncio.sleep(0) - return f'Bar {s}' + # pylint: disable=W0612 + @jsonrpc.method('app.fn1') + async def fn1(s: str) -> str: + await asyncio.sleep(0) + return f'Bar {s}' + with pytest.raises(ValueError, match='no type annotations present to: app.fn3'): # pylint: disable=W0612 @jsonrpc.method('app.fn3') async def fn3(s): # pylint: disable=W0612 @@ -254,7 +294,10 @@ async def fn3(s: str) -> t.NoReturn: raise ValueError(f'no return: {s}') with app.test_client() as client: - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn1', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn1', 'params': [':)']}, + ) assert rv.json == { 'error': { 'code': -32602, @@ -267,11 +310,17 @@ async def fn3(s: str) -> t.NoReturn: } assert rv.status_code == 400 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Bar :)'} assert rv.status_code == 200 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn3', 'params': ['OK']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn3', 'params': ['OK']}, + ) assert rv.json == { 'error': { 'code': -32000, @@ -295,7 +344,10 @@ async def fn2(s: str) -> t.Tuple[str, int, int, int]: # pylint: disable=W0612 return f'Bar {s}', 1, 2, 3 with app.test_client() as client: - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn1', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn1', 'params': [':)']}, + ) assert rv.json == { 'error': { 'code': -32000, @@ -328,7 +380,10 @@ async def fn1(s: str) -> str: jsonrpc.register(fn1.__new__, name='invalid') with app.test_client() as client: - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Foo :)'} assert rv.status_code == 200 @@ -377,27 +432,45 @@ async def fn4_v2(s: str) -> str: jsonrpc_v2.register(fn4_v2) with app.test_client() as client: - rv = client.post('/api/v1', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}) + rv = client.post( + '/api/v1', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'v1: Foo :)'} assert rv.status_code == 200 - rv = client.post('/api/v2', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':D']}) + rv = client.post( + '/api/v2', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn2', 'params': [':D']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'v2: Foo :D'} assert rv.status_code == 200 - rv = client.post('/api/v1', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn3', 'params': [';)']}) + rv = client.post( + '/api/v1', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn3', 'params': [';)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Poo ;)'} assert rv.status_code == 200 - rv = client.post('/api/v2', json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn1', 'params': ['\\oB']}) + rv = client.post( + '/api/v2', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'app.fn1', 'params': ['\\oB']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Bar \\oB'} assert rv.status_code == 200 - rv = client.post('/api/v1', json={'id': 1, 'jsonrpc': '2.0', 'method': 'fn4_v1', 'params': ['\\oB']}) + rv = client.post( + '/api/v1', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'fn4_v1', 'params': ['\\oB']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Poo \\oB'} assert rv.status_code == 200 - rv = client.post('/api/v2', json={'id': 1, 'jsonrpc': '2.0', 'method': 'fn4_v2', 'params': ['\\oB']}) + rv = client.post( + '/api/v2', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'fn4_v2', 'params': ['\\oB']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Bar \\oB'} assert rv.status_code == 200 @@ -440,19 +513,31 @@ async def fn1_b3(s: str) -> str: jsonrpc.register_blueprint(app, jsonrpc_api_3, url_prefix='/b3') with app.test_client() as client: - rv = client.post('/api/b1', json={'id': 1, 'jsonrpc': '2.0', 'method': 'blue1.fn2', 'params': [':)']}) + rv = client.post( + '/api/b1', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'blue1.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'b1: Foo :)'} assert rv.status_code == 200 - rv = client.post('/api/b2', json={'id': 1, 'jsonrpc': '2.0', 'method': 'blue2.fn2', 'params': [':)']}) + rv = client.post( + '/api/b2', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'blue2.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'b2: Foo :)'} assert rv.status_code == 200 - rv = client.post('/api/b2', json={'id': 1, 'jsonrpc': '2.0', 'method': 'blue2.fn1', 'params': [':)']}) + rv = client.post( + '/api/b2', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'blue2.fn1', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'b2: Bar :)'} assert rv.status_code == 200 - rv = client.post('/api/b3', json={'id': 1, 'jsonrpc': '2.0', 'method': 'blue3.fn2', 'params': [':)']}) + rv = client.post( + '/api/b3', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'blue3.fn2', 'params': [':)']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'b3: Foo :)'} assert rv.status_code == 200 @@ -534,7 +619,10 @@ async def headers_duplicate() -> t.Tuple[float, int, t.Dict[str, t.Any]]: with app.test_client() as client: idx = uuid.uuid4() - rv = client.post('/api', json={'id': idx.hex, 'jsonrpc': '2.0', 'method': 'sum', 'params': [1, 1]}) + rv = client.post( + '/api', + json={'id': idx.hex, 'jsonrpc': '2.0', 'method': 'sum', 'params': [1, 1]}, + ) assert rv.json == {'id': idx.hex, 'jsonrpc': '2.0', 'result': 2} assert rv.status_code == 200 @@ -628,7 +716,12 @@ async def headers_duplicate() -> t.Tuple[float, int, t.Dict[str, t.Any]]: json=[ {'id': '1', 'jsonrpc': '2.0', 'method': 'sum', 'params': [1, 1]}, {'id': '2', 'jsonrpc': '2.0', 'method': 'subtract', 'params': [2, 2]}, - {'id': '3', 'jsonrpc': '2.0', 'method': 'get_user', 'params': {'uid': '345'}}, + { + 'id': '3', + 'jsonrpc': '2.0', + 'method': 'get_user', + 'params': {'uid': '345'}, + }, {'jsonrpc': '2.0', 'method': 'notify_sum', 'params': [[1, 2, 3, 4, 5]]}, {'id': 'h1', 'jsonrpc': '2.0', 'method': 'headers1'}, {'id': 'h2', 'jsonrpc': '2.0', 'method': 'headers2'}, diff --git a/tests/test_async_client.py b/tests/unit/test_async_client.py similarity index 72% rename from tests/test_async_client.py rename to tests/unit/test_async_client.py index d454a1d6..3abbd96e 100644 --- a/tests/test_async_client.py +++ b/tests/unit/test_async_client.py @@ -36,12 +36,26 @@ def test_app_greeting(async_client): assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask JSON-RPC'} assert rv.status_code == 200 - rv = async_client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']}) + rv = async_client.post( + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['Python'], + }, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Python'} assert rv.status_code == 200 rv = async_client.post( - '/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': {'name': 'Flask'}} + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': {'name': 'Flask'}, + }, ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask'} assert rv.status_code == 200 @@ -58,7 +72,14 @@ def test_app_greeting_with_different_content_types(async_client): rv = async_client.post( '/api', - data=json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']}), + data=json.dumps( + { + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['Python'], + } + ), headers={'Content-Type': 'application/jsonrequest'}, ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Python'} @@ -66,7 +87,14 @@ def test_app_greeting_with_different_content_types(async_client): rv = async_client.post( '/api', - data=json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': {'name': 'Flask'}}), + data=json.dumps( + { + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': {'name': 'Flask'}, + } + ), headers={'Content-Type': 'application/json'}, ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask'} @@ -150,7 +178,15 @@ def test_app_greeting_raise_invalid_request_error(async_client): def test_app_greeting_raise_invalid_params_error(async_client): - rv = async_client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': 'Wrong'}) + rv = async_client.post( + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': 'Wrong', + }, + ) assert rv.json == { 'id': 1, 'jsonrpc': '2.0', @@ -163,7 +199,10 @@ def test_app_greeting_raise_invalid_params_error(async_client): } assert rv.status_code == 400 - rv = async_client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': [1]}) + rv = async_client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': [1]}, + ) assert rv.json == { 'id': 1, 'jsonrpc': '2.0', @@ -177,7 +216,13 @@ def test_app_greeting_raise_invalid_params_error(async_client): assert rv.status_code == 400 rv = async_client.post( - '/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': {'name': 2}} + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': {'name': 2}, + }, ) assert rv.json == { 'id': 1, @@ -208,19 +253,36 @@ def test_app_greeting_raise_method_not_found_error(async_client): def test_app_echo(async_client): - rv = async_client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': ['Python']}) + rv = async_client.post( + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.echo', + 'params': ['Python'], + }, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Python'} assert rv.status_code == 200 rv = async_client.post( - '/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': {'string': 'Flask'}} + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.echo', + 'params': {'string': 'Flask'}, + }, ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Flask'} assert rv.status_code == 200 def test_app_echo_raise_invalid_params_error(async_client): - rv = async_client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': 'Wrong'}) + rv = async_client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': 'Wrong'}, + ) assert rv.json == { 'id': 1, 'jsonrpc': '2.0', @@ -233,7 +295,10 @@ def test_app_echo_raise_invalid_params_error(async_client): } assert rv.status_code == 400 - rv = async_client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': [1]}) + rv = async_client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': [1]}, + ) assert rv.json == { 'id': 1, 'jsonrpc': '2.0', @@ -246,7 +311,15 @@ def test_app_echo_raise_invalid_params_error(async_client): } assert rv.status_code == 400 - rv = async_client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': {'name': 2}}) + rv = async_client.post( + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.echo', + 'params': {'name': 2}, + }, + ) assert rv.json == { 'id': 1, 'jsonrpc': '2.0', @@ -278,7 +351,10 @@ def test_app_notify(async_client): assert rv.json is None assert rv.status_code == 204 - rv = async_client.post('/api', json={'jsonrpc': '2.0', 'method': 'jsonrpc.notify', 'params': ['Some string']}) + rv = async_client.post( + '/api', + json={'jsonrpc': '2.0', 'method': 'jsonrpc.notify', 'params': ['Some string']}, + ) assert rv.json is None assert rv.status_code == 204 @@ -301,7 +377,13 @@ def test_app_not_allow_notify(async_client): assert rv.status_code == 400 rv = async_client.post( - '/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.not_allow_notify', 'params': ['Some string']} + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.not_allow_notify', + 'params': ['Some string'], + }, ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Not allow notify'} assert rv.status_code == 200 @@ -323,11 +405,17 @@ def test_app_no_return(async_client): def test_app_fails(async_client): - rv = async_client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.fails', 'params': [2]}) + rv = async_client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.fails', 'params': [2]}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 2} assert rv.status_code == 200 - rv = async_client.post('/api', json={'id': '1', 'jsonrpc': '2.0', 'method': 'jsonrpc.fails', 'params': [1]}) + rv = async_client.post( + '/api', + json={'id': '1', 'jsonrpc': '2.0', 'method': 'jsonrpc.fails', 'params': [1]}, + ) assert rv.json == { 'id': '1', 'jsonrpc': '2.0', @@ -349,7 +437,11 @@ def test_app_strange_echo(async_client): 'params': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Flask'], } rv = async_client.post('/api', json=data) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Flask']} + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Flask'], + } assert rv.status_code == 200 data = { @@ -359,7 +451,11 @@ def test_app_strange_echo(async_client): 'params': ['string', {'a': 1}, ['a', 'b', 'c'], 23], } rv = async_client.post('/api', json=data) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Default']} + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Default'], + } assert rv.status_code == 200 @@ -376,15 +472,30 @@ def test_app_sum(async_client): def test_app_decorators(async_client): - data = {'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.decorators', 'params': ['Python']} + data = { + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.decorators', + 'params': ['Python'], + } rv = async_client.post('/api', json=data) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Python from decorator, ;)'} + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': 'Hello Python from decorator, ;)', + } assert rv.status_code == 200 def test_app_return_status_code(async_client): rv = async_client.post( - '/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.returnStatusCode', 'params': ['OK']} + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.returnStatusCode', + 'params': ['OK'], + }, ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Status Code OK'} assert rv.status_code == 201 @@ -392,7 +503,13 @@ def test_app_return_status_code(async_client): def test_app_return_headers(async_client): rv = async_client.post( - '/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.returnHeaders', 'params': ['OK']} + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.returnHeaders', + 'params': ['OK'], + }, ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Headers OK'} assert rv.status_code == 200 @@ -401,9 +518,19 @@ def test_app_return_headers(async_client): def test_app_return_status_code_and_headers(async_client): rv = async_client.post( - '/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.returnStatusCodeAndHeaders', 'params': ['OK']} + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.returnStatusCodeAndHeaders', + 'params': ['OK'], + }, ) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Status Code and Headers OK'} + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': 'Status Code and Headers OK', + } assert rv.status_code == 400 assert ('X-JSONRPC', '1') in list(rv.headers) @@ -416,9 +543,24 @@ def test_app_with_rcp_batch(async_client): rv = async_client.post( '/api', json=[ - {'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']}, - {'id': 2, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Flask']}, - {'id': 3, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['JSON-RCP']}, + { + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['Python'], + }, + { + 'id': 2, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['Flask'], + }, + { + 'id': 3, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['JSON-RCP'], + }, ], ) assert rv.json == [ @@ -431,10 +573,20 @@ def test_app_with_rcp_batch(async_client): rv = async_client.post( '/api', json=[ - {'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']}, + { + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['Python'], + }, {'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Flask']}, {'id': 3, 'jsonrpc': '2.0', 'params': ['Flask']}, - {'id': 4, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['JSON-RCP']}, + { + 'id': 4, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['JSON-RCP'], + }, ], ) assert rv.json == [ @@ -463,15 +615,29 @@ def test_app_class(async_client): assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask JSON-RPC'} assert rv.status_code == 200 - rv = async_client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'greeting', 'params': ['Python']}) + rv = async_client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'greeting', 'params': ['Python']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Python'} assert rv.status_code == 200 - rv = async_client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'hello', 'params': {'name': 'Flask'}}) + rv = async_client.post( + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'hello', + 'params': {'name': 'Flask'}, + }, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask'} assert rv.status_code == 200 - rv = async_client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'echo', 'params': ['Python', 1]}) + rv = async_client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'echo', 'params': ['Python', 1]}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Python'} assert rv.status_code == 200 @@ -513,8 +679,18 @@ def test_app_system_describe(async_client): 'type': 'method', 'options': {'notification': True, 'validate': True}, 'params': [ - {'name': 'string', 'type': 'String', 'required': False, 'nullable': False}, - {'name': '_some', 'type': 'Object', 'required': False, 'nullable': False}, + { + 'name': 'string', + 'type': 'String', + 'required': False, + 'nullable': False, + }, + { + 'name': '_some', + 'type': 'Object', + 'required': False, + 'nullable': False, + }, ], 'returns': {'type': 'String'}, 'description': None, @@ -522,14 +698,28 @@ def test_app_system_describe(async_client): 'jsonrpc.notify': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': '_string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': '_string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'Null'}, 'description': None, }, 'jsonrpc.not_allow_notify': { 'type': 'method', 'options': {'notification': False, 'validate': True}, - 'params': [{'name': '_string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': '_string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, @@ -544,11 +734,26 @@ def test_app_system_describe(async_client): 'type': 'method', 'options': {'notification': True, 'validate': True}, 'params': [ - {'name': 'string', 'type': 'String', 'required': False, 'nullable': False}, + { + 'name': 'string', + 'type': 'String', + 'required': False, + 'nullable': False, + }, {'name': 'omg', 'type': 'Object', 'required': False, 'nullable': False}, {'name': 'wtf', 'type': 'Array', 'required': False, 'nullable': False}, - {'name': 'nowai', 'type': 'Number', 'required': False, 'nullable': False}, - {'name': 'yeswai', 'type': 'String', 'required': False, 'nullable': False}, + { + 'name': 'nowai', + 'type': 'Number', + 'required': False, + 'nullable': False, + }, + { + 'name': 'yeswai', + 'type': 'String', + 'required': False, + 'nullable': False, + }, ], 'returns': {'type': 'Array'}, 'description': None, @@ -566,7 +771,14 @@ def test_app_system_describe(async_client): 'jsonrpc.decorators': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 'string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 'string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, @@ -601,7 +813,14 @@ def test_app_system_describe(async_client): 'jsonrpc.noReturn': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': '_string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': '_string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'Null'}, 'description': None, }, @@ -630,8 +849,18 @@ def test_app_system_describe(async_client): 'type': 'method', 'options': {'notification': True, 'validate': True}, 'params': [ - {'name': 'string', 'type': 'String', 'required': False, 'nullable': False}, - {'name': '_some', 'type': 'Object', 'required': False, 'nullable': False}, + { + 'name': 'string', + 'type': 'String', + 'required': False, + 'nullable': False, + }, + { + 'name': '_some', + 'type': 'Object', + 'required': False, + 'nullable': False, + }, ], 'returns': {'type': 'String'}, 'description': None, @@ -639,14 +868,28 @@ def test_app_system_describe(async_client): 'notify': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': '_string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': '_string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'Null'}, 'description': None, }, 'not_allow_notify': { 'type': 'method', 'options': {'notification': False, 'validate': True}, - 'params': [{'name': '_string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': '_string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, diff --git a/tests/test_client.py b/tests/unit/test_client.py similarity index 72% rename from tests/test_client.py rename to tests/unit/test_client.py index 34b36ace..8b2e0ef2 100644 --- a/tests/test_client.py +++ b/tests/unit/test_client.py @@ -32,12 +32,26 @@ def test_app_greeting(client): assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask JSON-RPC'} assert rv.status_code == 200 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']}) + rv = client.post( + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['Python'], + }, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Python'} assert rv.status_code == 200 rv = client.post( - '/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': {'name': 'Flask'}} + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': {'name': 'Flask'}, + }, ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask'} assert rv.status_code == 200 @@ -54,7 +68,14 @@ def test_app_greeting_with_different_content_types(client): rv = client.post( '/api', - data=json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']}), + data=json.dumps( + { + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['Python'], + } + ), headers={'Content-Type': 'application/jsonrequest'}, ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Python'} @@ -62,7 +83,14 @@ def test_app_greeting_with_different_content_types(client): rv = client.post( '/api', - data=json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': {'name': 'Flask'}}), + data=json.dumps( + { + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': {'name': 'Flask'}, + } + ), headers={'Content-Type': 'application/json'}, ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask'} @@ -146,7 +174,15 @@ def test_app_greeting_raise_invalid_request_error(client): def test_app_greeting_raise_invalid_params_error(client): - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': 'Wrong'}) + rv = client.post( + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': 'Wrong', + }, + ) assert rv.json == { 'id': 1, 'jsonrpc': '2.0', @@ -159,7 +195,10 @@ def test_app_greeting_raise_invalid_params_error(client): } assert rv.status_code == 400 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': [1]}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': [1]}, + ) assert rv.json == { 'id': 1, 'jsonrpc': '2.0', @@ -172,7 +211,15 @@ def test_app_greeting_raise_invalid_params_error(client): } assert rv.status_code == 400 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': {'name': 2}}) + rv = client.post( + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': {'name': 2}, + }, + ) assert rv.json == { 'id': 1, 'jsonrpc': '2.0', @@ -202,17 +249,36 @@ def test_app_greeting_raise_method_not_found_error(client): def test_app_echo(client): - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': ['Python']}) + rv = client.post( + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.echo', + 'params': ['Python'], + }, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Python'} assert rv.status_code == 200 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': {'string': 'Flask'}}) + rv = client.post( + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.echo', + 'params': {'string': 'Flask'}, + }, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Flask'} assert rv.status_code == 200 def test_app_echo_raise_invalid_params_error(client): - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': 'Wrong'}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': 'Wrong'}, + ) assert rv.json == { 'id': 1, 'jsonrpc': '2.0', @@ -225,7 +291,10 @@ def test_app_echo_raise_invalid_params_error(client): } assert rv.status_code == 400 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': [1]}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': [1]}, + ) assert rv.json == { 'id': 1, 'jsonrpc': '2.0', @@ -238,7 +307,15 @@ def test_app_echo_raise_invalid_params_error(client): } assert rv.status_code == 400 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': {'name': 2}}) + rv = client.post( + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.echo', + 'params': {'name': 2}, + }, + ) assert rv.json == { 'id': 1, 'jsonrpc': '2.0', @@ -270,7 +347,10 @@ def test_app_notify(client): assert rv.json is None assert rv.status_code == 204 - rv = client.post('/api', json={'jsonrpc': '2.0', 'method': 'jsonrpc.notify', 'params': ['Some string']}) + rv = client.post( + '/api', + json={'jsonrpc': '2.0', 'method': 'jsonrpc.notify', 'params': ['Some string']}, + ) assert rv.json is None assert rv.status_code == 204 @@ -293,7 +373,13 @@ def test_app_not_allow_notify(client): assert rv.status_code == 400 rv = client.post( - '/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.not_allow_notify', 'params': ['Some string']} + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.not_allow_notify', + 'params': ['Some string'], + }, ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Not allow notify'} assert rv.status_code == 200 @@ -315,11 +401,17 @@ def test_app_no_return(client): def test_app_fails(client): - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.fails', 'params': [2]}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.fails', 'params': [2]}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 2} assert rv.status_code == 200 - rv = client.post('/api', json={'id': '1', 'jsonrpc': '2.0', 'method': 'jsonrpc.fails', 'params': [1]}) + rv = client.post( + '/api', + json={'id': '1', 'jsonrpc': '2.0', 'method': 'jsonrpc.fails', 'params': [1]}, + ) assert rv.json == { 'id': '1', 'jsonrpc': '2.0', @@ -341,7 +433,11 @@ def test_app_strange_echo(client): 'params': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Flask'], } rv = client.post('/api', json=data) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Flask']} + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Flask'], + } assert rv.status_code == 200 data = { @@ -351,7 +447,11 @@ def test_app_strange_echo(client): 'params': ['string', {'a': 1}, ['a', 'b', 'c'], 23], } rv = client.post('/api', json=data) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Default']} + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Default'], + } assert rv.status_code == 200 @@ -368,20 +468,45 @@ def test_app_sum(client): def test_app_decorators(client): - data = {'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.decorators', 'params': ['Python']} + data = { + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.decorators', + 'params': ['Python'], + } rv = client.post('/api', json=data) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Python from decorator, ;)'} + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': 'Hello Python from decorator, ;)', + } assert rv.status_code == 200 def test_app_return_status_code(client): - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.returnStatusCode', 'params': ['OK']}) + rv = client.post( + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.returnStatusCode', + 'params': ['OK'], + }, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Status Code OK'} assert rv.status_code == 201 def test_app_return_headers(client): - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.returnHeaders', 'params': ['OK']}) + rv = client.post( + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.returnHeaders', + 'params': ['OK'], + }, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Headers OK'} assert rv.status_code == 200 assert ('X-JSONRPC', '1') in list(rv.headers) @@ -389,9 +514,19 @@ def test_app_return_headers(client): def test_app_return_status_code_and_headers(client): rv = client.post( - '/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.returnStatusCodeAndHeaders', 'params': ['OK']} + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.returnStatusCodeAndHeaders', + 'params': ['OK'], + }, ) - assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Status Code and Headers OK'} + assert rv.json == { + 'id': 1, + 'jsonrpc': '2.0', + 'result': 'Status Code and Headers OK', + } assert rv.status_code == 400 assert ('X-JSONRPC', '1') in list(rv.headers) @@ -404,9 +539,24 @@ def test_app_with_rcp_batch(client): rv = client.post( '/api', json=[ - {'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']}, - {'id': 2, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Flask']}, - {'id': 3, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['JSON-RCP']}, + { + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['Python'], + }, + { + 'id': 2, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['Flask'], + }, + { + 'id': 3, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['JSON-RCP'], + }, ], ) assert rv.json == [ @@ -419,10 +569,20 @@ def test_app_with_rcp_batch(client): rv = client.post( '/api', json=[ - {'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']}, + { + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['Python'], + }, {'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Flask']}, {'id': 3, 'jsonrpc': '2.0', 'params': ['Flask']}, - {'id': 4, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['JSON-RCP']}, + { + 'id': 4, + 'jsonrpc': '2.0', + 'method': 'jsonrpc.greeting', + 'params': ['JSON-RCP'], + }, ], ) assert rv.json == [ @@ -451,15 +611,29 @@ def test_app_class(client): assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask JSON-RPC'} assert rv.status_code == 200 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'greeting', 'params': ['Python']}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'greeting', 'params': ['Python']}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Python'} assert rv.status_code == 200 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'hello', 'params': {'name': 'Flask'}}) + rv = client.post( + '/api', + json={ + 'id': 1, + 'jsonrpc': '2.0', + 'method': 'hello', + 'params': {'name': 'Flask'}, + }, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask'} assert rv.status_code == 200 - rv = client.post('/api', json={'id': 1, 'jsonrpc': '2.0', 'method': 'echo', 'params': ['Python', 1]}) + rv = client.post( + '/api', + json={'id': 1, 'jsonrpc': '2.0', 'method': 'echo', 'params': ['Python', 1]}, + ) assert rv.json == {'id': 1, 'jsonrpc': '2.0', 'result': 'Python'} assert rv.status_code == 200 @@ -501,8 +675,18 @@ def test_app_system_describe(client): 'type': 'method', 'options': {'notification': True, 'validate': True}, 'params': [ - {'name': 'string', 'type': 'String', 'required': False, 'nullable': False}, - {'name': '_some', 'type': 'Object', 'required': False, 'nullable': False}, + { + 'name': 'string', + 'type': 'String', + 'required': False, + 'nullable': False, + }, + { + 'name': '_some', + 'type': 'Object', + 'required': False, + 'nullable': False, + }, ], 'returns': {'type': 'String'}, 'description': None, @@ -510,14 +694,28 @@ def test_app_system_describe(client): 'jsonrpc.notify': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': '_string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': '_string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'Null'}, 'description': None, }, 'jsonrpc.not_allow_notify': { 'type': 'method', 'options': {'notification': False, 'validate': True}, - 'params': [{'name': '_string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': '_string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, @@ -532,11 +730,26 @@ def test_app_system_describe(client): 'type': 'method', 'options': {'notification': True, 'validate': True}, 'params': [ - {'name': 'string', 'type': 'String', 'required': False, 'nullable': False}, + { + 'name': 'string', + 'type': 'String', + 'required': False, + 'nullable': False, + }, {'name': 'omg', 'type': 'Object', 'required': False, 'nullable': False}, {'name': 'wtf', 'type': 'Array', 'required': False, 'nullable': False}, - {'name': 'nowai', 'type': 'Number', 'required': False, 'nullable': False}, - {'name': 'yeswai', 'type': 'String', 'required': False, 'nullable': False}, + { + 'name': 'nowai', + 'type': 'Number', + 'required': False, + 'nullable': False, + }, + { + 'name': 'yeswai', + 'type': 'String', + 'required': False, + 'nullable': False, + }, ], 'returns': {'type': 'Array'}, 'description': None, @@ -554,7 +767,14 @@ def test_app_system_describe(client): 'jsonrpc.decorators': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': 'string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': 'string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, @@ -589,7 +809,14 @@ def test_app_system_describe(client): 'jsonrpc.noReturn': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': '_string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': '_string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'Null'}, 'description': None, }, @@ -618,8 +845,18 @@ def test_app_system_describe(client): 'type': 'method', 'options': {'notification': True, 'validate': True}, 'params': [ - {'name': 'string', 'type': 'String', 'required': False, 'nullable': False}, - {'name': '_some', 'type': 'Object', 'required': False, 'nullable': False}, + { + 'name': 'string', + 'type': 'String', + 'required': False, + 'nullable': False, + }, + { + 'name': '_some', + 'type': 'Object', + 'required': False, + 'nullable': False, + }, ], 'returns': {'type': 'String'}, 'description': None, @@ -627,14 +864,28 @@ def test_app_system_describe(client): 'notify': { 'type': 'method', 'options': {'notification': True, 'validate': True}, - 'params': [{'name': '_string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': '_string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'Null'}, 'description': None, }, 'not_allow_notify': { 'type': 'method', 'options': {'notification': False, 'validate': True}, - 'params': [{'name': '_string', 'type': 'String', 'required': False, 'nullable': False}], + 'params': [ + { + 'name': '_string', + 'type': 'String', + 'required': False, + 'nullable': False, + } + ], 'returns': {'type': 'String'}, 'description': None, }, diff --git a/tests/test_exceptions.py b/tests/unit/test_exceptions.py similarity index 100% rename from tests/test_exceptions.py rename to tests/unit/test_exceptions.py diff --git a/tests/test_helpers.py b/tests/unit/test_helpers.py similarity index 100% rename from tests/test_helpers.py rename to tests/unit/test_helpers.py diff --git a/tests/test_settings.py b/tests/unit/test_settings.py similarity index 100% rename from tests/test_settings.py rename to tests/unit/test_settings.py diff --git a/tests/test_types.py b/tests/unit/test_types.py similarity index 100% rename from tests/test_types.py rename to tests/unit/test_types.py diff --git a/tox.ini b/tox.ini index 4f15ff2b..073c48e3 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = - py{311,310,39,38,37} - py{311,310,39,38,37}-async + py3{12,11,10,9,8} + py3{12,11,10,9,8}-async style typing pytype @@ -10,6 +10,11 @@ envlist = skip_missing_interpreters = true [testenv] +package = wheel +wheel_build_env = .pkg +envtmpdir = {toxworkdir}/tmp/{envname} +constrain_package_deps = true +use_frozen_constraints = true deps = -r requirements/test.txt async: Flask[async] @@ -23,31 +28,31 @@ commands = pre-commit run --all-files --show-diff-on-failure [testenv:typing] -basepython=python3.11 +basepython=python3.12 deps = - mypy==1.1.1 + mypy==1.8.0 commands = mypy --install-types --non-interactive src/ [testenv:pytype] -basepython=python3.10 +basepython=python3.11 deps = - pytype==2022.10.13 + pytype==2024.1.5 commands = pytype [testenv:security] -basepython=python3.11 +basepython=python3.12 deps = - bandit==1.7.5 - safety==2.3.5 + bandit==1.7.6 + safety==3.0.0 commands = - safety check -i 51457 + safety check bandit -r src/ [testenv:docs] -basepython=python3.11 +basepython=python3.12 deps = -r requirements/docs.txt commands = - sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/htmldoc + sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html