zpdatafetch 2.2.6__tar.gz → 2.3.0__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 (84) hide show
  1. {zpdatafetch-2.2.6/src/zpdatafetch.egg-info → zpdatafetch-2.3.0}/PKG-INFO +2 -2
  2. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/pyproject.toml +4 -6
  3. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/shared/http_client.py +29 -29
  4. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zdatafetch/activity.py +5 -5
  5. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zdatafetch/auth.py +7 -7
  6. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zdatafetch/followers.py +5 -5
  7. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zdatafetch/profile.py +5 -5
  8. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zdatafetch/rideons.py +8 -8
  9. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zdatafetch/ridersinworld.py +5 -5
  10. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zdatafetch/worlds.py +4 -4
  11. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/async_zp.py +22 -21
  12. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zp.py +17 -19
  13. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zpprimesfetch.py +3 -3
  14. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0/src/zpdatafetch.egg-info}/PKG-INFO +2 -2
  15. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch.egg-info/requires.txt +1 -1
  16. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zrdatafetch/async_zr.py +16 -16
  17. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zrdatafetch/zr.py +8 -8
  18. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zsdatafetch/async_zs.py +11 -7
  19. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zsdatafetch/zs.py +13 -9
  20. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/LICENSE +0 -0
  21. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/README.md +0 -0
  22. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/setup.cfg +0 -0
  23. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/shared/__init__.py +0 -0
  24. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/shared/cli.py +0 -0
  25. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/shared/config.py +0 -0
  26. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/shared/error_helpers.py +0 -0
  27. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/shared/exceptions.py +0 -0
  28. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/shared/json_helpers.py +0 -0
  29. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/shared/logging.py +0 -0
  30. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/shared/validation.py +0 -0
  31. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zdatafetch/__init__.py +0 -0
  32. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zdatafetch/cli.py +0 -0
  33. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zdatafetch/config.py +0 -0
  34. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zdatafetch/logging_config.py +0 -0
  35. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/__init__.py +0 -0
  36. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/cli.py +0 -0
  37. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/config.py +0 -0
  38. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/logging_config.py +0 -0
  39. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zp_obj.py +0 -0
  40. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zp_utils.py +0 -0
  41. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zpcyclist.py +0 -0
  42. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zpcyclistfetch.py +0 -0
  43. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zpleague.py +0 -0
  44. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zpleaguefetch.py +0 -0
  45. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zpprime.py +0 -0
  46. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zpracefinish.py +0 -0
  47. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zpracelog.py +0 -0
  48. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zpraceresult.py +0 -0
  49. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zpracesignup.py +0 -0
  50. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zpracesprint.py +0 -0
  51. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zpresultfetch.py +0 -0
  52. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zpsignupfetch.py +0 -0
  53. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zpsprintsfetch.py +0 -0
  54. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zpteam.py +0 -0
  55. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch/zpteamfetch.py +0 -0
  56. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch.egg-info/SOURCES.txt +0 -0
  57. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch.egg-info/dependency_links.txt +0 -0
  58. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch.egg-info/entry_points.txt +0 -0
  59. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zpdatafetch.egg-info/top_level.txt +0 -0
  60. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zrdatafetch/__init__.py +0 -0
  61. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zrdatafetch/cli.py +0 -0
  62. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zrdatafetch/config.py +0 -0
  63. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zrdatafetch/logging_config.py +0 -0
  64. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zrdatafetch/rate_limiter.py +0 -0
  65. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zrdatafetch/zr_utils.py +0 -0
  66. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zrdatafetch/zrraceresult.py +0 -0
  67. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zrdatafetch/zrresultfetch.py +0 -0
  68. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zrdatafetch/zrrider.py +0 -0
  69. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zrdatafetch/zrriderfetch.py +0 -0
  70. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zrdatafetch/zrteamfetch.py +0 -0
  71. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zrdatafetch/zrteamroster.py +0 -0
  72. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zsdatafetch/__init__.py +0 -0
  73. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zsdatafetch/cli.py +0 -0
  74. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zsdatafetch/logging_config.py +0 -0
  75. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zsdatafetch/models/__init__.py +0 -0
  76. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zsdatafetch/models/component.py +0 -0
  77. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zsdatafetch/models/incident.py +0 -0
  78. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zsdatafetch/models/maintenance.py +0 -0
  79. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zsdatafetch/models/page.py +0 -0
  80. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zsdatafetch/models/status.py +0 -0
  81. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zsdatafetch/models/summary.py +0 -0
  82. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zsdatafetch/zsincidentfetch.py +0 -0
  83. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zsdatafetch/zsmaintenancefetch.py +0 -0
  84. {zpdatafetch-2.2.6 → zpdatafetch-2.3.0}/src/zsdatafetch/zssummaryfetch.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zpdatafetch
