pyloid 0.23.9__tar.gz → 0.23.11__tar.gz
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.
- {pyloid-0.23.9 → pyloid-0.23.11}/PKG-INFO +1 -1
- {pyloid-0.23.9 → pyloid-0.23.11}/pyproject.toml +1 -1
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/browser_window.py +26 -3
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/pyloid.py +15 -10
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/rpc.py +69 -9
- {pyloid-0.23.9 → pyloid-0.23.11}/LICENSE +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/README.md +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/__init__.py +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/api.py +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/autostart.py +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/custom/titlebar.py +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/filewatcher.py +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/js_api/base.py +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/js_api/event_api.py +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/js_api/window_api.py +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/monitor.py +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/serve.py +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/store.py +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/thread_pool.py +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/timer.py +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/tray.py +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/url_interceptor.py +0 -0
- {pyloid-0.23.9 → pyloid-0.23.11}/src/pyloid/utils.py +0 -0
@@ -42,12 +42,13 @@ from PySide6.QtWebEngineCore import (
|
|
42
42
|
QWebEngineSettings,
|
43
43
|
QWebEngineDesktopMediaRequest,
|
44
44
|
)
|
45
|
+
import threading
|
45
46
|
|
46
47
|
# from .url_interceptor import CustomUrlInterceptor
|
47
48
|
from .rpc import PyloidRPC
|
48
49
|
|
49
50
|
if TYPE_CHECKING:
|
50
|
-
from .pyloid import _Pyloid
|
51
|
+
from .pyloid import _Pyloid, Pyloid
|
51
52
|
|
52
53
|
|
53
54
|
class CustomWebPage(QWebEnginePage):
|
@@ -299,6 +300,7 @@ class _BrowserWindow:
|
|
299
300
|
def __init__(
|
300
301
|
self,
|
301
302
|
app: "_Pyloid",
|
303
|
+
window_wrapper: "BrowserWindow",
|
302
304
|
title: str = "pyloid app",
|
303
305
|
width: int = 800,
|
304
306
|
height: int = 600,
|
@@ -315,6 +317,7 @@ class _BrowserWindow:
|
|
315
317
|
self._window = QMainWindow()
|
316
318
|
self.web_view = CustomWebEngineView(self)
|
317
319
|
|
320
|
+
|
318
321
|
if rpc:
|
319
322
|
self.rpc = rpc
|
320
323
|
self.rpc_url = rpc.url
|
@@ -329,6 +332,7 @@ class _BrowserWindow:
|
|
329
332
|
self._window.closeEvent = self.closeEvent # Override closeEvent method
|
330
333
|
###########################################################################################
|
331
334
|
self.app = app
|
335
|
+
self.window_wrapper = window_wrapper
|
332
336
|
self.title = title
|
333
337
|
self.width = width
|
334
338
|
self.height = height
|
@@ -346,8 +350,27 @@ class _BrowserWindow:
|
|
346
350
|
self.shortcuts = {}
|
347
351
|
self.close_on_load = True
|
348
352
|
self.splash_screen = None
|
349
|
-
###########################################################################################
|
353
|
+
###########################################################################################
|
354
|
+
# RPC 서버가 없으면 추가하지 않음
|
355
|
+
if not self.rpc:
|
356
|
+
return;
|
357
|
+
|
358
|
+
self.rpc.pyloid = self.app.pyloid_wrapper
|
359
|
+
self.rpc.window = self.window_wrapper
|
360
|
+
|
361
|
+
# RPC 서버 중복 방지
|
362
|
+
if self.rpc in self.app.rpc_servers:
|
363
|
+
return;
|
364
|
+
|
365
|
+
# RPC 서버 추가
|
366
|
+
self.app.rpc_servers.add(self.rpc)
|
367
|
+
|
368
|
+
# Start unique RPC servers
|
369
|
+
server_thread = threading.Thread(target=self.rpc.start, daemon=True)
|
370
|
+
server_thread.start()
|
371
|
+
###########################################################################################
|
350
372
|
|
373
|
+
|
351
374
|
def _set_custom_frame(
|
352
375
|
self,
|
353
376
|
use_custom: bool,
|
@@ -2037,7 +2060,7 @@ class BrowserWindow(QObject):
|
|
2037
2060
|
):
|
2038
2061
|
super().__init__()
|
2039
2062
|
self._window = _BrowserWindow(
|
2040
|
-
app, title, width, height, x, y, frame, context_menu, dev_tools, rpc
|
2063
|
+
app, self, title, width, height, x, y, frame, context_menu, dev_tools, rpc
|
2041
2064
|
)
|
2042
2065
|
self.command_signal.connect(self._handle_command)
|
2043
2066
|
|
@@ -87,6 +87,7 @@ class _WindowController(QObject):
|
|
87
87
|
class _Pyloid(QApplication):
|
88
88
|
def __init__(
|
89
89
|
self,
|
90
|
+
pyloid_wrapper: "Pyloid",
|
90
91
|
app_name,
|
91
92
|
single_instance=True,
|
92
93
|
data=None,
|
@@ -115,6 +116,8 @@ class _Pyloid(QApplication):
|
|
115
116
|
```
|
116
117
|
"""
|
117
118
|
super().__init__(sys.argv)
|
119
|
+
|
120
|
+
self.pyloid_wrapper = pyloid_wrapper
|
118
121
|
|
119
122
|
self.data = data
|
120
123
|
|
@@ -160,6 +163,8 @@ class _Pyloid(QApplication):
|
|
160
163
|
self.styleHints().colorSchemeChanged.connect(self._handle_color_scheme_change)
|
161
164
|
|
162
165
|
self.dirs = PlatformDirs(self.app_name, appauthor=False)
|
166
|
+
|
167
|
+
self.rpc_servers: Set[PyloidRPC] = set()
|
163
168
|
|
164
169
|
# def set_theme(self, theme: Literal["system", "dark", "light"]):
|
165
170
|
# """
|
@@ -328,16 +333,16 @@ class _Pyloid(QApplication):
|
|
328
333
|
```
|
329
334
|
"""
|
330
335
|
|
331
|
-
# Collect and deduplicate RPC servers
|
332
|
-
rpc_servers: Set[PyloidRPC] = set()
|
333
|
-
for window in self.windows_dict.values():
|
334
|
-
|
335
|
-
|
336
|
+
# # Collect and deduplicate RPC servers
|
337
|
+
# rpc_servers: Set[PyloidRPC] = set()
|
338
|
+
# for window in self.windows_dict.values():
|
339
|
+
# if window._window.rpc is not None:
|
340
|
+
# rpc_servers.add(window._window.rpc)
|
336
341
|
|
337
|
-
#
|
338
|
-
for rpc in rpc_servers:
|
339
|
-
|
340
|
-
|
342
|
+
# # Start unique RPC servers
|
343
|
+
# for rpc in rpc_servers:
|
344
|
+
# server_thread = threading.Thread(target=rpc.start, daemon=True)
|
345
|
+
# server_thread.start()
|
341
346
|
|
342
347
|
|
343
348
|
if is_production():
|
@@ -1679,7 +1684,7 @@ class Pyloid(QObject):
|
|
1679
1684
|
|
1680
1685
|
self.data = None # 나중에 데이터 필요 시 수정
|
1681
1686
|
|
1682
|
-
self.app = _Pyloid(app_name, single_instance, self.data)
|
1687
|
+
self.app = _Pyloid(self, app_name, single_instance, self.data)
|
1683
1688
|
|
1684
1689
|
self.command_signal.connect(self._handle_command)
|
1685
1690
|
|
@@ -1,18 +1,38 @@
|
|
1
1
|
import asyncio
|
2
2
|
import json
|
3
3
|
import logging
|
4
|
+
import inspect
|
4
5
|
from functools import wraps
|
5
6
|
from typing import Any, Callable, Coroutine, Dict, List, Optional, Union
|
6
7
|
from .utils import get_free_port
|
7
8
|
from aiohttp import web
|
8
9
|
import threading
|
9
10
|
import time
|
10
|
-
import aiohttp_cors
|
11
|
+
import aiohttp_cors
|
12
|
+
from typing import TYPE_CHECKING
|
13
|
+
if TYPE_CHECKING:
|
14
|
+
from .pyloid import Pyloid
|
15
|
+
from .browser_window import BrowserWindow
|
11
16
|
|
12
17
|
# Configure logging
|
13
18
|
logging.basicConfig(level=logging.INFO)
|
14
19
|
log = logging.getLogger("pyloid.rpc")
|
15
20
|
|
21
|
+
class RPCContext:
|
22
|
+
"""
|
23
|
+
Class that provides context information when calling RPC methods.
|
24
|
+
|
25
|
+
Attributes
|
26
|
+
----------
|
27
|
+
pyloid : Pyloid
|
28
|
+
Pyloid application instance.
|
29
|
+
window : BrowserWindow
|
30
|
+
Current browser window instance.
|
31
|
+
"""
|
32
|
+
def __init__(self, pyloid: "Pyloid", window: "BrowserWindow"):
|
33
|
+
self.pyloid: "Pyloid" = pyloid
|
34
|
+
self.window: "BrowserWindow" = window
|
35
|
+
|
16
36
|
class RPCError(Exception):
|
17
37
|
"""
|
18
38
|
Custom exception for RPC-related errors.
|
@@ -109,6 +129,9 @@ class PyloidRPC:
|
|
109
129
|
self._functions: Dict[str, Callable[..., Coroutine[Any, Any, Any]]] = {}
|
110
130
|
self._app = web.Application()
|
111
131
|
|
132
|
+
self.pyloid: Optional["Pyloid"] = None
|
133
|
+
self.window: Optional["BrowserWindow"] = None
|
134
|
+
|
112
135
|
# CORS 설정 추가
|
113
136
|
cors = aiohttp_cors.setup(self._app, defaults={
|
114
137
|
"*": aiohttp_cors.ResourceOptions(
|
@@ -129,13 +152,15 @@ class PyloidRPC:
|
|
129
152
|
|
130
153
|
def method(self, name: Optional[str] = None) -> Callable:
|
131
154
|
"""
|
132
|
-
|
155
|
+
Use a decorator to register an async function as an RPC method.
|
156
|
+
|
157
|
+
If there is a 'ctx' parameter, an RPCContext object is automatically injected.
|
158
|
+
This object allows access to the pyloid application and current window.
|
133
159
|
|
134
160
|
Parameters
|
135
161
|
----------
|
136
162
|
name : Optional[str], optional
|
137
|
-
|
138
|
-
function's name is used. Defaults to None.
|
163
|
+
Name to register the RPC method. If None, the function name is used. Default is None.
|
139
164
|
|
140
165
|
Returns
|
141
166
|
-------
|
@@ -157,7 +182,10 @@ class PyloidRPC:
|
|
157
182
|
rpc = PyloidRPC()
|
158
183
|
|
159
184
|
@rpc.method()
|
160
|
-
async def add(a: int, b: int) -> int:
|
185
|
+
async def add(ctx, a: int, b: int) -> int:
|
186
|
+
# Access the application and window through ctx.pyloid and ctx.window
|
187
|
+
if ctx.window:
|
188
|
+
print(f"Window title: {ctx.window.title}")
|
161
189
|
return a + b
|
162
190
|
```
|
163
191
|
"""
|
@@ -168,13 +196,25 @@ class PyloidRPC:
|
|
168
196
|
if rpc_name in self._functions:
|
169
197
|
raise ValueError(f"RPC function name '{rpc_name}' is already registered.")
|
170
198
|
|
199
|
+
# Analyze function signature
|
200
|
+
sig = inspect.signature(func)
|
201
|
+
has_ctx_param = 'ctx' in sig.parameters
|
202
|
+
|
203
|
+
# Store the original function
|
171
204
|
self._functions[rpc_name] = func
|
172
205
|
log.info(f"RPC function registered: {rpc_name}")
|
173
206
|
|
174
207
|
@wraps(func)
|
175
208
|
async def wrapper(*args, **kwargs):
|
176
|
-
|
177
|
-
|
209
|
+
# If the function has a 'ctx' parameter and it's not provided, inject the context object
|
210
|
+
if has_ctx_param and 'ctx' not in kwargs:
|
211
|
+
ctx = RPCContext(
|
212
|
+
pyloid=self.pyloid,
|
213
|
+
window=self.window
|
214
|
+
)
|
215
|
+
kwargs['ctx'] = ctx
|
216
|
+
|
217
|
+
# Call the original function
|
178
218
|
return await func(*args, **kwargs)
|
179
219
|
return wrapper
|
180
220
|
return decorator
|
@@ -276,10 +316,30 @@ class PyloidRPC:
|
|
276
316
|
|
277
317
|
try:
|
278
318
|
log.debug(f"Executing RPC method: {method_name}(params={params})")
|
319
|
+
|
320
|
+
# 함수의 서명 분석하여 ctx 매개변수 유무 확인
|
321
|
+
sig = inspect.signature(func)
|
322
|
+
has_ctx_param = 'ctx' in sig.parameters
|
323
|
+
|
324
|
+
# ctx 매개변수가 있으면 컨텍스트 객체 생성
|
325
|
+
if has_ctx_param and isinstance(params, dict) and 'ctx' not in params:
|
326
|
+
ctx = RPCContext(
|
327
|
+
pyloid=self.pyloid,
|
328
|
+
window=self.window
|
329
|
+
)
|
330
|
+
# 딕셔너리 형태로 params 사용할 때
|
331
|
+
params = params.copy() # 원본 params 복사
|
332
|
+
params['ctx'] = ctx
|
333
|
+
|
279
334
|
# Call the function with positional or keyword arguments
|
280
335
|
if isinstance(params, list):
|
281
|
-
|
282
|
-
|
336
|
+
# 리스트 형태로 params 사용할 때 처리 필요
|
337
|
+
if has_ctx_param:
|
338
|
+
ctx = RPCContext(pyloid=self.pyloid, window=self.window)
|
339
|
+
result = await func(ctx, *params)
|
340
|
+
else:
|
341
|
+
result = await func(*params)
|
342
|
+
else: # isinstance(params, dict)
|
283
343
|
result = await func(**params)
|
284
344
|
|
285
345
|
# 5. Format Success Response (only for non-notification requests)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|