Skip to content

Commit

Permalink
0.27rc4
Browse files Browse the repository at this point in the history
  • Loading branch information
UltrafunkAmsterdam committed Mar 12, 2024
1 parent fbf34f4 commit bb0fcaf
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 46 deletions.
20 changes: 11 additions & 9 deletions nodriver/core/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,30 +324,32 @@ async def sleep(self, t: Union[int, float] = 0.25):

async def wait(self, t: Union[int, float] = None):
"""
waits until the event listener
reports idle (which is when no new events had been received in .5 seconds
or, 1 second when in interactive mode)
waits until the event listener reports idle (no new events received in certain timespan).
when `t` is provided, ensures waiting for `t` seconds, no matter what.
:param t:
:type t:
:return:
:rtype:
"""
await self.update_target()

loop = asyncio.get_running_loop()
start_time = loop.time()
try:

await asyncio.wait_for(self.listener.idle.wait(), timeout=t)
if isinstance(t, (int, float)):
await asyncio.wait_for(self.listener.idle.wait(), timeout=t)
while (loop.time() - start_time ) < t:
await asyncio.sleep(.1)
else:
await self.listener.idle.wait()
except asyncio.TimeoutError:
if t is not None:
if isinstance(t, (int, float)):
# explicit time is given, which is now passed
# so bail out early
return
except AttributeError:
# no listener created yet
pass
if t is not None:
await self.sleep(t)

def __getattr__(self, item):
""":meta private:"""
Expand Down
82 changes: 46 additions & 36 deletions nodriver/core/tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,9 @@ async def find(
:type timeout: float,int
"""
loop = asyncio.get_running_loop()
now = loop.time()
start_time = loop.time()

text = text.strip()

item = await self.find_element_by_text(
text, best_match, return_enclosing_element
Expand All @@ -205,7 +207,7 @@ async def find(
item = await self.find_element_by_text(
text, best_match, return_enclosing_element
)
if loop.time() - now > timeout:
if loop.time() - start_time > timeout:
raise asyncio.TimeoutError(
"time ran out while waiting for text: %s" % text
)
Expand All @@ -229,13 +231,15 @@ async def select(
"""
loop = asyncio.get_running_loop()
now = loop.time()
start_time = loop.time()

selector = selector.strip()
item = await self.query_selector(selector)

while not item:
await self
item = await self.query_selector(selector)
if loop.time() - now > timeout:
if loop.time() - start_time > timeout:
raise asyncio.TimeoutError(
"time ran out while waiting for %s" % selector
)
Expand All @@ -260,16 +264,18 @@ async def find_all(
loop = asyncio.get_running_loop()
now = loop.time()

results = await self.find_elements_by_text(text)
while not results:
text = text.strip()
items = await self.find_elements_by_text(text)

while not items:
await self
results = await self.find_elements_by_text(text)
if loop.time() - now > timeout:
raise asyncio.TimeoutError(
"time ran out while waiting for text: %s" % text
)
await self.sleep(0.5)
return results
return items

async def select_all(
self,
Expand All @@ -288,16 +294,18 @@ async def select_all(

loop = asyncio.get_running_loop()
now = loop.time()
results = await self.query_selector_all(selector)
while not results:
selector = selector.strip()

items = await self.query_selector_all(selector)
while not items:
await self
results = await self.query_selector_all(selector)
items = await self.query_selector_all(selector)
if loop.time() - now > timeout:
raise asyncio.TimeoutError(
"time ran out while waiting for %s" % selector
)
await self.sleep(0.5)
return results
return items

async def get(
self, url="chrome://welcome", new_tab: bool = False, new_window: bool = False
Expand Down Expand Up @@ -377,7 +385,7 @@ async def query_selector_all(
raise
if not node_ids:
return []
results = []
items = []

for nid in node_ids:
node = util.filter_recurse(doc, lambda n: n.node_id == nid)
Expand All @@ -386,9 +394,9 @@ async def query_selector_all(
if not node:
continue
elem = element.create(node, self, doc)
results.append(elem)
items.append(elem)

return results
return items

async def query_selector(
self,
Expand All @@ -403,6 +411,7 @@ async def query_selector(
:return:
:rtype:
"""
selector = selector.strip()

if not _node:
doc: cdp.dom.Node = await self.send(cdp.dom.get_document(-1, True))
Expand Down Expand Up @@ -455,7 +464,7 @@ async def find_elements_by_text(
:return:
:rtype:
"""

text = text.strip()
doc = await self.send(cdp.dom.get_document(-1, True))
search_id, nresult = await self.send(cdp.dom.perform_search(text, True))
if nresult:
Expand All @@ -467,7 +476,7 @@ async def find_elements_by_text(

await self.send(cdp.dom.discard_search_results(search_id))

results = []
items = []
for nid in node_ids:
node = util.filter_recurse(doc, lambda n: n.node_id == nid)
if not node:
Expand All @@ -489,13 +498,13 @@ async def find_elements_by_text(
# check if parent actually has a parent and update it to be absolutely sure
await elem.update()

results.append(
items.append(
elem.parent or elem
) # when it really has no parent, use the text node itself
continue
else:
# just add the element itself
results.append(elem)
items.append(elem)

# since we already fetched the entire doc, including shadow and frames
# let's also search through the iframes
Expand All @@ -517,11 +526,11 @@ async def find_elements_by_text(
element.create(text_node, self, iframe_elem.tree)
for text_node in iframe_text_nodes
]
results.extend(
items.extend(
text_node.parent for text_node in iframe_text_elems
)
await self.send(cdp.dom.disable())
return results or []
return items or []

async def find_element_by_text(
self,
Expand All @@ -547,14 +556,14 @@ async def find_element_by_text(
"""
doc = await self.send(cdp.dom.get_document(-1, True))
search_id, nresult = await self.send(cdp.dom.perform_search(text, True))
# if nresult == 0:
# return
text = text.strip()

node_ids = await self.send(cdp.dom.get_search_results(search_id, 0, nresult))
await self.send(cdp.dom.discard_search_results(search_id))

if not node_ids:
node_ids = []
results = []
items = []
for nid in node_ids:
node = util.filter_recurse(doc, lambda n: n.node_id == nid)
try:
Expand All @@ -570,13 +579,13 @@ async def find_element_by_text(
# check if parent actually has a parent and update it to be absolutely sure
await elem.update()

results.append(
items.append(
elem.parent or elem
) # when it really has no parent, use the text node itself
continue
else:
# just add the element itself
results.append(elem)
items.append(elem)

# since we already fetched the entire doc, including shadow and frames
# let's also search through the iframes
Expand All @@ -597,20 +606,20 @@ async def find_element_by_text(
element.create(text_node, self, iframe_elem.tree)
for text_node in iframe_text_nodes
]
results.extend(text_node.parent for text_node in iframe_text_elems)
items.extend(text_node.parent for text_node in iframe_text_elems)
try:
if not results:
if not items:
return
if best_match:
closest_by_length = min(
results, key=lambda el: abs(len(text) - len(el.text_all))
items, key=lambda el: abs(len(text) - len(el.text_all))
)
elem = closest_by_length or results[0]
elem = closest_by_length or items[0]

return elem
else:
# naively just return the first result
for elem in results:
for elem in items:
if elem:
return elem
finally:
Expand Down Expand Up @@ -947,7 +956,10 @@ async def set_window_state(
self, left=0, top=0, width=1280, height=720, state="normal"
):
"""
sets the window size and state.
sets the window size or state.
for state you can provide the full name like minimized, maximized, normal, fullscreen, or
something which leads to either of those, like min, mini, mi, max, ma, maxi, full, fu, no, nor
in case state is set other than "normal", the left, top, width, and height are ignored.
:param left:
Expand Down Expand Up @@ -995,11 +1007,9 @@ async def set_window_state(
if window_state == cdp.browser.WindowState.NORMAL:
bounds = cdp.browser.Bounds(left, top, width, height, window_state)
else:
window_id, current_bounds = await self.get_window()
if current_bounds.window_state != cdp.browser.WindowState.NORMAL:
# min, max, full can only be used when current state == NORMAL
# therefore we first switch to NORMAL
await self.set_window_state(state="normal")
# min, max, full can only be used when current state == NORMAL
# therefore we first switch to NORMAL
await self.set_window_state(state="normal")
bounds = cdp.browser.Bounds(window_state=window_state)

await self.send(cdp.browser.set_window_bounds(window_id, bounds=bounds))
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "nodriver" # Required
version = "0.27rc3" # Required
version = "0.27rc4" # Required
description = """
* Official successor of Undetected Chromedriver
* Can be made to work for for all chromium based browsers.
Expand Down

0 comments on commit bb0fcaf

Please sign in to comment.