def__init__(self,proxies:List[ProxyType],strategy:RotationStrategy=cyclic_rotation,):""" Initialize the proxy rotator. :param proxies: List of proxy URLs or Playwright-style proxy dicts. - String format: "http://proxy1:8080" or "http://user:pass@proxy:8080" - Dict format: {"server": "http://proxy:8080", "username": "user", "password": "pass"} :param strategy: Rotation strategy function. Takes (proxies, current_index) and returns (proxy, next_index). Defaults to cyclic_rotation. """ifnotproxies:raiseValueError("At least one proxy must be provided")ifnotcallable(strategy):raiseTypeError(f"strategy must be callable, got {type(strategy).__name__}")self._strategy=strategyself._lock=Lock()# Validate and store proxiesself._proxies:List[ProxyType]=[]self._proxy_to_index:Dict[str,int]={}# O(1) lookup by unique key (server + username)fori,proxyinenumerate(proxies):ifisinstance(proxy,(str,dict)):ifisinstance(proxy,dict)and"server"notinproxy:raiseValueError("Proxy dict must have a 'server' key")self._proxy_to_index[_get_proxy_key(proxy)]=iself._proxies.append(proxy)else:raiseTypeError(f"Invalid proxy type: {type(proxy)}. Expected str or dict.")self._current_index=0
defget_proxy(self)->ProxyType:"""Get the next proxy according to the rotation strategy."""withself._lock:proxy,self._current_index=self._strategy(self._proxies,self._current_index)returnproxy