novaposhta-python-client 0.1.5__tar.gz → 0.1.7__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 (18) hide show
  1. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/PKG-INFO +28 -16
  2. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/README.md +26 -15
  3. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/novaposhta/client.py +79 -28
  4. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/pyproject.toml +2 -5
  5. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/LICENSE.md +0 -0
  6. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/novaposhta/__init__.py +0 -0
  7. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/novaposhta/models/__init__.py +0 -0
  8. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/novaposhta/models/additional_service.py +0 -0
  9. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/novaposhta/models/address.py +0 -0
  10. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/novaposhta/models/base.py +0 -0
  11. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/novaposhta/models/common.py +0 -0
  12. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/novaposhta/models/contact_person.py +0 -0
  13. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/novaposhta/models/counterparty.py +0 -0
  14. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/novaposhta/models/internet_document.py +0 -0
  15. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/novaposhta/models/scan_sheet.py +0 -0
  16. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/novaposhta/models/tracking_document.py +0 -0
  17. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/novaposhta/mypi.ini +0 -0
  18. {novaposhta_python_client-0.1.5 → novaposhta_python_client-0.1.7}/novaposhta/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: novaposhta-python-client
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: Python client for Nova Poshta API
5
5
  Home-page: https://github.com/semolex/novaposhta-python-client
6
6
  License: MIT
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3
15
15
  Classifier: Programming Language :: Python :: 3.9
16
16
  Classifier: Programming Language :: Python :: 3.10
17
17
  Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
18
19
  Classifier: Programming Language :: Python :: 3 :: Only
19
20
  Classifier: Topic :: Software Development :: Build Tools
20
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
@@ -63,7 +64,9 @@ return_reason = client.additional_service.get_return_reasons()
63
64
 
64
65
  # Print results
65
66
  print(settlements, my_pack_list, return_reason)
67
+ client.close_sync()
66
68
  ```
69
+ Please, close the client whenever you are done with it to avoid resource leaks.
67
70
 
68
71
  You can also use async client:
69
72
 
@@ -71,10 +74,28 @@ You can also use async client:
71
74
  import asyncio
72
75
  from novaposhta.client import NovaPoshtaApi
73
76
 
74
- async_client = NovaPoshtaApi('my-api-token', timeout=30, async_mode=True)
75
- a_address = async_client.address
76
- a_settlement = asyncio.run(a_address.search_settlements(city_name='Київ', limit=5))
77
- print(a_settlement)
77
+ async def use_api_async():
78
+ async_client = NovaPoshtaApi('your_api_key', timeout=30, async_mode=True, raise_for_errors=True)
79
+ address = async_client.address
80
+ settlements = await address.search_settlements(city_name='Київ', limit=5)
81
+ print(settlements)
82
+ await async_client.close_async()
83
+ asyncio.run(use_api_async())
84
+ ```
85
+
86
+ You can use context manager to automatically close the client:
87
+
88
+ ```python
89
+ from novaposhta.client import NovaPoshtaApi
90
+ def use_api_sync():
91
+ with NovaPoshtaApi(api_key='your_api_key', async_mode=False) as api:
92
+ # Do something with the API
93
+ pass
94
+
95
+ async def use_api_async():
96
+ async with NovaPoshtaApi(api_key='your_api_key', async_mode=True) as api:
97
+ # Do something with the API
98
+ pass
78
99
  ```
79
100
 
80
101
  ## Error handling
@@ -84,7 +105,7 @@ import httpx
84
105
  from novaposhta.client import NovaPoshtaApi, InvalidAPIKeyError, APIRequestError
85
106
 
86
107
  # Instantiate the client
87
- client = NovaPoshtaApi('my-api-token', timeout=30, raise_for_errors=True)
108
+ client = NovaPoshtaApi('your_api_key', timeout=30, raise_for_errors=True)
88
109
 
89
110
  try:
90
111
  client.common.get_cargo_types()
@@ -107,16 +128,7 @@ follows the same interface:
107
128
  from novaposhta.client import NovaPoshtaApi
108
129
  import my_http_client
109
130
 
110
- client = NovaPoshtaApi('my-api-token', http_client=my_http_client.Client)
111
- ```
112
-
113
- Custom client should support context manager, e.g:
114
-
115
- ```python
116
- with self.http_client() as client:
117
- response = client.post(
118
- self.api_endpoint, json=request, headers=HEADERS, timeout=self.timeout
119
- )
131
+ client = NovaPoshtaApi('your_api_key', http_client=my_http_client.Client)
120
132
  ```
121
133
 
122
134
  ### Adding New Methods
@@ -39,7 +39,9 @@ return_reason = client.additional_service.get_return_reasons()
39
39
 
40
40
  # Print results
41
41
  print(settlements, my_pack_list, return_reason)
42
+ client.close_sync()
42
43
  ```
44
+ Please, close the client whenever you are done with it to avoid resource leaks.
43
45
 
44
46
  You can also use async client:
45
47
 
@@ -47,10 +49,28 @@ You can also use async client:
47
49
  import asyncio
48
50
  from novaposhta.client import NovaPoshtaApi
49
51
 
50
- async_client = NovaPoshtaApi('my-api-token', timeout=30, async_mode=True)
51
- a_address = async_client.address
52
- a_settlement = asyncio.run(a_address.search_settlements(city_name='Київ', limit=5))
53
- print(a_settlement)
52
+ async def use_api_async():
53
+ async_client = NovaPoshtaApi('your_api_key', timeout=30, async_mode=True, raise_for_errors=True)
54
+ address = async_client.address
55
+ settlements = await address.search_settlements(city_name='Київ', limit=5)
56
+ print(settlements)
57
+ await async_client.close_async()
58
+ asyncio.run(use_api_async())
59
+ ```
60
+
61
+ You can use context manager to automatically close the client:
62
+
63
+ ```python
64
+ from novaposhta.client import NovaPoshtaApi
65
+ def use_api_sync():
66
+ with NovaPoshtaApi(api_key='your_api_key', async_mode=False) as api:
67
+ # Do something with the API
68
+ pass
69
+
70
+ async def use_api_async():
71
+ async with NovaPoshtaApi(api_key='your_api_key', async_mode=True) as api:
72
+ # Do something with the API
73
+ pass
54
74
  ```
55
75
 
56
76
  ## Error handling
@@ -60,7 +80,7 @@ import httpx
60
80
  from novaposhta.client import NovaPoshtaApi, InvalidAPIKeyError, APIRequestError
61
81
 
62
82
  # Instantiate the client
63
- client = NovaPoshtaApi('my-api-token', timeout=30, raise_for_errors=True)
83
+ client = NovaPoshtaApi('your_api_key', timeout=30, raise_for_errors=True)
64
84
 
65
85
  try:
66
86
  client.common.get_cargo_types()
@@ -83,16 +103,7 @@ follows the same interface:
83
103
  from novaposhta.client import NovaPoshtaApi
84
104
  import my_http_client
85
105
 
86
- client = NovaPoshtaApi('my-api-token', http_client=my_http_client.Client)
87
- ```
88
-
89
- Custom client should support context manager, e.g:
90
-
91
- ```python
92
- with self.http_client() as client:
93
- response = client.post(
94
- self.api_endpoint, json=request, headers=HEADERS, timeout=self.timeout
95
- )
106
+ client = NovaPoshtaApi('your_api_key', http_client=my_http_client.Client)
96
107
  ```
97
108
 
98
109
  ### Adding New Methods
@@ -1,20 +1,20 @@
1
1
  """Client for Nova Poshta API. """
2
2
 
3
+ from typing import Type, TypeVar, Optional, Callable
4
+
3
5
  import httpx
4
6
 
5
- from .models.base import BaseModel
7
+ from .models.additional_service import AdditionalService
6
8
  from .models.address import Address
7
- from .models.counterparty import Counterparty
8
- from .models.contact_person import ContactPerson
9
- from .models.scan_sheet import ScanSheet
9
+ from .models.base import BaseModel
10
10
  from .models.common import Common
11
- from .models.additional_service import AdditionalService
11
+ from .models.contact_person import ContactPerson
12
+ from .models.counterparty import Counterparty
12
13
  from .models.internet_document import InternetDocument
14
+ from .models.scan_sheet import ScanSheet
13
15
  from .models.tracking_document import TrackingDocument
14
16
  from .types import DictStrAny, MaybeAsync
15
17
 
16
- from typing import Type, TypeVar, Union
17
-
18
18
  HEADERS = {"Content-Type": "application/json"}
19
19
 
20
20
  BaseModelType = TypeVar("BaseModelType", bound=BaseModel)
@@ -29,14 +29,17 @@ class NovaPoshtaApi:
29
29
  https://developers.novaposhta.ua/documentation
30
30
  """
31
31
 
32
+ sync_http_client: Optional[httpx.Client] = None
33
+ async_http_client: Optional[httpx.AsyncClient] = None
34
+
32
35
  def __init__(
33
36
  self,
34
- api_key,
35
- api_endpoint="https://api.novaposhta.ua/v2.0/json/",
37
+ api_key: str,
38
+ api_endpoint: str = "https://api.novaposhta.ua/v2.0/json/",
36
39
  http_client=httpx,
37
- timeout=10,
38
- raise_for_errors=False,
39
- async_mode=False,
40
+ timeout: int = 10,
41
+ raise_for_errors: bool = False,
42
+ async_mode: bool = False,
40
43
  ):
41
44
  """
42
45
  Initialize Nova Poshta API client.
@@ -50,19 +53,21 @@ class NovaPoshtaApi:
50
53
  """
51
54
  self.api_key = api_key
52
55
  self.api_endpoint = api_endpoint
53
- self.http_client = (
54
- http_client.Client if not async_mode else http_client.AsyncClient
55
- )
56
56
  self.timeout = timeout
57
- self.raise_for_errors = raise_for_errors
58
- self.async_mode = async_mode
59
- self._models_pool = {}
60
-
61
- if self.async_mode:
57
+ self._send: Callable[[DictStrAny], MaybeAsync]
58
+ if async_mode:
59
+ self.async_http_client: Optional[httpx.AsyncClient] = (
60
+ http_client.AsyncClient(timeout=timeout)
61
+ )
62
62
  self._send = self._send_async
63
-
64
63
  else:
64
+ self.sync_http_client: Optional[httpx.Client] = http_client.Client(
65
+ timeout=timeout
66
+ )
65
67
  self._send = self._send_sync
68
+ self.raise_for_errors = raise_for_errors
69
+ self.async_mode = async_mode
70
+ self._models_pool: DictStrAny = {}
66
71
 
67
72
  def _send_sync(self, request: DictStrAny) -> DictStrAny:
68
73
  """
@@ -71,9 +76,9 @@ class NovaPoshtaApi:
71
76
  :param request: request dict.
72
77
  :return: response dict.
73
78
  """
74
-
75
- with self.http_client() as client:
76
- response = client.post(**request)
79
+ if not self.sync_http_client:
80
+ raise ValueError("Sync client is not initialized")
81
+ response = self.sync_http_client.post(**request)
77
82
  return self._maybe_check_errors(response.json())
78
83
 
79
84
  async def _send_async(self, request: DictStrAny) -> DictStrAny:
@@ -83,8 +88,9 @@ class NovaPoshtaApi:
83
88
  :param request: request dict.
84
89
  :return: response dict.
85
90
  """
86
- async with self.http_client() as client:
87
- response = await client.post(**request)
91
+ if not self.async_http_client:
92
+ raise ValueError("Async client is not initialized")
93
+ response: httpx.Response = await self.async_http_client.post(**request)
88
94
  return self._maybe_check_errors(response.json())
89
95
 
90
96
  def send(
@@ -127,7 +133,7 @@ class NovaPoshtaApi:
127
133
  self._models_pool[model.name] = model(self)
128
134
  return self._models_pool[model.name]
129
135
 
130
- def get(self, name: str) -> Union[BaseModelType, None]:
136
+ def get(self, name: str) -> Optional[BaseModel]:
131
137
  """
132
138
  Get model from the pool.
133
139
 
@@ -135,7 +141,13 @@ class NovaPoshtaApi:
135
141
  """
136
142
  return self._models_pool.get(name)
137
143
 
138
- def _maybe_check_errors(self, response):
144
+ def _maybe_check_errors(self, response: DictStrAny) -> DictStrAny:
145
+ """
146
+ Check response for errors.
147
+
148
+ :param response: response dict.
149
+ :return: response dict.
150
+ """
139
151
  if not self.raise_for_errors:
140
152
  return response
141
153
 
@@ -148,6 +160,45 @@ class NovaPoshtaApi:
148
160
  else:
149
161
  raise APIRequestError(error)
150
162
 
163
+ def close_sync(self):
164
+ """
165
+ Close sync client.
166
+ """
167
+ if self.sync_http_client:
168
+ self.sync_http_client.close()
169
+
170
+ async def close_async(self):
171
+ """
172
+ Close async client.
173
+ """
174
+ if self.async_http_client:
175
+ await self.async_http_client.aclose()
176
+
177
+ def __enter__(self):
178
+ """
179
+ Enter the context.
180
+ """
181
+ return self
182
+
183
+ def __exit__(self, exc_type, exc_val, exc_tb):
184
+ """
185
+ Exit the context.
186
+ Close the client.
187
+ """
188
+ self.close_sync()
189
+
190
+ async def __aenter__(self):
191
+ """
192
+ Enter the async context.
193
+ """
194
+ return self
195
+
196
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
197
+ """
198
+ Exit the async context.
199
+ """
200
+ await self.close_async()
201
+
151
202
  @property
152
203
  def address(self) -> Address:
153
204
  """
@@ -3,7 +3,7 @@ name = "novaposhta-python-client"
3
3
  packages = [
4
4
  { include = "novaposhta"}
5
5
  ]
6
- version = "0.1.5"
6
+ version = "0.1.7"
7
7
  description = "Python client for Nova Poshta API"
8
8
  authors = ["Oleksii <semolex@live.com>"]
9
9
  license = "MIT"
@@ -28,10 +28,7 @@ httpx = ">=0.24.0"
28
28
 
29
29
  [tool.poetry.group.dev.dependencies]
30
30
  mypy = "^1.5.1"
31
- black = "^23.9.1"
32
- sphinx = "^7.2.6"
33
- myst-parser = "^2.0.0"
34
- sphinx-rtd-theme = "^1.3.0"
31
+ black = "^24"
35
32
  pytest = "^7.4.2"
36
33
  pytest-httpx = "^0.26.0"
37
34
  coverage = "^7.3.2"