3
- Version: 2.2.6
3
+ Version: 2.3.0
4
4
  Summary: A package for fetching data from Zwiftpower and Zwiftracing.app
5
5
  Author-email: Doug Morris <doug@mhost.com>
6
6
  License-Expression: MIT
@@ -14,7 +14,7 @@ License-File: LICENSE
14
14
  Requires-Dist: anyio>=4.4.0
15
15
  Requires-Dist: beautifulsoup4>=4.12.3
16
16
  Requires-Dist: cryptography>=46.0.7
17
- Requires-Dist: httpx>=0.28.0
17
+ Requires-Dist: httpx2>=2.9.1
18
18
  Requires-Dist: idna>=3.16
19
19
  Requires-Dist: keyring>=25.2.0
20
20
  Requires-Dist: keyrings-alt
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "zpdatafetch"
3
- version = "2.2.6"
3
+ version = "2.3.0"
4
4
  description = "A package for fetching data from Zwiftpower and Zwiftracing.app"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -20,11 +20,9 @@ dependencies = [
20
20
  "anyio>=4.4.0",
21
21
  "beautifulsoup4>=4.12.3",
22
22
  "cryptography>=46.0.7",
23
- # Note: httpx 0.28.0 depends on httpcore 1.1.x which has a compatibility
24
- # issue with Python 3.14's typing.Union. This causes test failures on 3.14.
25
- # Workaround: test_shared_client_connection_pooling is marked as xfail on 3.14.
26
- # This will be resolved when httpx 0.29.0+ is released (depends on httpcore 1.2.0+).
27
- "httpx>=0.28.0",
23
+ # httpx2: Pydantic's maintained continuation of httpx, built on httpcore2,
24
+ # which resolves the httpcore 1.1.x / Python 3.14 typing.Union issue.
25
+ "httpx2>=2.9.1",
28
26
  "idna>=3.16",
29
27
  "keyring>=25.2.0",
30
28
  "keyrings-alt",
@@ -15,7 +15,7 @@ from abc import ABC, abstractmethod
15
15
  from typing import Any
16
16
 
17
17
  import anyio
18
- import httpx
18
+ import httpx2
19
19
 
20
20
  from shared.exceptions import NetworkError
21
21
 
@@ -25,21 +25,21 @@ from shared.exceptions import NetworkError
25
25
 
26
26
 
27
27
  def fetch_with_retry_sync(
28
- client: httpx.Client,
28
+ client: httpx2.Client,
29
29
  url: str,
30
30
  method: str = 'GET',
31
31
  max_retries: int = 3,
32
32
  backoff_factor: float = 1.0,
33
33
  logger: logging.Logger | None = None,
34
34
  **kwargs: Any,
35
- ) -> httpx.Response:
35
+ ) -> httpx2.Response:
36
36
  """Fetch URL with exponential backoff retry logic (sync variant).
37
37
 
38
38
  Retries on transient errors (connection errors, timeouts, 5xx errors)
39
39
  but not on client errors (4xx).
40
40
 
41
41
  Args:
42
- client: httpx.Client instance
42
+ client: httpx2.Client instance
43
43
  url: URL to fetch
44
44
  method: HTTP method (default: 'GET')
45
45
  max_retries: Maximum number of retry attempts (default: 3)
@@ -48,7 +48,7 @@ def fetch_with_retry_sync(
48
48
  **kwargs: Additional arguments to pass to client.request()
49
49
 
50
50
  Returns:
51
- httpx.Response: The successful response
51
+ httpx2.Response: The successful response
52
52
 
53
53
  Raises:
54
54
  NetworkError: If all retries are exhausted
@@ -64,7 +64,7 @@ def fetch_with_retry_sync(
64
64
  response = client.request(method, url, **kwargs)
65
65
  response.raise_for_status()
66
66
  return response
67
- except (httpx.ConnectError, httpx.TimeoutException) as e:
67
+ except (httpx2.ConnectError, httpx2.TimeoutException) as e:
68
68
  last_exception = e
69
69
  if attempt == max_retries - 1:
70
70
  break
@@ -74,7 +74,7 @@ def fetch_with_retry_sync(
74
74
  f'Retrying in {wait_time:.1f}s...',
75
75
  )
76
76
  time.sleep(wait_time)
77
- except httpx.HTTPStatusError as e:
77
+ except httpx2.HTTPStatusError as e:
78
78
  if 500 <= e.response.status_code < 600:
79
79
  last_exception = e
80
80
  if attempt == max_retries - 1:
@@ -88,7 +88,7 @@ def fetch_with_retry_sync(
88
88
  else:
89
89
  # Client errors (4xx) - don't retry, re-raise for caller to handle
90
90
  raise
91
- except httpx.RequestError as e:
91
+ except httpx2.RequestError as e:
92
92
  last_exception = e
93
93
  if attempt == max_retries - 1:
94
94
  break
@@ -109,21 +109,21 @@ def fetch_with_retry_sync(
109
109
 
110
110
 
111
111
  async def fetch_with_retry_async(
112
- client: httpx.AsyncClient,
112
+ client: httpx2.AsyncClient,
113
113
  url: str,
114
114
  method: str = 'GET',
115
115
  max_retries: int = 3,
116
116
  backoff_factor: float = 1.0,
117
117
  logger: logging.Logger | None = None,
118
118
  **kwargs: Any,
119
- ) -> httpx.Response:
119
+ ) -> httpx2.Response:
120
120
  """Fetch URL with exponential backoff retry logic (async variant).
