From c358a41e7c25e40c5686e44e491e32018f55f849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ianar=C3=A9=20S=C3=A9vi?= Date: Mon, 3 Nov 2025 16:34:08 +0100 Subject: [PATCH 1/2] :label: better field types --- mindee/parsing/v2/field/dynamic_field.py | 16 ++++++++++++++-- mindee/parsing/v2/field/inference_fields.py | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/mindee/parsing/v2/field/dynamic_field.py b/mindee/parsing/v2/field/dynamic_field.py index 53379aaa..d32a9128 100644 --- a/mindee/parsing/v2/field/dynamic_field.py +++ b/mindee/parsing/v2/field/dynamic_field.py @@ -1,9 +1,15 @@ from enum import Enum from importlib import import_module +from typing import TYPE_CHECKING, Union from mindee.error import MindeeApiV2Error from mindee.parsing.common.string_dict import StringDict +if TYPE_CHECKING: + from mindee.parsing.v2.field.list_field import ListField + from mindee.parsing.v2.field.object_field import ObjectField + from mindee.parsing.v2.field.simple_field import SimpleField + class FieldType(str, Enum): """Field types.""" @@ -13,6 +19,9 @@ class FieldType(str, Enum): SIMPLE = "SimpleField" +FieldTypeAlias = Union["SimpleField", "ListField", "ObjectField"] + + class DynamicField: """Field that can be displayed in rst format.""" @@ -30,7 +39,10 @@ def multi_str(self) -> str: return str(self) -def get_field_type(raw_response: StringDict, indent_level: int = 0) -> DynamicField: +def get_field_type( + raw_response: StringDict, + indent_level: int = 0, +) -> FieldTypeAlias: """Get appropriate field types.""" if isinstance(raw_response, dict): if "value" in raw_response: @@ -43,7 +55,7 @@ def get_field_type(raw_response: StringDict, indent_level: int = 0) -> DynamicFi field_file = import_module("mindee.parsing.v2.field.object_field") field_class = getattr(field_file, FieldType.OBJECT.value) else: - raise MindeeApiV2Error(f"Unrecognized field format in {raw_response}.") + raise MindeeApiV2Error(f"Unrecognized field type in {raw_response}.") return field_class(raw_response, indent_level) raise MindeeApiV2Error(f"Unrecognized field format {raw_response}.") diff --git a/mindee/parsing/v2/field/inference_fields.py b/mindee/parsing/v2/field/inference_fields.py index f23cda34..7e6368b0 100644 --- a/mindee/parsing/v2/field/inference_fields.py +++ b/mindee/parsing/v2/field/inference_fields.py @@ -2,13 +2,13 @@ from mindee.parsing.common.string_dict import StringDict from mindee.parsing.v2.field.dynamic_field import ( - DynamicField, FieldType, + FieldTypeAlias, get_field_type, ) -class InferenceFields(Dict[str, DynamicField]): +class InferenceFields(Dict[str, FieldTypeAlias]): """Inference fields dict.""" def __init__(self, raw_response: StringDict, indent_level: int = 0) -> None: From e16f8434815a5bbefe5660121395c5b6b00fa793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ianar=C3=A9=20S=C3=A9vi?= Date: Mon, 3 Nov 2025 17:34:22 +0100 Subject: [PATCH 2/2] :white_check_mark: :memo: update tests and docs --- .pre-commit-config.yaml | 6 +-- docs/product/ind/index.rst | 8 +++ pyproject.toml | 6 +-- tests/input/__init__.py | 0 .../{v1 => }/input/test_apply_page_options.py | 0 tests/{v1 => }/input/test_compression.py | 1 - tests/{v1 => }/input/test_fix_pdf.py | 0 tests/{v1 => }/input/test_inputs.py | 0 tests/v1/api/test_async_response.py | 1 - tests/v1/input/test_local_response.py | 47 +++++++----------- tests/v1/mindee_http/test_error.py | 3 +- tests/v2/input/test_local_response.py | 49 +++++++++++++++++++ tests/v2/parsing/__init__.py | 0 .../{ => parsing}/test_inference_response.py | 0 14 files changed, 81 insertions(+), 40 deletions(-) create mode 100644 docs/product/ind/index.rst create mode 100644 tests/input/__init__.py rename tests/{v1 => }/input/test_apply_page_options.py (100%) rename tests/{v1 => }/input/test_compression.py (99%) rename tests/{v1 => }/input/test_fix_pdf.py (100%) rename tests/{v1 => }/input/test_inputs.py (100%) create mode 100644 tests/v2/input/test_local_response.py create mode 100644 tests/v2/parsing/__init__.py rename tests/v2/{ => parsing}/test_inference_response.py (100%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e392940c..f5dac8bf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,11 +1,11 @@ repos: - repo: https://github.com/ambv/black - rev: 24.4.0 + rev: 25.9.0 hooks: - id: black - repo: https://github.com/pycqa/isort - rev: 5.13.2 + rev: 7.0.0 hooks: - id: isort args: [ "--profile", "black" , "--split-on-trailing-comma", "true"] @@ -39,7 +39,7 @@ repos: ] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.15.0 + rev: v1.18.2 hooks: - id: mypy args: [] diff --git a/docs/product/ind/index.rst b/docs/product/ind/index.rst new file mode 100644 index 00000000..e41cafa6 --- /dev/null +++ b/docs/product/ind/index.rst @@ -0,0 +1,8 @@ +India +----- + +.. toctree:: + :maxdepth: 1 + :glob: + + ./* diff --git a/pyproject.toml b/pyproject.toml index fb003d2c..b7f34d98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,9 +55,9 @@ test = [ "pytest-cov~=5.0", ] docs = [ - "sphinx~=5.3", - "sphinx_rtd_theme~=1.1", - "sphinx-autodoc-typehints~=1.20", + "sphinx~=7.3", + "sphinx_rtd_theme~=2.0", + "sphinx-autodoc-typehints~=2.2", ] build = [ "build", diff --git a/tests/input/__init__.py b/tests/input/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/v1/input/test_apply_page_options.py b/tests/input/test_apply_page_options.py similarity index 100% rename from tests/v1/input/test_apply_page_options.py rename to tests/input/test_apply_page_options.py diff --git a/tests/v1/input/test_compression.py b/tests/input/test_compression.py similarity index 99% rename from tests/v1/input/test_compression.py rename to tests/input/test_compression.py index 8b2235c8..b3b7c767 100644 --- a/tests/v1/input/test_compression.py +++ b/tests/input/test_compression.py @@ -1,7 +1,6 @@ import operator import os from functools import reduce -from pathlib import Path import pytest from PIL import Image diff --git a/tests/v1/input/test_fix_pdf.py b/tests/input/test_fix_pdf.py similarity index 100% rename from tests/v1/input/test_fix_pdf.py rename to tests/input/test_fix_pdf.py diff --git a/tests/v1/input/test_inputs.py b/tests/input/test_inputs.py similarity index 100% rename from tests/v1/input/test_inputs.py rename to tests/input/test_inputs.py diff --git a/tests/v1/api/test_async_response.py b/tests/v1/api/test_async_response.py index f1493535..5ca97391 100644 --- a/tests/v1/api/test_async_response.py +++ b/tests/v1/api/test_async_response.py @@ -1,5 +1,4 @@ import json -from pathlib import Path import pytest import requests diff --git a/tests/v1/input/test_local_response.py b/tests/v1/input/test_local_response.py index 30430980..2a7b8c0c 100644 --- a/tests/v1/input/test_local_response.py +++ b/tests/v1/input/test_local_response.py @@ -7,49 +7,36 @@ @pytest.fixture -def dummy_secret_key(): - return "ogNjY44MhvKPGTtVsI8zG82JqWQa68woYQH" +def file_path() -> Path: + return ASYNC_DIR / "get_completed_empty.json" -@pytest.fixture -def signature(): - return "5ed1673e34421217a5dbfcad905ee62261a3dd66c442f3edd19302072bbf70d0" - - -@pytest.fixture -def file_path(): - return Path(ASYNC_DIR / "get_completed_empty.json") +def _assert_local_response(local_response): + fake_hmac_signing = "ogNjY44MhvKPGTtVsI8zG82JqWQa68woYQH" + signature = "5ed1673e34421217a5dbfcad905ee62261a3dd66c442f3edd19302072bbf70d0" + assert local_response._file is not None + assert not local_response.is_valid_hmac_signature( + fake_hmac_signing, "invalid signature" + ) + assert signature == local_response.get_hmac_signature(fake_hmac_signing) + assert local_response.is_valid_hmac_signature(fake_hmac_signing, signature) -def test_valid_file_local_response(dummy_secret_key, signature, file_path): +def test_valid_file_local_response(file_path): with open(file_path, "rb") as file: local_response = LocalResponse(file) - assert local_response._file is not None - assert not local_response.is_valid_hmac_signature( - dummy_secret_key, "invalid signature" - ) - assert signature == local_response.get_hmac_signature(dummy_secret_key) - assert local_response.is_valid_hmac_signature(dummy_secret_key, signature) + _assert_local_response(local_response) -def test_valid_path_local_response(dummy_secret_key, signature, file_path): +def test_valid_path_local_response(file_path): local_response = LocalResponse(file_path) assert local_response._file is not None - assert not local_response.is_valid_hmac_signature( - dummy_secret_key, "invalid signature" - ) - assert signature == local_response.get_hmac_signature(dummy_secret_key) - assert local_response.is_valid_hmac_signature(dummy_secret_key, signature) + _assert_local_response(local_response) -def test_valid_bytes_local_response(dummy_secret_key, signature, file_path): +def test_valid_bytes_local_response(file_path): with open(file_path, "r") as f: str_response = f.read().replace("\r", "").replace("\n", "") file_bytes = str_response.encode("utf-8") local_response = LocalResponse(file_bytes) - assert local_response._file is not None - assert not local_response.is_valid_hmac_signature( - dummy_secret_key, "invalid signature" - ) - assert signature == local_response.get_hmac_signature(dummy_secret_key) - assert local_response.is_valid_hmac_signature(dummy_secret_key, signature) + _assert_local_response(local_response) diff --git a/tests/v1/mindee_http/test_error.py b/tests/v1/mindee_http/test_error.py index 19349d84..c4b32098 100644 --- a/tests/v1/mindee_http/test_error.py +++ b/tests/v1/mindee_http/test_error.py @@ -9,8 +9,7 @@ handle_error, ) from mindee.input.sources.path_input import PathInput -from tests.utils import V1_DATA_DIR, V1_PRODUCT_DATA_DIR, clear_envvars, dummy_envvars -from tests.v1.input.test_inputs import FILE_TYPES_DIR +from tests.utils import FILE_TYPES_DIR, V1_DATA_DIR, clear_envvars, dummy_envvars V1_ERROR_DATA_DIR = V1_DATA_DIR / "errors" diff --git a/tests/v2/input/test_local_response.py b/tests/v2/input/test_local_response.py new file mode 100644 index 00000000..449e35df --- /dev/null +++ b/tests/v2/input/test_local_response.py @@ -0,0 +1,49 @@ +from pathlib import Path + +import pytest + +from mindee import InferenceResponse +from mindee.input import LocalResponse +from tests.utils import V2_DATA_DIR + + +@pytest.fixture +def file_path() -> Path: + return V2_DATA_DIR / "inference" / "standard_field_types.json" + + +def _assert_local_response(local_response): + fake_hmac_signing = "ogNjY44MhvKPGTtVsI8zG82JqWQa68woYQH" + signature = "a1bc9012fa63539d602f163d8980604a0cf2b2ae88e56009cfa1db33382736cf" + + assert local_response._file is not None + assert not local_response.is_valid_hmac_signature( + fake_hmac_signing, "invalid signature" + ) + assert signature == local_response.get_hmac_signature(fake_hmac_signing) + assert local_response.is_valid_hmac_signature(fake_hmac_signing, signature) + reponse: InferenceResponse = local_response.deserialize_response(InferenceResponse) + assert isinstance(reponse, InferenceResponse) + assert reponse.inference is not None + assert reponse.inference.result is not None + assert reponse.inference.result.fields is not None + + +def test_valid_file_local_response(file_path): + with open(file_path, "rb") as file: + local_response = LocalResponse(file) + _assert_local_response(local_response) + + +def test_valid_path_local_response(file_path): + local_response = LocalResponse(file_path) + assert local_response._file is not None + _assert_local_response(local_response) + + +def test_valid_bytes_local_response(file_path): + with open(file_path, "r") as f: + str_response = f.read().replace("\r", "").replace("\n", "") + file_bytes = str_response.encode("utf-8") + local_response = LocalResponse(file_bytes) + _assert_local_response(local_response) diff --git a/tests/v2/parsing/__init__.py b/tests/v2/parsing/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/v2/test_inference_response.py b/tests/v2/parsing/test_inference_response.py similarity index 100% rename from tests/v2/test_inference_response.py rename to tests/v2/parsing/test_inference_response.py