-
Notifications
You must be signed in to change notification settings - Fork 101
Mmokko/java inspector backend #1040
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
Draft
mmokko
wants to merge
11
commits into
master
Choose a base branch
from
mmokko/java_inspector_backend
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
9ce67ae
Add java inspector backend
mmokko 328615e
Get context tree
mmokko 927e317
Use thread to get the context tree
mmokko 7cdccbb
Add search depth for context tree instead of running in thread
mmokko 47c504a
Get tree with matches and hierarchy
mmokko 18d68e2
Collect tree to save the context
mmokko 315bfc8
lint fixes
mmokko 94c50e2
Get wrapper as future
mmokko 47cc45a
fix linting
mmokko 0a33c1c
Add java-access-bridge to conda.yaml
mmokko c762b18
fix linting
mmokko File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
96 changes: 96 additions & 0 deletions
96
robocorp-code/src/robocorp_code/inspector/java/java_inspector.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| from typing import List, Optional, TypedDict, cast | ||
|
|
||
| from JABWrapper.context_tree import ContextNode # type: ignore | ||
| from JABWrapper.jab_wrapper import JavaWindow # type: ignore | ||
| from robocorp_ls_core.robotframework_log import get_logger | ||
|
|
||
| from robocorp_code.inspector.java.robocorp_java._inspector import ColletedTreeTypedDict | ||
|
|
||
| log = get_logger(__name__) | ||
|
|
||
| JavaWindowInfoTypedDict = TypedDict( | ||
| "JavaWindowInfoTypedDict", | ||
| { | ||
| "pid": int, | ||
| "hwnd": int, | ||
| "title": str, | ||
| }, | ||
| ) | ||
|
|
||
| LocatorNodeInfoTypedDict = TypedDict( | ||
| "LocatorNodeInfoTypedDict", | ||
| { | ||
| "name": str, | ||
| "role": str, | ||
| "description": str, | ||
| "states": str, | ||
| "indexInParent": int, | ||
| "childrenCount": int, | ||
| "x": int, | ||
| "y": int, | ||
| "width": int, | ||
| "height": int, | ||
| "ancestry": int, | ||
| }, | ||
| ) | ||
|
|
||
|
|
||
| class MatchesAndHierarchyTypedDict(TypedDict): | ||
| # A list with the nodes matched (these are the ones that the | ||
| # locator matched) | ||
| matched_paths: List[str] | ||
| # This includes all the entries found along with the full hierarchy | ||
| # to reach the matched entries. | ||
| hierarchy: List[LocatorNodeInfoTypedDict] | ||
|
|
||
|
|
||
| def to_window_info(java_window: JavaWindow) -> JavaWindowInfoTypedDict: | ||
| ret = {} | ||
| for dct_name in JavaWindowInfoTypedDict.__annotations__: | ||
| ret[dct_name] = getattr(java_window, dct_name) | ||
| return cast(JavaWindowInfoTypedDict, ret) | ||
|
|
||
|
|
||
| def to_locator_info(context_node: ContextNode) -> LocatorNodeInfoTypedDict: | ||
| ret = {} | ||
| for dct_name in LocatorNodeInfoTypedDict.__annotations__: | ||
| if (dct_name) == "ancestry": | ||
| ret["ancestry"] = getattr(context_node, dct_name) | ||
| else: | ||
| ret[dct_name] = getattr(context_node.context_info, dct_name) | ||
| return cast(LocatorNodeInfoTypedDict, ret) | ||
|
|
||
|
|
||
| def to_matches_and_hierarchy( | ||
| matches_and_hierarchy: ColletedTreeTypedDict, | ||
| ) -> MatchesAndHierarchyTypedDict: | ||
| matches = ( | ||
| [str(matches_and_hierarchy["matches"])] | ||
| if type(matches_and_hierarchy["matches"]) == ContextNode | ||
| else [str(match) for match in matches_and_hierarchy["matches"]] | ||
| ) | ||
| hierarchy = [to_locator_info(node) for node in matches_and_hierarchy["tree"]] | ||
| return {"matched_paths": matches, "hierarchy": hierarchy} | ||
|
|
||
|
|
||
| class JavaInspector: | ||
| def __init__(self): | ||
| from robocorp_code.inspector.java.robocorp_java._inspector import ( | ||
| ElementInspector, | ||
| ) | ||
|
|
||
| self._inspector = ElementInspector() | ||
|
|
||
| def list_windows(self) -> List[JavaWindowInfoTypedDict]: | ||
| windows = self._inspector.list_windows() | ||
| return [to_window_info(window) for window in windows] | ||
|
|
||
| def collect_tree( | ||
| self, window: str, search_depth=1, locator: Optional[str] = None | ||
| ) -> MatchesAndHierarchyTypedDict: | ||
| log.info(f"Collect tree from locator: {locator}") | ||
|
|
||
| matches_and_hierarchy = self._inspector.collect_tree( | ||
| window, search_depth, locator | ||
| ) | ||
| return to_matches_and_hierarchy(matches_and_hierarchy) |
Empty file.
10 changes: 10 additions & 0 deletions
10
robocorp-code/src/robocorp_code/inspector/java/robocorp_java/_errors.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| class NoMatchingLocatorException(Exception): | ||
| """Match for locator not found.""" | ||
|
|
||
|
|
||
| class ContextNotAvailable(Exception): | ||
| """The Java context has not been created yet.""" | ||
|
|
||
|
|
||
| class LocatorNotProvidedException(Exception): | ||
| """Locator not provided.""" |
69 changes: 69 additions & 0 deletions
69
robocorp-code/src/robocorp_code/inspector/java/robocorp_java/_event_pump.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import ctypes | ||
| import platform | ||
| import threading | ||
| import time | ||
| from concurrent import futures | ||
|
|
||
| from JABWrapper.jab_wrapper import JavaAccessBridgeWrapper # type: ignore | ||
| from robocorp_ls_core.robotframework_log import get_logger | ||
|
|
||
| log = get_logger(__name__) | ||
|
|
||
|
|
||
| REMOVE_FROM_QUEUE = 0x0001 | ||
|
|
||
|
|
||
| class EventPumpThread(threading.Thread): | ||
| def __init__( | ||
| self, | ||
| ) -> None: | ||
| super().__init__() | ||
| # Jab wrapper needs to be part of the thread that pumps the window events | ||
| self._jab_wrapper: JavaAccessBridgeWrapper = None | ||
| self._future: futures.Future = futures.Future() | ||
| self._quit_event_loop = threading.Event() | ||
|
|
||
| def _pump_background(self) -> bool: | ||
| try: | ||
| PeekMessage = ctypes.windll.user32.PeekMessageW # type: ignore | ||
| TranslateMessage = ctypes.windll.user32.TranslateMessage # type: ignore | ||
| DispatchMessage = ctypes.windll.user32.DispatchMessageW # type: ignore | ||
|
|
||
| message = ctypes.byref(ctypes.wintypes.MSG()) | ||
| # Nonblocking API to get windows window events from the queue. | ||
| # https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-peekmessagea | ||
| # Use non blocing API here so that the thread can quit. | ||
| if PeekMessage(message, 0, 0, 0, REMOVE_FROM_QUEUE): | ||
| TranslateMessage(message) | ||
| log.debug("Dispatching msg={}".format(repr(message))) | ||
| DispatchMessage(message) | ||
| return True | ||
| except Exception as err: | ||
| log.error(f"Pump error: {err}") | ||
| finally: | ||
| log.info("Stopped processing events") | ||
| return False | ||
|
|
||
| def run(self) -> None: | ||
| if platform.system() != "Windows": | ||
| return | ||
|
|
||
| # Raise the error to the main thread from here | ||
| self._jab_wrapper = JavaAccessBridgeWrapper(ignore_callbacks=True) | ||
| self._future.set_result(self._jab_wrapper) | ||
| while not self._quit_event_loop.is_set(): | ||
| # The pump is non blocking. If the is no message in the queue | ||
| # wait for 10 milliseconds until check again to prevent too | ||
| # fast loop. | ||
| # TODO: add backoff timer | ||
| if not self._pump_background(): | ||
| time.sleep(0.01) | ||
|
|
||
| def stop(self): | ||
| self._quit_event_loop.set() | ||
| self._jab_wrapper = None | ||
| if not self._future.done(): | ||
| self._future.cancel() | ||
|
|
||
| def get_wrapper(self) -> JavaAccessBridgeWrapper: | ||
| return self._future.result() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this windows only?