77import logging
88import pathlib
99import pickle
10+ import re
1011import shutil
1112import urllib .parse
1213import urllib .request
@@ -52,26 +53,26 @@ class Browser:
5253
5354 """
5455
55- _process : asyncio .subprocess .Process
56- _process_pid : int
57- _http : HTTPApi = None
58- _cookies : CookieJar = None
56+ _process : asyncio .subprocess .Process | None
57+ _process_pid : int | None
58+ _http : HTTPApi | None = None
59+ _cookies : CookieJar | None = None
5960
6061 config : Config
61- connection : Connection
62+ connection : Connection | None
6263
6364 @classmethod
6465 async def create (
6566 cls ,
66- config : Config = None ,
67+ config : Config | None = None ,
6768 * ,
68- user_data_dir : PathLike = None ,
69+ user_data_dir : PathLike | None = None ,
6970 headless : bool = False ,
70- browser_executable_path : PathLike = None ,
71- browser_args : List [str ] = None ,
71+ browser_executable_path : PathLike | None = None ,
72+ browser_args : List [str ] | None = None ,
7273 sandbox : bool = True ,
73- host : str = None ,
74- port : int = None ,
74+ host : str | None = None ,
75+ port : int | None = None ,
7576 ** kwargs ,
7677 ) -> Browser :
7778 """
@@ -119,18 +120,21 @@ def __init__(self, config: Config, **kwargs):
119120 self .config = config
120121
121122 self .targets : List = []
122- """current targets (all types"""
123- self .info = None
123+ """current targets (all types) """
124+ self .info : ContraDict | None = None
124125 self ._target = None
125126 self ._process = None
126127 self ._process_pid = None
127128 self ._keep_user_data_dir = None
128129 self ._is_updating = asyncio .Event ()
129- self .connection : Connection = None
130+ self .connection = None
130131 logger .debug ("Session object initialized: %s" % vars (self ))
131132
132133 @property
133134 def websocket_url (self ):
135+ if not self .info :
136+ raise RuntimeError ("Browser not yet started. use await browser.start()" )
137+
134138 return self .info .webSocketDebuggerUrl
135139
136140 @property
@@ -205,7 +209,7 @@ def _handle_target_update(
205209 current_tab .target = target_info
206210
207211 elif isinstance (event , cdp .target .TargetCreated ):
208- target_info : cdp . target . TargetInfo = event .target_info
212+ target_info = event .target_info
209213 from .tab import Tab
210214
211215 new_target = Tab (
@@ -246,6 +250,9 @@ async def get(
246250 :param new_window: open new window
247251 :return: Page
248252 """
253+ if not self .connection :
254+ raise RuntimeError ("Browser not yet started. use await browser.start()" )
255+
249256 if new_tab or new_window :
250257 # creat new target using the browser session
251258 target_id = await self .connection .send (
@@ -264,13 +271,9 @@ async def get(
264271
265272 else :
266273 # first tab from browser.tabs
267- connection : tab .Tab = next (
268- filter (lambda item : item .type_ == "page" , self .targets )
269- )
274+ connection = next (filter (lambda item : item .type_ == "page" , self .targets ))
270275 # use the tab to navigate to new url
271- frame_id , loader_id , * _ = await connection .send (cdp .page .navigate (url ))
272- # update the frame_id on the tab
273- connection .frame_id = frame_id
276+ await connection .send (cdp .page .navigate (url ))
274277 connection .browser = self
275278
276279 await connection .sleep (0.25 )
@@ -279,16 +282,16 @@ async def get(
279282 async def start (self ) -> Browser :
280283 """launches the actual browser"""
281284 if not self :
282- warnings .warn ("use ``await Browser.create()`` to create a new instance" )
283- return
285+ raise ValueError (
286+ "Cannot be called as a class method. Use `await Browser.create()` to create a new instance"
287+ )
284288
285289 if self ._process or self ._process_pid :
286- if self ._process .returncode is not None :
290+ if self ._process and self . _process .returncode is not None :
287291 return await self .create (config = self .config )
288292 warnings .warn ("ignored! this call has no effect when already running." )
289- return
293+ return self
290294
291- # self.config.update(kwargs)
292295 connect_existing = False
293296 if self .config .host is not None and self .config .port is not None :
294297 connect_existing = True
@@ -401,9 +404,8 @@ async def start(self) -> Browser:
401404 self ._handle_target_update
402405 ]
403406 await self .connection .send (cdp .target .set_discover_targets (discover = True ))
404- await self
405- # self.connection.handlers[cdp.inspector.Detached] = [self.stop]
406- # return self
407+ await self .update_targets ()
408+ return self
407409
408410 async def grant_all_permissions (self ):
409411 """
@@ -435,6 +437,9 @@ async def grant_all_permissions(self):
435437 wakeLockSystem
436438 windowManagement
437439 """
440+ if not self .connection :
441+ raise RuntimeError ("Browser not yet started. use await browser.start()" )
442+
438443 permissions = list (cdp .browser .PermissionType )
439444 permissions .remove (cdp .browser .PermissionType .FLASH )
440445 permissions .remove (cdp .browser .PermissionType .CAPTURED_SURFACE_CONTROL )
@@ -454,7 +459,7 @@ async def tile_windows(self, windows=None, max_columns: int = 0):
454459 if not screen or not screen_width or not screen_height :
455460 warnings .warn ("no monitors detected" )
456461 return
457- await self
462+ await self . update_targets ()
458463 distinct_windows = defaultdict (list )
459464
460465 if windows :
@@ -499,6 +504,8 @@ async def tile_windows(self, windows=None, max_columns: int = 0):
499504 return grid
500505
501506 async def _get_targets (self ) -> List [cdp .target .TargetInfo ]:
507+ if not self .connection :
508+ raise RuntimeError ("Browser not yet started. use await browser.start()" )
502509 info = await self .connection .send (cdp .target .get_targets (), _is_update = True )
503510 return info
504511
@@ -557,6 +564,9 @@ def __next__(self):
557564 del self ._i
558565
559566 async def stop (self ):
567+ if not self .connection or not self ._process :
568+ return
569+
560570 await self .connection .aclose ()
561571 logger .debug ("closed the connection" )
562572
@@ -598,10 +608,6 @@ async def _cleanup_temporary_profile(self) -> None:
598608 await asyncio .sleep (0.15 )
599609 continue
600610
601- def __await__ (self ):
602- # return ( asyncio.sleep(0)).__await__()
603- return self .update_targets ().__await__ ()
604-
605611 def __del__ (self ):
606612 pass
607613
@@ -623,14 +629,17 @@ async def get_all(
623629 :rtype:
624630
625631 """
626- connection = None
632+ connection : Connection | None = None
627633 for tab_ in self ._browser .tabs :
628634 if tab_ .closed :
629635 continue
630636 connection = tab_
631637 break
632638 else :
633639 connection = self ._browser .connection
640+ if not connection :
641+ raise RuntimeError ("Browser not yet started. use await browser.start()" )
642+
634643 cookies = await connection .send (cdp .storage .get_cookies ())
635644 if requests_cookie_format :
636645 import requests .cookies
@@ -657,14 +666,17 @@ async def set_all(self, cookies: List[cdp.network.CookieParam]):
657666 :return:
658667 :rtype:
659668 """
660- connection = None
669+ connection : Connection | None = None
661670 for tab_ in self ._browser .tabs :
662671 if tab_ .closed :
663672 continue
664673 connection = tab_
665674 break
666675 else :
667676 connection = self ._browser .connection
677+ if not connection :
678+ raise RuntimeError ("Browser not yet started. use await browser.start()" )
679+
668680 await connection .send (cdp .storage .set_cookies (cookies ))
669681
670682 async def save (self , file : PathLike = ".session.dat" , pattern : str = ".*" ):
@@ -685,18 +697,19 @@ async def save(self, file: PathLike = ".session.dat", pattern: str = ".*"):
685697 :return:
686698 :rtype:
687699 """
688- import re
689-
690- pattern = re .compile (pattern )
700+ compiled_pattern = re .compile (pattern )
691701 save_path = pathlib .Path (file ).resolve ()
692- connection = None
702+ connection : Connection | None = None
693703 for tab_ in self ._browser .tabs :
694704 if tab_ .closed :
695705 continue
696706 connection = tab_
697707 break
698708 else :
699709 connection = self ._browser .connection
710+ if not connection :
711+ raise RuntimeError ("Browser not yet started. use await browser.start()" )
712+
700713 cookies = await connection .send (cdp .storage .get_cookies ())
701714 # if not connection:
702715 # return
@@ -707,10 +720,10 @@ async def save(self, file: PathLike = ".session.dat", pattern: str = ".*"):
707720 cookies = await self .get_all (requests_cookie_format = False )
708721 included_cookies = []
709722 for cookie in cookies :
710- for match in pattern .finditer (str (cookie .__dict__ )):
723+ for match in compiled_pattern .finditer (str (cookie .__dict__ )):
711724 logger .debug (
712725 "saved cookie for matching pattern '%s' => (%s: %s)" ,
713- pattern .pattern ,
726+ compiled_pattern .pattern ,
714727 cookie .name ,
715728 cookie .value ,
716729 )
@@ -738,16 +751,16 @@ async def load(self, file: PathLike = ".session.dat", pattern: str = ".*"):
738751 """
739752 import re
740753
741- pattern = re .compile (pattern )
754+ compiled_pattern = re .compile (pattern )
742755 save_path = pathlib .Path (file ).resolve ()
743756 cookies = pickle .load (save_path .open ("r+b" ))
744757 included_cookies = []
745758 for cookie in cookies :
746- for match in pattern .finditer (str (cookie .__dict__ )):
759+ for match in compiled_pattern .finditer (str (cookie .__dict__ )):
747760 included_cookies .append (cookie )
748761 logger .debug (
749762 "loaded cookie for matching pattern '%s' => (%s: %s)" ,
750- pattern .pattern ,
763+ compiled_pattern .pattern ,
751764 cookie .name ,
752765 cookie .value ,
753766 )
@@ -763,14 +776,17 @@ async def clear(self):
763776 :return:
764777 :rtype:
765778 """
766- connection = None
779+ connection : Connection | None = None
767780 for tab_ in self ._browser .tabs :
768781 if tab_ .closed :
769782 continue
770783 connection = tab_
771784 break
772785 else :
773786 connection = self ._browser .connection
787+ if not connection :
788+ raise RuntimeError ("Browser not yet started. use await browser.start()" )
789+
774790 await connection .send (cdp .storage .clear_cookies ())
775791
776792
@@ -785,7 +801,7 @@ async def get(self, endpoint: str):
785801 async def post (self , endpoint , data ):
786802 return await self ._request (endpoint , data )
787803
788- async def _request (self , endpoint , method : str = "get" , data : dict = None ):
804+ async def _request (self , endpoint , method : str = "get" , data : dict | None = None ):
789805 url = urllib .parse .urljoin (
790806 self .api , f"json/{ endpoint } " if endpoint else "/json"
791807 )
0 commit comments