pyloid 0.23.9__py3-none-any.whl → 0.23.11__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.
pyloid/browser_window.py CHANGED
@@ -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
 
pyloid/pyloid.py CHANGED
@@ -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
- if window._window.rpc is not None:
335
- rpc_servers.add(window._window.rpc)
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
- # 고유한 RPC 서버만 시작
338
- for rpc in rpc_servers:
339
- server_thread = threading.Thread(target=rpc.start, daemon=True)
340
- server_thread.start()
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
 
pyloid/rpc.py CHANGED
@@ -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 # 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
- Decorator to register an async function as an RPC method.
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
- The name to register the RPC method under. If None, the
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
- # This wrapper exists to follow the decorator pattern.
177
- # The actual call uses the original function stored in _functions.
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
- result = await func(*params)
282
- else: # isinstance(params, dict)
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pyloid
3
- Version: 0.23.9
3
+ Version: 0.23.11
4
4
  Summary:
5
5
  Author: aesthetics-of-record
6
6
  Author-email: 111675679+aesthetics-of-record@users.noreply.github.com
@@ -1,15 +1,15 @@
1
1
  pyloid/__init__.py,sha256=YKwMCSOds1QVi9N7EGfY0Z7BEjJn8j6HGqRblZlZClA,235
2
2
  pyloid/api.py,sha256=A61Kmddh8BlpT3LfA6NbPQNzFmD95vQ4WKX53oKsGYU,2419
3
3
  pyloid/autostart.py,sha256=K7DQYl4LHItvPp0bt1V9WwaaZmVSTeGvadkcwG-KKrI,3899
4
- pyloid/browser_window.py,sha256=t7YKrdIZ5EDyO1R8r4sUCHi60DcQaBqoMgnks2BLYAs,99775
4
+ pyloid/browser_window.py,sha256=FJPD9H47yCjtw-v2QjJmm8ENvznspOdKiu72MKBh-Xs,100591
5
5
  pyloid/custom/titlebar.py,sha256=itzK9pJbZMQ7BKca9kdbuHMffurrw15UijR6OU03Xsk,3894
6
6
  pyloid/filewatcher.py,sha256=3M5zWVUf1OhlkWJcDFC8ZA9agO4Q-U8WdgGpy6kaVz0,4601
7
7
  pyloid/js_api/base.py,sha256=XD3sqWYAb1nw6VgY3xXsU4ls5xLGrxpOqmLRJm_vBso,8669
8
8
  pyloid/js_api/event_api.py,sha256=w0z1DcmwcmseqfcoZWgsQmFC2iBCgTMVJubTaHeXI1c,957
9
9
  pyloid/js_api/window_api.py,sha256=-isphU3m2wGB5U0yZrSuK_4XiBz2mG45HsjYTUq7Fxs,7348
10
10
  pyloid/monitor.py,sha256=1mXvHm5deohnNlTLcRx4sT4x-stnOIb0dUQnnxN50Uo,28295
11
- pyloid/pyloid.py,sha256=yWZ6wJjdKaDPz3k80fa9quFqLhzV4asyy4mDKGVEt-M,85593
12
- pyloid/rpc.py,sha256=V7iPdKPHpsG6LOgXTSIshYTR-dkLXWHoQchB_7DvwKY,15648
11
+ pyloid/pyloid.py,sha256=r70Xa-SKk0yZsJ_d2Kl-adLy7P2VX1yqxymVBVeNNa8,85762
12
+ pyloid/rpc.py,sha256=_T7zDdKgnoUg9pvBaHHn3uG4LW4W7euHp_80QZs8wlk,18111
13
13
  pyloid/serve.py,sha256=wJIBqiLr1-8FvBdV3yybeBtVXsu94FfWYKjHL0eQ68s,1444
14
14
  pyloid/store.py,sha256=p0plJj52hQjjtNMVJhy20eNLXfQ3Qmf7LtGHQk7FiPg,4471
15
15
  pyloid/thread_pool.py,sha256=fKOBb8jMfZn_7crA_fJCno8dObBRZE31EIWaNQ759aw,14616
@@ -17,7 +17,7 @@ pyloid/timer.py,sha256=RqMsChFUd93cxMVgkHWiIKrci0QDTBgJSTULnAtYT8M,8712
17
17
  pyloid/tray.py,sha256=D12opVEc2wc2T4tK9epaN1oOdeziScsIVNM2uCN7C-A,1710
18
18
  pyloid/url_interceptor.py,sha256=AFjPANDELc9-E-1TnVvkNVc-JZBJYf0677dWQ8LDaqw,726
19
19
  pyloid/utils.py,sha256=NqB8W-irXDtTGb74rrJ2swU6tgzU0HSE8lGewrStOKc,5685
20
- pyloid-0.23.9.dist-info/LICENSE,sha256=F96EzotgWhhpnQTW2TcdoqrMDir1jyEo6H915tGQ-QE,11524
21
- pyloid-0.23.9.dist-info/METADATA,sha256=9GuOWACqO3ILpuAH02dacBVY6AHzDRcEDpb0ZJZcq-I,3197
22
- pyloid-0.23.9.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
23
- pyloid-0.23.9.dist-info/RECORD,,
20
+ pyloid-0.23.11.dist-info/LICENSE,sha256=F96EzotgWhhpnQTW2TcdoqrMDir1jyEo6H915tGQ-QE,11524
21
+ pyloid-0.23.11.dist-info/METADATA,sha256=M9MiMVNioWt0DDWUU9FTWJiChcc7YwrVdPoBnpYCtSE,3198
22
+ pyloid-0.23.11.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
23
+ pyloid-0.23.11.dist-info/RECORD,,