pydoll-python 2.15.1__tar.gz → 2.16.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/PKG-INFO +1 -1
  2. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/browser/chromium/base.py +3 -3
  3. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/browser/tab.py +25 -4
  4. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/constants.py +25 -0
  5. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/elements/mixins/find_elements_mixin.py +2 -2
  6. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/elements/web_element.py +24 -0
  7. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pyproject.toml +1 -1
  8. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/LICENSE +0 -0
  9. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/README.md +0 -0
  10. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/__init__.py +0 -0
  11. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/browser/__init__.py +0 -0
  12. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/browser/chromium/__init__.py +0 -0
  13. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/browser/chromium/chrome.py +0 -0
  14. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/browser/chromium/edge.py +0 -0
  15. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/browser/interfaces.py +0 -0
  16. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/browser/managers/__init__.py +0 -0
  17. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/browser/managers/browser_options_manager.py +0 -0
  18. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/browser/managers/browser_process_manager.py +0 -0
  19. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/browser/managers/proxy_manager.py +0 -0
  20. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/browser/managers/temp_dir_manager.py +0 -0
  21. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/browser/options.py +0 -0
  22. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/browser/requests/__init__.py +0 -0
  23. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/browser/requests/request.py +0 -0
  24. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/browser/requests/response.py +0 -0
  25. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/commands/__init__.py +0 -0
  26. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/commands/browser_commands.py +0 -0
  27. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/commands/dom_commands.py +0 -0
  28. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/commands/fetch_commands.py +0 -0
  29. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/commands/input_commands.py +0 -0
  30. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/commands/network_commands.py +0 -0
  31. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/commands/page_commands.py +0 -0
  32. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/commands/runtime_commands.py +0 -0
  33. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/commands/storage_commands.py +0 -0
  34. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/commands/target_commands.py +0 -0
  35. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/connection/__init__.py +0 -0
  36. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/connection/connection_handler.py +0 -0
  37. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/connection/managers/__init__.py +0 -0
  38. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/connection/managers/commands_manager.py +0 -0
  39. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/connection/managers/events_manager.py +0 -0
  40. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/decorators.py +0 -0
  41. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/elements/__init__.py +0 -0
  42. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/elements/mixins/__init__.py +0 -0
  43. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/exceptions.py +0 -0
  44. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/interactions/__init__.py +0 -0
  45. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/interactions/iframe.py +0 -0
  46. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/interactions/keyboard.py +0 -0
  47. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/interactions/scroll.py +0 -0
  48. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/__init__.py +0 -0
  49. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/base.py +0 -0
  50. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/browser/__init__.py +0 -0
  51. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/browser/events.py +0 -0
  52. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/browser/methods.py +0 -0
  53. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/browser/types.py +0 -0
  54. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/debugger/types.py +0 -0
  55. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/dom/__init__.py +0 -0
  56. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/dom/events.py +0 -0
  57. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/dom/methods.py +0 -0
  58. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/dom/types.py +0 -0
  59. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/emulation/types.py +0 -0
  60. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/fetch/__init__.py +0 -0
  61. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/fetch/events.py +0 -0
  62. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/fetch/methods.py +0 -0
  63. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/fetch/types.py +0 -0
  64. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/input/__init__.py +0 -0
  65. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/input/events.py +0 -0
  66. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/input/methods.py +0 -0
  67. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/input/types.py +0 -0
  68. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/io/types.py +0 -0
  69. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/network/__init__.py +0 -0
  70. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/network/events.py +0 -0
  71. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/network/methods.py +0 -0
  72. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/network/types.py +0 -0
  73. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/page/__init__.py +0 -0
  74. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/page/events.py +0 -0
  75. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/page/methods.py +0 -0
  76. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/page/types.py +0 -0
  77. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/runtime/__init__.py +0 -0
  78. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/runtime/events.py +0 -0
  79. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/runtime/methods.py +0 -0
  80. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/runtime/types.py +0 -0
  81. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/security/types.py +0 -0
  82. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/storage/__init__.py +0 -0
  83. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/storage/events.py +0 -0
  84. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/storage/methods.py +0 -0
  85. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/storage/types.py +0 -0
  86. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/target/__init__.py +0 -0
  87. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/target/events.py +0 -0
  88. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/target/methods.py +0 -0
  89. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/protocol/target/types.py +0 -0
  90. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/py.typed +0 -0
  91. {pydoll_python-2.15.1 → pydoll_python-2.16.0}/pydoll/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydoll-python
3
- Version: 2.15.1
3
+ Version: 2.16.0
4
4
  Summary: Pydoll is a library for automating chromium-based browsers without a WebDriver, offering realistic interactions.
5
5
  License-File: LICENSE
6
6
  Author: Thalison Fernandes
@@ -906,9 +906,9 @@ class Browser(ABC): # noqa: PLR0904
906
906
  def _validate_ws_address(ws_address: str):
907
907
  """Validate WebSocket address."""
908
908
  min_slashes = 4
909
- if not ws_address.startswith('ws://'):
910
- logger.error('Invalid WebSocket address: missing ws:// prefix')
911
- raise InvalidWebSocketAddress('WebSocket address must start with ws://')
909
+ if not ws_address.startswith(('ws://', 'wss://')):
910
+ logger.error('Invalid WebSocket address: missing ws:// or wss:// prefix')
911
+ raise InvalidWebSocketAddress('WebSocket address must start with ws:// or wss://')
912
912
  if len(ws_address.split('/')) < min_slashes:
913
913
  logger.error('Invalid WebSocket address: not enough slashes')
914
914
  raise InvalidWebSocketAddress(
@@ -34,7 +34,7 @@ from pydoll.commands import (
34
34
  StorageCommands,
35
35
  )
36
36
  from pydoll.connection import ConnectionHandler
37
- from pydoll.constants import By
37
+ from pydoll.constants import By, PageLoadState
38
38
  from pydoll.elements.mixins import FindElementsMixin
39
39
  from pydoll.exceptions import (
40
40
  DownloadTimeout,
@@ -104,6 +104,8 @@ class Tab(FindElementsMixin):
104
104
  like Cloudflare bypass.
105
105
  """
106
106
 
107
+ _READY_STATE_ORDER = (PageLoadState.LOADING, PageLoadState.INTERACTIVE, PageLoadState.COMPLETE)
108
+
107
109
  def __init__(
108
110
  self,
109
111
  browser: Browser,
@@ -231,6 +233,14 @@ class Tab(FindElementsMixin):
231
233
  )
232
234
  return response['result']['result']['value']
233
235
 
236
+ @property
237
+ async def title(self) -> str:
238
+ """Get current page title."""
239
+ response: EvaluateResponse = await self._execute_command(
240
+ RuntimeCommands.evaluate('document.title')
241
+ )
242
+ return response['result']['result'].get('value', '')
243
+
234
244
  async def enable_page_events(self):
235
245
  """Enable CDP Page domain events (load, navigation, dialogs, etc.)."""
236
246
  logger.debug('Enabling Page events')
@@ -1409,17 +1419,28 @@ class Tab(FindElementsMixin):
1409
1419
  """
1410
1420
  Wait for page to finish loading.
1411
1421
 
1422
+ Waits until ``document.readyState`` reaches **at least** the level
1423
+ configured in ``browser.options.page_load_state``. For example, when
1424
+ the target state is ``interactive``, both ``"interactive"`` and
1425
+ ``"complete"`` satisfy the condition — this prevents an infinite
1426
+ polling loop when the page transitions past ``interactive`` before the
1427
+ first check runs.
1428
+
1412
1429
  Raises:
1413
- asyncio.TimeoutError: If page doesn't load within timeout.
1430
+ WaitElementTimeout: If page doesn't load within *timeout* seconds.
1414
1431
  """
1432
+ target_state = self._browser.options.page_load_state.value
1433
+ target_index = self._READY_STATE_ORDER.index(target_state)
1415
1434
  start_time = asyncio.get_event_loop().time()
1416
1435
  logger.debug(f'Waiting for page load (timeout={timeout}s)')
1417
1436
  while True:
1418
1437
  response: EvaluateResponse = await self._execute_command(
1419
1438
  RuntimeCommands.evaluate(expression='document.readyState')
1420
1439
  )
1421
- if response['result']['result']['value'] == self._browser.options.page_load_state.value:
1422
- logger.debug(f'Page load state reached: {self._browser.options.page_load_state}')
1440
+ current_state = response['result']['result']['value']
1441
+ current_index = self._READY_STATE_ORDER.index(current_state)
1442
+ if current_index >= target_index:
1443
+ logger.debug(f'Page load state reached: {current_state} (target: {target_state})')
1423
1444
  break
1424
1445
  if asyncio.get_event_loop().time() - start_time > timeout:
1425
1446
  raise WaitElementTimeout('Page load timed out')
@@ -13,6 +13,7 @@ class By(str, Enum):
13
13
  class PageLoadState(str, Enum):
14
14
  COMPLETE = 'complete'
15
15
  INTERACTIVE = 'interactive'
16
+ LOADING = 'loading'
16
17
 
17
18
 
18
19
  class ScrollPosition(str, Enum):
@@ -436,6 +437,30 @@ new Promise((resolve) => {{
436
437
  }
437
438
  """
438
439
 
440
+ CLEAR_INPUT = """
441
+ function() {
442
+ const el = this;
443
+
444
+ // Standard input/textarea
445
+ if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {
446
+ el.value = '';
447
+ el.dispatchEvent(new Event('input', { bubbles: true }));
448
+ el.dispatchEvent(new Event('change', { bubbles: true }));
449
+ return true;
450
+ }
451
+
452
+ // ContentEditable elements
453
+ if (el.isContentEditable) {
454
+ el.focus();
455
+ el.innerHTML = '';
456
+ el.dispatchEvent(new Event('input', { bubbles: true }));
457
+ return true;
458
+ }
459
+
460
+ return false;
461
+ }
462
+ """
463
+
439
464
  IS_EDITABLE = """
440
465
  function() {
441
466
  const el = this;
@@ -646,11 +646,11 @@ class FindElementsMixin:
646
646
  async def _execute_command(
647
647
  self, command: Command[T_CommandParams, T_CommandResponse]
648
648
  ) -> T_CommandResponse:
649
- """Execute CDP command via resolved handler (60s timeout)."""
649
+ """Execute CDP command via resolved handler (10s timeout)."""
650
650
  handler, session_id = self._resolve_routing()
651
651
  if session_id:
652
652
  command['sessionId'] = session_id
653
- return await handler.execute_command(command, timeout=60)
653
+ return await handler.execute_command(command, timeout=10)
654
654
 
655
655
  def _get_find_element_command(
656
656
  self,
@@ -123,6 +123,11 @@ class WebElement(FindElementsMixin): # noqa: PLR0904
123
123
  self._iframe_resolver = IFrameContextResolver(self)
124
124
  return self._iframe_resolver
125
125
 
126
+ @property
127
+ def attributes(self) -> dict[str, str]:
128
+ """Read-only copy of the element's cached attributes."""
129
+ return dict(self._attributes)
130
+
126
131
  @property
127
132
  def value(self) -> Optional[str]:
128
133
  """Element's value attribute (for form elements)."""
@@ -530,6 +535,25 @@ class WebElement(FindElementsMixin): # noqa: PLR0904
530
535
  await asyncio.sleep(hold_time)
531
536
  await self._connection_handler.execute_command(release_command)
532
537
 
538
+ async def clear(self):
539
+ """
540
+ Clear the current value of the element.
541
+
542
+ Supports standard inputs, textareas, and contenteditable elements.
543
+ Dispatches ``input`` and ``change`` events so frameworks detect the update.
544
+
545
+ Raises:
546
+ ElementNotInteractable: If the element does not accept text input.
547
+ """
548
+ logger.info('Clearing element value')
549
+ result = await self.execute_script(Scripts.CLEAR_INPUT, return_by_value=True)
550
+ success = result['result'].get('result', {}).get('value', False)
551
+ if not success:
552
+ logger.error('Element does not accept text input')
553
+ raise ElementNotInteractable('Element does not accept text input')
554
+ if self._attributes.get('tag_name', '').lower() in {'input', 'textarea'}:
555
+ self._attributes['value'] = ''
556
+
533
557
  async def insert_text(self, text: str):
534
558
  """
535
559
  Insert text into element using JavaScript.
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pydoll-python"
3
- version = "2.15.1"
3
+ version = "2.16.0"
4
4
  description = "Pydoll is a library for automating chromium-based browsers without a WebDriver, offering realistic interactions."
5
5
  authors = ["Thalison Fernandes <thalissfernandes99@gmail.com>"]
6
6
  readme = "README.md"
File without changes
File without changes