121
121
 
122
122
  Async version using anyio.sleep() for compatibility with both asyncio
123
123
  and trio.
124
124
 
125
125
  Args:
126
- client: httpx.AsyncClient instance
126
+ client: httpx2.AsyncClient instance
127
127
  url: URL to fetch
128
128
  method: HTTP method (default: 'GET')
129
129
  max_retries: Maximum number of retry attempts (default: 3)
@@ -132,7 +132,7 @@ async def fetch_with_retry_async(
132
132
  **kwargs: Additional arguments to pass to client.request()
133
133
 
134
134
  Returns:
135
- httpx.Response: The successful response
135
+ httpx2.Response: The successful response
136
136
 
137
137
  Raises:
138
138
  NetworkError: If all retries are exhausted
@@ -148,7 +148,7 @@ async def fetch_with_retry_async(
148
148
  response = await client.request(method, url, **kwargs)
149
149
  response.raise_for_status()
150
150
  return response
151
- except (httpx.ConnectError, httpx.TimeoutException) as e:
151
+ except (httpx2.ConnectError, httpx2.TimeoutException) as e:
152
152
  last_exception = e
153
153
  if attempt == max_retries - 1:
154
154
  break
@@ -158,7 +158,7 @@ async def fetch_with_retry_async(
158
158
  f'Retrying in {wait_time:.1f}s...',
159
159
  )
160
160
  await anyio.sleep(wait_time)
161
- except httpx.HTTPStatusError as e:
161
+ except httpx2.HTTPStatusError as e:
162
162
  if 500 <= e.response.status_code < 600:
163
163
  last_exception = e
164
164
  if attempt == max_retries - 1:
@@ -172,7 +172,7 @@ async def fetch_with_retry_async(
172
172
  else:
173
173
  # Client errors (4xx) - don't retry, re-raise for caller to handle
174
174
  raise
175
- except httpx.RequestError as e:
175
+ except httpx2.RequestError as e:
176
176
  last_exception = e
177
177
  if attempt == max_retries - 1:
178
178
  break
@@ -204,8 +204,8 @@ class BaseHTTPClient(ABC):
204
204
  Subclasses implement hooks for package-specific behavior.
205
205
  """
206
206
 
207
- _client: httpx.Client | None = None
208
- _shared_client: httpx.Client | None = None
207
+ _client: httpx2.Client | None = None
208
+ _shared_client: httpx2.Client | None = None
209
209
  _owns_client: bool = False
210
210
 
211
211
  # ----------------------------------------------------------------------------
@@ -213,13 +213,13 @@ class BaseHTTPClient(ABC):
213
213
  # ----------------------------------------------------------------------------
214
214
 
215
215
  @abstractmethod
216
- def _create_client(self) -> httpx.Client:
216
+ def _create_client(self) -> httpx2.Client:
217
217
  """Create and configure an HTTP client.
218
218
 
219
219
  Subclasses implement this to provide package-specific client configuration.
220
220
 
221
221
  Returns:
222
- Configured httpx.Client instance
222
+ Configured httpx2.Client instance
223
223
  """
224
224
 
225
225
  def _before_request(
@@ -241,7 +241,7 @@ class BaseHTTPClient(ABC):
241
241
  **kwargs: Additional request parameters
242
242
  """
243
243
 
244
- def _after_request(self, response: httpx.Response) -> None:
244
+ def _after_request(self, response: httpx2.Response) -> None:
245
245
  """Hook called after receiving a response.
246
246
 
247
247
  Default: no-op. Override in subclasses for post-response operations like:
@@ -266,7 +266,7 @@ class BaseHTTPClient(ABC):
266
266
  # SHARED IMPLEMENTATIONS
267
267
  # ----------------------------------------------------------------------------
268
268
 
269
- def init_client(self, client: httpx.Client | None = None) -> None:
269
+ def init_client(self, client: httpx2.Client | None = None) -> None:
270
270
  """Initialize or replace the HTTP client.
271
271
 
272
272
  Three-way logic:
@@ -275,7 +275,7 @@ class BaseHTTPClient(ABC):
275
275
  3. Else create new client via _create_client()
276
276
 
277
277
  Args:
278
- client: Optional httpx.Client instance to use
278
+ client: Optional httpx2.Client instance to use
279
279
  """
280
280
  if client:
281
281
  self._client = client
@@ -327,8 +327,8 @@ class AsyncBaseHTTPClient(ABC):
327
327
  Async version of BaseHTTPClient with same pattern but async methods.
328
328
  """
329
329
 
330
- _client: httpx.AsyncClient | None = None
331
- _shared_client: httpx.AsyncClient | None = None
330
+ _client: httpx2.AsyncClient | None = None
331
+ _shared_client: httpx2.AsyncClient | None = None
332
332
  _owns_client: bool = False
333
333
 
334
334
  # ----------------------------------------------------------------------------
@@ -336,11 +336,11 @@ class AsyncBaseHTTPClient(ABC):
336
336
  # ----------------------------------------------------------------------------
337
337
 
338
338
  @abstractmethod
339
- async def _create_client(self) -> httpx.AsyncClient:
339
+ async def _create_client(self) -> httpx2.AsyncClient:
340
340
  """Create and configure an async HTTP client.
341
341
 
342
342
  Args:
343
- Returns: Configured httpx.AsyncClient instance
343
+ Returns: Configured httpx2.AsyncClient instance
344
344
  """
345
345
 
346
346
  async def _before_request(
@@ -354,7 +354,7 @@ class AsyncBaseHTTPClient(ABC):
354
354
  Default: no-op. Override for pre-request operations.
355
355
  """
356
356
 
357
- async def _after_request(self, response: httpx.Response) -> None:
357
+ async def _after_request(self, response: httpx2.Response) -> None:
358
358
  """Hook called after receiving a response.
359
359
 
360
360
  Default: no-op. Override for post-response operations.
@@ -370,7 +370,7 @@ class AsyncBaseHTTPClient(ABC):
370
370
  # SHARED IMPLEMENTATIONS
371
371
  # ----------------------------------------------------------------------------
372
372
 
373
- async def init_client(self, client: httpx.AsyncClient | None = None) -> None:
373
+ async def init_client(self, client: httpx2.AsyncClient | None = None) -> None:
374
374
  """Initialize or replace the async HTTP client.
375
375
 
376
376
  Three-way logic:
@@ -379,7 +379,7 @@ class AsyncBaseHTTPClient(ABC):
379
379
  3. Else create new client via _create_client()
380
380
 
381
381
  Args:
382
- client: Optional httpx.AsyncClient instance to use
382
+ client: Optional httpx2.AsyncClient instance to use
383
383
  """
384
384
  if client:
385
385
  self._client = client
@@ -6,7 +6,7 @@ Provides access to activity history from Zwift's unofficial API.
6
6
  import json
7
7
  from typing import Any
8
8
 
9
- import httpx
9
+ import httpx2
10
10
 
11
11
  from shared.exceptions import ConfigError, NetworkError
12
12
  from shared.json_helpers import parse_json_safe
@@ -110,7 +110,7 @@ class ZwiftActivity:
110
110
  params = {'start': start, 'limit': limit}
111
111
 
112
112
  try:
113
- with httpx.Client() as client:
113
+ with httpx2.Client() as client:
114
114
  response = client.get(url, headers=headers, params=params, timeout=30.0)
115
115
 
116
116
  if response.status_code == 404:
@@ -130,11 +130,11 @@ class ZwiftActivity:
130
130
  f'Successfully fetched {len(self.activities)} activities for rider {rider_id}',
131
131
  )
132
132
 
133
- except httpx.TimeoutException as e:
133
+ except httpx2.TimeoutException as e:
134
134
  raise NetworkError(
135
135
  f'Request timed out fetching activities for rider {rider_id}: {e}',
136
136
  ) from e
137
- except httpx.HTTPError as e:
137
+ except httpx2.HTTPError as e:
138
138
  raise NetworkError(
139
139
  f'Network error fetching activities for rider {rider_id}: {e}',
140
140
  ) from e
@@ -188,7 +188,7 @@ class ZwiftActivity:
188
188
 
189
189
  # Fetch all activities
190
190
  results = {}
191
- with httpx.Client() as client:
191
+ with httpx2.Client() as client:
192
192
  for rider_id in rider_ids:
193
193
  try:
194
194
  url = f'{cls.BASE_URL}/api/profiles/{rider_id}/activities/'
@@ -7,7 +7,7 @@ token acquisition, storage, and automatic refresh.
7
7
  import time
8
8
  from typing import Any
9
9
 
10
- import httpx
10
+ import httpx2
11
11
 
12
12
  from shared.exceptions import AuthenticationError, NetworkError
13
13
  from zdatafetch.logging_config import get_logger
@@ -75,7 +75,7 @@ class ZwiftAuth:
75
75
  }
76
76
 
77
77
  try:
78
- with httpx.Client() as client:
78
+ with httpx2.Client() as client:
79
79
  response = client.post(self.AUTH_URL, data=data, timeout=30.0)
80
80
 
81
81
  if response.status_code == 401:
@@ -91,9 +91,9 @@ class ZwiftAuth:
91
91
  f'Authentication successful (token expires in {self.expires_in}s)',
92
92
  )
93
93
 
94
- except httpx.TimeoutException as e:
94
+ except httpx2.TimeoutException as e:
95
95
  raise NetworkError(f'Authentication request timed out: {e}') from e
96
- except httpx.HTTPError as e:
96
+ except httpx2.HTTPError as e:
97
97
  raise NetworkError(f'Authentication request failed: {e}') from e
98
98
 
99
99
  def _parse_token_response(self, token_data: dict[str, Any]) -> None:
@@ -162,7 +162,7 @@ class ZwiftAuth:
162
162
  }
