valentina-python-client 1.3.0__tar.gz → 1.3.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.
Files changed (47) hide show
  1. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/PKG-INFO +12 -12
  2. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/README.md +11 -11
  3. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/pyproject.toml +1 -21
  4. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/__init__.py +1 -1
  5. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/client.py +2 -5
  6. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/campaigns.py +6 -5
  7. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/characters.py +33 -24
  8. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/companies.py +4 -3
  9. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/shared.py +3 -3
  10. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/users.py +5 -4
  11. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/base.py +11 -66
  12. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/LICENSE +0 -0
  13. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/config.py +0 -0
  14. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/constants.py +0 -0
  15. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/endpoints.py +0 -0
  16. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/exceptions.py +0 -0
  17. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/__init__.py +0 -0
  18. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/books.py +0 -0
  19. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/chapters.py +0 -0
  20. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/character_autogen.py +0 -0
  21. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/character_blueprint.py +0 -0
  22. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/character_trait.py +0 -0
  23. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/developers.py +0 -0
  24. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/diceroll.py +0 -0
  25. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/dictionary.py +0 -0
  26. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/global_admin.py +0 -0
  27. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/pagination.py +0 -0
  28. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/models/system.py +0 -0
  29. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/py.typed +0 -0
  30. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/registry.py +0 -0
  31. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/__init__.py +0 -0
  32. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/campaign_book_chapters.py +0 -0
  33. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/campaign_books.py +0 -0
  34. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/campaigns.py +0 -0
  35. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/character_autogen.py +0 -0
  36. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/character_blueprint.py +0 -0
  37. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/character_traits.py +0 -0
  38. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/characters.py +0 -0
  39. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/companies.py +0 -0
  40. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/developers.py +0 -0
  41. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/dicerolls.py +0 -0
  42. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/dictionary.py +0 -0
  43. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/global_admin.py +0 -0
  44. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/options.py +0 -0
  45. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/system.py +0 -0
  46. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/services/users.py +0 -0
  47. {valentina_python_client-1.3.0 → valentina_python_client-1.3.1}/src/vclient/validate_constants.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: valentina-python-client
3
- Version: 1.3.0
3
+ Version: 1.3.1
4
4
  Summary: Async Python client library for the Valentina Noir API
5
5
  Author: Nate Landau
6
6
  Author-email: Nate Landau <github@natenate.org>
@@ -30,13 +30,13 @@ Async Python client library for accessing the Valentina Noir API.
30
30
 
31
31
  ## Features
32
32
 
33
- - **Async-first design** - Built on httpx for efficient async HTTP operations
34
- - **Type-safe** - Full type hints with Pydantic models for request/response validation
35
- - **Convenient factory pattern** - Create a client once, access services from anywhere
36
- - **Automatic pagination** - Stream through large datasets with `iter_all()` or fetch everything with `list_all()`
37
- - **Robust error handling** - Specific exception types for different error conditions
38
- - **Idempotency support** - Optional automatic idempotency keys for safe retries
39
- - **Rate limit handling** - Built-in support for automatic rate limit retries
33
+ - **Async-first design** - Built on httpx for efficient async HTTP operations
34
+ - **Type-safe** - Full type hints with Pydantic models for request/response validation
35
+ - **Convenient factory pattern** - Create a client once, access services from anywhere
36
+ - **Automatic pagination** - Stream through large datasets with `iter_all()` or fetch everything with `list_all()`
37
+ - **Robust error handling** - Specific exception types for different error conditions
38
+ - **Idempotency support** - Optional automatic idempotency keys for safe retries
39
+ - **Rate limit handling** - Built-in support for automatic rate limit retries
40
40
 
41
41
  This client is a supported and up-to-date reference implementation for the Valentina Noir API. The full documentation for is available at https://docs.valentina-noir.com/python-api-client/.
42
42
 
@@ -62,12 +62,12 @@ The script reads configuration from (highest precedence first):
62
62
 
63
63
  1. CLI arguments (`--api-url`, `--api-key`, `--company-id`)
64
64
  2. System environment variables (`VALENTINA_CLIENT_BASE_URL`, `VALENTINA_CLIENT_API_KEY`, `VALENTINA_CLIENT_DEFAULT_COMPANY_ID`)
65
- 3. A `.env.secrets` file in the project root
65
+ 3. A `.env.secret` file in the project root
66
66
 
