@@ -1263,47 +1263,84 @@ All code examples in this plan follow these guidelines and must be maintained th
12631263
12641264 import logging
12651265 import time
1266- import typing as t
1266+ from typing import Any, Dict, Optional, Union, Tuple, List, TypeVar, cast
12671267 from urllib.parse import urlparse
1268+ import dataclasses
12681269
12691270 import requests
12701271 from requests.exceptions import ConnectionError, Timeout
12711272
1272- from vcspull.exc import NetworkError
1273+ from vcspull.exc import NetworkError, ErrorCode
12731274
12741275 log = logging.getLogger(__name__)
12751276
12761277
1278+ @dataclasses.dataclass
12771279 class RetryStrategy:
12781280 """Strategy for retrying network operations."""
12791281
1280- def __init__(self, max_retries=3, initial_delay=1.0, backoff_factor=2.0):
1281- self.max_retries = max_retries
1282- self.initial_delay = initial_delay
1283- self.backoff_factor = backoff_factor
1282+ max_retries: int = 3
1283+ initial_delay: float = 1.0
1284+ backoff_factor: float = 2.0
1285+
1286+ def get_delay(self, attempt: int) -> float:
1287+ """
1288+ Get delay for a specific retry attempt.
12841289
1285- def get_delay(self, attempt):
1286- """Get delay for a specific retry attempt."""
1290+ Parameters
1291+ ----------
1292+ attempt : int
1293+ Current attempt number (1-based)
1294+
1295+ Returns
1296+ -------
1297+ float
1298+ Delay in seconds
1299+ """
12871300 return self.initial_delay * (self.backoff_factor ** (attempt - 1))
12881301
12891302
1303+ ResponseType = TypeVar('ResponseType')
1304+
1305+
12901306 class NetworkManager:
12911307 """Manager for network operations."""
12921308
1293- def __init__(self, session=None, retry_strategy=None):
1309+ def __init__(
1310+ self,
1311+ *,
1312+ session: Optional[requests.Session] = None,
1313+ retry_strategy: Optional[RetryStrategy] = None
1314+ ) -> None:
1315+ """
1316+ Initialize network manager.
1317+
1318+ Parameters
1319+ ----------
1320+ session : requests.Session, optional
1321+ Session to use for requests
1322+ retry_strategy : RetryStrategy, optional
1323+ Strategy for retrying failed requests
1324+ """
12941325 self.session = session or requests.Session()
12951326 self.retry_strategy = retry_strategy or RetryStrategy()
12961327
1297- def request(self, method, url, **kwargs):
1298- """Perform HTTP request with retry logic.
1328+ def request(
1329+ self,
1330+ method: str,
1331+ url: str,
1332+ **kwargs: Any
1333+ ) -> requests.Response:
1334+ """
1335+ Perform HTTP request with retry logic.
12991336
13001337 Parameters
13011338 ----------
13021339 method : str
13031340 HTTP method (GET, POST, etc.)
13041341 url : str
13051342 URL to request
1306- **kwargs
1343+ **kwargs : Any
13071344 Additional parameters for requests
13081345
13091346 Returns
@@ -1324,7 +1361,7 @@ All code examples in this plan follow these guidelines and must be maintained th
13241361
13251362 # Initialize retry counter
13261363 attempt = 0
1327- last_exception = None
1364+ last_exception: Optional[NetworkError] = None
13281365
13291366 while attempt < max_retries:
13301367 attempt += 1
@@ -1340,7 +1377,8 @@ All code examples in this plan follow these guidelines and must be maintained th
13401377 f"Server error: {response.status_code}",
13411378 url=url,
13421379 status_code=response.status_code,
1343- retry_count=attempt
1380+ retry_count=attempt,
1381+ error_code=ErrorCode.NETWORK_UNREACHABLE
13441382 )
13451383 continue
13461384 elif response.status_code == 429:
@@ -1349,7 +1387,8 @@ All code examples in this plan follow these guidelines and must be maintained th
13491387 "Rate limited",
13501388 url=url,
13511389 status_code=429,
1352- retry_count=attempt
1390+ retry_count=attempt,
1391+ error_code=ErrorCode.RATE_LIMITED
13531392 )
13541393 # Get retry-after header if available
13551394 retry_after = response.headers.get('Retry-After')
@@ -1368,7 +1407,8 @@ All code examples in this plan follow these guidelines and must be maintained th
13681407 raise NetworkError(
13691408 f"Client error: {response.status_code}",
13701409 url=url,
1371- status_code=response.status_code
1410+ status_code=response.status_code,
1411+ error_code=ErrorCode.NETWORK_UNREACHABLE
13721412 )
13731413
13741414 # Success
@@ -1380,7 +1420,11 @@ All code examples in this plan follow these guidelines and must be maintained th
13801420 last_exception = NetworkError(
13811421 f"Network error: {str(e)}",
13821422 url=url,
1383- retry_count=attempt
1423+ retry_count=attempt,
1424+ error_code=(
1425+ ErrorCode.TIMEOUT if isinstance(e, Timeout)
1426+ else ErrorCode.CONNECTION_REFUSED
1427+ )
13841428 )
13851429
13861430 # Wait before retrying
@@ -1393,19 +1437,83 @@ All code examples in this plan follow these guidelines and must be maintained th
13931437 if last_exception:
13941438 raise last_exception
13951439 else:
1396- raise NetworkError(f"Failed after {max_retries} attempts", url=url)
1440+ raise NetworkError(
1441+ f"Failed after {max_retries} attempts",
1442+ url=url,
1443+ error_code=ErrorCode.NETWORK_UNREACHABLE
1444+ )
1445+
1446+ def get(
1447+ self,
1448+ url: str,
1449+ **kwargs: Any
1450+ ) -> requests.Response:
1451+ """
1452+ Perform HTTP GET request.
1453+
1454+ Parameters
1455+ ----------
1456+ url : str
1457+ URL to request
1458+ **kwargs : Any
1459+ Additional parameters for requests
13971460
1398- def get(self, url, **kwargs):
1399- """Perform HTTP GET request."""
1461+ Returns
1462+ -------
1463+ requests.Response
1464+ Response object
1465+ """
14001466 return self.request('GET', url, **kwargs)
14011467
1402- def post(self, url, **kwargs):
1403- """Perform HTTP POST request."""
1468+ def post(
1469+ self,
1470+ url: str,
1471+ **kwargs: Any
1472+ ) -> requests.Response:
1473+ """
1474+ Perform HTTP POST request.
1475+
1476+ Parameters
1477+ ----------
1478+ url : str
1479+ URL to request
1480+ **kwargs : Any
1481+ Additional parameters for requests
1482+
1483+ Returns
1484+ -------
1485+ requests.Response
1486+ Response object
1487+ """
14041488 return self.request('POST', url, **kwargs)
14051489
14061490
1407- def perform_request(url, auth=None, retry_strategy=None, **kwargs):
1408- """Perform HTTP request with configurable retry strategy."""
1491+ def perform_request(
1492+ url: str,
1493+ *,
1494+ auth: Optional[Tuple[str, str]] = None,
1495+ retry_strategy: Optional[RetryStrategy] = None,
1496+ **kwargs: Any
1497+ ) -> requests.Response:
1498+ """
1499+ Perform HTTP request with configurable retry strategy.
1500+
1501+ Parameters
1502+ ----------
1503+ url : str
1504+ URL to request
1505+ auth : Tuple[str, str], optional
1506+ Authentication credentials (username, password)
1507+ retry_strategy : RetryStrategy, optional
1508+ Strategy for retrying failed requests
1509+ **kwargs : Any
1510+ Additional parameters for requests
1511+
1512+ Returns
1513+ -------
1514+ requests.Response
1515+ Response object
1516+ """
14091517 manager = NetworkManager(retry_strategy=retry_strategy)
14101518 return manager.get(url, auth=auth, **kwargs)
14111519 ```
0 commit comments