pymobiledevice3 5.0.0__py3-none-any.whl → 5.0.2__py3-none-any.whl

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.

Potentially problematic release.


This version of pymobiledevice3 might be problematic. Click here for more details.

Files changed (143) hide show
  1. misc/plist_sniffer.py +15 -15
  2. misc/remotexpc_sniffer.py +29 -28
  3. pymobiledevice3/__main__.py +128 -102
  4. pymobiledevice3/_version.py +2 -2
  5. pymobiledevice3/bonjour.py +26 -49
  6. pymobiledevice3/ca.py +32 -24
  7. pymobiledevice3/cli/activation.py +7 -7
  8. pymobiledevice3/cli/afc.py +19 -19
  9. pymobiledevice3/cli/amfi.py +4 -4
  10. pymobiledevice3/cli/apps.py +51 -39
  11. pymobiledevice3/cli/backup.py +58 -32
  12. pymobiledevice3/cli/bonjour.py +25 -18
  13. pymobiledevice3/cli/cli_common.py +112 -81
  14. pymobiledevice3/cli/companion_proxy.py +4 -4
  15. pymobiledevice3/cli/completions.py +10 -10
  16. pymobiledevice3/cli/crash.py +37 -31
  17. pymobiledevice3/cli/developer.py +602 -520
  18. pymobiledevice3/cli/diagnostics.py +38 -33
  19. pymobiledevice3/cli/lockdown.py +79 -74
  20. pymobiledevice3/cli/mounter.py +85 -68
  21. pymobiledevice3/cli/notification.py +10 -10
  22. pymobiledevice3/cli/pcap.py +19 -14
  23. pymobiledevice3/cli/power_assertion.py +12 -10
  24. pymobiledevice3/cli/processes.py +10 -10
  25. pymobiledevice3/cli/profile.py +88 -77
  26. pymobiledevice3/cli/provision.py +17 -17
  27. pymobiledevice3/cli/remote.py +186 -110
  28. pymobiledevice3/cli/restore.py +43 -40
  29. pymobiledevice3/cli/springboard.py +30 -28
  30. pymobiledevice3/cli/syslog.py +85 -58
  31. pymobiledevice3/cli/usbmux.py +21 -20
  32. pymobiledevice3/cli/version.py +3 -2
  33. pymobiledevice3/cli/webinspector.py +157 -79
  34. pymobiledevice3/common.py +1 -1
  35. pymobiledevice3/exceptions.py +154 -60
  36. pymobiledevice3/irecv.py +49 -53
  37. pymobiledevice3/irecv_devices.py +1489 -492
  38. pymobiledevice3/lockdown.py +394 -241
  39. pymobiledevice3/lockdown_service_provider.py +5 -7
  40. pymobiledevice3/osu/os_utils.py +18 -9
  41. pymobiledevice3/osu/posix_util.py +28 -15
  42. pymobiledevice3/osu/win_util.py +14 -8
  43. pymobiledevice3/pair_records.py +19 -19
  44. pymobiledevice3/remote/common.py +4 -4
  45. pymobiledevice3/remote/core_device/app_service.py +94 -67
  46. pymobiledevice3/remote/core_device/core_device_service.py +17 -14
  47. pymobiledevice3/remote/core_device/device_info.py +5 -5
  48. pymobiledevice3/remote/core_device/diagnostics_service.py +10 -8
  49. pymobiledevice3/remote/core_device/file_service.py +47 -33
  50. pymobiledevice3/remote/remote_service_discovery.py +53 -35
  51. pymobiledevice3/remote/remotexpc.py +62 -41
  52. pymobiledevice3/remote/tunnel_service.py +371 -293
  53. pymobiledevice3/remote/utils.py +12 -11
  54. pymobiledevice3/remote/xpc_message.py +145 -125
  55. pymobiledevice3/resources/dsc_uuid_map.py +19 -19
  56. pymobiledevice3/resources/firmware_notifications.py +16 -16
  57. pymobiledevice3/restore/asr.py +27 -27
  58. pymobiledevice3/restore/base_restore.py +90 -47
  59. pymobiledevice3/restore/consts.py +87 -66
  60. pymobiledevice3/restore/device.py +11 -11
  61. pymobiledevice3/restore/fdr.py +46 -46
  62. pymobiledevice3/restore/ftab.py +19 -19
  63. pymobiledevice3/restore/img4.py +130 -133
  64. pymobiledevice3/restore/mbn.py +35 -54
  65. pymobiledevice3/restore/recovery.py +125 -135
  66. pymobiledevice3/restore/restore.py +524 -523
  67. pymobiledevice3/restore/restore_options.py +122 -115
  68. pymobiledevice3/restore/restored_client.py +25 -22
  69. pymobiledevice3/restore/tss.py +378 -270
  70. pymobiledevice3/service_connection.py +50 -46
  71. pymobiledevice3/services/accessibilityaudit.py +136 -126
  72. pymobiledevice3/services/afc.py +350 -291
  73. pymobiledevice3/services/amfi.py +21 -18
  74. pymobiledevice3/services/companion.py +23 -19
  75. pymobiledevice3/services/crash_reports.py +60 -46
  76. pymobiledevice3/services/debugserver_applist.py +3 -3
  77. pymobiledevice3/services/device_arbitration.py +8 -8
  78. pymobiledevice3/services/device_link.py +55 -47
  79. pymobiledevice3/services/diagnostics.py +971 -968
  80. pymobiledevice3/services/dtfetchsymbols.py +8 -8
  81. pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
  82. pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
  83. pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
  84. pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
  85. pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
  86. pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
  87. pymobiledevice3/services/dvt/instruments/device_info.py +11 -11
  88. pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
  89. pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
  90. pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
  91. pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
  92. pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
  93. pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
  94. pymobiledevice3/services/dvt/instruments/process_control.py +25 -10
  95. pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
  96. pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
  97. pymobiledevice3/services/dvt/testmanaged/xcuitest.py +40 -50
  98. pymobiledevice3/services/file_relay.py +10 -10
  99. pymobiledevice3/services/heartbeat.py +8 -7
  100. pymobiledevice3/services/house_arrest.py +12 -15
  101. pymobiledevice3/services/installation_proxy.py +119 -100
  102. pymobiledevice3/services/lockdown_service.py +12 -5
  103. pymobiledevice3/services/misagent.py +22 -19
  104. pymobiledevice3/services/mobile_activation.py +84 -72
  105. pymobiledevice3/services/mobile_config.py +330 -301
  106. pymobiledevice3/services/mobile_image_mounter.py +137 -116
  107. pymobiledevice3/services/mobilebackup2.py +188 -150
  108. pymobiledevice3/services/notification_proxy.py +11 -11
  109. pymobiledevice3/services/os_trace.py +69 -51
  110. pymobiledevice3/services/pcapd.py +306 -306
  111. pymobiledevice3/services/power_assertion.py +10 -9
  112. pymobiledevice3/services/preboard.py +4 -4
  113. pymobiledevice3/services/remote_fetch_symbols.py +16 -14
  114. pymobiledevice3/services/remote_server.py +176 -146
  115. pymobiledevice3/services/restore_service.py +16 -16
  116. pymobiledevice3/services/screenshot.py +13 -10
  117. pymobiledevice3/services/simulate_location.py +7 -7
  118. pymobiledevice3/services/springboard.py +15 -15
  119. pymobiledevice3/services/syslog.py +5 -5
  120. pymobiledevice3/services/web_protocol/alert.py +3 -3
  121. pymobiledevice3/services/web_protocol/automation_session.py +180 -176
  122. pymobiledevice3/services/web_protocol/cdp_screencast.py +44 -36
  123. pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
  124. pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
  125. pymobiledevice3/services/web_protocol/driver.py +47 -45
  126. pymobiledevice3/services/web_protocol/element.py +74 -63
  127. pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
  128. pymobiledevice3/services/web_protocol/selenium_api.py +2 -2
  129. pymobiledevice3/services/web_protocol/session_protocol.py +15 -10
  130. pymobiledevice3/services/web_protocol/switch_to.py +11 -12
  131. pymobiledevice3/services/webinspector.py +127 -116
  132. pymobiledevice3/tcp_forwarder.py +35 -22
  133. pymobiledevice3/tunneld/api.py +20 -15
  134. pymobiledevice3/tunneld/server.py +212 -133
  135. pymobiledevice3/usbmux.py +183 -138
  136. pymobiledevice3/utils.py +14 -11
  137. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/METADATA +1 -1
  138. pymobiledevice3-5.0.2.dist-info/RECORD +173 -0
  139. pymobiledevice3-5.0.0.dist-info/RECORD +0 -173
  140. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/WHEEL +0 -0
  141. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/entry_points.txt +0 -0
  142. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/licenses/LICENSE +0 -0
  143. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/top_level.txt +0 -0
@@ -5,24 +5,24 @@ from pymobiledevice3.services.web_protocol.element import WebElement
5
5
  from pymobiledevice3.services.web_protocol.selenium_api import By, SeleniumApi
6
6
  from pymobiledevice3.services.web_protocol.switch_to import SwitchTo
7
7
 
8
- ENTER_FULLSCREEN = (RESOURCES / 'enter_fullscreen.js').read_text()
8
+ ENTER_FULLSCREEN = (RESOURCES / "enter_fullscreen.js").read_text()
9
9
 
10
10
 
11
11
  @dataclass
12
12
  class Cookie:
13
13
  name: str
14
14
  value: str
15
- domain: str = ''
16
- path: str = '/'
15
+ domain: str = ""
16
+ path: str = "/"
17
17
  expires: int = 0
18
18
  httpOnly: bool = False
19
19
  secure: bool = False
20
20
  session: bool = True
21
- sameSite: str = 'None'
21
+ sameSite: str = "None"
22
22
 
23
23
  @classmethod
24
24
  def from_automation(cls, d):
25
- d.pop('size')
25
+ d.pop("size")
26
26
  return cls(**d)
27
27
 
28
28
 
@@ -35,38 +35,38 @@ class WebDriver(SeleniumApi):
35
35
  self.switch_to = SwitchTo(session)
36
36
 
37
37
  def add_cookie(self, cookie: Cookie):
38
- """ Adds a cookie to your current session. """
38
+ """Adds a cookie to your current session."""
39
39
  if isinstance(cookie, Cookie):
40
40
  cookie = asdict(cookie)
41
41
  self.session.add_single_cookie(cookie)
42
42
 
43
43
  def back(self):
44
- """ Goes one step backward in the browser history. """
44
+ """Goes one step backward in the browser history."""
45
45
  self.session.wait_for_navigation_to_complete()
46
46
  self.session.go_back_in_browsing_context()
47
- self.session.switch_to_browsing_context('')
47
+ self.session.switch_to_browsing_context("")
48
48
 
49
49
  def close(self):
50
- """ Closes the current window. """
50
+ """Closes the current window."""
51
51
  self.session.close_window()
52
52
 
53
53
  @property
54
54
  def current_url(self):
55
- """ Gets the URL of the current page. """
55
+ """Gets the URL of the current page."""
56
56
  self.session.wait_for_navigation_to_complete()
57
- return self.session.get_browsing_context()['url']
57
+ return self.session.get_browsing_context()["url"]
58
58
 
59
59
  @property
60
60
  def current_window_handle(self):
61
- """ Returns the handle of the current window. """
62
- return self.session.get_browsing_context()['handle']
61
+ """Returns the handle of the current window."""
62
+ return self.session.get_browsing_context()["handle"]
63
63
 
64
64
  def delete_all_cookies(self):
65
- """ Delete all cookies in the scope of the session. """
65
+ """Delete all cookies in the scope of the session."""
66
66
  self.session.delete_all_cookies()
67
67
 
68
68
  def delete_cookie(self, name: str):
69
- """ Deletes a single cookie with the given name. """
69
+ """Deletes a single cookie with the given name."""
70
70
  self.session.delete_single_cookie(name)
71
71
 
72
72
  def execute_async_script(self, script: str, *args):
@@ -86,110 +86,112 @@ class WebDriver(SeleniumApi):
86
86
  return self.session.execute_script(script, args)
87
87
 
88
88
  def find_element(self, by=By.ID, value=None) -> WebElement:
89
- """ Find an element given a By strategy and locator. """
89
+ """Find an element given a By strategy and locator."""
90
90
  elem = self.session.find_elements(by, value)
91
91
  return None if elem is None else WebElement(self.session, elem)
92
92
 
93
93
  def find_elements(self, by=By.ID, value=None) -> list[WebElement]:
94
- """ Find elements given a By strategy and locator. """
94
+ """Find elements given a By strategy and locator."""
95
95
  elements = self.session.find_elements(by, value, single=False)
96
- return list(map(lambda elem: WebElement(self.session, elem), elements))
96
+ return [WebElement(self.session, elem) for elem in elements]
97
97
 
98
98
  def forward(self):
99
- """ Goes one step forward in the browser history. """
99
+ """Goes one step forward in the browser history."""
100
100
  self.session.wait_for_navigation_to_complete()
101
101
  self.session.go_forward_in_browsing_context()
102
- self.session.switch_to_browsing_context('')
102
+ self.session.switch_to_browsing_context("")
103
103
 
104
104
  def fullscreen_window(self):
105
- """ Invokes the window manager-specific 'full screen' operation. """
105
+ """Invokes the window manager-specific 'full screen' operation."""
106
106
  self.session.evaluate_js_function(ENTER_FULLSCREEN, implicit_callback=True)
107
107
 
108
108
  def get(self, url: str):
109
- """ Loads a web page in the current browser session. """
109
+ """Loads a web page in the current browser session."""
110
110
  self.session.wait_for_navigation_to_complete()
111
111
  self.session.navigate_broswing_context(url)
112
- self.session.switch_to_browsing_context('')
112
+ self.session.switch_to_browsing_context("")
113
113
 
114
114
  def get_cookie(self, name: str) -> Cookie:
115
- """ Get a single cookie by name. Returns the cookie if found, None if not. """
115
+ """Get a single cookie by name. Returns the cookie if found, None if not."""
116
116
  for cookie in self.get_cookies():
117
117
  if cookie.name == name:
118
118
  return cookie
119
119
 
120
120
  def get_cookies(self) -> list[Cookie]:
121
- """ Returns cookies visible in the current session. """
121
+ """Returns cookies visible in the current session."""
122
122
  return list(map(Cookie.from_automation, self.session.get_all_cookies()))
123
123
 
124
124
  @property
125
125
  def screenshot_as_base64(self) -> str:
126
- """ Gets the screenshot of the current window as a base64 encoded string. """
126
+ """Gets the screenshot of the current window as a base64 encoded string."""
127
127
  return self.session.screenshot_as_base64()
128
128
 
129
129
  def get_window_position(self) -> Point:
130
- """ Gets the x,y position of the current window. """
130
+ """Gets the x,y position of the current window."""
131
131
  rect = self.get_window_rect()
132
132
  return Point(x=rect.x, y=rect.y)
133
133
 
134
134
  def get_window_rect(self) -> Rect:
135
- """ Gets the x, y coordinates of the window as well as height and width of the current window. """
135
+ """Gets the x, y coordinates of the window as well as height and width of the current window."""
136
136
  context = self.session.get_browsing_context()
137
137
  return Rect(
138
- context['windowOrigin']['x'], context['windowOrigin']['y'],
139
- context['windowSize']['width'], context['windowSize']['height']
138
+ context["windowOrigin"]["x"],
139
+ context["windowOrigin"]["y"],
140
+ context["windowSize"]["width"],
141
+ context["windowSize"]["height"],
140
142
  )
141
143
 
142
144
  def get_window_size(self) -> Size:
143
- """ Gets the width and height of the current window. """
145
+ """Gets the width and height of the current window."""
144
146
  rect = self.get_window_rect()
145
147
  return Size(height=rect.height, width=rect.width)
146
148
 
147
149
  def implicitly_wait(self, time_to_wait):
148
- """ Sets a sticky timeout to implicitly wait for an element to be found, or a command to complete. """
150
+ """Sets a sticky timeout to implicitly wait for an element to be found, or a command to complete."""
149
151
  self.session.implicit_wait_timeout = time_to_wait * 1000
150
152
 
151
153
  def maximize_window(self):
152
- """ Maximizes the current window. """
154
+ """Maximizes the current window."""
153
155
  self.session.maximize_window()
154
156
 
155
157
  def minimize_window(self):
156
- """ Invokes the window manager-specific 'minimize' operation. """
158
+ """Invokes the window manager-specific 'minimize' operation."""
157
159
  self.session.hide_window()
158
160
 
159
161
  @property
160
162
  def page_source(self) -> str:
161
- """ Gets the source of the current page. """
162
- return self.session.evaluate_js_function('function() { return document.documentElement.outerHTML; }')
163
+ """Gets the source of the current page."""
164
+ return self.session.evaluate_js_function("function() { return document.documentElement.outerHTML; }")
163
165
 
164
166
  def refresh(self):
165
- """ Refreshes the current page. """
167
+ """Refreshes the current page."""
166
168
  self.session.wait_for_navigation_to_complete()
167
169
  self.session.reload_browsing_context()
168
- self.session.switch_to_browsing_context('')
170
+ self.session.switch_to_browsing_context("")
169
171
 
170
172
  def set_window_position(self, x, y):
171
- """ Sets the x,y position of the current window. """
173
+ """Sets the x,y position of the current window."""
172
174
  self.set_window_rect(x=int(x, 0), y=int(y, 0))
173
175
 
174
176
  def set_window_rect(self, x=None, y=None, width=None, height=None):
175
- """ Sets the x, y coordinates of the window as well as height and width of the current window. """
177
+ """Sets the x, y coordinates of the window as well as height and width of the current window."""
176
178
  self.session.set_window_frame(x, y, width, height)
177
179
 
178
180
  def set_window_size(self, width, height):
179
- """ Sets the width and height of the current window. """
181
+ """Sets the width and height of the current window."""
180
182
  self.set_window_rect(width=int(width, 0), height=int(height, 0))
181
183
 
182
184
  def start_session(self):
183
- """ Creates a new session. """
185
+ """Creates a new session."""
184
186
  self.session.start_session()
185
187
 
186
188
  @property
187
189
  def title(self) -> str:
188
- """ Returns the title of the current page. """
190
+ """Returns the title of the current page."""
189
191
  self.session.wait_for_navigation_to_complete()
190
- return self.session.evaluate_js_function('function() { return document.title; }')
192
+ return self.session.evaluate_js_function("function() { return document.title; }")
191
193
 
192
194
  @property
193
195
  def window_handles(self) -> list[str]:
194
- """ Returns the handles of all windows within the current session. """
196
+ """Returns the handles of all windows within the current session."""
195
197
  return self.session.get_window_handles()
@@ -1,15 +1,24 @@
1
1
  from pymobiledevice3.exceptions import WirError
2
- from pymobiledevice3.services.web_protocol.automation_session import MODIFIER_TO_KEY, RESOURCES, VIRTUAL_KEYS, \
3
- KeyboardInteractionType, MouseButton, MouseInteraction, Point, Rect, Size
2
+ from pymobiledevice3.services.web_protocol.automation_session import (
3
+ MODIFIER_TO_KEY,
4
+ RESOURCES,
5
+ VIRTUAL_KEYS,
6
+ KeyboardInteractionType,
7
+ MouseButton,
8
+ MouseInteraction,
9
+ Point,
10
+ Rect,
11
+ Size,
12
+ )
4
13
  from pymobiledevice3.services.web_protocol.selenium_api import By, SeleniumApi
5
14
 
6
- IS_EDITABLE = (RESOURCES / 'is_editable.js').read_text()
7
- ELEMENT_CLEAR = (RESOURCES / 'element_clear.js').read_text()
8
- GET_ATTRIBUTE = (RESOURCES / 'get_attribute.js').read_text()
9
- ELEMENT_ATTRIBUTE = (RESOURCES / 'element_attribute.js').read_text()
10
- IS_DISPLAYED = (RESOURCES / 'is_displayed.js').read_text()
11
- IS_ENABLED = (RESOURCES / 'is_enabled.js').read_text()
12
- FOCUS = (RESOURCES / 'focus.js').read_text()
15
+ IS_EDITABLE = (RESOURCES / "is_editable.js").read_text()
16
+ ELEMENT_CLEAR = (RESOURCES / "element_clear.js").read_text()
17
+ GET_ATTRIBUTE = (RESOURCES / "get_attribute.js").read_text()
18
+ ELEMENT_ATTRIBUTE = (RESOURCES / "element_attribute.js").read_text()
19
+ IS_DISPLAYED = (RESOURCES / "is_displayed.js").read_text()
20
+ IS_ENABLED = (RESOURCES / "is_enabled.js").read_text()
21
+ FOCUS = (RESOURCES / "focus.js").read_text()
13
22
 
14
23
 
15
24
  class WebElement(SeleniumApi):
@@ -20,83 +29,83 @@ class WebElement(SeleniumApi):
20
29
  """
21
30
  self.session = session
22
31
  self.id_ = id_
23
- self.node_id = id_[f'session-node-{self.session.id_}']
32
+ self.node_id = id_[f"session-node-{self.session.id_}"]
24
33
 
25
34
  def clear(self):
26
- """ Clears the text if it's a text entry element. """
35
+ """Clears the text if it's a text entry element."""
27
36
  if not self.is_editable():
28
37
  return
29
- rect, center, is_obscured = self._compute_layout()
38
+ rect, center, _is_obscured = self._compute_layout()
30
39
  if rect is None or center is None:
31
40
  return
32
41
  self._evaluate_js_function(ELEMENT_CLEAR)
33
42
 
34
43
  def click(self):
35
- """ Clicks the element. """
44
+ """Clicks the element."""
36
45
  rect, center, is_obscured = self._compute_layout(use_viewport=True)
37
46
  if rect is None or is_obscured or center is None:
38
47
  return
39
- if self.tag_name == 'option':
48
+ if self.tag_name == "option":
40
49
  self._select_option_element()
41
50
  else:
42
51
  self.session.perform_mouse_interaction(center.x, center.y, MouseButton.LEFT, MouseInteraction.SINGLE_CLICK)
43
52
 
44
53
  def find_element(self, by=By.ID, value=None):
45
- """ Find an element given a By strategy and locator. """
54
+ """Find an element given a By strategy and locator."""
46
55
  elem = self.session.find_elements(by, value, root=self.id_)
47
56
  return None if elem is None else WebElement(self.session, elem)
48
57
 
49
58
  def find_elements(self, by=By.ID, value=None):
50
- """ Find elements given a By strategy and locator. """
59
+ """Find elements given a By strategy and locator."""
51
60
  elements = self.session.find_elements(by, value, single=False, root=self.id_)
52
- return list(map(lambda elem: WebElement(self.session, elem), elements))
61
+ return [WebElement(self.session, elem) for elem in elements]
53
62
 
54
63
  def get_attribute(self, name: str) -> str:
55
- """ Gets the given attribute or property of the element. """
56
- return self.session.execute_script(f'return ({GET_ATTRIBUTE}).apply(null, arguments);', self.id_, name)
64
+ """Gets the given attribute or property of the element."""
65
+ return self.session.execute_script(f"return ({GET_ATTRIBUTE}).apply(null, arguments);", self.id_, name)
57
66
 
58
67
  def get_dom_attribute(self, name: str) -> str:
59
- """ Gets the given attribute of the element. """
68
+ """Gets the given attribute of the element."""
60
69
  return self._evaluate_js_function(ELEMENT_ATTRIBUTE, name)
61
70
 
62
71
  def get_property(self, name: str) -> str:
63
- """ Gets the given property of the element. """
64
- return self._evaluate_js_function(f'function(element) {{ return element.{name}; }}')
72
+ """Gets the given property of the element."""
73
+ return self._evaluate_js_function(f"function(element) {{ return element.{name}; }}")
65
74
 
66
75
  def is_displayed(self) -> bool:
67
- """ Whether the element is visible to a user. """
68
- return self.session.execute_script(f'return ({IS_DISPLAYED}).apply(null, arguments);', self.id_)
76
+ """Whether the element is visible to a user."""
77
+ return self.session.execute_script(f"return ({IS_DISPLAYED}).apply(null, arguments);", self.id_)
69
78
 
70
79
  def is_enabled(self) -> bool:
71
- """ Returns whether the element is enabled. """
80
+ """Returns whether the element is enabled."""
72
81
  return self._evaluate_js_function(IS_ENABLED)
73
82
 
74
83
  def is_selected(self) -> bool:
75
- """ Returns whether the element is selected. Can be used to check if a checkbox or radio button is selected. """
76
- return bool(self.get_dom_attribute('selected'))
84
+ """Returns whether the element is selected. Can be used to check if a checkbox or radio button is selected."""
85
+ return bool(self.get_dom_attribute("selected"))
77
86
 
78
87
  @property
79
88
  def location(self) -> Point:
80
- """ The location of the element in the renderable canvas. """
89
+ """The location of the element in the renderable canvas."""
81
90
  rect = self.rect
82
91
  return Point(x=rect.x, y=rect.y)
83
92
 
84
93
  @property
85
94
  def location_once_scrolled_into_view(self) -> Point:
86
- """ Returns the top lefthand corner location on the screen, or ``None`` if the element is not visible. """
95
+ """Returns the top lefthand corner location on the screen, or ``None`` if the element is not visible."""
87
96
  rect = self.session.execute_script(
88
- 'arguments[0].scrollIntoView(true); return arguments[0].getBoundingClientRect(); ', self.id_
97
+ "arguments[0].scrollIntoView(true); return arguments[0].getBoundingClientRect(); ", self.id_
89
98
  )
90
- return Point(x=round(rect['x']), y=round(rect['y']))
99
+ return Point(x=round(rect["x"]), y=round(rect["y"]))
91
100
 
92
101
  @property
93
102
  def rect(self) -> Rect:
94
- """ The size and location of the element. """
103
+ """The size and location of the element."""
95
104
  return self._compute_layout(scroll_if_needed=False)[0]
96
105
 
97
106
  @property
98
107
  def screenshot_as_base64(self) -> str:
99
- """ Gets the screenshot of the current element as a base64 encoded string. """
108
+ """Gets the screenshot of the current element as a base64 encoded string."""
100
109
  return self.session.screenshot_as_base64(scroll=True, node_id=self.node_id)
101
110
 
102
111
  def send_keys(self, value):
@@ -110,80 +119,82 @@ class WebElement(SeleniumApi):
110
119
  for key in value:
111
120
  if key in VIRTUAL_KEYS:
112
121
  virtual_key, modifier = VIRTUAL_KEYS[key]
113
- interaction = {'type': KeyboardInteractionType.INSERT_BY_KEY, 'key': virtual_key}
122
+ interaction = {"type": KeyboardInteractionType.INSERT_BY_KEY, "key": virtual_key}
114
123
  if modifier is not None:
115
124
  sticky_modifier ^= {modifier}
116
- interaction['type'] = (KeyboardInteractionType.KEY_PRESS if modifier in sticky_modifier
117
- else KeyboardInteractionType.KEY_RELEASE)
125
+ interaction["type"] = (
126
+ KeyboardInteractionType.KEY_PRESS
127
+ if modifier in sticky_modifier
128
+ else KeyboardInteractionType.KEY_RELEASE
129
+ )
118
130
  interactions.append(interaction)
119
131
  else:
120
- interactions.append({'type': KeyboardInteractionType.INSERT_BY_KEY, 'text': key})
132
+ interactions.append({"type": KeyboardInteractionType.INSERT_BY_KEY, "text": key})
121
133
  for modifier in sticky_modifier:
122
- interactions.append({'type': KeyboardInteractionType.KEY_RELEASE, 'key': MODIFIER_TO_KEY[modifier]})
134
+ interactions.append({"type": KeyboardInteractionType.KEY_RELEASE, "key": MODIFIER_TO_KEY[modifier]})
123
135
  self.session.perform_keyboard_interactions(interactions)
124
136
 
125
137
  @property
126
138
  def size(self) -> Size:
127
- """ The size of the element. """
139
+ """The size of the element."""
128
140
  rect = self.rect
129
141
  return Size(height=rect.height, width=rect.width)
130
142
 
131
143
  def submit(self):
132
- """ Submits a form. """
144
+ """Submits a form."""
133
145
  form = self.find_element(By.XPATH, "./ancestor-or-self::form")
134
146
  submit_code = (
135
- 'var e = arguments[0].ownerDocument.createEvent(\'Event\');'
136
- 'e.initEvent(\'submit\', true, true);'
137
- 'if (arguments[0].dispatchEvent(e)) { arguments[0].submit() }'
147
+ "var e = arguments[0].ownerDocument.createEvent('Event');"
148
+ "e.initEvent('submit', true, true);"
149
+ "if (arguments[0].dispatchEvent(e)) { arguments[0].submit() }"
138
150
  )
139
151
  self.session.execute_script(submit_code, form.id_)
140
152
 
141
153
  @property
142
154
  def tag_name(self) -> str:
143
- """ This element's ``tagName`` property. """
144
- return self._evaluate_js_function('function(element) { return element.tagName.toLowerCase() }')
155
+ """This element's ``tagName`` property."""
156
+ return self._evaluate_js_function("function(element) { return element.tagName.toLowerCase() }")
145
157
 
146
158
  @property
147
159
  def text(self) -> str:
148
- """ The text of the element. """
160
+ """The text of the element."""
149
161
  return self._evaluate_js_function(
150
162
  'function(element) { return element.innerText.replace(/^[^\\S\\xa0]+|[^\\S\\xa0]+$/g, "") }'
151
163
  )
152
164
 
153
165
  def touch(self):
154
- """ Simulate touch interaction on the element. """
155
- rect, center, is_obscured = self._compute_layout(use_viewport=True)
166
+ """Simulate touch interaction on the element."""
167
+ _rect, center, _is_obscured = self._compute_layout(use_viewport=True)
156
168
  self.session.perform_interaction_sequence(
157
- [{'sourceId': self.session.id_, 'sourceType': 'Touch'}],
158
- [{'states': [
159
- {'sourceId': self.session.id_, 'location': {'x': center.x, 'y': center.y}}
160
- ]}]
169
+ [{"sourceId": self.session.id_, "sourceType": "Touch"}],
170
+ [{"states": [{"sourceId": self.session.id_, "location": {"x": center.x, "y": center.y}}]}],
161
171
  )
162
172
 
163
173
  def value_of_css_property(self, property_name) -> str:
164
- """ The value of a CSS property. """
174
+ """The value of a CSS property."""
165
175
  return self._evaluate_js_function(
166
- 'function(element) {'
176
+ "function(element) {"
167
177
  f' return document.defaultView.getComputedStyle(element).getPropertyValue("{property_name}"); '
168
- '}'
178
+ "}"
169
179
  )
170
180
 
171
181
  def is_editable(self) -> bool:
172
- """ Returns whether the element is editable. """
182
+ """Returns whether the element is editable."""
173
183
  return self._evaluate_js_function(IS_EDITABLE)
174
184
 
175
185
  def _compute_layout(self, scroll_if_needed=True, use_viewport=False):
176
186
  try:
177
- result = self.session.compute_element_layout(self.node_id, scroll_if_needed,
178
- 'LayoutViewport' if use_viewport else 'Page')
187
+ result = self.session.compute_element_layout(
188
+ self.node_id, scroll_if_needed, "LayoutViewport" if use_viewport else "Page"
189
+ )
179
190
  except WirError:
180
191
  return
181
192
 
182
- origin = result['rect']['origin']
183
- size = result['rect']['size']
184
- rect = Rect(x=round(origin['x']), y=round(origin['y']), width=size['width'], height=size['height'])
185
- center = Point(x=result['inViewCenterPoint']['x'], y=result['inViewCenterPoint']['y'])
186
- return rect, center, result['isObscured']
193
+ origin = result["rect"]["origin"]
194
+ size = result["rect"]["size"]
195
+ rect = Rect(x=round(origin["x"]), y=round(origin["y"]), width=size["width"], height=size["height"])
196
+ center = Point(x=result["inViewCenterPoint"]["x"], y=result["inViewCenterPoint"]["y"])
197
+ return rect, center, result["isObscured"]
187
198
 
188
199
  def _select_option_element(self):
189
200
  self.session.select_option_element(self.node_id)