Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

refactor(tab): separate execute_script concerns #258

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
3xp0rt wants to merge 3 commits into autoscrape-labs:main
base: main
Choose a base branch
Loading
from 3xp0rt:execute-script

Conversation

Copy link

@3xp0rt 3xp0rt commented Aug 31, 2025

Refactoring Pull Request

Refactoring Scope

The Tab class's execute_script method in pydoll/browser/tab.py, related tests and documentation.

Related Issue(s)

Addresses user feedback from GitHub Discussion #108 regarding async script execution capabilities

Description

Refactor the execute_script method by separating concerns to improve the API design. The new implementation should support asynchronous execution using await_promise=True and return_by_value=True, provide additional options for flexible usage in code. The original method handled both general script execution and element-specific execution, which has been split into two focused methods:

  • execute_script: Enhanced with comprehensive runtime options for general script execution
  • execute_element_script: New method specifically for executing scripts with element context

Motivation

Support for asynchronous code.

Before / After

Before

 @overload
 async def execute_script(self, script: str) -> EvaluateResponse: ...
 @overload
 async def execute_script(self, script: str, element: WebElement) -> CallFunctionOnResponse: ...
 async def execute_script(
 self, script: str, element: Optional[WebElement] = None
 ) -> Union[EvaluateResponse, CallFunctionOnResponse]:

After

 async def execute_script(
 self,
 script: str,
 *,
 object_group: Optional[str] = None,
 include_command_line_api: Optional[bool] = None,
 silent: Optional[bool] = None,
 context_id: Optional[int] = None,
 return_by_value: Optional[bool] = None,
 generate_preview: Optional[bool] = None,
 user_gesture: Optional[bool] = None,
 await_promise: Optional[bool] = None,
 throw_on_side_effect: Optional[bool] = None,
 timeout: Optional[float] = None,
 disable_breaks: Optional[bool] = None,
 repl_mode: Optional[bool] = None,
 allow_unsafe_eval_blocked_by_csp: Optional[bool] = None,
 unique_context_id: Optional[str] = None,
 serialization_options: Optional[SerializationOptions] = None,
 ) -> EvaluateResponse:
 
 async def execute_element_script(
 self,
 script: str,
 element: WebElement,
 *,
 arguments: Optional[list[CallArgument]] = None,
 silent: Optional[bool] = None,
 return_by_value: Optional[bool] = None,
 generate_preview: Optional[bool] = None,
 user_gesture: Optional[bool] = None,
 await_promise: Optional[bool] = None,
 execution_context_id: Optional[int] = None,
 object_group: Optional[str] = None,
 throw_on_side_effect: Optional[bool] = None,
 unique_context_id: Optional[str] = None,
 serialization_options: Optional[SerializationOptions] = None,
 ) -> CallFunctionOnResponse:

Performance Impact

  • Performance improved
  • Performance potentially decreased
  • No significant performance change
  • Performance impact unknown

Technical Debt

This refactoring addresses several technical debt issues:

  • Mixed Responsibilities: The original method handled two different use cases
  • Complex Method Signatures: Overloaded methods with optional parameters made the API confusing
  • Limited Extensibility: The previous design lacked support for passing additional options and asynchronous execution, restricting flexibility

API Changes

  • No changes to public API
  • Public API changed, but backward compatible
  • Breaking changes to public API

Testing Strategy

  • Updated existing tests to use the new method signatures
  • Verified that all existing functionality is preserved through the new API

Testing Checklist

  • Existing tests updated
  • New tests added for previously uncovered cases
  • All tests pass
  • Code coverage maintained or improved

Risks and Mitigations

  • Breaking change for users who relied on the old execute_script(script, element) signature

Checklist before requesting a review

  • My code follows the style guidelines of this project
  • I have performed a thorough self-review of the refactored code
  • I have commented my code, particularly in complex areas
  • I have updated documentation if needed
  • I have run poetry run task lint and fixed any issues
  • I have run poetry run task test and all tests pass
  • My commits follow the conventional commits style

Copy link

codecov bot commented Aug 31, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@3xp0rt 3xp0rt changed the title (削除) refactor(tab): separate execute_script concerns and enhance with comp... (削除ここまで) (追記) refactor(tab): separate execute_script concerns (追記ここまで) Aug 31, 2025
Copy link
Author

3xp0rt commented Sep 3, 2025

Last commit refactors JavaScript execution by moving execute_element_script functionality from the Tab class to the WebElement class for better encapsulation.

Copy link
Member

I think this is too much. We can keep something with a minimal useful API, and for advanced users, they can execute the scripts directly.

For instance, something like this would be better:

 @overload
 async def execute_script(self, script: str, await_promise: bool = True, return_by_value: bool = True) -> EvaluateResponse: ...
 @overload
 async def execute_script(self, script: str, element: WebElement, await_promise: bool = True, return_by_value: bool = True) -> CallFunctionOnResponse: ...

