novaposhta-python-client 0.1.9__tar.gz → 0.1.11__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.
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/PKG-INFO +101 -8
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/README.md +93 -6
- novaposhta_python_client-0.1.11/novaposhta/chains.py +104 -0
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/novaposhta/client.py +35 -19
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/novaposhta/models/additional_service.py +1 -2
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/novaposhta/models/address.py +1 -2
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/novaposhta/models/common.py +1 -2
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/novaposhta/models/counterparty.py +1 -2
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/novaposhta/models/internet_document.py +1 -8
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/novaposhta/models/scan_sheet.py +2 -1
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/novaposhta/models/tracking_document.py +2 -2
- novaposhta_python_client-0.1.11/novaposhta/types.py +41 -0
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/pyproject.toml +18 -4
- novaposhta_python_client-0.1.9/novaposhta/types.py +0 -15
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/LICENSE.md +0 -0
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/novaposhta/__init__.py +0 -0
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/novaposhta/models/__init__.py +0 -0
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/novaposhta/models/base.py +0 -0
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/novaposhta/models/contact_person.py +0 -0
- {novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/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.
|
|
3
|
+
Version: 0.1.11
|
|
4
4
|
Summary: Python client for Nova Poshta API
|
|
5
5
|
Home-page: https://github.com/semolex/novaposhta-python-client
|
|
6
6
|
License: MIT
|
|
@@ -8,17 +8,23 @@ Keywords: api,nova-poshta,novaposhta,nova-poshta-api,novaposhta-api
|
|
|
8
8
|
Author: Oleksii
|
|
9
9
|
Author-email: semolex@live.com
|
|
10
10
|
Requires-Python: >=3.9,<4.0
|
|
11
|
-
Classifier: Development Status ::
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
12
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Natural Language :: English
|
|
13
15
|
Classifier: Operating System :: OS Independent
|
|
14
16
|
Classifier: Programming Language :: Python :: 3
|
|
15
17
|
Classifier: Programming Language :: Python :: 3.9
|
|
16
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
22
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
23
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
20
24
|
Classifier: Topic :: Software Development :: Build Tools
|
|
21
25
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Dist: h11 (>=0.16.0,<0.17.0)
|
|
22
28
|
Requires-Dist: httpx (>=0.24.0)
|
|
23
29
|
Project-URL: Repository, https://github.com/semolex/novaposhta-python-client
|
|
24
30
|
Description-Content-Type: text/markdown
|
|
@@ -28,8 +34,14 @@ Description-Content-Type: text/markdown
|
|
|
28
34
|
A Python client for interfacing with the Nova Poshta API. Designed to provide easy access to all API functionalities
|
|
29
35
|
with emphasis on consistency and usability.
|
|
30
36
|
|
|
31
|
-
[](https://github.com/semolex/novaposhta-python-client/actions/workflows/tests.yml)
|
|
38
|
+
[](https://github.com/semolex/novaposhta-python-client/blob/master/LICENSE.md)
|
|
39
|
+
[](https://pypi.org/project/novaposhta-python-client/)
|
|
40
|
+
[](https://pepy.tech/project/novaposhta-python-client)
|
|
41
|
+
[](https://pepy.tech/project/novaposhta-python-client)
|
|
42
|
+
[](https://pypi.org/project/novaposhta-python-client/)
|
|
43
|
+
[](https://github.com/psf/black)
|
|
44
|
+
[](https://pycqa.github.io/isort/)
|
|
33
45
|
|
|
34
46
|
## Description
|
|
35
47
|
|
|
@@ -161,17 +173,98 @@ To get your custom model instance, use the get method:
|
|
|
161
173
|
my_custom_model = client.get(MyCustomModel.name)
|
|
162
174
|
```
|
|
163
175
|
|
|
164
|
-
##
|
|
176
|
+
## Experimental: Chain Operations
|
|
177
|
+
|
|
178
|
+
The chain functionality allows you to sequence multiple API operations, where each operation's output can be transformed and passed to the next operation. This is particularly useful for scenarios that require multiple dependent API calls, like searching for addresses or creating shipments.
|
|
179
|
+
Each result of `prepare_next` is passed to the next operation as updated `kwargs`.
|
|
180
|
+
> ⚠️ **Note**: This feature is experimental and currently optimized for async usage.
|
|
181
|
+
|
|
182
|
+
### Basic Usage
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from novaposhta.client import NovaPoshtaApi
|
|
186
|
+
from novaposhta.chains import Chain
|
|
187
|
+
|
|
188
|
+
# Initialize client in async mode
|
|
189
|
+
client = NovaPoshtaApi("API_KEY", async_mode=True)
|
|
190
|
+
|
|
191
|
+
# Create a chain of operations
|
|
192
|
+
chain = (
|
|
193
|
+
Chain(
|
|
194
|
+
client.address.get_areas,
|
|
195
|
+
prepare_next=lambda x: {'ref': x['data'][0]['AreasCenter']}
|
|
196
|
+
) |
|
|
197
|
+
Chain(
|
|
198
|
+
client.address.get_cities,
|
|
199
|
+
prepare_next=lambda x: {'city_ref': x['data'][0]['Ref']}
|
|
200
|
+
) |
|
|
201
|
+
Chain(
|
|
202
|
+
client.address.get_street,
|
|
203
|
+
kwargs={'find_by_string': 'Street Name'}
|
|
204
|
+
)
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Execute chain
|
|
208
|
+
results = await chain.execute_async()
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Features
|
|
212
|
+
|
|
213
|
+
- Chain multiple API operations using the `|` operator
|
|
214
|
+
- Transform operation results for next operation using `prepare_next`
|
|
215
|
+
- Automatic error handling and chain interruption on failure
|
|
216
|
+
- Preserve both original API responses and transformed data
|
|
217
|
+
- Initial parameters via `kwargs`
|
|
218
|
+
|
|
219
|
+
### Common Patterns
|
|
165
220
|
|
|
221
|
+
```python
|
|
222
|
+
# Address search chain
|
|
223
|
+
from novaposhta.client import NovaPoshtaApi
|
|
224
|
+
|
|
225
|
+
client = NovaPoshtaApi("API_KEY", async_mode=True)
|
|
226
|
+
from novaposhta.chains import Chain
|
|
227
|
+
address = client.address
|
|
228
|
+
chain = (
|
|
229
|
+
Chain(
|
|
230
|
+
address.search_settlements,
|
|
231
|
+
kwargs={'city_name': 'Київ'},
|
|
232
|
+
prepare_next=lambda x: {'settlement_ref': x['data'][0]['Ref']}
|
|
233
|
+
) |
|
|
234
|
+
Chain(
|
|
235
|
+
address.search_settlement_streets,
|
|
236
|
+
kwargs={'street_name': 'Main'}
|
|
237
|
+
)
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# Execute and process results
|
|
241
|
+
results = await chain.execute_async()
|
|
242
|
+
for result in results:
|
|
243
|
+
print(f"Success: {result.success}")
|
|
244
|
+
print(f"Data: {result.data}")
|
|
245
|
+
print(f"Next kwargs: {result.next_kwargs}")
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Limitations
|
|
249
|
+
|
|
250
|
+
- Currently optimized for async usage
|
|
251
|
+
- Experimental API that may change
|
|
252
|
+
- Sequential execution only (no parallel operations)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
## Testing and linting
|
|
257
|
+
Note: install dev dependencies first
|
|
166
258
|
```bash
|
|
167
|
-
poetry run black novaposhta
|
|
259
|
+
poetry run black novaposhta/
|
|
260
|
+
poetry run isort novaposhta/
|
|
168
261
|
poetry run mypy novaposhta/
|
|
169
|
-
poetry run pytest tests/
|
|
262
|
+
poetry run pytest --cov=novaposhta tests/
|
|
170
263
|
```
|
|
171
264
|
|
|
172
265
|
## Contributing
|
|
173
266
|
|
|
174
267
|
We welcome contributions that can help in enhancing the functionality and improving the consistency of the client. For
|
|
175
268
|
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.
|
|
269
|
+
Please, use `black`, `isort` and `mypy` as your instrument for code formatting and type checking.
|
|
177
270
|
|
|
@@ -3,8 +3,14 @@
|
|
|
3
3
|
A Python client for interfacing with the Nova Poshta API. Designed to provide easy access to all API functionalities
|
|
4
4
|
with emphasis on consistency and usability.
|
|
5
5
|
|
|
6
|
-
[](https://github.com/semolex/novaposhta-python-client/actions/workflows/tests.yml)
|
|
7
|
+
[](https://github.com/semolex/novaposhta-python-client/blob/master/LICENSE.md)
|
|
8
|
+
[](https://pypi.org/project/novaposhta-python-client/)
|
|
9
|
+
[](https://pepy.tech/project/novaposhta-python-client)
|
|
10
|
+
[](https://pepy.tech/project/novaposhta-python-client)
|
|
11
|
+
[](https://pypi.org/project/novaposhta-python-client/)
|
|
12
|
+
[](https://github.com/psf/black)
|
|
13
|
+
[](https://pycqa.github.io/isort/)
|
|
8
14
|
|
|
9
15
|
## Description
|
|
10
16
|
|
|
@@ -136,16 +142,97 @@ To get your custom model instance, use the get method:
|
|
|
136
142
|
my_custom_model = client.get(MyCustomModel.name)
|
|
137
143
|
```
|
|
138
144
|
|
|
139
|
-
##
|
|
145
|
+
## Experimental: Chain Operations
|
|
146
|
+
|
|
147
|
+
The chain functionality allows you to sequence multiple API operations, where each operation's output can be transformed and passed to the next operation. This is particularly useful for scenarios that require multiple dependent API calls, like searching for addresses or creating shipments.
|
|
148
|
+
Each result of `prepare_next` is passed to the next operation as updated `kwargs`.
|
|
149
|
+
> ⚠️ **Note**: This feature is experimental and currently optimized for async usage.
|
|
150
|
+
|
|
151
|
+
### Basic Usage
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from novaposhta.client import NovaPoshtaApi
|
|
155
|
+
from novaposhta.chains import Chain
|
|
156
|
+
|
|
157
|
+
# Initialize client in async mode
|
|
158
|
+
client = NovaPoshtaApi("API_KEY", async_mode=True)
|
|
159
|
+
|
|
160
|
+
# Create a chain of operations
|
|
161
|
+
chain = (
|
|
162
|
+
Chain(
|
|
163
|
+
client.address.get_areas,
|
|
164
|
+
prepare_next=lambda x: {'ref': x['data'][0]['AreasCenter']}
|
|
165
|
+
) |
|
|
166
|
+
Chain(
|
|
167
|
+
client.address.get_cities,
|
|
168
|
+
prepare_next=lambda x: {'city_ref': x['data'][0]['Ref']}
|
|
169
|
+
) |
|
|
170
|
+
Chain(
|
|
171
|
+
client.address.get_street,
|
|
172
|
+
kwargs={'find_by_string': 'Street Name'}
|
|
173
|
+
)
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# Execute chain
|
|
177
|
+
results = await chain.execute_async()
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Features
|
|
181
|
+
|
|
182
|
+
- Chain multiple API operations using the `|` operator
|
|
183
|
+
- Transform operation results for next operation using `prepare_next`
|
|
184
|
+
- Automatic error handling and chain interruption on failure
|
|
185
|
+
- Preserve both original API responses and transformed data
|
|
186
|
+
- Initial parameters via `kwargs`
|
|
187
|
+
|
|
188
|
+
### Common Patterns
|
|
140
189
|
|
|
190
|
+
```python
|
|
191
|
+
# Address search chain
|
|
192
|
+
from novaposhta.client import NovaPoshtaApi
|
|
193
|
+
|
|
194
|
+
client = NovaPoshtaApi("API_KEY", async_mode=True)
|
|
195
|
+
from novaposhta.chains import Chain
|
|
196
|
+
address = client.address
|
|
197
|
+
chain = (
|
|
198
|
+
Chain(
|
|
199
|
+
address.search_settlements,
|
|
200
|
+
kwargs={'city_name': 'Київ'},
|
|
201
|
+
prepare_next=lambda x: {'settlement_ref': x['data'][0]['Ref']}
|
|
202
|
+
) |
|
|
203
|
+
Chain(
|
|
204
|
+
address.search_settlement_streets,
|
|
205
|
+
kwargs={'street_name': 'Main'}
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# Execute and process results
|
|
210
|
+
results = await chain.execute_async()
|
|
211
|
+
for result in results:
|
|
212
|
+
print(f"Success: {result.success}")
|
|
213
|
+
print(f"Data: {result.data}")
|
|
214
|
+
print(f"Next kwargs: {result.next_kwargs}")
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Limitations
|
|
218
|
+
|
|
219
|
+
- Currently optimized for async usage
|
|
220
|
+
- Experimental API that may change
|
|
221
|
+
- Sequential execution only (no parallel operations)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
## Testing and linting
|
|
226
|
+
Note: install dev dependencies first
|
|
141
227
|
```bash
|
|
142
|
-
poetry run black novaposhta
|
|
228
|
+
poetry run black novaposhta/
|
|
229
|
+
poetry run isort novaposhta/
|
|
143
230
|
poetry run mypy novaposhta/
|
|
144
|
-
poetry run pytest tests/
|
|
231
|
+
poetry run pytest --cov=novaposhta tests/
|
|
145
232
|
```
|
|
146
233
|
|
|
147
234
|
## Contributing
|
|
148
235
|
|
|
149
236
|
We welcome contributions that can help in enhancing the functionality and improving the consistency of the client. For
|
|
150
237
|
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.
|
|
238
|
+
Please, use `black`, `isort` and `mypy` as your instrument for code formatting and type checking.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Chain of calls with error handling and data passing between calls. """
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Any, Callable, Dict, List, Optional, TypeVar, Union
|
|
6
|
+
|
|
7
|
+
T = TypeVar("T")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class ChainResult:
|
|
12
|
+
"""
|
|
13
|
+
Result of a single chain execution.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
success: bool
|
|
17
|
+
data: Optional[Any]
|
|
18
|
+
error: Optional[str]
|
|
19
|
+
next_kwargs: Optional[Dict[str, Any]] = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class Chain:
|
|
24
|
+
"""
|
|
25
|
+
Chain object that represents a single call in the chain.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
method: Callable
|
|
29
|
+
kwargs: Optional[Dict[str, Any]] = None
|
|
30
|
+
prepare_next: Optional[Callable[[Any], Dict[str, Any]]] = None
|
|
31
|
+
|
|
32
|
+
async def execute(self, prev_result: Optional[ChainResult] = None) -> ChainResult:
|
|
33
|
+
"""
|
|
34
|
+
Execute the chain.
|
|
35
|
+
|
|
36
|
+
:param prev_result: result of the previous chain.
|
|
37
|
+
:return: result of the current chain.
|
|
38
|
+
"""
|
|
39
|
+
try:
|
|
40
|
+
execution_kwargs = {**(self.kwargs or {})}
|
|
41
|
+
if prev_result and prev_result.next_kwargs:
|
|
42
|
+
execution_kwargs.update(prev_result.next_kwargs)
|
|
43
|
+
|
|
44
|
+
result = self.method(**execution_kwargs)
|
|
45
|
+
if asyncio.iscoroutine(result):
|
|
46
|
+
result = await result
|
|
47
|
+
|
|
48
|
+
next_kwargs = self.prepare_next(result) if self.prepare_next else {}
|
|
49
|
+
|
|
50
|
+
return ChainResult(
|
|
51
|
+
success=result.get("success", False),
|
|
52
|
+
data=result.get("data"),
|
|
53
|
+
error=None,
|
|
54
|
+
next_kwargs=next_kwargs,
|
|
55
|
+
)
|
|
56
|
+
except Exception as e:
|
|
57
|
+
return ChainResult(success=False, data=None, error=str(e), next_kwargs=None)
|
|
58
|
+
|
|
59
|
+
def __or__(self, other: "Chain") -> "ChainExecutor":
|
|
60
|
+
"""
|
|
61
|
+
Create a chain executor with the current chain and another chain.
|
|
62
|
+
Allows chaining multiple chains together via the `|` operator.
|
|
63
|
+
|
|
64
|
+
:param other: another chain.
|
|
65
|
+
:return ChainExecutor: chain executor with the current chain and another chain.
|
|
66
|
+
"""
|
|
67
|
+
return ChainExecutor([self, other])
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ChainExecutor:
|
|
71
|
+
"""
|
|
72
|
+
Chain executor that executes multiple chains in a sequence.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def __init__(self, chains: Optional[List[Chain]] = None):
|
|
76
|
+
self.chains = chains or []
|
|
77
|
+
|
|
78
|
+
def __or__(self, chain: Chain) -> "ChainExecutor":
|
|
79
|
+
"""
|
|
80
|
+
Add a chain to the chain executor.
|
|
81
|
+
|
|
82
|
+
:param chain: chain to add.
|
|
83
|
+
:return ChainExecutor: chain executor with the added chain.
|
|
84
|
+
"""
|
|
85
|
+
self.chains.append(chain)
|
|
86
|
+
return self
|
|
87
|
+
|
|
88
|
+
async def execute_async(self) -> List[ChainResult]:
|
|
89
|
+
"""
|
|
90
|
+
Execute all chains in the chain executor asynchronously.
|
|
91
|
+
|
|
92
|
+
:return: list of results of each chain execution
|
|
93
|
+
"""
|
|
94
|
+
results = []
|
|
95
|
+
prev_result = None
|
|
96
|
+
|
|
97
|
+
for chain in self.chains:
|
|
98
|
+
result = await chain.execute(prev_result)
|
|
99
|
+
results.append(result)
|
|
100
|
+
if not result.success:
|
|
101
|
+
break
|
|
102
|
+
prev_result = result
|
|
103
|
+
|
|
104
|
+
return results
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Client for Nova Poshta API. """
|
|
2
2
|
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import 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 =
|
|
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:
|
|
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:
|
|
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:
|
|
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,23 +157,29 @@ 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
|
|
153
165
|
|
|
154
166
|
if response["success"]:
|
|
155
167
|
return response
|
|
168
|
+
|
|
156
169
|
errors = response["errors"]
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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)
|
|
167
183
|
|
|
168
184
|
def close_sync(self):
|
|
169
185
|
"""
|
|
@@ -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):
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
|
|
11
|
+
apiKey: str
|
|
12
|
+
modelName: str
|
|
13
|
+
calledMethod: str
|
|
14
|
+
methodProperties: Dict[str, Any]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class HttpRequest(TypedDict):
|
|
18
|
+
"""
|
|
19
|
+
Request data for HTTP client.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
url: str
|
|
23
|
+
headers: Dict[str, str]
|
|
24
|
+
json: RequestData
|
|
25
|
+
timeout: int
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
OptStr = Optional[str]
|
|
29
|
+
StrOrNum = Union[str, float, int]
|
|
30
|
+
DictStrAny = Dict[str, Any]
|
|
31
|
+
MaybeAsync = Union[Dict[str, Any], Coroutine[Any, Any, Dict[str, Any]]]
|
|
32
|
+
SyncSender = Callable[[HttpRequest], Dict[str, Any]]
|
|
33
|
+
AsyncSender = Callable[[HttpRequest], Coroutine[Any, Any, Dict[str, Any]]]
|
|
34
|
+
RequestSender = Union[SyncSender, AsyncSender]
|
|
35
|
+
OptStrOrNum = Optional[StrOrNum]
|
|
36
|
+
OptDict = Optional[Dict[str, str]]
|
|
37
|
+
OptListOfDicts = Optional[List[Dict[str, Any]]]
|
|
38
|
+
OptInt = Optional[int]
|
|
39
|
+
OptBool = Optional[
|
|
40
|
+
Union[bool, int]
|
|
41
|
+
] # 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.
|
|
6
|
+
version = "0.1.11"
|
|
7
7
|
description = "Python client for Nova Poshta API"
|
|
8
8
|
authors = ["Oleksii <semolex@live.com>"]
|
|
9
9
|
license = "MIT"
|
|
@@ -12,19 +12,31 @@ homepage = "https://github.com/semolex/novaposhta-python-client"
|
|
|
12
12
|
repository = "https://github.com/semolex/novaposhta-python-client"
|
|
13
13
|
keywords = [ "api", "nova-poshta", "novaposhta", "nova-poshta-api", "novaposhta-api"]
|
|
14
14
|
classifiers = [
|
|
15
|
-
"Development Status ::
|
|
15
|
+
"Development Status :: 5 - Production/Stable",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
16
17
|
"Topic :: Software Development :: Build Tools",
|
|
17
18
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
18
19
|
"Operating System :: OS Independent",
|
|
19
20
|
"Programming Language :: Python :: 3 :: Only",
|
|
20
21
|
"Programming Language :: Python :: 3.9",
|
|
21
|
-
"
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
25
|
+
"License :: OSI Approved :: MIT License",
|
|
26
|
+
"Natural Language :: English",
|
|
27
|
+
"Typing :: Typed"
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
exclude = [
|
|
31
|
+
"tests/*",
|
|
32
|
+
"tests/**/*",
|
|
33
|
+
"**/tests/*",
|
|
22
34
|
]
|
|
23
35
|
|
|
24
36
|
[tool.poetry.dependencies]
|
|
25
37
|
python = "^3.9"
|
|
26
38
|
httpx = ">=0.24.0"
|
|
27
|
-
|
|
39
|
+
h11 = "^0.16.0"
|
|
28
40
|
|
|
29
41
|
[tool.poetry.group.dev.dependencies]
|
|
30
42
|
mypy = "^1.5.1"
|
|
@@ -33,6 +45,8 @@ pytest = "^7.4.2"
|
|
|
33
45
|
pytest-httpx = "^0.26.0"
|
|
34
46
|
coverage = "^7.3.2"
|
|
35
47
|
pytest-asyncio = "^0.21.1"
|
|
48
|
+
isort = "^5.13.2"
|
|
49
|
+
pytest-cov = "^6.0.0"
|
|
36
50
|
|
|
37
51
|
[build-system]
|
|
38
52
|
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
|
|
File without changes
|
|
File without changes
|
{novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/novaposhta/models/__init__.py
RENAMED
|
File without changes
|
{novaposhta_python_client-0.1.9 → novaposhta_python_client-0.1.11}/novaposhta/models/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|