b24api 0.3.10__tar.gz → 0.4.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: b24api
3
- Version: 0.3.10
3
+ Version: 0.4.1
4
4
  Summary: Bitrix24 API
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -3,6 +3,7 @@ import logging
3
3
  from collections.abc import Generator, Iterable
4
4
  from itertools import chain, islice
5
5
  from operator import itemgetter
6
+ from typing import Any
6
7
 
7
8
  import h2.exceptions
8
9
  import httpx
@@ -58,7 +59,7 @@ class Bitrix24:
58
59
  )
59
60
 
60
61
  with contextlib.suppress(httpx.ResponseNotRead, ValidationError):
61
- ErrorResponse.model_validate_json(http_response.content).raise_error(self.settings.retry_errors)
62
+ ErrorResponse.model_validate_json(http_response.content).raise_error(request, self.settings.retry_errors)
62
63
 
63
64
  try:
64
65
  http_response.raise_for_status()
@@ -78,16 +79,26 @@ class Bitrix24:
78
79
 
79
80
  def batch(
80
81
  self,
81
- requests: Iterable[Request | dict],
82
+ requests: Iterable[Request | dict | tuple[Request | dict, Any]],
83
+ *,
82
84
  batch_size: int | None = None,
83
- ) -> Generator[ApiTypes]:
85
+ with_payload: bool = False,
86
+ ) -> Generator[ApiTypes | tuple[ApiTypes, Any]]:
84
87
  """Call unlimited sequence of methods within batches and return `result` from responses."""
85
88
  batch_size = batch_size or self.settings.batch_size
86
89
 
87
90
  tail_requests = iter(requests)
88
91
  while batched_requests := list(islice(tail_requests, batch_size)):
89
- for response in self.retry(self._batch, batched_requests):
90
- yield response.result
92
+ if with_payload:
93
+ batched_requests, batched_payloads = zip(*batched_requests, strict=True)
94
+ else:
95
+ batched_payloads = None
96
+
97
+ for i, response in enumerate(self.retry(self._batch, batched_requests)):
98
+ if with_payload:
99
+ yield response.result, batched_payloads[i]
100
+ else:
101
+ yield response.result
91
102
 
92
103
  def _batch(self, requests: Iterable[Request | dict]) -> list[Response]:
93
104
  """Call limited batch of methods and return full responses."""
@@ -108,7 +119,10 @@ class Bitrix24:
108
119
  key = f"_{i}"
109
120
 
110
121
  if key in result.result_error:
111
- ErrorResponse.model_validate(result.result_error[key]).raise_error(self.settings.retry_errors)
122
+ ErrorResponse.model_validate(result.result_error[key]).raise_error(
123
+ commands[key],
124
+ self.settings.retry_errors,
125
+ )
112
126
 
113
127
  command = commands[key]
114
128
  if key not in result.result:
@@ -134,6 +148,7 @@ class Bitrix24:
134
148
  def list_sequential(
135
149
  self,
136
150
  request: Request | dict,
151
+ *,
137
152
  list_size: int | None = None,
138
153
  ) -> Generator[ApiTypes]:
139
154
  """Call `list` method and return full `result`.
@@ -167,6 +182,7 @@ class Bitrix24:
167
182
  def list_batched(
168
183
  self,
169
184
  request: Request | dict,
185
+ *,
170
186
  list_size: int | None = None,
171
187
  batch_size: int | None = None,
172
188
  ) -> Generator[ApiTypes]:
@@ -194,7 +210,7 @@ class Bitrix24:
194
210
  tail_request.parameters["start"] = start
195
211
  yield tail_request
196
212
 
197
- tail_responses = self.batch(_tail_requests(), batch_size)
213
+ tail_responses = self.batch(_tail_requests(), batch_size=batch_size)
198
214
  tail_responses = map(self._fix_list_result, tail_responses)
199
215
  tail_responses = chain.from_iterable(tail_responses)
200
216
  yield from tail_responses
@@ -202,6 +218,7 @@ class Bitrix24:
202
218
  def list_batched_no_count(
203
219
  self,
204
220
  request: ListRequest | dict,
221
+ *,
205
222
  id_key: str = "ID",
206
223
  list_size: int | None = None,
207
224
  batch_size: int | None = None,
@@ -238,7 +255,7 @@ class Bitrix24:
238
255
  tail_request.parameters.start = -1
239
256
  tail_request.parameters.order = {"ID": "DESC"}
240
257
 
241
- head_tail_result = self.batch([head_request, tail_request])
258
+ head_tail_result = self.batch([head_request, tail_request], batch_size=batch_size)
242
259
  head_result, tail_result = tuple(map(self._fix_list_result, head_tail_result))
243
260
  yield from head_result
244
261
 
@@ -253,7 +270,7 @@ class Bitrix24:
253
270
  yield body_request
254
271
 
255
272
  if max_head_id and min_tail_id and max_head_id < min_tail_id:
256
- body = self.batch(_body_requests(), batch_size)
273
+ body = self.batch(_body_requests(), batch_size=batch_size)
257
274
  body = map(self._fix_list_result, body)
258
275
  body = chain.from_iterable(body)
259
276
  yield from body
@@ -265,11 +282,13 @@ class Bitrix24:
265
282
  def reference_batched_no_count(
266
283
  self,
267
284
  request: ListRequest | dict,
268
- updates: Iterable[dict],
285
+ updates: Iterable[dict | tuple[dict, Any]],
286
+ *,
269
287
  id_key: str = "ID",
270
288
  list_size: int | None = None,
271
289
  batch_size: int | None = None,
272
- ) -> Generator[ApiTypes]:
290
+ with_payload: bool = False,
291
+ ) -> Generator[ApiTypes | tuple[ApiTypes, Any]]:
273
292
  """Call `list` method with reference `updates` for `filter` and return full `result`.