163
163
 
164
164
  try:
165
- with httpx.Client() as client:
165
+ with httpx2.Client() as client:
166
166
  response = client.post(self.AUTH_URL, data=data, timeout=30.0)
167
167
 
168
168
  if response.status_code == 401:
@@ -178,9 +178,9 @@ class ZwiftAuth:
178
178
 
179
179
  logger.debug('Token refreshed successfully')
180
180
 
181
- except httpx.TimeoutException as e:
181
+ except httpx2.TimeoutException as e:
182
182
  raise NetworkError(f'Token refresh request timed out: {e}') from e
183
- except httpx.HTTPError as e:
183
+ except httpx2.HTTPError as e:
184
184
  raise NetworkError(f'Token refresh request failed: {e}') from e
185
185
 
186
186
  def is_authenticated(self) -> bool:
@@ -6,7 +6,7 @@ Provides access to follower/followee relationship data from Zwift's unofficial A
6
6
  import json
7
7
  from typing import Any
8
8
 
9
- import httpx
9
+ import httpx2
10
10
 
11
11
  from shared.exceptions import ConfigError, NetworkError
12
12
  from shared.json_helpers import parse_json_safe
@@ -112,7 +112,7 @@ class ZwiftFollowers:
112
112
  raw_data = {}
113
113
 
114
114
  try:
115
- with httpx.Client() as client:
115
+ with httpx2.Client() as client:
116
116
  # Fetch followers
117
117
  if include_followers:
118
118
  url = f'{self.BASE_URL}/api/profiles/{rider_id}/followers'
@@ -154,11 +154,11 @@ class ZwiftFollowers:
154
154
  self._raw = json.dumps(self._fetched, indent=2)
155
155
  logger.info(f'Successfully fetched follower data for rider {rider_id}')
156
156
 
157
- except httpx.TimeoutException as e:
157
+ except httpx2.TimeoutException as e:
158
158
  raise NetworkError(
159
159
  f'Request timed out fetching follower data for rider {rider_id}: {e}',
160
160
  ) from e
161
- except httpx.HTTPError as e:
161
+ except httpx2.HTTPError as e:
162
162
  raise NetworkError(
163
163
  f'Network error fetching follower data for rider {rider_id}: {e}',
164
164
  ) from e
@@ -218,7 +218,7 @@ class ZwiftFollowers:
218
218
 
219
219
  # Fetch all follower data
220
220
  results = {}
221
- with httpx.Client() as client:
221
+ with httpx2.Client() as client:
222
222
  for rider_id in rider_ids:
223
223
  try:
224
224
  raw_data = {}
@@ -7,7 +7,7 @@ including demographics, statistics, and connected services.
7
7
  import json
8
8
  from typing import Any
9
9
 
10
- import httpx
10
+ import httpx2
11
11
 
12
12
  from shared.exceptions import ConfigError, NetworkError
13
13
  from shared.json_helpers import parse_json_safe
@@ -199,7 +199,7 @@ class ZwiftProfile:
199
199
  headers = {'Authorization': f'Bearer {token}', 'Accept': 'application/json'}
200
200
 
201
201
  try:
202
- with httpx.Client() as client:
202
+ with httpx2.Client() as client:
203
203
  response = client.get(url, headers=headers, timeout=30.0)
204
204
 
205
205
  if response.status_code == 404:
@@ -217,11 +217,11 @@ class ZwiftProfile:
217
217
  self._parse_response()
218
218
  logger.info(f'Successfully fetched profile for rider {rider_id}')
219
219
 
220
- except httpx.TimeoutException as e:
220
+ except httpx2.TimeoutException as e:
221
221
  raise NetworkError(
222
222
  f'Request timed out fetching profile for rider {rider_id}: {e}',
223
223
  ) from e
224
- except httpx.HTTPError as e:
224
+ except httpx2.HTTPError as e:
225
225
  raise NetworkError(
226
226
  f'Network error fetching profile for rider {rider_id}: {e}',
227
227
  ) from e
@@ -268,7 +268,7 @@ class ZwiftProfile:
268
268
 
269
269
  # Fetch all profiles
270
270
  results = {}
271
- with httpx.Client() as client:
271
+ with httpx2.Client() as client:
272
272
  for rider_id in rider_ids:
273
273
  try:
274
274
  url = f'{cls.BASE_URL}/api/profiles/{rider_id}'
@@ -7,7 +7,7 @@ fetching RideOns received on activities and giving RideOns.
7
7
  import json
8
8
  from typing import Any
9
9
 
10
- import httpx
10
+ import httpx2
11
11
 
12
12
  from shared.exceptions import ConfigError, NetworkError
13
13
  from shared.json_helpers import parse_json_safe
@@ -109,7 +109,7 @@ class ZwiftRideOns:
109
109
  url = f'{self.BASE_URL}/api/profiles/{rider_id}/activities/{activity_id}/rideons'
110
110
 
111
111
  try:
112
- with httpx.Client() as client:
112
+ with httpx2.Client() as client:
113
113
  response = client.get(url, headers=headers, timeout=30.0)
114
114
 
115
115
  if response.status_code == 404:
@@ -131,11 +131,11 @@ class ZwiftRideOns:
131
131
  f'Successfully fetched RideOns for activity {activity_id} (rider {rider_id})',
132
132
  )
133
133
 
134
- except httpx.TimeoutException as e:
134
+ except httpx2.TimeoutException as e:
135
135
  raise NetworkError(
136
136
  f'Request timed out fetching RideOns for activity {activity_id}: {e}',
137
137
  ) from e
138
- except httpx.HTTPError as e:
138
+ except httpx2.HTTPError as e:
139
139
  raise NetworkError(
140
140
  f'Network error fetching RideOns for activity {activity_id}: {e}',
141
141
  ) from e
@@ -187,7 +187,7 @@ class ZwiftRideOns:
187
187
 
188
188
  # Fetch all RideOns
189
189
  results = {}
190
- with httpx.Client() as client:
190
+ with httpx2.Client() as client:
191
191
  for rider_id, activity_id in activity_tuples:
192
192
  try:
193
193
  url = f'{cls.BASE_URL}/api/profiles/{rider_id}/activities/{activity_id}/rideons'
@@ -261,7 +261,7 @@ class ZwiftRideOns:
261
261
  url = f'{ZwiftRideOns.BASE_URL}/api/profiles/{rider_id}/activities/{activity_id}/rideon'
262
262
 
263
263
  try:
264
- with httpx.Client() as client:
264
+ with httpx2.Client() as client:
265
265
  response = client.post(url, headers=headers, timeout=30.0)
266
266
 
267
267
  if response.status_code in (200, 201, 204):
@@ -280,12 +280,12 @@ class ZwiftRideOns:
280
280
  )
281
281
  return False
282
282
 
283
- except httpx.TimeoutException as e:
283
+ except httpx2.TimeoutException as e:
284
284
  logger.error(
285
285
  f'Request timed out giving RideOn to activity {activity_id}: {e}',
286
286
  )
287
287
  return False
288
- except httpx.HTTPError as e:
288
+ except httpx2.HTTPError as e:
289
289
  logger.error(
290
290
  f'Network error giving RideOn to activity {activity_id}: {e}',
291
291
  )
@@ -6,7 +6,7 @@ Provides access to riders currently in a specific world from Zwift's unofficial
6
6
  import json
7
7
  from typing import Any
8
8
 
9
- import httpx
9
+ import httpx2
10
10
 
11
11
  from shared.exceptions import ConfigError, NetworkError
12
12
  from shared.json_helpers import parse_json_safe
@@ -99,7 +99,7 @@ class ZwiftRidersInWorld:
99
99
  url = f'{self.BASE_URL}/relay/worlds/{world_id}'
100
100
 
101
101
  try:
102
- with httpx.Client() as client:
102
+ with httpx2.Client() as client:
103
103
  response = client.get(url, headers=headers, timeout=30.0)
104
104
 
105
105
  if response.status_code == 404:
@@ -119,11 +119,11 @@ class ZwiftRidersInWorld:
119
119
  f'Successfully fetched {len(self.riders)} riders in world {world_id}',
120
120
  )
121
121
 
122
- except httpx.TimeoutException as e:
122
+ except httpx2.TimeoutException as e:
123
123
  raise NetworkError(
124
124
  f'Request timed out fetching riders for world {world_id}: {e}',
125
125
  ) from e
126
- except httpx.HTTPError as e:
126
+ except httpx2.HTTPError as e:
127
127
  raise NetworkError(
128
128
  f'Network error fetching riders for world {world_id}: {e}',
129
129
  ) from e
@@ -195,7 +195,7 @@ class ZwiftRidersInWorld:
195
195
 
196
196
  # Fetch all riders
197
197
  results = {}
198
- with httpx.Client() as client:
198
+ with httpx2.Client() as client:
199
199
  for world_id in world_ids:
200
200
  try:
201
201
  url = f'{cls.BASE_URL}/relay/worlds/{world_id}'
@@ -6,7 +6,7 @@ Provides access to active worlds from Zwift's unofficial API.
6
6
  import json
7
7
  from typing import Any
8
8
 
9
- import httpx
9
+ import httpx2
10
10
 
11
11
  from shared.exceptions import ConfigError, NetworkError
12
12
  from shared.json_helpers import parse_json_safe
@@ -129,7 +129,7 @@ class ZwiftWorlds:
129
129
  url = f'{self.BASE_URL}/relay/worlds'
130
130
 
131
131
  try:
132
- with httpx.Client() as client:
132
+ with httpx2.Client() as client:
133
133
  response = client.get(url, headers=headers, timeout=30.0)
134
134
 
135
135
  if response.status_code != 200:
@@ -144,9 +144,9 @@ class ZwiftWorlds:
144
144
  self._raw = json.dumps(self._fetched, indent=2)
145
145
  logger.info(f'Successfully fetched {len(self.worlds)} active worlds')
146
146
 
147
- except httpx.TimeoutException as e:
147
+ except httpx2.TimeoutException as e:
148
148
  raise NetworkError(f'Request timed out fetching worlds: {e}') from e
149
- except httpx.HTTPError as e:
149
+ except httpx2.HTTPError as e:
150
150
  raise NetworkError(f'Network error fetching worlds: {e}') from e
151
151
 
152
152
  def _parse_response(self, raw_json: str) -> None: