Skip to content

Commit

Permalink
Merge pull request #7 from workfloworchestrator/py311
Browse files Browse the repository at this point in the history
Python 3.11 + Bump version to 0.2.0
  • Loading branch information
pboers1988 authored Mar 30, 2023
2 parents 7c25912 + 5751d82 commit 582839e
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.1.0
current_version = 0.2.0
commit = False
tag = False
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)((\-rc)(?P<build>\d+))?
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v2
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/run-linting-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
fail-fast: false

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
Expand All @@ -28,7 +28,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install flit
flit install --deps develop --symlink
flit install --deps develop --extras fastapi
- name: Check formatting
run: |
isort -c .
Expand Down
17 changes: 15 additions & 2 deletions .github/workflows/run-unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,32 @@ jobs:
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11']
pydantic-version: ['1.9.0', '1.10.0', 'lockfile']
fastapi-version: ['0.80.0', '0.90.0', 'lockfile']
exclude:
- python-version: '3.11'
pydantic-version: '1.9.0'
fail-fast: false
container: python:${{ matrix.python-version }}-slim
steps:
# Downloads a copy of the code in your repository before running CI tests
- name: Check out repository code
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install dependencies
run: |
apt update
apt install curl git build-essential libpq-dev libffi-dev -y
python -m pip install --upgrade pip
pip install flit
flit install --deps develop --symlink
flit install --deps develop
if [ "${{ matrix.pydantic-version }}" != "lockfile" ]; then
pip install pydantic[email]~=${{ matrix.pydantic-version }}
fi
if [ "${{ matrix.fastapi-version }}" != "lockfile" ]; then
pip install fastapi~=${{ matrix.fastapi-version }}
else
flit install --extras fastapi
fi
env:
FLIT_ROOT_INSTALL: 1
- name: Run Unit tests
Expand Down
118 changes: 118 additions & 0 deletions .stignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
.git
# Byte-compiled / optimized / DLL files
__pycache__
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build
develop-eggs
dist
downloads
eggs
.eggs
lib
lib64
parts
sdist
var
wheels
pip-wheel-metadata
share/python-wheels
*.egg-info
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build

# PyBuilder
target

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default
ipython_config.py

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv*
env
venv
ENV
env.bak
venv.bak

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mypy
.mypy_cache
.dmypy.json
dmypy.json

# Pyre type checker
.pyre

# Misc cache
.cache
.pytest_cache

# Editors
.idea
.code
.vscode
.pycharm_helpers

# Exclude files
.*ignore

# OSX stuff
.DS_Store

2 changes: 1 addition & 1 deletion pydantic_forms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@

"""This is the pydantic-forms engine."""

__version__ = "0.1.0"
__version__ = "0.2.0"
31 changes: 2 additions & 29 deletions pydantic_forms/exception_handlers/fastapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,14 @@
# TODO Decide how to expose this so pydantic-forms can be framework agnostic

from http import HTTPStatus
from typing import Any, Dict, NoReturn, Optional, Union

from fastapi.exceptions import HTTPException
from starlette.datastructures import MutableHeaders
from starlette.requests import Request
from starlette.responses import JSONResponse
from fastapi.requests import Request
from fastapi.responses import JSONResponse

from pydantic_forms.exceptions import FormException, FormNotCompleteError, FormValidationError, show_ex
from pydantic_forms.utils.json import json_dumps, json_loads


class ProblemDetailException(HTTPException):
def __init__(
self,
status: int,
title: Optional[str] = None,
detail: Any = None,
headers: Optional[dict] = None,
error_type: Optional[str] = None,
) -> None:
if headers is None:
headers = {}

super().__init__(status_code=status, detail=detail, headers=headers)
self.title = title
self.type = error_type


def raise_status(status: int, detail: Any = None, headers: Optional[Union[MutableHeaders, Dict]] = None) -> NoReturn:
status = HTTPStatus(status)
if isinstance(headers, MutableHeaders):
headers = dict(**headers)
raise ProblemDetailException(status=status.value, title=status.phrase, detail=detail, headers=headers)


async def form_error_handler(request: Request, exc: FormException) -> JSONResponse:
if isinstance(exc, FormValidationError):
return JSONResponse(
Expand Down
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ requires = [

]
description-file = "README.md"
requires-python = ">=3.9,<=3.11"
requires-python = ">=3.9,<=3.12"

[tool.flit.metadata.urls]
Documentation = "https://github.com/workfloworchestrator/pydantic-forms/blob/main/README.md"
Expand Down Expand Up @@ -100,6 +100,9 @@ rapidjson = [
orjson = [
"orjson >=3.6.0,<4.0.0",
]
fastapi = [
"fastapi >=0.80.0,<1.0.0"
]


[tool.isort]
Expand Down
Empty file.
48 changes: 48 additions & 0 deletions tests/unit_tests/exception_handlers/test_fastapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from http import HTTPStatus
from unittest import mock

import pytest
from fastapi.requests import Request
from pydantic import ValidationError

from pydantic_forms.core import FormPage
from pydantic_forms.exception_handlers.fastapi import form_error_handler
from pydantic_forms.exceptions import FormNotCompleteError, FormOverflowError, FormValidationError


async def test_form_not_complete():
exception = FormNotCompleteError({"message": "foobar"})
response = await form_error_handler(mock.Mock(spec=Request), exception)
assert response.status_code == HTTPStatus.NOT_EXTENDED
body = response.body.decode()
assert "FormNotCompleteError" in body
assert "foobar" in body


@pytest.fixture
def example_form_error_invalid_int():
class Form(FormPage):
number: int

with pytest.raises(ValidationError) as error_info:
assert Form(number="foo")

return error_info.value.errors()


async def test_form_validation(example_form_error_invalid_int):
exception = FormValidationError("myvalidator", example_form_error_invalid_int)
response = await form_error_handler(mock.Mock(spec=Request), exception)
assert response.status_code == HTTPStatus.BAD_REQUEST
body = response.body.decode()
assert "FormValidationError" in body
assert "is not a valid integer" in body


async def test_overflow_error():
exception = FormOverflowError("my error")
response = await form_error_handler(mock.Mock(spec=Request), exception)
assert response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR
body = response.body.decode()
assert "FormOverflowError" in body
assert "my error" in body

0 comments on commit 582839e

Please sign in to comment.