274
293
 
275
294
  Fastest (batched, no count) list gathering for methods with `filter` parameter and required `reference`
@@ -295,8 +314,13 @@ class Bitrix24:
295
314
  if request.parameters.order:
296
315
  raise ValueError("Ordering parameters are reserved `order`in `reference_batched_no_count`")
297
316
 
298
- def _tail_requests() -> Generator[ListRequest]:
317
+ def _tail_requests() -> Generator[ListRequest | tuple[ListRequest, Any]]:
299
318
  for update in updates:
319
+ if with_payload:
320
+ update, payload = update
321
+ else:
322
+ payload = None
323
+
300
324
  if id_from in update:
301
325
  raise ValueError(
302
326
  f"Filter parameters `{id_from}` is reserved in `reference_batched_no_count`",
@@ -305,21 +329,37 @@ class Bitrix24:
305
329
  tail_request.parameters.filter |= update
306
330
  tail_request.parameters.start = -1
307
331
  tail_request.parameters.order = {"ID": "ASC"}
308
- yield tail_request
332
+
333
+ if with_payload:
334
+ yield tail_request, payload
335
+ else:
336
+ yield tail_request
309
337
 
310
338
  head_requests = []
311
339
  tail_requests = iter(_tail_requests())
312
340
  while body_requests := head_requests + list(islice(tail_requests, batch_size - len(head_requests))):
313
- body_results = self.batch(body_requests, batch_size)
341
+ if with_payload:
342
+ body_requests, body_payloads = zip(*body_requests, strict=True)
343
+ else:
344
+ body_payloads = None
345
+
346
+ body_results = self.batch(body_requests, batch_size=batch_size)
314
347
  body_results = map(self._fix_list_result, body_results)
315
348
 
316
349
  head_requests = []
317
- for body_request, body_result in zip(body_requests, body_results, strict=True):
350
+ for i, (body_request, body_result) in enumerate(zip(body_requests, body_results, strict=True)):
318
351
  if len(body_result) == list_size:
319
352
  max_id = max(map(int, map(get_id, body_result)), default=None)
320
353
  head_request = body_request.model_copy(deep=True)
321
354
  head_request.parameters.filter[id_from] = max_id
322
- head_requests.append(head_request)
355
+ if with_payload:
356
+ head_requests.append((head_request, body_payloads[i]))
357
+ else:
358
+ head_requests.append(head_request)
359
+
360
+ if with_payload:
361
+ body_payload = [body_payloads[i]] * len(body_result)
362
+ body_result = zip(body_result, body_payload, strict=True)
323
363
 
324
364
  yield from body_result
325
365
 
@@ -57,15 +57,17 @@ class ErrorResponse(BaseModel):
57
57
  value = value.lower()
58
58
  return value
59
59
 
60
- def raise_error(self, retry_errors: list[str]) -> None:
60
+ def raise_error(self, request: Request, retry_errors: list[str]) -> None:
61
61
  if self.error in retry_errors:
62
62
  raise RetryApiResponseError(
63
63
  code=self.error,
64
64
  description=self.error_description,
65
+ request=str(request),
65
66
  )
66
67
  raise ApiResponseError(
67
68
  code=self.error,
68
69
  description=self.error_description,
70
+ request=str(request),
69
71
  )
70
72
 
71
73
 
@@ -8,18 +8,13 @@ class RetryHTTPStatusError(httpx.HTTPStatusError):
8
8
  class ApiResponseError(Exception):
9
9
  """API error with description."""
10
10
 
11
- def __init__(
12
- self,
13
- *,
14
- code: str,
15
- description: str | None,
16
- ) -> None:
11
+ def __init__(self, *, code: str, description: str | None, request: str) -> None:
17
12
  if code and description:
18
- message = f"API error [{code}]: {description}"
13
+ message = f"API error [{code}]: {description} for request {request}"
19
14
  elif code:
20
- message = f"API error [{code}]"
15
+ message = f"API error [{code}] for request {request}"
21
16
  else:
22
- message = f"API error: {description}"
17
+ message = f"API error: {description} for request {request}"
23
18
  super().__init__(message)
24
19
 
25
20
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: b24api
3
- Version: 0.3.10
3
+ Version: 0.4.1
4
4
  Summary: Bitrix24 API
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes