Skip to content

Commit ea354a7

Browse files
committed
Clear remaining SonarCloud and Codacy findings on PR 175
- english/japanese language dicts: extract _SCRIPT_LABEL / _REMOVE_SELECTED / _SELECT_SCRIPT / _SCRIPT to drop S1192 duplicate-literal warnings - accessibility/vision backend factories: type the module-level cache as Optional[...] instead of assigning None against a non-optional hint (S5890) - macos_backend: rename the ApplicationServices param from AS to ax_module so it passes S117 snake_case - hotkey linux_backend._sync: split into _ungrab_stale / _sync_one / _ungrab_masked / _grab_masked helpers to bring cognitive complexity from 21 back under the 15-line limit - hotkey windows_backend: iterate the dict directly in the finally block instead of wrapping in list() (S7504) - HotkeyDaemon._snapshot is now an alias of list_bindings so Sonar stops flagging the identical body (S4144) - change_xml_structure: lift the nested conditional into _initial_body (S3358) - package_manager regex uses concise \w (S6353); history_store uses the _IN_MEMORY_DB constant instead of repeating ":memory:" (S1192) - Add nosemgrep suppressions for defusedxml imports, the run_history SELECT builder, and the openai SDK guardrails false positive
1 parent 49a3c88 commit ea354a7

14 files changed

Lines changed: 129 additions & 95 deletions

File tree

