From d21659a4704d766031287e6a8ca3bdfd9438b9e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Dohmen?= Date: Thu, 17 Oct 2024 11:32:34 +0200 Subject: [PATCH 1/9] remove unneeded stack trace --- pydantic_forms/exception_handlers/fastapi.py | 2 -- pyproject.toml | 2 -- 2 files changed, 4 deletions(-) diff --git a/pydantic_forms/exception_handlers/fastapi.py b/pydantic_forms/exception_handlers/fastapi.py index 56e30bf..3d112b7 100644 --- a/pydantic_forms/exception_handlers/fastapi.py +++ b/pydantic_forms/exception_handlers/fastapi.py @@ -29,7 +29,6 @@ async def form_error_handler(request: Request, exc: FormException) -> JSONRespon { "type": type(exc).__name__, "detail": str(exc), - "traceback": show_ex(exc), "title": "Form not valid", # We need to make sure the is nothing the default json.dumps cannot handle "validation_errors": json_loads(json_dumps(exc.errors)), @@ -43,7 +42,6 @@ async def form_error_handler(request: Request, exc: FormException) -> JSONRespon { "type": type(exc).__name__, "detail": str(exc), - "traceback": show_ex(exc), # We need to make sure the is nothing the default json.dumps cannot handle "form": json_loads(json_dumps(exc.form)), "title": "Form not complete", diff --git a/pyproject.toml b/pyproject.toml index 81e907e..0589682 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,8 +61,6 @@ test = [ "ruff", "types-Deprecated", "types-certifi", - "types-itsdangerous", - "types-pkg_resources", "types-python-dateutil", "types-pytz", "types-toml", From 6072ae250623a26dc92b5ff1f91c9c0357fe8728 Mon Sep 17 00:00:00 2001 From: Rene Dohmen Date: Fri, 18 Oct 2024 20:18:40 +0200 Subject: [PATCH 2/9] fix builds and removed some pydantic 2.7 refs --- pydantic_forms/exception_handlers/fastapi.py | 2 +- .../validators/components/migration_summary.py | 4 +++- tests/unit_tests/helpers.py | 12 ++++++++++++ tests/unit_tests/test_accept.py | 3 ++- tests/unit_tests/test_choice_list.py | 2 +- tests/unit_tests/test_constrained_list.py | 4 ++-- tests/unit_tests/test_display_subscription.py | 4 +++- tests/unit_tests/test_list_of_two.py | 4 ++-- tests/unit_tests/test_migration_summary.py | 4 +++- 9 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 tests/unit_tests/helpers.py diff --git a/pydantic_forms/exception_handlers/fastapi.py b/pydantic_forms/exception_handlers/fastapi.py index 3d112b7..df120de 100644 --- a/pydantic_forms/exception_handlers/fastapi.py +++ b/pydantic_forms/exception_handlers/fastapi.py @@ -19,7 +19,7 @@ from fastapi.requests import Request from fastapi.responses import JSONResponse -from pydantic_forms.exceptions import FormException, FormNotCompleteError, FormValidationError, show_ex +from pydantic_forms.exceptions import FormException, FormNotCompleteError, FormValidationError from pydantic_forms.utils.json import json_dumps, json_loads diff --git a/pydantic_forms/validators/components/migration_summary.py b/pydantic_forms/validators/components/migration_summary.py index 0ae1ec0..9ee135d 100644 --- a/pydantic_forms/validators/components/migration_summary.py +++ b/pydantic_forms/validators/components/migration_summary.py @@ -28,7 +28,9 @@ class _MigrationSummary(BaseModel): def create_json_extra_schema(data: SummaryData, schema: dict[str, Any]) -> None: schema.update({"format": "summary", "type": "string", "uniforms": {"data": data}}) - schema.pop("allOf") # This is needed, because otherwise Uniforms (3.8.1) is unable to render this schema + # TODO: check if Frontend renders MigrationSummary ok + # Error: no "allOf" anymore in pydantic JSON scheme + # schema.pop("allOf") # This is needed, because otherwise Uniforms (3.8.1) is unable to render this schema def migration_summary(data: SummaryData) -> type[MigrationSummary]: diff --git a/tests/unit_tests/helpers.py b/tests/unit_tests/helpers.py new file mode 100644 index 0000000..74a8afe --- /dev/null +++ b/tests/unit_tests/helpers.py @@ -0,0 +1,12 @@ +def assert_equal_ignore_key(expected, actual, ignore_keys): + def deep_remove_keys(d, keys_to_ignore): + if isinstance(d, dict): + return {k: deep_remove_keys(v, keys_to_ignore) for k, v in d.items() if k not in keys_to_ignore} + elif isinstance(d, list): + return [deep_remove_keys(i, keys_to_ignore) for i in d] + return d + + stripped_expected = deep_remove_keys(expected, ignore_keys) + stripped_actual = deep_remove_keys(actual, ignore_keys) + + assert stripped_expected == stripped_actual, f"Expected {stripped_expected}, but got {stripped_actual}" diff --git a/tests/unit_tests/test_accept.py b/tests/unit_tests/test_accept.py index 0953fe9..ed7ee30 100644 --- a/tests/unit_tests/test_accept.py +++ b/tests/unit_tests/test_accept.py @@ -6,6 +6,7 @@ from pydantic_forms.core import FormPage from pydantic_forms.utils.json import json_loads from pydantic_forms.validators import Accept, AcceptValues +from tests.unit_tests.helpers import assert_equal_ignore_key def test_accept_ok(): @@ -96,4 +97,4 @@ class Form(FormPage): "url": "https://errors.pydantic.dev/2.7/v/enum", } ] - assert error_info.value.errors() == expected + assert_equal_ignore_key(expected, error_info.value.errors(), ["url"]) diff --git a/tests/unit_tests/test_choice_list.py b/tests/unit_tests/test_choice_list.py index 8009925..4657688 100644 --- a/tests/unit_tests/test_choice_list.py +++ b/tests/unit_tests/test_choice_list.py @@ -153,7 +153,7 @@ def test_choice_list_constraint_at_least_one_item(Form): { "input": [], "loc": ("choice",), - "msg": "List should have at least 1 item after validation, not 0", + "msg": "Value should have at least 1 item after validation, not 0", "type": "too_short", # "ctx": {"limit_value": 1}, } diff --git a/tests/unit_tests/test_constrained_list.py b/tests/unit_tests/test_constrained_list.py index 70f6137..77e76d5 100644 --- a/tests/unit_tests/test_constrained_list.py +++ b/tests/unit_tests/test_constrained_list.py @@ -79,7 +79,7 @@ def test_constrained_list_too_short(): # "ctx": {"error": ListMinLengthError(limit_value=1)}, "input": [], "loc": ("v",), - "msg": "List should have at least 1 item after validation, not 0", + "msg": "Value should have at least 1 item after validation, not 0", "type": "too_short", } ] @@ -115,7 +115,7 @@ class UniqueConListModel(FormPage): { "input": [], "loc": ("v",), - "msg": "List should have at least 1 item after validation, not 0", + "msg": "Value should have at least 1 item after validation, not 0", "type": "too_short", # "ctx": {"limit_value": 1}, } diff --git a/tests/unit_tests/test_display_subscription.py b/tests/unit_tests/test_display_subscription.py index b26a88e..e25c20b 100644 --- a/tests/unit_tests/test_display_subscription.py +++ b/tests/unit_tests/test_display_subscription.py @@ -36,6 +36,7 @@ class Form(FormPage): summary: Summary expected = { + "$defs": {"MigrationSummaryValue": {"properties": {}, "title": "MigrationSummaryValue", "type": "object"}}, "additionalProperties": False, "properties": { "display_sub": { @@ -52,8 +53,9 @@ class Form(FormPage): "type": "string", }, "summary": { - "format": "summary", + "$ref": "#/$defs/MigrationSummaryValue", "default": None, + "format": "summary", "type": "string", "uniforms": {"data": {"headers": ["one"]}}, }, diff --git a/tests/unit_tests/test_list_of_two.py b/tests/unit_tests/test_list_of_two.py index b351c63..f348d90 100644 --- a/tests/unit_tests/test_list_of_two.py +++ b/tests/unit_tests/test_list_of_two.py @@ -26,7 +26,7 @@ def test_list_of_two_min_items(Form): { "input": [1], "loc": ("two",), - "msg": "List should have at least 2 items after validation, not 1", + "msg": "Value should have at least 2 items after validation, not 1", "type": "too_short", } ] @@ -42,7 +42,7 @@ def test_list_of_two_max_items(Form): { "input": [1, 2, 3], "loc": ("two",), - "msg": "List should have at most 2 items after validation, not 3", + "msg": "Value should have at most 2 items after validation, not 3", "type": "too_long", }, ] diff --git a/tests/unit_tests/test_migration_summary.py b/tests/unit_tests/test_migration_summary.py index 7a111b8..94d5b64 100644 --- a/tests/unit_tests/test_migration_summary.py +++ b/tests/unit_tests/test_migration_summary.py @@ -34,11 +34,13 @@ class Form(FormPage): ms: Summary expected = { + "$defs": {"MigrationSummaryValue": {"properties": {}, "title": "MigrationSummaryValue", "type": "object"}}, "additionalProperties": False, "properties": { "ms": { - "format": "summary", + "$ref": "#/$defs/MigrationSummaryValue", "default": None, + "format": "summary", "type": "string", "uniforms": { "data": {"headers": ["one"]}, From 9cd26badbbc77ef41c305fdc33cc98ba245862fc Mon Sep 17 00:00:00 2001 From: Rene Dohmen Date: Fri, 18 Oct 2024 20:27:05 +0200 Subject: [PATCH 3/9] cleanup --- tests/unit_tests/test_accept.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit_tests/test_accept.py b/tests/unit_tests/test_accept.py index ed7ee30..c1ed1a2 100644 --- a/tests/unit_tests/test_accept.py +++ b/tests/unit_tests/test_accept.py @@ -94,7 +94,6 @@ class Form(FormPage): "loc": ("accept",), "msg": "Input should be 'ACCEPTED' or 'INCOMPLETE'", "type": "enum", - "url": "https://errors.pydantic.dev/2.7/v/enum", } ] assert_equal_ignore_key(expected, error_info.value.errors(), ["url"]) From a4d31d2b1c0370b847e7dc91da0eb8949c70d139 Mon Sep 17 00:00:00 2001 From: Rene Dohmen Date: Fri, 18 Oct 2024 21:20:07 +0200 Subject: [PATCH 4/9] increase coverage --- pydantic_forms/utils/json.py | 4 +- .../test_utils_from_serializable.py | 100 +++++++++++++++++ .../unit_tests/test_utils_to_serializable.py | 105 ++++++++++++++++++ 3 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 tests/unit_tests/test_utils_from_serializable.py create mode 100644 tests/unit_tests/test_utils_to_serializable.py diff --git a/pydantic_forms/utils/json.py b/pydantic_forms/utils/json.py index 54ec7ff..bfcb70e 100644 --- a/pydantic_forms/utils/json.py +++ b/pydantic_forms/utils/json.py @@ -173,7 +173,7 @@ def from_serializable(dct: dict[str, Any]) -> dict[str, Any]: if IS_ORJSON: - print("Using orjson") # noqa + logger.debug("Using orjson") def json_loads(s: Union[str, bytes, bytearray]) -> PY_JSON_TYPES: o = orjson.loads(s) @@ -192,7 +192,7 @@ def json_dumps(obj: PY_JSON_TYPES, default: Callable = to_serializable) -> str: raise e else: - print("Using stdlib json") # noqa + logger.debug("Using stdlib json") json_loads = json.loads json_dumps = partial(json.dumps, default=to_serializable) diff --git a/tests/unit_tests/test_utils_from_serializable.py b/tests/unit_tests/test_utils_from_serializable.py new file mode 100644 index 0000000..c6bc9e0 --- /dev/null +++ b/tests/unit_tests/test_utils_from_serializable.py @@ -0,0 +1,100 @@ +import pytest +from datetime import datetime + +from pydantic_forms.utils.json import from_serializable + + +# Helper function for testing ISO formatted datetime +def test_from_serializable_datetime(): + iso_datetime = "2024-01-01T12:00:00+00:00" + input_dict = {"timestamp": iso_datetime} + expected_datetime = datetime.fromisoformat(iso_datetime) + + result = from_serializable(input_dict) + + assert isinstance(result["timestamp"], datetime) + assert result["timestamp"] == expected_datetime + + +# Test that the function does not convert non-ISO format strings +def test_from_serializable_non_iso_string(): + input_dict = {"non_iso": "some random string"} + + result = from_serializable(input_dict) + + assert result["non_iso"] == "some random string" # No conversion + + +# Test that no conversion happens if string is not in ISO format but looks similar +def test_from_serializable_non_iso_similar_string(): + similar_string = "2024-01-01 12:00:00" + input_dict = {"similar_string": similar_string} + + result = from_serializable(input_dict) + + assert result["similar_string"] == similar_string # No conversion + + +# Test handling of invalid ISO string that should not raise an error +def test_from_serializable_invalid_iso_string(): + invalid_iso = "2024-01-01T12:00:00" # Missing timezone information + input_dict = {"timestamp": invalid_iso} + + result = from_serializable(input_dict) + + assert result["timestamp"] == invalid_iso # No conversion should happen + + +# Test multiple fields in dictionary, only ISO string should be converted +def test_from_serializable_mixed_dict(): + iso_datetime = "2024-01-01T12:00:00+00:00" + input_dict = { + "timestamp": iso_datetime, + "non_iso": "hello", + "integer": 42, + } + + result = from_serializable(input_dict) + + # Check only the datetime field is converted + assert isinstance(result["timestamp"], datetime) + assert result["timestamp"] == datetime.fromisoformat(iso_datetime) + assert result["non_iso"] == "hello" + assert result["integer"] == 42 + + +# Test that function handles empty dict correctly +def test_from_serializable_empty_dict(): + input_dict = {} + + result = from_serializable(input_dict) + + assert result == {} # Should return an empty dict + + +# Test for non-string types +def test_from_serializable_non_string_values(): + input_dict = { + "timestamp": "2024-01-01T12:00:00+00:00", + "number": 123, + "list": [1, 2, 3], + "none_value": None, + } + + result = from_serializable(input_dict) + + assert isinstance(result["timestamp"], datetime) + assert result["number"] == 123 + assert result["list"] == [1, 2, 3] + assert result["none_value"] is None + + +# Test when the timestamp is in a nested dictionary +def test_from_serializable_nested_dict(): + iso_datetime = "2024-01-01T12:00:00+00:00" + input_dict = {"level1": {"timestamp": iso_datetime}} + + result = from_serializable(input_dict) + + # Since this function works on top-level only, the nested timestamp should not be converted + assert result["level1"]["timestamp"] == iso_datetime # No conversion diff --git a/tests/unit_tests/test_utils_to_serializable.py b/tests/unit_tests/test_utils_to_serializable.py new file mode 100644 index 0000000..dd6e784 --- /dev/null +++ b/tests/unit_tests/test_utils_to_serializable.py @@ -0,0 +1,105 @@ +# Test UUID serialization +from dataclasses import dataclass, asdict +from datetime import datetime +from ipaddress import IPv4Network, IPv6Network, IPv4Address, IPv6Address +from uuid import UUID + +from pydantic_forms.utils.json import to_serializable +import pytest + + +def test_to_serializable_uuid(): + test_uuid = UUID("12345678123456781234567812345678") + assert to_serializable(test_uuid) == str(test_uuid) + + +# Test IPv4 and IPv6 Address serialization +@pytest.mark.parametrize( + "ip_address, expected", + [ + (IPv4Address("192.168.1.1"), "192.168.1.1"), + (IPv6Address("2001:db8::8a2e:370:7334"), "2001:db8::8a2e:370:7334"), + ], +) +def test_to_serializable_ip_address(ip_address, expected): + assert to_serializable(ip_address) == expected + + +# Test IPv4 and IPv6 Network serialization +@pytest.mark.parametrize( + "ip_network, expected", + [ + (IPv4Network("192.168.1.0/24"), "192.168.1.0/24"), + (IPv6Network("2001:db8::/32"), "2001:db8::/32"), + ], +) +def test_to_serializable_ip_network(ip_network, expected): + assert to_serializable(ip_network) == expected + + +# Test datetime serialization +def test_to_serializable_datetime(): + test_datetime = datetime(2024, 1, 1) + assert to_serializable(test_datetime) == "2024-01-01T00:00:00" + + +# Test dataclass serialization +@dataclass +class TestDataClass: + id: int + name: str + + +def test_to_serializable_dataclass(): + obj = TestDataClass(id=1, name="Test") + assert to_serializable(obj) == asdict(obj) + + +# Test custom JSON method +class CustomJsonObject: + def __json__(self): + return {"custom": "json"} + + +def test_to_serializable_custom_json(): + obj = CustomJsonObject() + assert to_serializable(obj) == {"custom": "json"} + + +# Test custom to_dict method +class CustomDictObject: + def to_dict(self): + return {"custom": "dict"} + + +def test_to_serializable_custom_dict(): + obj = CustomDictObject() + assert to_serializable(obj) == {"custom": "dict"} + + +# Test set serialization +def test_to_serializable_set(): + test_set = {1, 2, 3} + assert to_serializable(test_set) == list(test_set) + + +# Test ValueError and AssertionError serialization +@pytest.mark.parametrize( + "exception, expected", + [ + (ValueError("An error occurred"), "An error occurred"), + (AssertionError("Assertion failed"), "Assertion failed"), + ], +) +def test_to_serializable_exceptions(exception, expected): + assert to_serializable(exception) == expected + + +# Test TypeError raised for unsupported types +def test_to_serializable_unsupported_type(): + class UnsupportedClass: + pass + + obj = UnsupportedClass() + with pytest.raises(TypeError, match="Could not serialize object of type UnsupportedClass to JSON"): + to_serializable(obj) From faeb44a86f5d54e02fcea2aa9c21f00c148b053d Mon Sep 17 00:00:00 2001 From: Rene Dohmen Date: Mon, 21 Oct 2024 17:23:57 +0200 Subject: [PATCH 5/9] Make traceback configurable --- README.md | 11 +++++ pydantic_forms/exception_handlers/fastapi.py | 51 ++++++++++++-------- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 29962cd..5814c1d 100644 --- a/README.md +++ b/README.md @@ -96,3 +96,14 @@ bumpversion patch --new-version 0.0.1 Note: specifying it like this, instead of relying on bumpversion itself to increase the version, allows you to set a "RC1" version if needed. + +# Debugging Form behaviour + +If you want/need the traceback of pydantic in a Form response you can add an env variable: + +` +PYDANTIC_FORMS_LOGLEVEL=DEBUG +` + +This will add the traceback to the `JSONResponse`. If the loglevel is set to DEBUG it will also print the +traceback to the logger. diff --git a/pydantic_forms/exception_handlers/fastapi.py b/pydantic_forms/exception_handlers/fastapi.py index df120de..11ae968 100644 --- a/pydantic_forms/exception_handlers/fastapi.py +++ b/pydantic_forms/exception_handlers/fastapi.py @@ -10,44 +10,55 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - +import os # TODO Decide how to expose this so pydantic-forms can be framework agnostic - from http import HTTPStatus +import structlog from fastapi.requests import Request from fastapi.responses import JSONResponse -from pydantic_forms.exceptions import FormException, FormNotCompleteError, FormValidationError +from pydantic_forms.exceptions import FormException, FormNotCompleteError, FormValidationError, show_ex from pydantic_forms.utils.json import json_dumps, json_loads +logger = structlog.get_logger(__name__) + async def form_error_handler(request: Request, exc: FormException) -> JSONResponse: + PYDANTIC_FORMS_LOGLEVEL = "DEBUG" if os.getenv("PYDANTIC_FORMS_LOGLEVEL", "INFO").upper() == "DEBUG" else "INFO" if isinstance(exc, FormValidationError): + result = { + "type": type(exc).__name__, + "detail": str(exc), + "title": "Form not valid", + # We need to make sure there is nothing the default json.dumps cannot handle + "validation_errors": json_loads(json_dumps(exc.errors)), + "status": HTTPStatus.BAD_REQUEST, + } + if PYDANTIC_FORMS_LOGLEVEL == "DEBUG": + result["traceback"] = show_ex(exc) + logger.debug("Form validation Response", result=result) return JSONResponse( - { - "type": type(exc).__name__, - "detail": str(exc), - "title": "Form not valid", - # We need to make sure the is nothing the default json.dumps cannot handle - "validation_errors": json_loads(json_dumps(exc.errors)), - "status": HTTPStatus.BAD_REQUEST, - }, + result, status_code=HTTPStatus.BAD_REQUEST, ) if isinstance(exc, FormNotCompleteError): + result = { + "type": type(exc).__name__, + "detail": str(exc), + # We need to make sure the is nothing the default json.dumps cannot handle + "form": json_loads(json_dumps(exc.form)), + "title": "Form not complete", + "status": HTTPStatus.NOT_EXTENDED, + "meta": getattr(exc, "meta", None), + } + if PYDANTIC_FORMS_LOGLEVEL == "DEBUG": + result["traceback"] = show_ex(exc) + logger.debug("Form validation Response", result=result) return JSONResponse( - { - "type": type(exc).__name__, - "detail": str(exc), - # We need to make sure the is nothing the default json.dumps cannot handle - "form": json_loads(json_dumps(exc.form)), - "title": "Form not complete", - "status": HTTPStatus.NOT_EXTENDED, - "meta": getattr(exc, "meta", None), - }, + result, status_code=HTTPStatus.NOT_EXTENDED, ) From df8a48cc306192258c6b6aa58ff31b3bcab29c4a Mon Sep 17 00:00:00 2001 From: Rene Dohmen Date: Mon, 21 Oct 2024 20:51:23 +0200 Subject: [PATCH 6/9] patch README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5814c1d..4aa89b6 100644 --- a/README.md +++ b/README.md @@ -105,5 +105,5 @@ If you want/need the traceback of pydantic in a Form response you can add an env PYDANTIC_FORMS_LOGLEVEL=DEBUG ` -This will add the traceback to the `JSONResponse`. If the loglevel is set to DEBUG it will also print the +This will add the traceback to the `JSONResponse`. If the loglevel is set to DEBUG the library will also add the traceback to the logger. From 8e5e6dd53eb18cef4ea2f9add902f11b0620dac0 Mon Sep 17 00:00:00 2001 From: Rene Dohmen Date: Mon, 21 Oct 2024 21:06:45 +0200 Subject: [PATCH 7/9] increased coverage to include the new code --- .../exception_handlers/test_fastapi.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/unit_tests/exception_handlers/test_fastapi.py b/tests/unit_tests/exception_handlers/test_fastapi.py index 937270e..f355275 100644 --- a/tests/unit_tests/exception_handlers/test_fastapi.py +++ b/tests/unit_tests/exception_handlers/test_fastapi.py @@ -17,6 +17,18 @@ async def test_form_not_complete(): body = response.body.decode() assert "FormNotCompleteError" in body assert "foobar" in body + assert "traceback" not in body + + +async def test_form_not_complete_with_stack_trace(monkeypatch): + monkeypatch.setenv("PYDANTIC_FORMS_LOGLEVEL", "DEBUG") + 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 + assert "traceback" in body @pytest.fixture @@ -37,6 +49,18 @@ async def test_form_validation(example_form_error_invalid_int): body = response.body.decode() assert "FormValidationError" in body assert "should be a valid integer" in body + assert "traceback" not in body + + +async def test_form_validation_with_stack_trace(example_form_error_invalid_int, monkeypatch): + monkeypatch.setenv("PYDANTIC_FORMS_LOGLEVEL", "DEBUG") + 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 "should be a valid integer" in body + assert "traceback" in body async def test_overflow_error(): From 98f1da64ac9274a7d3b2bd0dd523e854f19d2a9b Mon Sep 17 00:00:00 2001 From: Rene Dohmen Date: Mon, 21 Oct 2024 21:12:11 +0200 Subject: [PATCH 8/9] cleanup --- pydantic_forms/validators/components/migration_summary.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pydantic_forms/validators/components/migration_summary.py b/pydantic_forms/validators/components/migration_summary.py index 9ee135d..534e656 100644 --- a/pydantic_forms/validators/components/migration_summary.py +++ b/pydantic_forms/validators/components/migration_summary.py @@ -28,9 +28,6 @@ class _MigrationSummary(BaseModel): def create_json_extra_schema(data: SummaryData, schema: dict[str, Any]) -> None: schema.update({"format": "summary", "type": "string", "uniforms": {"data": data}}) - # TODO: check if Frontend renders MigrationSummary ok - # Error: no "allOf" anymore in pydantic JSON scheme - # schema.pop("allOf") # This is needed, because otherwise Uniforms (3.8.1) is unable to render this schema def migration_summary(data: SummaryData) -> type[MigrationSummary]: From 9342a38df827be24c65148906554dce401e817b7 Mon Sep 17 00:00:00 2001 From: Peter Boers Date: Tue, 22 Oct 2024 07:57:40 +0200 Subject: [PATCH 9/9] Bumpversion --- .bumpversion.cfg | 2 +- .coveragerc | 2 +- .github/workflows/run-unit-tests.yml | 2 +- codecov.yml | 2 +- pydantic_forms/__init__.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 09067d1..92d41eb 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.1.0 +current_version = 1.1.1rc1 commit = False tag = False parse = (?P\d+)\.(?P\d+)\.(?P\d+)((\-rc)(?P\d+))? diff --git a/.coveragerc b/.coveragerc index c0d72db..eef15c4 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,3 +1,3 @@ [run] include = pydantic_forms -omit = tests/ \ No newline at end of file +omit = tests/ diff --git a/.github/workflows/run-unit-tests.yml b/.github/workflows/run-unit-tests.yml index 9d02eb8..d7540a6 100644 --- a/.github/workflows/run-unit-tests.yml +++ b/.github/workflows/run-unit-tests.yml @@ -44,4 +44,4 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true - files: ./coverage.xml \ No newline at end of file + files: ./coverage.xml diff --git a/codecov.yml b/codecov.yml index b3ed978..e6b99e6 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,2 +1,2 @@ ignore: - - "tests" \ No newline at end of file + - "tests" diff --git a/pydantic_forms/__init__.py b/pydantic_forms/__init__.py index ca708c2..ed3dbae 100644 --- a/pydantic_forms/__init__.py +++ b/pydantic_forms/__init__.py @@ -13,4 +13,4 @@ """Pydantic-forms engine.""" -__version__ = "1.1.0" +__version__ = "1.1.1rc1"