Kea2-python 1.0.6b0__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 Kea2-python might be problematic. Click here for more details.

Files changed (52) hide show
  1. kea2/__init__.py +3 -0
  2. kea2/absDriver.py +56 -0
  3. kea2/adbUtils.py +554 -0
  4. kea2/assets/config_version.json +16 -0
  5. kea2/assets/fastbot-thirdpart.jar +0 -0
  6. kea2/assets/fastbot_configs/abl.strings +2 -0
  7. kea2/assets/fastbot_configs/awl.strings +3 -0
  8. kea2/assets/fastbot_configs/max.config +7 -0
  9. kea2/assets/fastbot_configs/max.fuzzing.strings +699 -0
  10. kea2/assets/fastbot_configs/max.schema.strings +1 -0
  11. kea2/assets/fastbot_configs/max.strings +3 -0
  12. kea2/assets/fastbot_configs/max.tree.pruning +27 -0
  13. kea2/assets/fastbot_configs/teardown.py +18 -0
  14. kea2/assets/fastbot_configs/widget.block.py +38 -0
  15. kea2/assets/fastbot_libs/arm64-v8a/libfastbot_native.so +0 -0
  16. kea2/assets/fastbot_libs/armeabi-v7a/libfastbot_native.so +0 -0
  17. kea2/assets/fastbot_libs/x86/libfastbot_native.so +0 -0
  18. kea2/assets/fastbot_libs/x86_64/libfastbot_native.so +0 -0
  19. kea2/assets/framework.jar +0 -0
  20. kea2/assets/kea2-thirdpart.jar +0 -0
  21. kea2/assets/monkeyq.jar +0 -0
  22. kea2/assets/quicktest.py +126 -0
  23. kea2/cli.py +320 -0
  24. kea2/fastbotManager.py +267 -0
  25. kea2/fastbotx/ActivityTimes.py +52 -0
  26. kea2/fastbotx/ReuseEntry.py +74 -0
  27. kea2/fastbotx/ReuseModel.py +63 -0
  28. kea2/fastbotx/__init__.py +7 -0
  29. kea2/fbm_parser.py +871 -0
  30. kea2/fs_lock.py +131 -0
  31. kea2/kea2_api.py +166 -0
  32. kea2/keaUtils.py +1112 -0
  33. kea2/kea_launcher.py +319 -0
  34. kea2/logWatcher.py +92 -0
  35. kea2/mixin.py +22 -0
  36. kea2/report/__init__.py +0 -0
  37. kea2/report/bug_report_generator.py +793 -0
  38. kea2/report/mixin.py +482 -0
  39. kea2/report/report_merger.py +797 -0
  40. kea2/report/templates/bug_report_template.html +3876 -0
  41. kea2/report/templates/merged_bug_report_template.html +3333 -0
  42. kea2/report/utils.py +10 -0
  43. kea2/resultSyncer.py +65 -0
  44. kea2/u2Driver.py +610 -0
  45. kea2/utils.py +184 -0
  46. kea2/version_manager.py +102 -0
  47. kea2_python-1.0.6b0.dist-info/METADATA +447 -0
  48. kea2_python-1.0.6b0.dist-info/RECORD +52 -0
  49. kea2_python-1.0.6b0.dist-info/WHEEL +5 -0
  50. kea2_python-1.0.6b0.dist-info/entry_points.txt +2 -0
  51. kea2_python-1.0.6b0.dist-info/licenses/LICENSE +16 -0
  52. kea2_python-1.0.6b0.dist-info/top_level.txt +1 -0
kea2/utils.py ADDED
@@ -0,0 +1,184 @@
1
+ import os
2
+ import logging
3
+ import traceback
4
+ import time
5
+
6
+ from pathlib import Path
7
+ from functools import wraps
8
+ from typing import Callable, Dict, Optional, Union
9
+
10
+
11
+ def singleton(cls):
12
+ _instance = {}
13
+
14
+ def inner():
15
+ if cls not in _instance:
16
+ _instance[cls] = cls()
17
+ return _instance[cls]
18
+ return inner
19
+
20
+
21
+ class LoggingLevel:
22
+ level = logging.INFO
23
+ _instance: Optional["LoggingLevel"] = None # 单例缓存
24
+
25
+ def __new__(cls):
26
+ if cls._instance is None:
27
+ cls._instance = super().__new__(cls)
28
+ return cls._instance
29
+
30
+ @classmethod
31
+ def set_level(cls, level: int):
32
+ cls.level = level
33
+
34
+
35
+ class DynamicLevelFilter(logging.Filter):
36
+ def filter(self, record: logging.LogRecord) -> bool:
37
+ return record.levelno >= LoggingLevel.level
38
+
39
+
40
+ def getLogger(name: str) -> logging.Logger:
41
+ logger = logging.getLogger(name)
42
+
43
+ def enable_pretty_logging():
44
+ if not logger.handlers:
45
+ # Configure handler
46
+ handler = logging.StreamHandler()
47
+ handler.flush = lambda: handler.stream.flush()
48
+ handler.setFormatter(logging.Formatter('[%(levelname)1s][%(asctime)s %(module)s:%(lineno)d pid:%(process)d] %(message)s'))
49
+ handler.setLevel(logging.NOTSET)
50
+ handler.addFilter(DynamicLevelFilter())
51
+ logger.addHandler(handler)
52
+ logger.setLevel(logging.DEBUG)
53
+ logger.propagate = False
54
+
55
+ enable_pretty_logging()
56
+ return logger
57
+
58
+
59
+ logger = getLogger(__name__)
60
+
61
+
62
+
63
+
64
+ @singleton
65
+ class TimeStamp:
66
+ time_stamp = None
67
+
68
+ def getTimeStamp(cls):
69
+ if cls.time_stamp is None:
70
+ import datetime
71
+ cls.time_stamp = datetime.datetime.now().strftime('%Y%m%d%H_%M%S%f')
72
+ return cls.time_stamp
73
+
74
+ def getCurrentTimeStamp(cls):
75
+ import datetime
76
+ return datetime.datetime.now().strftime('%Y%m%d%H_%M%S%f')
77
+
78
+
79
+ from uiautomator2 import Device
80
+ d = Device
81
+
82
+
83
+ _CUSTOM_PROJECT_ROOT: Optional[Path] = None
84
+
85
+
86
+ def setCustomProjectRoot(configs_path: Optional[Union[str, Path]]):
87
+ """
88
+ Set a custom project root directory (containing the configs directory). Passing None can restore the default behavior.
89
+ """
90
+ global _CUSTOM_PROJECT_ROOT
91
+
92
+ if configs_path is None:
93
+ _CUSTOM_PROJECT_ROOT = None
94
+ return
95
+
96
+ candidate = Path(configs_path).expanduser()
97
+ if candidate.name == "configs":
98
+ candidate = candidate.parent
99
+
100
+ candidate = candidate.resolve()
101
+ _CUSTOM_PROJECT_ROOT = candidate
102
+
103
+
104
+ def getProjectRoot():
105
+ if _CUSTOM_PROJECT_ROOT:
106
+ return _CUSTOM_PROJECT_ROOT
107
+
108
+ root = Path(Path.cwd().anchor)
109
+ cur_dir = Path.absolute(Path(os.curdir))
110
+ while not os.path.isdir(cur_dir / "configs"):
111
+ if cur_dir == root:
112
+ return None
113
+ cur_dir = cur_dir.parent
114
+ return cur_dir
115
+
116
+
117
+ def timer(log_info: str=None):
118
+ """ ### Decorator to measure the execution time of a function.
119
+
120
+ This decorator can be used to wrap functions where you want to log the time taken for execution
121
+
122
+ ### Usage:
123
+ - @timer("Function execution took %cost_time seconds.")
124
+ - @timer() # If no log_info is provided, it will print the function name and execution time.
125
+
126
+ `%cost_time` will be replaced with the actual time taken for execution.
127
+ """
128
+ def accept(func):
129
+ @wraps(func)
130
+ def wrapper(*args, **kwargs):
131
+ start_time = time.time()
132
+ result = func(*args, **kwargs)
133
+ end_time = time.time()
134
+ if log_info:
135
+ logger.info(log_info.replace(r"%cost_time", f"{end_time - start_time:.4f}"))
136
+ else:
137
+ logger.info(f"Function '{func.__name__}' executed in {(end_time - start_time):.4f} seconds.")
138
+ return result
139
+ return wrapper
140
+ return accept
141
+
142
+
143
+ def catchException(log_info: str):
144
+ """ ### Decorator to catch exceptions and print log info.
145
+
146
+ This decorator can be used to wrap functions that may raise exceptions,
147
+ allowing you to log a message when the exception is raised.
148
+
149
+ ### Usage:
150
+ - @catchException("An error occurred in the function ****.")
151
+ """
152
+ def accept(func):
153
+ @wraps(func)
154
+ def wrapper(*args, **kwargs):
155
+ try:
156
+ return func(*args, **kwargs)
157
+ except Exception as e:
158
+ logger.info(log_info)
159
+ tb = traceback.format_exception(type(e), e, e.__traceback__.tb_next)
160
+ print(''.join(tb), end='', flush=True)
161
+ return wrapper
162
+ return accept
163
+
164
+
165
+ def loadFuncsFromFile(file_path: str) -> Dict[str, Callable]:
166
+ if not os.path.exists(file_path):
167
+ raise FileNotFoundError(f"{file_path} not found.")
168
+
169
+ def __get_module():
170
+ import importlib.util
171
+ module_name = Path(file_path).stem
172
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
173
+ mod = importlib.util.module_from_spec(spec)
174
+ spec.loader.exec_module(mod)
175
+ return mod
176
+
177
+ mod = __get_module()
178
+
179
+ import inspect
180
+ funcs = dict()
181
+ for func_name, func in inspect.getmembers(mod, inspect.isfunction):
182
+ funcs[func_name] = func
183
+
184
+ return funcs
@@ -0,0 +1,102 @@
1
+ import os
2
+ import json
3
+ import shutil
4
+
5
+ from pathlib import Path
6
+ from packaging.version import Version
7
+ from typing import List, Set, TypedDict
8
+ from importlib.metadata import version
9
+
10
+ from .utils import getLogger, getProjectRoot
11
+
12
+ logger = getLogger(__name__)
13
+
14
+
15
+ CompatibilityInfo = TypedDict('CompatibilityInfo', {
16
+ "name": str,
17
+ "description": str,
18
+ "from": str,
19
+ "to": str,
20
+ })
21
+ VersionInfo = TypedDict('VersionInfo', {
22
+ "compatibility infos": List[CompatibilityInfo],
23
+ })
24
+
25
+
26
+ def ls_files(dir_path: Path) -> Set[Path]:
27
+ """list all files in the directory"""
28
+ return set(f for f in dir_path.rglob('*') if f.is_file())
29
+
30
+
31
+ def check_config_compatibility():
32
+ config_version_sanitizer = ConfigVersionSanitizer()
33
+ config_version_sanitizer.check_config_compatibility()
34
+ config_version_sanitizer.config_auto_update()
35
+
36
+
37
+ def get_cur_version():
38
+ return version("Kea2-python")
39
+
40
+
41
+ class ConfigVersionSanitizer:
42
+ def __init__(self):
43
+ self._version_infos = None
44
+ self._config_version = None
45
+ self.user_config_path = getProjectRoot() / "configs"
46
+ self.kea2_assets_path = Path(__file__).parent / "assets"
47
+ self.kea2_version = get_cur_version()
48
+
49
+ @property
50
+ def version_infos(self) -> VersionInfo:
51
+ if self._version_infos is None:
52
+ with open(self.kea2_assets_path / "config_version.json") as fp:
53
+ self._version_infos = json.load(fp)
54
+ return self._version_infos
55
+
56
+ @property
57
+ def config_version(self):
58
+ if self._config_version is not None:
59
+ return self._config_version
60
+
61
+ user_version_json = self.user_config_path / "version.json"
62
+ if not user_version_json.exists():
63
+ self._config_version = "0.3.6"
64
+ else:
65
+ with open(user_version_json) as fp:
66
+ self._config_version = json.load(fp)["version"]
67
+ return self._config_version
68
+
69
+ def check_config_compatibility(self):
70
+ """Check if the user config version is compatible with the current Kea2 version."""
71
+ update_infos = []
72
+ for info in self.version_infos["compatibility infos"]:
73
+ if Version(info["from"]) > Version(self.config_version):
74
+ update_infos.append(info)
75
+
76
+ if not update_infos:
77
+ return
78
+
79
+ logger.warning("Configuration update required! Please update your configuration files.")
80
+ logger.warning(f"Current kea2 version {self.kea2_version}. Current config version {self.config_version}.")
81
+ for info in update_infos:
82
+ logger.info(
83
+ f"Since version {info['from']}: {info['description']}"
84
+ )
85
+
86
+ def config_auto_update(self):
87
+ self._copy_new_configs()
88
+
89
+ def _copy_new_configs(self):
90
+ src = self.kea2_assets_path / "fastbot_configs"
91
+ dst = self.user_config_path
92
+
93
+ src_files = set(os.listdir(src))
94
+ dst_files = set(os.listdir(dst))
95
+
96
+ new_files = src_files - dst_files
97
+
98
+ for file in new_files:
99
+ src_path = src / file
100
+ dst_path = dst / file
101
+ logger.info(f"Copying new config file: {file}")
102
+ shutil.copy2(src_path, dst_path)