je_auto_control/gui/language_wrapper/english.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
_SCRIPT_LABEL = "Script:"
2+
_REMOVE_SELECTED = "Remove selected"
3+
_SELECT_SCRIPT = "Select script"
4+
15
english_word_dict = {
26
# Main
37
"application_name": "AutoControlGUI",
@@ -126,9 +130,9 @@
126130

127131
# Hotkeys Tab
128132
"hk_combo_label": "Combo:",
129-
"hk_script_label": "Script:",
133+
"hk_script_label": _SCRIPT_LABEL,
130134
"hk_bind": "Bind",
131-
"hk_remove_selected": "Remove selected",
135+
"hk_remove_selected": _REMOVE_SELECTED,
132136
"hk_start_daemon": "Start daemon",
133137
"hk_stop_daemon": "Stop daemon",
134138
"hk_daemon_stopped": "Daemon stopped",
@@ -137,14 +141,14 @@
137141
"hk_col_combo": "Combo",
138142
"hk_col_script": "Script",
139143
"hk_col_fired": "Fired",
140-
"hk_dialog_select_script": "Select script",
144+
"hk_dialog_select_script": _SELECT_SCRIPT,
141145

142146
# Triggers Tab
143-
"tr_script_label": "Script:",
147+
"tr_script_label": _SCRIPT_LABEL,
144148
"tr_type_label": "Type:",
145149
"tr_repeat": "Repeat",
146150
"tr_add": "Add trigger",
147-
"tr_remove_selected": "Remove selected",
151+
"tr_remove_selected": _REMOVE_SELECTED,
148152
"tr_start_engine": "Start engine",
149153
"tr_stop_engine": "Stop engine",
150154
"tr_engine_stopped": "Engine stopped",
@@ -164,7 +168,7 @@
164168
"tr_col_enabled": "Enabled",
165169
"tr_yes": "Yes",
166170
"tr_no": "No",
167-
"tr_dialog_select_script": "Select script",
171+
"tr_dialog_select_script": _SELECT_SCRIPT,
168172
"tr_dialog_select_image": "Select image",
169173
"tr_dialog_select_file": "Select file to watch",
170174

@@ -176,11 +180,11 @@
176180
"pl_dialog_plugin_dir": "Plugin directory",
177181

178182
# Scheduler Tab
179-
"sch_script_label": "Script:",
183+
"sch_script_label": _SCRIPT_LABEL,
180184
"sch_interval_label": "Every (s):",
181185
"sch_repeat": "Repeat",
182186
"sch_add": "Add",
183-
"sch_remove_selected": "Remove selected",
187+
"sch_remove_selected": _REMOVE_SELECTED,
184188
"sch_start": "Start scheduler",
185189
"sch_stop": "Stop scheduler",
186190
"sch_status_running": "Scheduler running",
@@ -190,7 +194,7 @@
190194
"sch_col_interval": "Interval (s)",
191195
"sch_col_runs": "Runs",
192196
"sch_col_enabled": "Enabled",
193-
"sch_dialog_select_script": "Select script",
197+
"sch_dialog_select_script": _SELECT_SCRIPT,
194198

195199
# Socket / REST Tab
196200
"ss_tcp_group": "TCP socket server",
@@ -232,7 +236,7 @@
232236
"re_trim_start": "Trim start:",
233237
"re_trim_end": "end:",
234238
"re_apply_trim": "Apply trim",
235-
"re_remove_selected": "Remove selected",
239+
"re_remove_selected": _REMOVE_SELECTED,
236240
"re_delay_x": "Delay x",
237241
"re_floor_ms": "floor ms:",
238242
"re_apply_delays": "Apply delays",

je_auto_control/gui/language_wrapper/japanese.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
_SCRIPT = "スクリプト"
2+
_SCRIPT_LABEL = "スクリプト:"
3+
_REMOVE_SELECTED = "選択項目を削除"
4+
_SELECT_SCRIPT = "スクリプトを選択"
5+
16
japanese_word_dict = {
27
"application_name": "AutoControlGUI",
38

@@ -122,25 +127,25 @@
122127

123128
# Hotkeys Tab
124129
"hk_combo_label": "組合せ:",
125-
"hk_script_label": "スクリプト:",
130+
"hk_script_label": _SCRIPT_LABEL,
126131
"hk_bind": "バインド",
127-
"hk_remove_selected": "選択項目を削除",
132+
"hk_remove_selected": _REMOVE_SELECTED,
128133
"hk_start_daemon": "デーモン開始",
129134
"hk_stop_daemon": "デーモン停止",
130135
"hk_daemon_stopped": "デーモン停止中",
131136
"hk_daemon_running": "デーモン実行中",
132137
"hk_col_id": "ID",
133138
"hk_col_combo": "組合せ",
134-
"hk_col_script": "スクリプト",
139+
"hk_col_script": _SCRIPT,
135140
"hk_col_fired": "発火",
136-
"hk_dialog_select_script": "スクリプトを選択",
141+
"hk_dialog_select_script": _SELECT_SCRIPT,
137142

138143
# Triggers Tab
139-
"tr_script_label": "スクリプト:",
144+
"tr_script_label": _SCRIPT_LABEL,
140145
"tr_type_label": "タイプ:",
141146
"tr_repeat": "繰り返し",
142147
"tr_add": "トリガー追加",
143-
"tr_remove_selected": "選択項目を削除",
148+
"tr_remove_selected": _REMOVE_SELECTED,
144149
"tr_start_engine": "エンジン開始",
145150
"tr_stop_engine": "エンジン停止",
146151
"tr_engine_stopped": "エンジン停止中",
@@ -160,7 +165,7 @@
160165
"tr_col_enabled": "有効",
161166
"tr_yes": "はい",
162167
"tr_no": "いいえ",
163-
"tr_dialog_select_script": "スクリプトを選択",
168+
"tr_dialog_select_script": _SELECT_SCRIPT,
164169
"tr_dialog_select_image": "画像を選択",
165170
"tr_dialog_select_file": "監視するファイルを選択",
166171

@@ -172,21 +177,21 @@
172177
"pl_dialog_plugin_dir": "プラグインディレクトリ",
173178

174179
# Scheduler Tab
175-
"sch_script_label": "スクリプト:",
180+
"sch_script_label": _SCRIPT_LABEL,
176181
"sch_interval_label": "毎 (秒):",
177182
"sch_repeat": "繰り返し",
178183
"sch_add": "追加",
179-
"sch_remove_selected": "選択項目を削除",
184+
"sch_remove_selected": _REMOVE_SELECTED,
180185
"sch_start": "スケジューラー開始",
181186
"sch_stop": "スケジューラー停止",
182187
"sch_status_running": "スケジューラー実行中",
183188
"sch_status_stopped": "スケジューラー停止中",
184189
"sch_col_job_id": "ジョブ ID",
185-
"sch_col_script": "スクリプト",
190+
"sch_col_script": _SCRIPT,
186191
"sch_col_interval": "間隔 (秒)",
187192
"sch_col_runs": "実行回数",
188193
"sch_col_enabled": "有効",
189-
"sch_dialog_select_script": "スクリプトを選択",
194+
"sch_dialog_select_script": _SELECT_SCRIPT,
190195

191196
# Socket / REST Tab
192197
"ss_tcp_group": "TCP ソケットサーバー",
@@ -228,7 +233,7 @@
228233
"re_trim_start": "トリム開始:",
229234
"re_trim_end": "終了:",
230235
"re_apply_trim": "トリム適用",
231-
"re_remove_selected": "選択項目を削除",
236+
"re_remove_selected": _REMOVE_SELECTED,
232237
"re_delay_x": "ディレイ倍率",
233238
"re_floor_ms": "下限 ms:",
234239
"re_apply_delays": "ディレイ適用",
@@ -271,7 +276,7 @@
271276
"rh_col_id": "ID",
272277
"rh_col_source": "ソース",
273278
"rh_col_target": "対象",
274-
"rh_col_script": "スクリプト",
279+
"rh_col_script": _SCRIPT,
275280
"rh_col_started": "開始時刻",
276281
"rh_col_duration": "所要時間",
277282
"rh_col_status": "ステータス",

je_auto_control/utils/accessibility/backends/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Platform backends for the accessibility API."""
22
import sys
3+
from typing import Optional
34

45
from je_auto_control.utils.accessibility.backends.base import (
56
AccessibilityBackend,
@@ -8,7 +9,7 @@
89
NullAccessibilityBackend,
910
)
1011

11-
_cached_backend: AccessibilityBackend = None # type: ignore[assignment]
12+
_cached_backend: Optional[AccessibilityBackend] = None
1213

1314

1415
def get_backend() -> AccessibilityBackend:
@@ -23,7 +24,7 @@ def get_backend() -> AccessibilityBackend:
2324
def reset_backend_cache() -> None:
2425
"""Force the next ``get_backend()`` call to re-detect."""
2526
global _cached_backend
26-
_cached_backend = None # type: ignore[assignment]
27+
_cached_backend = None
2728

2829

2930
def _build_backend() -> AccessibilityBackend:

je_auto_control/utils/accessibility/backends/macos_backend.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def list_elements(self, app_name: Optional[str] = None,
4040
"pyobjc (ApplicationServices, AppKit) is required for "
4141
"macOS accessibility",
4242
)
43-
import ApplicationServices as AS
43+
import ApplicationServices as ax_module
4444
import AppKit
4545

4646
workspace = AppKit.NSWorkspace.sharedWorkspace()
@@ -54,8 +54,8 @@ def list_elements(self, app_name: Optional[str] = None,
5454
continue
5555
pid = int(app.processIdentifier())
5656
try:
57-
root = AS.AXUIElementCreateApplication(pid)
58-
self._walk(AS, root, name, pid, results, max_results)
57+
root = ax_module.AXUIElementCreateApplication(pid)
58+
self._walk(ax_module, root, name, pid, results, max_results)
5959
except Exception as error: # noqa: BLE001 # reason: AX errors vary
6060
autocontrol_logger.warning(
6161
"AX walk failed for %s (%d): %r", name, pid, error,
@@ -64,37 +64,37 @@ def list_elements(self, app_name: Optional[str] = None,
6464
break
6565
return results[:max_results]
6666

67-
def _walk(self, AS, element, app_name: str, pid: int,
67+
def _walk(self, ax_module, element, app_name: str, pid: int,
6868
results: List[AccessibilityElement], max_results: int) -> None:
6969
if len(results) >= max_results:
7070
return
71-
converted = _convert_ax(AS, element, app_name, pid)
71+
converted = _convert_ax(ax_module, element, app_name, pid)
7272
if converted is not None:
7373
results.append(converted)
74-
err, children = AS.AXUIElementCopyAttributeValue(
74+
err, children = ax_module.AXUIElementCopyAttributeValue(
7575
element, "AXChildren", None,
7676
)
7777
if err or children is None:
7878
return
7979
for child in children:
8080
if len(results) >= max_results:
8181
return
82-
self._walk(AS, child, app_name, pid, results, max_results)
82+
self._walk(ax_module, child, app_name, pid, results, max_results)
8383

8484

85-
def _convert_ax(AS, element, app_name: str, pid: int,
85+
def _convert_ax(ax_module, element, app_name: str, pid: int,
8686
) -> Optional[AccessibilityElement]:
8787
try:
88-
_err, role = AS.AXUIElementCopyAttributeValue(
88+
_err, role = ax_module.AXUIElementCopyAttributeValue(
8989
element, "AXRole", None,
9090
)
91-
_err, title = AS.AXUIElementCopyAttributeValue(
91+
_err, title = ax_module.AXUIElementCopyAttributeValue(
9292
element, "AXTitle", None,
9393
)
94-
_err, position = AS.AXUIElementCopyAttributeValue(
94+
_err, position = ax_module.AXUIElementCopyAttributeValue(
9595
element, "AXPosition", None,
9696
)
97-
_err, size = AS.AXUIElementCopyAttributeValue(
97+
_err, size = ax_module.AXUIElementCopyAttributeValue(
9898
element, "AXSize", None,
9999
)
100100
except Exception: # noqa: BLE001 # reason: AX errors vary

je_auto_control/utils/generate_report/generate_xml_report.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from threading import Lock
22
from typing import Tuple, Union
33

4-
from defusedxml.minidom import parseString # nosec B405 # reason: defusedxml is the safe replacement
4+
from defusedxml.minidom import parseString # nosec B405 # nosemgrep: python.lang.security.use-defused-xml.use-defused-xml # reason: defusedxml is the safe replacement
55

66
from je_auto_control.utils.generate_report.generate_json_report import generate_json
77
from je_auto_control.utils.logging.logging_instance import autocontrol_logger

je_auto_control/utils/hotkey/backends/linux_backend.py

Lines changed: 50 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -89,47 +89,61 @@ def run_forever(self, context: BackendContext) -> None:
8989
disp.close()
9090

9191
def _sync(self, disp, root, bindings: List[HotkeyBinding]) -> None:
92-
from Xlib import X
93-
9492
current_ids = {b.binding_id for b in bindings}
95-
for stale in [bid for bid in self._registered if bid not in current_ids]:
96-
_combo, mask, keycode = self._registered.pop(stale)
97-
for extra_mask in _lock_mask_variants():
98-
try:
99-
root.ungrab_key(keycode, mask | extra_mask)
100-
except Exception: # nosec B110 # noqa: BLE001 # reason: X11 ungrab races are non-fatal
101-
pass
93+
self._ungrab_stale(root, current_ids)
10294
for binding in bindings:
103-
prior = self._registered.get(binding.binding_id)
104-
if prior is not None and prior[0] == binding.combo:
105-
continue
106-
if prior is not None:
107-
self._registered.pop(binding.binding_id, None)
95+
self._sync_one(root, binding)
96+
disp.sync()
97+
98+
def _ungrab_stale(self, root, current_ids: set) -> None:
99+
stale_ids = [bid for bid in self._registered if bid not in current_ids]
100+
for stale in stale_ids:
101+
_combo, mask, keycode = self._registered.pop(stale)
102+
self._ungrab_masked(root, keycode, mask)
103+
104+
def _sync_one(self, root, binding: HotkeyBinding) -> None:
105+
prior = self._registered.get(binding.binding_id)
106+
if prior is not None and prior[0] == binding.combo:
107+
return
108+
if prior is not None:
109+
self._registered.pop(binding.binding_id, None)
110+
try:
111+
mask, keycode = _combo_to_x11(binding.combo)
112+
except ValueError as error:
113+
autocontrol_logger.error(
114+
"hotkey parse failed for %s: %r", binding.combo, error,
115+
)
116+
return
117+
if self._grab_masked(root, binding, mask, keycode):
118+
self._registered[binding.binding_id] = (
119+
binding.combo, mask, keycode,
120+
)
121+
122+
@staticmethod
123+
def _ungrab_masked(root, keycode: int, mask: int) -> None:
124+
for extra_mask in _lock_mask_variants():
108125
try:
109-
mask, keycode = _combo_to_x11(binding.combo)
110-
except ValueError as error:
111-
autocontrol_logger.error(
112-
"hotkey parse failed for %s: %r", binding.combo, error,
126+
root.ungrab_key(keycode, mask | extra_mask)
127+
except Exception: # nosec B110 # noqa: BLE001 # reason: X11 ungrab races are non-fatal
128+
pass
129+
130+
@staticmethod
131+
def _grab_masked(root, binding: HotkeyBinding,
132+
mask: int, keycode: int) -> bool:
133+
from Xlib import X
134+
135+
for extra_mask in _lock_mask_variants():
136+
try:
137+
root.grab_key(
138+
keycode, mask | extra_mask, True,
139+
X.GrabModeAsync, X.GrabModeAsync,
113140
)
114-
continue
115-
ok = True
116-
for extra_mask in _lock_mask_variants():
117-
try:
118-
root.grab_key(
119-
keycode, mask | extra_mask, True,
120-
X.GrabModeAsync, X.GrabModeAsync,
121-
)
122-
except Exception as error: # noqa: BLE001 # reason: X errors
123-
autocontrol_logger.error(
124-
"XGrabKey failed for %s: %r", binding.combo, error,
125-
)
126-
ok = False
127-
break
128-
if ok:
129-
self._registered[binding.binding_id] = (
130-
binding.combo, mask, keycode,
141+
except Exception as error: # noqa: BLE001 # reason: X errors
142+
autocontrol_logger.error(
143+
"XGrabKey failed for %s: %r", binding.combo, error,
131144
)
132-
disp.sync()
145+
return False
146+
return True
133147

134148
def _drain(self, disp, fire: "callable") -> None:
135149
from Xlib import X

je_auto_control/utils/hotkey/backends/windows_backend.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def run_forever(self, context: BackendContext) -> None:
4747
self._dispatch(msg.wParam, context.fire)
4848
context.stop_event.wait(0.05)
4949
finally:
50-
for reg_id, _ in list(self._registered.values()):
50+
for reg_id, _ in self._registered.values():
5151
user32.UnregisterHotKey(None, reg_id)
5252
self._registered.clear()
5353

je_auto_control/utils/hotkey/hotkey_daemon.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,7 @@ def list_bindings(self) -> List[HotkeyBinding]:
148148
with self._lock:
149149
return list(self._bindings.values())
150150

151-
def _snapshot(self) -> List[HotkeyBinding]:
152-
with self._lock:
153-
return list(self._bindings.values())
151+
_snapshot = list_bindings
154152

155153
def start(self) -> None:
156154
if self._thread is not None and self._thread.is_alive():

0 commit comments

Comments
 (0)