67
67
  Exit codes: `0` = all constants match, `1` = mismatches found, `2` = missing configuration.
68
68
 
69
69
  ## Resources
70
70
 
71
- - [Full Client Documentation](https://docs.valentina-noir.com/python-api-client/)
72
- - [API Concepts](https://docs.valentina-noir.com/concepts/)
73
- - [API Reference](https://api.valentina-noir.com/docs)
71
+ - [Full Client Documentation](https://docs.valentina-noir.com/python-api-client/)
72
+ - [API Concepts](https://docs.valentina-noir.com/concepts/)
73
+ - [API Reference](https://api.valentina-noir.com/docs)
@@ -4,13 +4,13 @@ Async Python client library for accessing the Valentina Noir API.
4
4
 
5
5
  ## Features
6
6
 
7
- - **Async-first design** - Built on httpx for efficient async HTTP operations
8
- - **Type-safe** - Full type hints with Pydantic models for request/response validation
9
- - **Convenient factory pattern** - Create a client once, access services from anywhere
10
- - **Automatic pagination** - Stream through large datasets with `iter_all()` or fetch everything with `list_all()`
11
- - **Robust error handling** - Specific exception types for different error conditions
12
- - **Idempotency support** - Optional automatic idempotency keys for safe retries
13
- - **Rate limit handling** - Built-in support for automatic rate limit retries
7
+ - **Async-first design** - Built on httpx for efficient async HTTP operations
8
+ - **Type-safe** - Full type hints with Pydantic models for request/response validation
9
+ - **Convenient factory pattern** - Create a client once, access services from anywhere
10
+ - **Automatic pagination** - Stream through large datasets with `iter_all()` or fetch everything with `list_all()`
11
+ - **Robust error handling** - Specific exception types for different error conditions
12
+ - **Idempotency support** - Optional automatic idempotency keys for safe retries
13
+ - **Rate limit handling** - Built-in support for automatic rate limit retries
14
14
 
15
15
  This client is a supported and up-to-date reference implementation for the Valentina Noir API. The full documentation for is available at https://docs.valentina-noir.com/python-api-client/.
16
16
 
@@ -36,12 +36,12 @@ The script reads configuration from (highest precedence first):
36
36
 
37
37
  1. CLI arguments (`--api-url`, `--api-key`, `--company-id`)
38
38
  2. System environment variables (`VALENTINA_CLIENT_BASE_URL`, `VALENTINA_CLIENT_API_KEY`, `VALENTINA_CLIENT_DEFAULT_COMPANY_ID`)
39
- 3. A `.env.secrets` file in the project root
39
+ 3. A `.env.secret` file in the project root
40
40
 
41
41
  Exit codes: `0` = all constants match, `1` = mismatches found, `2` = missing configuration.
42
42
 
43
43
  ## Resources
44
44
 
45
- - [Full Client Documentation](https://docs.valentina-noir.com/python-api-client/)
46
- - [API Concepts](https://docs.valentina-noir.com/concepts/)
47
- - [API Reference](https://api.valentina-noir.com/docs)
45
+ - [Full Client Documentation](https://docs.valentina-noir.com/python-api-client/)
46
+ - [API Concepts](https://docs.valentina-noir.com/concepts/)
47
+ - [API Reference](https://api.valentina-noir.com/docs)
@@ -10,7 +10,7 @@
10
10
  name = "valentina-python-client"
11
11
  readme = "README.md"
12
12
  requires-python = ">=3.13"
13
- version = "1.3.0"
13
+ version = "1.3.1"
14
14
 
15
15
  [project.urls]
16
16
  Homepage = "https://docs.valentina-noir.com/python-api-client/"
@@ -80,26 +80,6 @@
80
80
  [tool.coverage.xml]
81
81
  output = ".cache/coverage.xml"
82
82
 
83
- [tool.mypy] # https://mypy.readthedocs.io/en/latest/config_file.html
84
- cache_dir = ".cache/mypy"
85
- disallow_any_unimported = false
86
- disallow_subclassing_any = false
87
- disallow_untyped_decorators = false
88
- disallow_untyped_defs = true
89
- exclude = ['duties.py', 'tests/']
90
- follow_imports = "normal"
91
- ignore_missing_imports = true
92
- junit_xml = ".cache/mypy.xml"
93
- no_implicit_optional = true
94
- pretty = false
95
- show_column_numbers = true
96
- show_error_codes = true
97
- show_error_context = true
98
- strict_optional = false
99
- warn_redundant_casts = true
100
- warn_unreachable = true
101
- warn_unused_ignores = true
102
-
103
83
  [tool.pytest.ini_options]
104
84
 
105
85
  addopts = "--color=yes --doctest-modules --strict-config --strict-markers -n auto --dist loadfile"
@@ -71,4 +71,4 @@ __all__ = (
71
71
  "users_service",
72
72
  )
73
73
 
74
- __version__ = "1.3.0"
74
+ __version__ = "1.3.1"
@@ -160,10 +160,7 @@ class VClient:
160
160
  base_url=self._config.base_url,
161
161
  timeout=self._config.timeout,
162
162
  max_retries=self._config.max_retries,
163
- ).debug(
164
- "Initialize VClient (base_url={base_url})",
165
- base_url=self._config.base_url,
166
- )
163
+ ).info("Initialize VClient")
167
164
 
168
165
  def _create_http_client(self) -> httpx.AsyncClient:
169
166
  """Create and configure the HTTP client."""
@@ -204,7 +201,7 @@ class VClient:
204
201
  """Close the HTTP client and release resources."""
205
202
  from vclient.registry import clear_default_client
206
203
 
207
- logger.bind(base_url=self._config.base_url).debug("Close VClient")
204
+ logger.bind(base_url=self._config.base_url).info("Close VClient")
208
205
  await self._http.aclose()
209
206
  clear_default_client(self)
210
207
 
@@ -1,6 +1,7 @@
1
1
  """Pydantic models for Campaign API responses and requests."""
2
2
 
3
3
  from datetime import datetime
4
+ from typing import Annotated
4
5
 
5
6
  from pydantic import BaseModel, Field
6
7
 
@@ -39,7 +40,7 @@ class CampaignCreate(BaseModel):
39
40
  """
40
41
 
41
42
  name: str = Field(min_length=3, max_length=50)
42
- description: str | None = Field(default=None, min_length=3)
43
+ description: Annotated[str, Field(min_length=3)] | None = None
43
44
  desperation: int = Field(default=0, ge=0, le=5)
44
45
  danger: int = Field(default=0, ge=0, le=5)
45
46
 
@@ -50,10 +51,10 @@ class CampaignUpdate(BaseModel):
50
51
  Only include fields that need to be changed; omitted fields remain unchanged.
51
52
  """
52
53
 
53
- name: str | None = Field(default=None, min_length=3, max_length=50)
54
- description: str | None = Field(default=None, min_length=3)
55
- desperation: int | None = Field(default=None, ge=0, le=5)
56
- danger: int | None = Field(default=None, ge=0, le=5)
54
+ name: Annotated[str, Field(min_length=3, max_length=50)] | None = None
55
+ description: Annotated[str, Field(min_length=3)] | None = None
56
+ desperation: Annotated[int, Field(ge=0, le=5)] | None = None
57
+ danger: Annotated[int, Field(ge=0, le=5)] | None = None
57
58
 
58
59
 
59
60
  __all__ = [
@@ -1,6 +1,7 @@
1
1
  """Pydantic models for Character API responses and requests."""
2
2
 
3
3
  from datetime import datetime
4
+ from typing import Annotated
4
5
 
5
6
  from pydantic import BaseModel, Field
6
7
 
@@ -155,20 +156,22 @@ class Character(BaseModel):
155
156
  # Identity
156
157
  name_first: str = Field(..., min_length=3, description="Character's first name.")
157
158
  name_last: str = Field(..., min_length=3, description="Character's last name.")
158
- name_nick: str | None = Field(
159
- default=None, min_length=3, max_length=50, description="Character's nickname."
159
+ name_nick: Annotated[str, Field(min_length=3, max_length=50)] | None = Field(
160
+ default=None, description="Character's nickname."
160
161
  )
161
162
  name: str = Field(..., description="Character's display name.")
162
163
  name_full: str = Field(..., description="Character's full name.")
163
164
 
164
165
  # Biography
165
166
  age: int | None = Field(default=None, description="Character's age.")
166
- biography: str | None = Field(default=None, min_length=3, description="Character biography.")
167
- demeanor: str | None = Field(
168
- default=None, min_length=3, max_length=50, description="Character's demeanor."
167
+ biography: Annotated[str, Field(min_length=3)] | None = Field(
168
+ default=None, description="Character biography."
169
169
  )
170
- nature: str | None = Field(
171
- default=None, min_length=3, max_length=50, description="Character's nature."
170
+ demeanor: Annotated[str, Field(min_length=3, max_length=50)] | None = Field(
171
+ default=None, description="Character's demeanor."
172
+ )
173
+ nature: Annotated[str, Field(min_length=3, max_length=50)] | None = Field(
174
+ default=None, description="Character's nature."
172
175
  )
173
176
  concept_id: str | None = Field(default=None, description="ID of the character concept.")
174
177
 
@@ -220,16 +223,18 @@ class CharacterCreate(BaseModel):
220
223
 
221
224
  # Optional fields
222
225
  type: CharacterType | None = Field(default=None, description="Character type.")
223
- name_nick: str | None = Field(
224
- default=None, min_length=3, max_length=50, description="Character's nickname."
226
+ name_nick: Annotated[str, Field(min_length=3, max_length=50)] | None = Field(
227
+ default=None, description="Character's nickname."
225
228
  )
226
229
  age: int | None = Field(default=None, description="Character's age.")
227
- biography: str | None = Field(default=None, min_length=3, description="Character biography.")
228
- demeanor: str | None = Field(
229
- default=None, min_length=3, max_length=50, description="Character's demeanor."
230
+ biography: Annotated[str, Field(min_length=3)] | None = Field(
231
+ default=None, description="Character biography."
232
+ )
233
+ demeanor: Annotated[str, Field(min_length=3, max_length=50)] | None = Field(
234
+ default=None, description="Character's demeanor."
230
235
  )
231
- nature: str | None = Field(
232
- default=None, min_length=3, max_length=50, description="Character's nature."
236
+ nature: Annotated[str, Field(min_length=3, max_length=50)] | None = Field(
237
+ default=None, description="Character's nature."
233
238
  )
234
239
  concept_id: str | None = Field(default=None, description="ID of the character concept.")
235
240
  user_player_id: str | None = Field(
@@ -263,21 +268,25 @@ class CharacterUpdate(BaseModel):
263
268
  game_version: GameVersion | None = None
264
269
  status: CharacterStatus | None = None
265
270
 
266
- name_first: str | None = Field(
267
- default=None, min_length=3, description="Character's first name."
271
+ name_first: Annotated[str, Field(min_length=3)] | None = Field(
272
+ default=None, description="Character's first name."
268
273
  )
269
- name_last: str | None = Field(default=None, min_length=3, description="Character's last name.")
270
- name_nick: str | None = Field(
271
- default=None, min_length=3, max_length=50, description="Character's nickname."
274
+ name_last: Annotated[str, Field(min_length=3)] | None = Field(
275
+ default=None, description="Character's last name."
276
+ )
277
+ name_nick: Annotated[str, Field(min_length=3, max_length=50)] | None = Field(
278
+ default=None, description="Character's nickname."
272
279
  )
273
280
 
274
281
  age: int | None = None
275
- biography: str | None = Field(default=None, min_length=3, description="Character biography.")
276
- demeanor: str | None = Field(
277
- default=None, min_length=3, max_length=50, description="Character's demeanor."
282
+ biography: Annotated[str, Field(min_length=3)] | None = Field(
283
+ default=None, description="Character biography."
284
+ )
285
+ demeanor: Annotated[str, Field(min_length=3, max_length=50)] | None = Field(
286
+ default=None, description="Character's demeanor."
278
287
  )
279
- nature: str | None = Field(
280
- default=None, min_length=3, max_length=50, description="Character's nature."
288
+ nature: Annotated[str, Field(min_length=3, max_length=50)] | None = Field(
289
+ default=None, description="Character's nature."
281
290
  )
282
291
  concept_id: str | None = Field(default=None, description="ID of the character concept.")
283
292
 
@@ -1,6 +1,7 @@
1
1
  """Pydantic models for Company API responses."""
2
2
 
3
3
  from datetime import datetime
4
+ from typing import Annotated
4
5
 
5
6
  from pydantic import BaseModel, Field
6
7
 
@@ -77,7 +78,7 @@ class CompanyCreate(BaseModel):
77
78
 
78
79
  name: str = Field(min_length=3, max_length=50)
79
80
  email: str
80
- description: str | None = Field(default=None, min_length=3)
81
+ description: Annotated[str, Field(min_length=3)] | None = None
81
82
  settings: CompanySettings | None = None
82
83
 
83
84
 
@@ -87,9 +88,9 @@ class CompanyUpdate(BaseModel):
87
88
  Only include fields that need to be changed; omitted fields remain unchanged.
88
89
  """
89
90
 
90
- name: str | None = Field(default=None, min_length=3, max_length=50)
91
+ name: Annotated[str, Field(min_length=3, max_length=50)] | None = None
91
92
  email: str | None = None
92
- description: str | None = Field(default=None, min_length=3)
93
+ description: Annotated[str, Field(min_length=3)] | None = None
93
94
  settings: CompanySettings | None = None
94
95
 
95
96
 
@@ -1,7 +1,7 @@
1
1
  """Shared Pydantic models used across multiple services."""
2
2
 
3
3
  from datetime import datetime
4
- from typing import Any
4
+ from typing import Annotated, Any
5
5
 
6
6
  from pydantic import BaseModel, Field
7
7
 
@@ -84,8 +84,8 @@ class NoteUpdate(BaseModel):
84
84
  Only include fields that need to be changed; omitted fields remain unchanged.
85
85
  """
86
86
 
87
- title: str | None = Field(default=None, min_length=3, max_length=50)
88
- content: str | None = Field(default=None, min_length=3)
87
+ title: Annotated[str, Field(min_length=3, max_length=50)] | None = None
88
+ content: Annotated[str, Field(min_length=3)] | None = None
89
89
 
90
90
 
91
91
  # -----------------------------------------------------------------------------
@@ -1,6 +1,7 @@
1
1
  """Pydantic models for User API responses and requests."""
2
2
 
3
3
  from datetime import datetime
4
+ from typing import Annotated
4
5
 
5
6
  from pydantic import BaseModel, Field
6
7
 
@@ -89,7 +90,7 @@ class UserUpdate(BaseModel):
89
90
  Only include fields that need to be changed; omitted fields remain unchanged.
90
91
  """
91
92
 
92
- name: str | None = Field(default=None, min_length=3, max_length=50)
93
+ name: Annotated[str, Field(min_length=3, max_length=50)] | None = None
93
94
  email: str | None = None
94
95
  role: UserRole | None = None
95
96
  discord_profile: DiscordProfile | None = None
@@ -124,7 +125,7 @@ class QuickrollCreate(BaseModel):
124
125
  """
125
126
 
126
127
  name: str = Field(min_length=3, max_length=50)
127
- description: str | None = Field(default=None, min_length=3)
128
+ description: Annotated[str, Field(min_length=3)] | None = None
128
129
  trait_ids: list[str] = Field(default_factory=list)
129
130
 
130
131
 
@@ -134,8 +135,8 @@ class QuickrollUpdate(BaseModel):
134
135
  Only include fields that need to be changed; omitted fields remain unchanged.
135
136
  """
136
137
 
137
- name: str | None = Field(default=None, min_length=3, max_length=50)
138
- description: str | None = Field(default=None, min_length=3)
138
+ name: Annotated[str, Field(min_length=3, max_length=50)] | None = None
139
+ description: Annotated[str, Field(min_length=3)] | None = None
139
140
  trait_ids: list[str] | None = None
140
141
 
141
142
 
@@ -173,7 +173,7 @@ class BaseService:
173
173
  retry_statuses = config.retry_statuses
174
174
  request_logger = logger.bind(method=method, url=path)
175
175
 
176
- request_logger.debug("Send {method} {url}", method=method, url=path)
176
+ request_logger.debug("Send request")
177
177
 
178
178
  last_error: RateLimitError | ServerError | None = None
179
179
 
@@ -199,14 +199,7 @@ class BaseService:
199
199
  attempt=attempt + 1,
200
200
  max_attempts=max_attempts,
201
201
  delay=delay,
202
- ).warning(
203
- "Retry {method} {url} after {error_type} (attempt {attempt}/{max_attempts})",
204
- method=method,
205
- url=path,
206
- error_type=error_type,
207
- attempt=attempt + 1,
208
- max_attempts=max_attempts,
209
- )
202
+ ).warning("Retry after network error")
210
203
  await asyncio.sleep(delay)
211
204
  continue
212
205
 
@@ -217,13 +210,7 @@ class BaseService:
217
210
  request_logger.bind(
218
211
  status=response.status_code,
219
212
  elapsed_ms=elapsed_ms,
220
- ).debug(
221
- "Receive {status} from {method} {url} ({elapsed_ms}ms)",
222
- status=response.status_code,
223
- method=method,
224
- url=path,
225
- elapsed_ms=f"{elapsed_ms:.0f}",
226
- )
213
+ ).debug("Receive response")
227
214
  return response # noqa: TRY300
228
215
  except RateLimitError as e:
229
216
  last_error = e
@@ -236,14 +223,7 @@ class BaseService:
236
223
  attempt=attempt + 1,
237
224
  max_attempts=max_attempts,
238
225
  delay=delay,
239
- ).warning(
240
- "Retry {method} {url} after rate limit (attempt {attempt}/{max_attempts}, delay {delay}s)",
241
- method=method,
242
- url=path,
243
- attempt=attempt + 1,
244
- max_attempts=max_attempts,
245
- delay=f"{delay:.1f}",
246
- )
226
+ ).warning("Retry after rate limit")
247
227
  await asyncio.sleep(delay)
248
228
  except ServerError as e:
249
229
  if e.status_code not in retry_statuses or not self._is_retryable_method(
@@ -262,24 +242,11 @@ class BaseService:
262
242
  attempt=attempt + 1,
263
243
  max_attempts=max_attempts,
264
244
  delay=delay,
265
- ).warning(
266
- "Retry {method} {url} after {status} error (attempt {attempt}/{max_attempts}, delay {delay}s)",
267
- method=method,
268
- url=path,
269
- status=e.status_code,
270
- attempt=attempt + 1,
271
- max_attempts=max_attempts,
272
- delay=f"{delay:.1f}",
273
- )
245
+ ).warning("Retry after server error")
274
246
  await asyncio.sleep(delay)
275
247
 
276
248
  if last_error is not None:
277
- request_logger.bind(attempts=max_attempts).error(
278
- "Exhaust retries for {method} {url} after {attempts} attempts",
279
- method=method,
280
- url=path,
281
- attempts=max_attempts,
282
- )
249
+ request_logger.bind(attempts=max_attempts).error("Exhaust retries")
283
250
  raise last_error
284
251
 
285
252
  msg = "Unexpected state: no response or error"
@@ -324,45 +291,23 @@ class BaseService:
324
291
  )
325
292
 
326
293
  if status_code == 401: # noqa: PLR2004
327
- error_logger.error(
328
- "Fail authentication for {method} {url} ({status})",
329
- method=method,
330
- url=url,
331
- status=status_code,
332
- )
294
+ error_logger.error("Fail authentication")
333
295
  raise AuthenticationError(message, status_code, response_data)
334
296
 
335
297
  if status_code == 403: # noqa: PLR2004
336
- error_logger.error(
337
- "Deny authorization for {method} {url} ({status})",
338
- method=method,
339
- url=url,
340
- status=status_code,
341
- )
298
+ error_logger.error("Deny authorization")
342
299
  raise AuthorizationError(message, status_code, response_data)
343
300
 
344
301
  if status_code == 404: # noqa: PLR2004
345
- error_logger.debug(
346
- "Return 404 for {method} {url}",
347
- method=method,
348
- url=url,
349
- )
302
+ error_logger.debug("Return 404")
350
303
  raise NotFoundError(message, status_code, response_data)
351
304
 
352
305
  if status_code == 400: # noqa: PLR2004
353
- error_logger.warning(
354
- "Reject {method} {url} with validation error",
355
- method=method,
356
- url=url,
357
- )
306
+ error_logger.warning("Reject with validation error")
358
307
  raise ValidationError(message, status_code, response_data)
359
308
 
360
309
  if status_code == 409: # noqa: PLR2004
361
- error_logger.warning(
362
- "Return 409 conflict for {method} {url}",
363
- method=method,
364
- url=url,
365
- )
310
+ error_logger.warning("Return 409 conflict")
366
311
  raise ConflictError(message, status_code, response_data)
367
312
 
368
313
  if HTTP_500_INTERNAL_SERVER_ERROR <= status_code < HTTP_600_UPPER_BOUND: