novaposhta-python-client 0.1.8__tar.gz → 0.1.10__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (19) hide show
  1. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/PKG-INFO +4 -3
  2. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/README.md +3 -2
  3. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/novaposhta/client.py +35 -14
  4. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/novaposhta/models/additional_service.py +1 -2
  5. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/novaposhta/models/address.py +1 -2
  6. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/novaposhta/models/common.py +1 -2
  7. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/novaposhta/models/counterparty.py +1 -2
  8. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/novaposhta/models/internet_document.py +1 -8
  9. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/novaposhta/models/scan_sheet.py +2 -1
  10. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/novaposhta/models/tracking_document.py +2 -2
  11. novaposhta_python_client-0.1.10/novaposhta/types.py +39 -0
  12. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/pyproject.toml +2 -1
  13. novaposhta_python_client-0.1.8/novaposhta/types.py +0 -15
  14. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/LICENSE.md +0 -0
  15. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/novaposhta/__init__.py +0 -0
  16. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/novaposhta/models/__init__.py +0 -0
  17. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/novaposhta/models/base.py +0 -0
  18. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/novaposhta/models/contact_person.py +0 -0
  19. {novaposhta_python_client-0.1.8 → novaposhta_python_client-0.1.10}/novaposhta/mypi.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: novaposhta-python-client
3
- Version: 0.1.8
3
+ Version: 0.1.10
4
4
  Summary: Python client for Nova Poshta API
5
5
  Home-page: https://github.com/semolex/novaposhta-python-client
6
6
  License: MIT
@@ -164,7 +164,8 @@ my_custom_model = client.get(MyCustomModel.name)
164
164
  ## Testing and linting
165
165
 
166
166
  ```bash
167
- poetry run black novaposhta
167
+ poetry run black novaposhta/
168
+ poetry run isort novaposhta/
168
169
  poetry run mypy novaposhta/
169
170
  poetry run pytest tests/
170
171
  ```
@@ -173,5 +174,5 @@ poetry run pytest tests/
173
174
 
174
175
  We welcome contributions that can help in enhancing the functionality and improving the consistency of the client. For
175
176
  bugs or feature requests, please open an issue on the GitHub repository.
176
- Please, use `black` and `mypy` as your instrument for code formatting and type checking.
177
+ Please, use `black`, `isort` and `mypy` as your instrument for code formatting and type checking.
177
178
 
@@ -139,7 +139,8 @@ my_custom_model = client.get(MyCustomModel.name)
139
139
  ## Testing and linting
140
140
 
141
141
  ```bash
142
- poetry run black novaposhta
142
+ poetry run black novaposhta/
143
+ poetry run isort novaposhta/
143
144
  poetry run mypy novaposhta/
144
145
  poetry run pytest tests/
145
146
  ```
@@ -148,4 +149,4 @@ poetry run pytest tests/
148
149
 
149
150
  We welcome contributions that can help in enhancing the functionality and improving the consistency of the client. For
150
151
  bugs or feature requests, please open an issue on the GitHub repository.
151
- Please, use `black` and `mypy` as your instrument for code formatting and type checking.
152
+ Please, use `black`, `isort` and `mypy` as your instrument for code formatting and type checking.
@@ -1,6 +1,6 @@
1
1
  """Client for Nova Poshta API. """
2
2
 
3
- from typing import Type, TypeVar, Optional, Callable
3
+ from typing import Callable, Final, Optional, Type, TypeVar
4
4
 
5
5
  import httpx
6
6
 
@@ -13,9 +13,11 @@ from .models.counterparty import Counterparty
13
13
  from .models.internet_document import InternetDocument
14
14
  from .models.scan_sheet import ScanSheet
15
15
  from .models.tracking_document import TrackingDocument
16
- from .types import DictStrAny, MaybeAsync
16
+ from .types import DictStrAny, HttpRequest, MaybeAsync, RequestData, RequestSender
17
17
 
18
- HEADERS = {"Content-Type": "application/json"}
18
+ HEADERS: Final[dict[str, str]] = {"Content-Type": "application/json"}
19
+ API_DEFAULT_ENDPOINT: Final[str] = "https://api.novaposhta.ua/v2.0/json/"
20
+ DEFAULT_TIMEOUT: Final[int] = 10
19
21
 
20
22
  BaseModelType = TypeVar("BaseModelType", bound=BaseModel)
21
23
 
@@ -35,7 +37,7 @@ class NovaPoshtaApi:
35
37
  def __init__(
36
38
  self,
37
39
  api_key: str,
38
- api_endpoint: str = "https://api.novaposhta.ua/v2.0/json/",
40
+ api_endpoint: str = API_DEFAULT_ENDPOINT,
39
41
  http_client=httpx,
40
42
  timeout: int = 10,
41
43
  raise_for_errors: bool = False,
@@ -51,10 +53,17 @@ class NovaPoshtaApi:
51
53
  :param raise_for_errors: Whether to check and raise errors as exceptions.
52
54
  :param async_mode: Whether to use async mode.
53
55
  """
56
+ if not api_key:
57
+ raise ValueError("API key is required")
58
+ if timeout <= 0:
59
+ raise ValueError("Timeout must be positive")
60
+ if not api_endpoint or not api_endpoint.startswith(("http://", "https://")):
61
+ raise ValueError("Invalid API endpoint URL")
62
+
54
63
  self.api_key = api_key
55
64
  self.api_endpoint = api_endpoint
56
65
  self.timeout = timeout
57
- self._send: Callable[[DictStrAny], MaybeAsync]
66
+ self._send: RequestSender
58
67
  if async_mode:
59
68
  self.async_http_client: Optional[httpx.AsyncClient] = (
60
69
  http_client.AsyncClient(timeout=timeout)
@@ -65,11 +74,12 @@ class NovaPoshtaApi:
65
74
  timeout=timeout
66
75
  )
67
76
  self._send = self._send_sync
77
+ self._send = self._send_sync
68
78
  self.raise_for_errors = raise_for_errors
69
79
  self.async_mode = async_mode
70
80
  self._models_pool: DictStrAny = {}
71
81
 
72
- def _send_sync(self, request: DictStrAny) -> DictStrAny:
82
+ def _send_sync(self, request: HttpRequest) -> DictStrAny:
73
83
  """
74
84
  Sends sync request to the API.
75
85
 
@@ -81,7 +91,7 @@ class NovaPoshtaApi:
81
91
  response = self.sync_http_client.post(**request)
82
92
  return self._maybe_check_errors(response.json())
83
93
 
84
- async def _send_async(self, request: DictStrAny) -> DictStrAny:
94
+ async def _send_async(self, request: HttpRequest) -> DictStrAny:
85
95
  """
86
96
  Sends async request to the API.
87
97
 
@@ -104,13 +114,13 @@ class NovaPoshtaApi:
104
114
  :param method_props: properties to pass to the method.
105
115
  :return: response dict.
106
116
  """
107
- data = {
117
+ data: RequestData = {
108
118
  "apiKey": self.api_key,
109
119
  "modelName": model_name,
110
120
  "calledMethod": api_method,
111
121
  "methodProperties": method_props,
112
122
  }
113
- request = {
123
+ request: HttpRequest = {
114
124
  "url": self.api_endpoint,
115
125
  "headers": HEADERS,
116
126
  "json": data,
@@ -147,6 +157,8 @@ class NovaPoshtaApi:
147
157
 
148
158
  :param response: response dict.
149
159
  :return: response dict.
160
+ :raises InvalidAPIKeyError: if API key is invalid
161
+ :raises APIRequestError: if any other API error occurs
150
162
  """
151
163
  if not self.raise_for_errors:
152
164
  return response
@@ -154,11 +166,20 @@ class NovaPoshtaApi:
154
166
  if response["success"]:
155
167
  return response
156
168
 
157
- error = response["errors"][0]
158
- if error.startswith("API key"):
159
- raise InvalidAPIKeyError(error)
160
- else:
161
- raise APIRequestError(error)
169
+ errors = response["errors"]
170
+ error_msg = (
171
+ ", ".join(errors)
172
+ if isinstance(errors, list)
173
+ else (
174
+ ", ".join(f"{k}: {v}" for k, v in errors.items())
175
+ if isinstance(errors, dict)
176
+ else str(errors)
177
+ )
178
+ )
179
+
180
+ if "API key" in error_msg:
181
+ raise InvalidAPIKeyError(error_msg)
182
+ raise APIRequestError(error_msg)
162
183
 
163
184
  def close_sync(self):
164
185
  """
@@ -1,8 +1,7 @@
1
1
  """AdditionalService model module."""
2
2
 
3
- from .base import BaseModel, api_method
4
-
5
3
  from ..types import OptStr, StrOrNum
4
+ from .base import BaseModel, api_method
6
5
 
7
6
 
8
7
  class AdditionalService(BaseModel):
@@ -1,9 +1,8 @@
1
1
  """Address model module."""
2
2
 
3
+ from ..types import OptBool, OptStr
3
4
  from .base import BaseModel, api_method
4
5
 
5
- from ..types import OptStr, OptBool
6
-
7
6
 
8
7
  class Address(BaseModel):
9
8
  """
@@ -1,9 +1,8 @@
1
1
  """Common model module."""
2
2
 
3
+ from ..types import OptInt, OptStr
3
4
  from .base import BaseModel, api_method
4
5
 
5
- from ..types import OptStr, OptInt
6
-
7
6
 
8
7
  class Common(BaseModel):
9
8
  """
@@ -1,8 +1,7 @@
1
1
  """Counterparty model module."""
2
2
 
3
- from .base import BaseModel, api_method
4
-
5
3
  from ..types import OptStr
4
+ from .base import BaseModel, api_method
6
5
 
7
6
 
8
7
  class Counterparty(BaseModel):
@@ -2,15 +2,8 @@
2
2
 
3
3
  from typing import List
4
4
 
5
+ from ..types import OptDict, OptInt, OptListOfDicts, OptStr, OptStrOrNum, StrOrNum
5
6
  from .base import BaseModel, api_method
6
- from ..types import (
7
- OptStr,
8
- StrOrNum,
9
- OptListOfDicts,
10
- OptDict,
11
- OptInt,
12
- OptStrOrNum,
13
- )
14
7
 
15
8
 
16
9
  class InternetDocument(BaseModel):
@@ -1,8 +1,9 @@
1
1
  """ScanSheet model module."""
2
2
 
3
- from .base import BaseModel, api_method
4
3
  from typing import List
5
4
 
5
+ from .base import BaseModel, api_method
6
+
6
7
 
7
8
  class ScanSheet(BaseModel):
8
9
  """
@@ -1,8 +1,8 @@
1
1
  """TrackingDocument model module."""
2
2
 
3
- from .base import BaseModel, api_method
3
+ from typing import Dict, List
4
4
 
5
- from typing import List, Dict
5
+ from .base import BaseModel, api_method
6
6
 
7
7
 
8
8
  class TrackingDocument(BaseModel):
@@ -0,0 +1,39 @@
1
+ """Type aliases for novaposhta package."""
2
+
3
+ from typing import Any, Callable, Coroutine, Dict, List, Optional, TypedDict, Union
4
+
5
+
6
+ class RequestData(TypedDict):
7
+ """
8
+ Request data for Nova Poshta API.
9
+ """
10
+ apiKey: str
11
+ modelName: str
12
+ calledMethod: str
13
+ methodProperties: Dict[str, Any]
14
+
15
+
16
+ class HttpRequest(TypedDict):
17
+ """
18
+ Request data for HTTP client.
19
+ """
20
+ url: str
21
+ headers: Dict[str, str]
22
+ json: RequestData
23
+ timeout: int
24
+
25
+
26
+ OptStr = Optional[str]
27
+ StrOrNum = Union[str, float, int]
28
+ DictStrAny = Dict[str, Any]
29
+ MaybeAsync = Union[Dict[str, Any], Coroutine[Any, Any, Dict[str, Any]]]
30
+ SyncSender = Callable[[HttpRequest], Dict[str, Any]]
31
+ AsyncSender = Callable[[HttpRequest], Coroutine[Any, Any, Dict[str, Any]]]
32
+ RequestSender = Union[SyncSender, AsyncSender]
33
+ OptStrOrNum = Optional[StrOrNum]
34
+ OptDict = Optional[Dict[str, str]]
35
+ OptListOfDicts = Optional[List[Dict[str, Any]]]
36
+ OptInt = Optional[int]
37
+ OptBool = Optional[
38
+ Union[bool, int]
39
+ ] # assume that support for cases: 0 is False, 1 is True
@@ -3,7 +3,7 @@ name = "novaposhta-python-client"
3
3
  packages = [
4
4
  { include = "novaposhta"}
5
5
  ]
6
- version = "0.1.8"
6
+ version = "0.1.10"
7
7
  description = "Python client for Nova Poshta API"
8
8
  authors = ["Oleksii <semolex@live.com>"]
9
9
  license = "MIT"
@@ -33,6 +33,7 @@ pytest = "^7.4.2"
33
33
  pytest-httpx = "^0.26.0"
34
34
  coverage = "^7.3.2"
35
35
  pytest-asyncio = "^0.21.1"
36
+ isort = "^5.13.2"
36
37
 
37
38
  [build-system]
38
39
  requires = ["poetry-core"]
@@ -1,15 +0,0 @@
1
- """Type aliases for novaposhta package."""
2
-
3
- from typing import List, Optional, Dict, Union, Any, Coroutine
4
-
5
- OptStr = Optional[str]
6
- StrOrNum = Union[str, float, int]
7
- DictStrAny = Dict[str, Any]
8
- MaybeAsync = Union[DictStrAny, Coroutine[Any, Any, Any]]
9
- OptStrOrNum = Optional[StrOrNum]
10
- OptDict = Optional[Dict[str, str]]
11
- OptListOfDicts = Optional[List[Dict[str, Any]]]
12
- OptInt = Optional[int]
13
- OptBool = Optional[
14
- Union[bool, int]
15
- ] # assume that support for cases: 0 is False, 1 is True