Copy link
Author

3xp0rt commented Oct 15, 2025

I think this is too much. We can keep something with a minimal useful API, and for advanced users, they can execute the scripts directly.

For instance, something like this would be better:

 @overload
 async def execute_script(self, script: str, await_promise: bool = True, return_by_value: bool = True) -> EvaluateResponse: ...
 @overload
 async def execute_script(self, script: str, element: WebElement, await_promise: bool = True, return_by_value: bool = True) -> CallFunctionOnResponse: ...

Oh, I completely forgot about this PR due to the delayed response, and you probably closed it because of inactivity. If that’s the case, it would’ve been better to just ping me instead of closing it. I’ve just submitted the latest commit with the requested changes, so please consider reopening this PR. Also, let me explain the situation a little better.

The current code from the PR uses explicit methods. Tab.execute_script is clearly intended for page-level JavaScript execution, while WebElement.execute_script is meant for element-specific operations. Both rely on different runtime functions and return different data types, so combining them would blur their distinct use cases and create confusion. Since the WebElement class already implements this function, duplicating it in WebElement isn’t necessary.

  1. Tab.execute_script (in pydoll/browser/tab.py):
async def execute_script(
 self,
 script: str,
 *,
 return_by_value: Optional[bool] = None,
 await_promise: Optional[bool] = None,
) -> EvaluateResponse:
  1. WebElement.execute_script (in pydoll/elements/web_element.py):
async def execute_script(
 self,
 script: str,
 *,
 return_by_value: Optional[bool] = None,
 await_promise: Optional[bool] = None,
) -> CallFunctionOnResponse:

Copy link
Author

3xp0rt commented Oct 15, 2025

Honestly, it’d be great to keep the other arguments, as having the flexibility to tweak these parameters could be useful in some situations.

Copy link
Member

thalissonvs commented Oct 17, 2025
edited
Loading

Hello @3xp0rt, thanks for the feedback. We have to keep the API consistent, otherwise it'll be a breaking change. I understand your point of view, and that makes sense. My idea would be: if the user pass an WebElement to the Tab.execute_script method, we can show a warning message informing that this is deprecated, and to use WebElement.execute_script instead;

For now, we can keep all the arguments, but we should preserve in just one method for clarity. Lets keep just execute_script method, and use overload for correct typing inference:

 
 @overload # this is the signature without passing an WebElement as argument;
 async def execute_script(
 self,
 script: str,
 *,
 object_group: Optional[str] = None,
 include_command_line_api: Optional[bool] = None,
 silent: Optional[bool] = None,
 context_id: Optional[int] = None,
 return_by_value: Optional[bool] = None,
 generate_preview: Optional[bool] = None,
 user_gesture: Optional[bool] = None,
 await_promise: Optional[bool] = None,
 throw_on_side_effect: Optional[bool] = None,
 timeout: Optional[float] = None,
 disable_breaks: Optional[bool] = None,
 repl_mode: Optional[bool] = None,
 allow_unsafe_eval_blocked_by_csp: Optional[bool] = None,
 unique_context_id: Optional[str] = None,
 serialization_options: Optional[SerializationOptions] = None,
 ) -> EvaluateResponse:
 
 @overload
 async def execute_element( # this is the signature passing an WebElement as arg
 self,
 script: str,
 element: WebElement,
 *,
 arguments: Optional[list[CallArgument]] = None,
 silent: Optional[bool] = None,
 return_by_value: Optional[bool] = None,
 generate_preview: Optional[bool] = None,
 user_gesture: Optional[bool] = None,
 await_promise: Optional[bool] = None,
 execution_context_id: Optional[int] = None,
 object_group: Optional[str] = None,
 throw_on_side_effect: Optional[bool] = None,
 unique_context_id: Optional[str] = None,
 serialization_options: Optional[SerializationOptions] = None,
 ) -> CallFunctionOnResponse:
 async def execute_element( # this is the actual implementation
 self,
 script: str,
 element: Optional[WebElement] = None,
 *,
 arguments: Optional[list[CallArgument]] = None,
 silent: Optional[bool] = None,
 return_by_value: Optional[bool] = None,
 generate_preview: Optional[bool] = None,
 user_gesture: Optional[bool] = None,
 await_promise: Optional[bool] = None,
 execution_context_id: Optional[int] = None,
 object_group: Optional[str] = None,
 throw_on_side_effect: Optional[bool] = None,
 unique_context_id: Optional[str] = None,
 serialization_options: Optional[SerializationOptions] = None,
 ) -> Union[EvaluateResponse, CallFunctionOnResponse]:

What do you think? Just make sure to document every param properly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

AltStyle によって変換されたページ (->オリジナル) /