diff --git a/pyproject.toml b/pyproject.toml index e87f0e3..21054f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,10 +18,11 @@ classifiers = [ ] requires-python = ">=3.10" dependencies = [ + "requests", "asyncio-atexit>=1.0.1", "deprecated>=1.2.14", "mss>=9.0.2", - "websockets>=13.1,<14", + "websockets>=13.1", ] [build-system] diff --git a/zendriver/cdp/network.py b/zendriver/cdp/network.py index 943e1fd..bbcb42d 100644 --- a/zendriver/cdp/network.py +++ b/zendriver/cdp/network.py @@ -1424,6 +1424,9 @@ def to_json(self) -> T_JSON_DICT: @classmethod def from_json(cls, json: T_JSON_DICT) -> CookiePartitionKey: + if isinstance(json, str): + # Some chrome versions return partitionKey as string + return cls(json, False) return cls( top_level_site=str(json["topLevelSite"]), has_cross_site_ancestor=bool(json["hasCrossSiteAncestor"]), diff --git a/zendriver/core/browser.py b/zendriver/core/browser.py index 8c8cd93..cebdf9e 100644 --- a/zendriver/core/browser.py +++ b/zendriver/core/browser.py @@ -276,6 +276,11 @@ async def get( await connection.sleep(0.25) return connection + async def communicate(self) -> tuple[bytes, bytes]: + if self._process is None: + raise ValueError("Browser process not running") + return await self._process.communicate() + async def start(self) -> Browser: """launches the actual browser""" if not self: @@ -358,6 +363,12 @@ async def start(self) -> Browser: break if not self.info: + stderr = None + try : + _, stderr_bytes = await self._process.communicate() + stderr = stderr_bytes.decode()[:1000] + except Exception : + pass raise Exception( ( """ @@ -366,7 +377,8 @@ async def start(self) -> Browser: --------------------- One of the causes could be when you are running as root. In that case you need to pass no_sandbox=True - """ + """ + + ("Browser error output:" + stderr if stderr else '') ) ) @@ -702,8 +714,6 @@ async def save(self, file: PathLike = ".session.dat", pattern: str = ".*"): # return # if not connection.websocket: # return - # if connection.websocket.closed: - # return cookies = await self.get_all(requests_cookie_format=False) included_cookies = [] for cookie in cookies: diff --git a/zendriver/core/connection.py b/zendriver/core/connection.py index a0ee46c..891e5d8 100644 --- a/zendriver/core/connection.py +++ b/zendriver/core/connection.py @@ -201,7 +201,7 @@ def __init__( ): super().__init__() self._target = target - self.__count__ = itertools.count(0) + self._cdp_id_generator = itertools.count(0) self._owner = _owner self.websocket_url: str = websocket_url self.websocket = None @@ -230,7 +230,7 @@ def target(self, target: cdp.target.TargetInfo): def closed(self): if not self.websocket: return True - return self.websocket.closed + return (not self.websocket) def add_handler( self, @@ -282,7 +282,7 @@ async def aopen(self, **kw): :return: """ - if not self.websocket or self.websocket.closed: + if not self.websocket: try: self.websocket = await websockets.connect( self.websocket_url, @@ -308,11 +308,12 @@ async def aclose(self): """ closes the websocket connection. should not be called manually by users. """ - if self.websocket and not self.websocket.closed: + if self.websocket: if self.listener and self.listener.running: self.listener.cancel() self.enabled_domains.clear() await self.websocket.close() + self.websocket = None logger.debug("\n❌ closed websocket connection to %s", self.websocket_url) async def sleep(self, t: Union[int, float] = 0.25): @@ -411,7 +412,7 @@ async def send( :return: """ await self.aopen() - if not self.websocket or self.closed: + if not self.websocket: return if self._owner: browser = self._owner @@ -425,9 +426,7 @@ async def send( try: tx = Transaction(cdp_obj) tx.connection = self - if not self.mapper: - self.__count__ = itertools.count(0) - tx.id = next(self.__count__) + tx.id = next(self._cdp_id_generator) self.mapper.update({tx.id: tx}) if not _is_update: await self._register_handlers() @@ -603,15 +602,17 @@ async def listener_loop(self): # breathe # await asyncio.sleep(self.time_before_considered_idle / 10) continue - except (Exception,) as e: - # break on any other exception - # which is mostly socket is closed or does not exist - # or is not allowed - + except (websockets.exceptions.ConnectionClosedError,) as e: logger.debug( "connection listener exception while reading websocket:\n%s", e ) break + except (Exception,) as e: + # we don't expect here other exceptions, need to debug if it will appear. + logger.exception( + "connection listener exception while reading websocket" + ) + break if not self.running: # if we have been cancelled or otherwise stopped running @@ -646,9 +647,7 @@ async def listener_loop(self): try: event = cdp.util.parse_json_event(message) event_tx = EventTransaction(event) - if not self.connection.mapper: - self.connection.__count__ = itertools.count(0) - event_tx.id = next(self.connection.__count__) + event_tx.id = next(self.connection._cdp_id_generator) self.connection.mapper[event_tx.id] = event_tx except Exception as e: logger.info(