Skip to content

Commit 9a77744

Browse files
fix most syntaxes, add tests (integration testing not fully working)
1 parent 145f8b4 commit 9a77744

21 files changed

+462
-71
lines changed

mindee/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from mindee import product
22
from mindee.client import Client
3+
from mindee.client_v2 import ClientV2
34
from mindee.input.inference_predict_options import InferencePredictOptions
45
from mindee.input.local_response import LocalResponse
56
from mindee.input.page_options import PageOptions

mindee/client_v2.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,6 @@
1919
from mindee.parsing.v2.polling_response import PollingResponse
2020

2121

22-
def load_prediction(local_response: LocalResponse) -> InferenceResponse:
23-
"""
24-
Load a prediction.
25-
26-
:param local_response: Local response to load.
27-
:return: A valid prediction.
28-
"""
29-
try:
30-
return InferenceResponse(local_response.as_dict)
31-
except KeyError as exc:
32-
raise MindeeError("No prediction found in local response.") from exc
33-
34-
3522
class ClientV2(ClientMixin):
3623
"""
3724
Mindee API Client.
@@ -182,3 +169,16 @@ def enqueue_and_parse(
182169
)
183170

184171
return poll_results
172+
173+
@staticmethod
174+
def load_inference(local_response: LocalResponse) -> InferenceResponse:
175+
"""
176+
Load a prediction from the V2 API.
177+
178+
:param local_response: Local response to load.
179+
:return: A valid prediction.
180+
"""
181+
try:
182+
return InferenceResponse(local_response.as_dict)
183+
except KeyError as exc:
184+
raise MindeeError("No prediction found in local response.") from exc

mindee/error/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
from mindee.error.geometry_error import GeometryError
22
from mindee.error.mimetype_error import MimeTypeError
3-
from mindee.error.mindee_error import MindeeClientError, MindeeError, MindeeProductError
3+
from mindee.error.mindee_error import (
4+
MindeeApiError,
5+
MindeeApiV2Error,
6+
MindeeClientError,
7+
MindeeError,
8+
MindeeProductError,
9+
)
410
from mindee.error.mindee_http_error import (
511
MindeeHTTPClientError,
612
MindeeHTTPError,

mindee/error/mindee_error.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class MindeeApiError(MindeeError):
1414
"""An exception relating to settings of the MindeeClient."""
1515

1616

17-
class MindeeAPIV2Error(MindeeError):
17+
class MindeeApiV2Error(MindeeError):
1818
"""An exception relating to settings of the MindeeClient V2."""
1919

2020

mindee/error/mindee_http_error_v2.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,20 @@ def __init__(self, status: int, detail: Optional[str]) -> None:
2525
class MindeeHTTPUnknownErrorV2(MindeeHTTPErrorV2):
2626
"""HTTP error with unknown status code."""
2727

28-
def __init__(self, status: int, detail: Optional[str]) -> None:
28+
def __init__(self, detail: Optional[str]) -> None:
2929
super().__init__(-1, f"Couldn't deserialize server error. Found: {detail}")
3030

3131

32-
def handle_error_v2(json_response: StringDict) -> None:
32+
def handle_error_v2(raw_response: StringDict) -> None:
3333
"""
3434
Handles HTTP errors by raising MindeeHTTPErrorV2 exceptions with proper details.
3535
3636
:raises MindeeHTTPErrorV2: If the response has been caught.
3737
:raises MindeeHTTPUnknownErrorV2: If the json return format is unreadable.
3838
"""
39-
try:
40-
raise MindeeHTTPErrorV2(
41-
json_response["job"]["error"]["status"],
42-
json_response["job"]["error"]["detail"],
43-
)
44-
except Exception as exc:
45-
raise MindeeHTTPUnknownErrorV2(-1, json.dumps(json_response, indent=2)) from exc
39+
if "status" not in raw_response or "detail" not in raw_response:
40+
raise MindeeHTTPUnknownErrorV2(json.dumps(raw_response, indent=2))
41+
raise MindeeHTTPErrorV2(
42+
raw_response["status"],
43+
raw_response["detail"],
44+
)

mindee/mindee_http/mindee_api_v2.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1+
import os
12
from typing import Dict, Optional
23

34
import requests
45

5-
from mindee import InferencePredictOptions
6-
from mindee.error.mindee_error import MindeeApiError
6+
from mindee.error.mindee_error import MindeeApiV2Error
77
from mindee.input import LocalInputSource
8+
from mindee.input.inference_predict_options import InferencePredictOptions
9+
from mindee.logger import logger
810
from mindee.mindee_http.base_settings import USER_AGENT
911
from mindee.mindee_http.settings_mixin import SettingsMixin
1012

1113
API_KEY_V2_ENV_NAME = "MINDEE_V2_API_KEY"
1214
API_KEY_V2_DEFAULT = ""
1315

1416
BASE_URL_ENV_NAME = "MINDEE_V2_BASE_URL"
15-
BASE_URL_DEFAULT = "https://api-v2.mindee.com/v2"
17+
BASE_URL_DEFAULT = "https://api-v2.mindee.net/v2"
1618

1719
REQUEST_TIMEOUT_ENV_NAME = "MINDEE_REQUEST_TIMEOUT"
1820
TIMEOUT_DEFAULT = 120
@@ -32,24 +34,39 @@ def __init__(
3234
):
3335
self.api_key = api_key
3436
if not self.api_key or len(self.api_key) == 0:
35-
raise MindeeApiError(
37+
raise MindeeApiV2Error(
3638
(
3739
f"Missing API key,"
3840
" check your Client configuration.\n"
3941
"You can set this using the "
4042
f"'{API_KEY_V2_ENV_NAME}' environment variable."
4143
)
4244
)
45+
self.request_timeout = TIMEOUT_DEFAULT
46+
self.set_base_url(BASE_URL_DEFAULT)
47+
self.set_from_env()
4348
self.url_root = f"{self.base_url.rstrip('/')}"
4449

4550
@property
4651
def base_headers(self) -> Dict[str, str]:
4752
"""Base headers to send with all API requests."""
4853
return {
49-
"Authorization": f"Token {self.api_key}",
54+
"Authorization": self.api_key or "",
5055
"User-Agent": USER_AGENT,
5156
}
5257

58+
def set_from_env(self) -> None:
59+
"""Set various parameters from environment variables, if present."""
60+
env_vars = {
61+
BASE_URL_ENV_NAME: self.set_base_url,
62+
REQUEST_TIMEOUT_ENV_NAME: self.set_timeout,
63+
}
64+
for name, func in env_vars.items():
65+
env_val = os.getenv(name, "")
66+
if env_val:
67+
func(env_val)
68+
logger.debug("Value was set from env: %s", name)
69+
5370
def predict_async_req_post(
5471
self,
5572
input_source: LocalInputSource,
@@ -64,7 +81,7 @@ def predict_async_req_post(
6481
:param close_file: Whether to `close()` the file after parsing it.
6582
:return: requests response.
6683
"""
67-
data = {}
84+
data = {"model_id": options.model_id}
6885
params = {}
6986
url = f"{self.url_root}/inferences/enqueue"
7087

@@ -77,7 +94,10 @@ def predict_async_req_post(
7794
if options.alias and len(options.alias):
7895
data["alias"] = options.alias
7996

80-
files = {"document": input_source.read_contents(close_file)}
97+
files = {
98+
"file": input_source.read_contents(close_file)
99+
+ (input_source.file_mimetype,)
100+
}
81101
response = requests.post(
82102
url=url,
83103
files=files,

mindee/mindee_http/response_validation_v2.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ def is_valid_post_response(response: requests.Response) -> bool:
1717
response_json = json.loads(response.content)
1818
if not "job" in response_json:
1919
return False
20-
if "job" in response_json and "error" in response_json["job"]:
20+
if (
21+
"job" in response_json
22+
and "error" in response_json["job"]
23+
and response_json["job"]["error"] is not None
24+
):
2125
return False
2226
return True
2327

mindee/parsing/common/api_request.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ class ApiRequest:
2121
"""HTTP status code."""
2222
url: str
2323

24-
def __init__(self, json_response: dict) -> None:
25-
self.url = json_response["url"]
26-
self.error = json_response["error"]
27-
self.resources = json_response["resources"]
28-
self.status = RequestStatus(json_response["status"])
29-
self.status_code = json_response["status_code"]
24+
def __init__(self, raw_response: StringDict) -> None:
25+
self.url = raw_response["url"]
26+
self.error = raw_response["error"]
27+
self.resources = raw_response["resources"]
28+
self.status = RequestStatus(raw_response["status"])
29+
self.status_code = raw_response["status_code"]

mindee/parsing/common/execution_file.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ class ExecutionFile:
1212
alias: Optional[str]
1313
"""File name."""
1414

15-
def __init__(self, json_response: StringDict):
16-
self.name = json_response["name"]
17-
self.alias = json_response["alias"]
15+
def __init__(self, raw_response: StringDict):
16+
self.name = raw_response["name"]
17+
self.alias = raw_response["alias"]

mindee/parsing/v2/base_field.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Dict, List, Union
22

3-
from mindee.error.mindee_error import MindeeAPIV2Error
3+
from mindee.error.mindee_error import MindeeApiV2Error
44
from mindee.parsing.common.string_dict import StringDict
55

66

@@ -14,7 +14,9 @@ def __init__(self, indent_level=0) -> None:
1414
self._indent_level = indent_level
1515

1616
@staticmethod
17-
def create_field(raw_response: StringDict, indent_level: int = 0) -> "BaseField":
17+
def create_field(
18+
raw_response: StringDict, indent_level: int = 0
19+
) -> Union["ListField", "ObjectField", "SimpleField"]:
1820
"""Factory function to create appropriate field instances."""
1921
if isinstance(raw_response, dict):
2022
if "items" in raw_response:
@@ -23,14 +25,14 @@ def create_field(raw_response: StringDict, indent_level: int = 0) -> "BaseField"
2325
return ObjectField(raw_response, indent_level)
2426
if "value" in raw_response:
2527
return SimpleField(raw_response, indent_level)
26-
raise MindeeAPIV2Error("Unrecognized field format.")
27-
raise MindeeAPIV2Error("Unrecognized field format.")
28+
raise MindeeApiV2Error(f"Unrecognized field format in {raw_response}.")
29+
raise MindeeApiV2Error(f"Unrecognized field format {raw_response}.")
2830

2931

3032
class ListField(BaseField):
3133
"""List field containing multiple fields."""
3234

33-
items: List[BaseField]
35+
items: List[Union["ListField", "ObjectField", "SimpleField"]]
3436
"""Items contained in the list."""
3537

3638
def __init__(self, raw_response: StringDict, indent_level: int = 0):
@@ -40,23 +42,26 @@ def __init__(self, raw_response: StringDict, indent_level: int = 0):
4042
for item in raw_response["items"]:
4143
if isinstance(item, dict):
4244
self.items.append(BaseField.create_field(item, 1))
43-
raise MindeeAPIV2Error("Unrecognized field format.")
45+
else:
46+
raise MindeeApiV2Error(f"Unrecognized field format '{item}'.")
4447

4548

4649
class ObjectField(BaseField):
4750
"""Object field containing multiple fields."""
4851

49-
fields: Dict[str, BaseField]
52+
fields: Dict[str, Union[ListField, "ObjectField", "SimpleField"]]
5053
"""Fields contained in the object."""
5154

5255
def __init__(self, raw_response: StringDict, indent_level: int = 0):
5356
super().__init__(indent_level)
54-
fields: Dict[str, BaseField] = {}
55-
for field_key, field_value in raw_response.items():
57+
inner_fields = raw_response.get("fields", raw_response)
58+
59+
self.fields: Dict[str, Union["ListField", "ObjectField", "SimpleField"]] = {}
60+
for field_key, field_value in inner_fields.items():
5661
if isinstance(field_value, dict):
57-
fields[field_key] = BaseField.create_field(field_value, 1)
62+
self.fields[field_key] = BaseField.create_field(field_value, 1)
5863
else:
59-
raise MindeeAPIV2Error("Unrecognized field format.")
64+
raise MindeeApiV2Error(f"Unrecognized field format '{field_value}'.")
6065

6166
def __str__(self) -> str:
6267
out_str = ""
@@ -72,7 +77,7 @@ class SimpleField(BaseField):
7277

7378
def __init__(self, raw_response: StringDict, indent_level: int = 0):
7479
super().__init__(indent_level)
75-
self.value = raw_response["value"] if "value" in raw_response else None
80+
self.value = raw_response["value"] = raw_response.get("value", None)
7681

7782
def __str__(self) -> str:
7883
return f"{' ' * self._indent_level}{self.value}\n"

mindee/parsing/v2/inference_fields.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
11
from typing import Union
22

3-
from mindee.parsing.v2.base_field import ListField, ObjectField, SimpleField
3+
from mindee.parsing.common.string_dict import StringDict
4+
from mindee.parsing.v2.base_field import BaseField, ListField, ObjectField, SimpleField
45

56

6-
class InferenceFields(dict[str, Union[ObjectField, ListField, SimpleField]]):
7+
class InferenceFields(dict[str, Union[SimpleField, ObjectField, ListField]]):
78
"""Inference fields dict."""
9+
10+
def __init__(self, raw_response: StringDict) -> None:
11+
super().__init__()
12+
for key, value in raw_response.items():
13+
field_obj = BaseField.create_field(value, 0)
14+
self[key] = field_obj
15+
16+
def __getattr__(self, item):
17+
try:
18+
return self[item]
19+
except KeyError:
20+
raise AttributeError(item) from None

mindee/parsing/v2/inference_file.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
from mindee.parsing.common.string_dict import StringDict
2+
3+
14
class InferenceFile:
25
"""Inference File info."""
36

@@ -6,6 +9,6 @@ class InferenceFile:
69
alais: str
710
"""Alias of the file."""
811

9-
def __init__(self, json_response: dict) -> None:
10-
self.name = json_response["name"]
11-
self.alias = json_response["alias"]
12+
def __init__(self, raw_response: StringDict) -> None:
13+
self.name = raw_response["name"]
14+
self.alias = raw_response["alias"]
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
from typing import List
1+
from typing import List, Optional
22

33
from mindee.parsing.common.string_dict import StringDict
44

55

66
class InferenceOptions:
77
"""Optional information about the document."""
88

9-
raw_text: List[str]
9+
raw_text: Optional[List[str]]
1010

1111
def __init__(self, raw_response: StringDict):
12-
self.raw_text = raw_response["raw_text"]
12+
self.raw_text = raw_response["raw_text"] if "raw_text" in raw_response else None

mindee/parsing/v2/inference_result.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Optional
2+
13
from mindee.parsing.common.string_dict import StringDict
24
from mindee.parsing.v2.inference_fields import InferenceFields
35
from mindee.parsing.v2.inference_options import InferenceOptions
@@ -8,12 +10,16 @@ class InferenceResult:
810

911
fields: InferenceFields
1012
"""Fields contained in the inference."""
11-
options: InferenceOptions
13+
options: Optional[InferenceOptions]
1214
"""Potential options retrieved alongside the inference."""
1315

14-
def __init__(self, json_response: StringDict) -> None:
15-
self.fields = InferenceFields(json_response["fields"])
16-
self.options = InferenceOptions(json_response["options"])
16+
def __init__(self, raw_response: StringDict) -> None:
17+
self.fields = InferenceFields(raw_response["fields"])
18+
self.options = (
19+
InferenceOptions(raw_response["options"])
20+
if raw_response.get("options")
21+
else None
22+
)
1723

1824
def __str__(self) -> str:
1925
str_fields = ""

0 commit comments

Comments
 (0)