pyloid 0.18.2__tar.gz → 0.20.2.dev2__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -198,4 +198,4 @@ Apache License
198
198
  distributed under the License is distributed on an "AS IS" BASIS,
199
199
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
200
  See the License for the specific language governing permissions and
201
- limitations under the License.
201
+ limitations under the License.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: pyloid
3
- Version: 0.18.2
3
+ Version: 0.20.2.dev2
4
4
  Summary:
5
5
  Author: aesthetics-of-record
6
6
  Author-email: 111675679+aesthetics-of-record@users.noreply.github.com
@@ -10,6 +10,8 @@ Classifier: Programming Language :: Python :: 3.9
10
10
  Classifier: Programming Language :: Python :: 3.10
11
11
  Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Requires-Dist: pyinstaller (>=6.11.1,<7.0.0)
13
15
  Requires-Dist: pyside6 (>=6.8.1,<7.0.0)
14
16
  Description-Content-Type: text/markdown
15
17
 
@@ -85,7 +87,7 @@ This project uses PySide6, which is licensed under the LGPL (Lesser General Publ
85
87
 
86
88
  ## Contributing 🤝
87
89
 
88
- (Add information about how to contribute here)
90
+ Not Yet
89
91
 
90
92
  ## Issues
91
93
 
@@ -70,7 +70,7 @@ This project uses PySide6, which is licensed under the LGPL (Lesser General Publ
70
70
 
71
71
  ## Contributing 🤝
72
72
 
73
- (Add information about how to contribute here)
73
+ Not Yet
74
74
 
75
75
  ## Issues
76
76
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pyloid"
3
- version = "0.18.2"
3
+ version = "0.20.2-dev2"
4
4
  description = ""
5
5
  authors = ["aesthetics-of-record <111675679+aesthetics-of-record@users.noreply.github.com>"]
6
6
  readme = "README.md"
@@ -11,6 +11,7 @@ packages = [
11
11
  [tool.poetry.dependencies]
12
12
  python = ">=3.9,<3.14"
13
13
  pyside6 = "^6.8.1"
14
+ pyinstaller = "^6.11.1"
14
15
 
15
16
 
16
17
  [build-system]
@@ -0,0 +1,8 @@
1
+ from .pyloid import Pyloid
2
+ from .api import PyloidAPI, Bridge
3
+ from .utils import get_production_path, is_production
4
+ from .tray import TrayEvent
5
+ from .timer import PyloidTimer
6
+ from .builder import build_from_spec, cleanup_before_build, create_spec_from_json, get_site_packages
7
+
8
+ __all__ = ['Pyloid', 'PyloidAPI', 'Bridge', 'get_production_path', 'is_production', 'TrayEvent', 'PyloidTimer', 'build_from_spec', 'cleanup_before_build', 'create_spec_from_json', 'get_site_packages']
@@ -11,7 +11,11 @@ from PySide6.QtGui import (
11
11
  QCursor,
12
12
  )
13
13
  from PySide6.QtCore import Qt, QPoint, QUrl, QEvent, QFile
14
- from PySide6.QtWebEngineCore import QWebEnginePage, QWebEngineSettings
14
+ from PySide6.QtWebEngineCore import (
15
+ QWebEnginePage,
16
+ QWebEngineSettings,
17
+ QWebEngineUrlRequestInterceptor,
18
+ )
15
19
  from .api import PyloidAPI
16
20
  import uuid
17
21
  from typing import List, Optional, Dict, Callable
@@ -40,10 +44,15 @@ class CustomWebPage(QWebEnginePage):
40
44
  self.desktopMediaRequested.connect(self._handleDesktopMediaRequest)
41
45
  self._permission_handlers = {}
42
46
  self._desktop_media_handler = None
47
+ self._url_handlers = {} # URL 핸들러 저장을 위한 딕셔너리 추가
48
+
49
+ # interceptor ( all url request )
50
+ self.interceptor = CustomUrlInterceptor()
51
+ self.profile().setUrlRequestInterceptor(self.interceptor)
43
52
 
44
53
  def _handlePermissionRequest(self, origin: QUrl, feature: QWebEnginePage.Feature):
45
54
  print(origin, feature)
46
-
55
+
47
56
  """Default permission request handler"""
48
57
  if feature in self._permission_handlers:
49
58
  # Execute if a handler is registered
@@ -62,9 +71,41 @@ class CustomWebPage(QWebEnginePage):
62
71
  def _handleDesktopMediaRequest(self, *args, **kwargs):
63
72
  print("Desktop media request received:", args, kwargs)
64
73
 
65
- # def setDesktopMediaHandler(self, handler):
66
- # """desktop media handler"""
67
- # self._desktop_media_handler = handler
74
+ # interceptor ( navigation request )
75
+ def acceptNavigationRequest(self, url, navigation_type, is_main_frame):
76
+ """네비게이션 요청을 처리하는 메서드"""
77
+ print(f"Navigation Request - URL: {url.toString()}")
78
+ print(f"Navigation Type: {navigation_type}")
79
+ print(f"Is Main Frame: {is_main_frame}")
80
+
81
+ # # URL이 구글이 아닐 경우에만 구글로 리다이렉트
82
+ # if "example.com" not in url.toString():
83
+ # self.setUrl(QUrl("https://www.example.com"))
84
+ # return False
85
+
86
+ return True
87
+
88
+ def add_url_handler(self, pattern: str, handler):
89
+ """URL 패턴에 대한 핸들러 등록
90
+
91
+ Parameters:
92
+ -----------
93
+ pattern : str
94
+ 정규표현식 패턴
95
+ handler : callable
96
+ URL을 인자로 받고 bool을 반환해야 함
97
+ True를 반환하면 네비게이션을 허용, False를 반환하면 차단
98
+ """
99
+ import re
100
+
101
+ self._url_handlers[re.compile(pattern)] = handler
102
+
103
+
104
+ # interceptor ( all url request )
105
+ class CustomUrlInterceptor(QWebEngineUrlRequestInterceptor):
106
+ def interceptRequest(self, info):
107
+ url = info.requestUrl().toString()
108
+ print(url)
68
109
 
69
110
 
70
111
  class CustomWebEngineView(QWebEngineView):
@@ -81,15 +122,15 @@ class CustomWebEngineView(QWebEngineView):
81
122
  self.resize_direction = None
82
123
  self.screen_geometry = self.screen().virtualGeometry()
83
124
  self.is_resizing_enabled = True
84
-
125
+
85
126
  self.setAttribute(Qt.WA_SetCursor, False)
86
-
127
+
87
128
  self.is_in_resize_area = False
88
129
 
89
130
  def mouse_press_event(self, event):
90
131
  if self.parent.frame or not self.is_resizing_enabled:
91
132
  return
92
-
133
+
93
134
  if event.button() == Qt.LeftButton:
94
135
  self.resize_direction = self.get_resize_direction(event.pos())
95
136
  if self.resize_direction:
@@ -104,19 +145,19 @@ class CustomWebEngineView(QWebEngineView):
104
145
  def mouse_move_event(self, event):
105
146
  if self.parent.frame or not self.is_resizing_enabled:
106
147
  return
107
-
148
+
108
149
  # Check resize direction
109
150
  was_in_resize_area = self.is_in_resize_area
110
151
  resize_direction = self.get_resize_direction(event.pos())
111
152
  self.is_in_resize_area = bool(resize_direction)
112
-
153
+
113
154
  if resize_direction and not self.is_resizing:
114
155
  self.set_cursor_for_resize_direction(resize_direction)
115
-
156
+
116
157
  if self.is_resizing:
117
158
  self.resize_window(event.globalPos())
118
159
  return
119
-
160
+
120
161
  # Change cursor when entering/leaving resize area
121
162
  if self.is_in_resize_area != was_in_resize_area:
122
163
  if self.is_in_resize_area:
@@ -129,12 +170,12 @@ class CustomWebEngineView(QWebEngineView):
129
170
  def mouse_release_event(self, event):
130
171
  if self.parent.frame or not self.is_resizing_enabled:
131
172
  return
132
-
173
+
133
174
  if event.button() == Qt.LeftButton:
134
175
  self.is_resizing = False
135
-
176
+
136
177
  if self.resize_direction:
137
- self.unsetCursor()
178
+ self.unsetCursor()
138
179
  self.resize_direction = None
139
180
 
140
181
  self.setAttribute(Qt.WA_SetCursor, False)
@@ -181,7 +222,7 @@ class CustomWebEngineView(QWebEngineView):
181
222
  cursor = Qt.SizeFDiagCursor
182
223
  elif direction in ["right-top", "left-bottom"]:
183
224
  cursor = Qt.SizeBDiagCursor
184
-
225
+
185
226
  if cursor:
186
227
  self.setCursor(cursor)
187
228
  self.setAttribute(Qt.WA_SetCursor, True)
@@ -378,7 +419,7 @@ class BrowserWindow:
378
419
  js_api.window_id = self.id
379
420
  js_api.window = self
380
421
  js_api.app = self.app
381
-
422
+
382
423
  self.channel.registerObject(js_api.__class__.__name__, js_api)
383
424
 
384
425
  self.web_view.page().setWebChannel(self.channel)
@@ -1887,12 +1928,12 @@ class BrowserWindow:
1887
1928
  def set_desktop_media_handler(self, handler):
1888
1929
  """
1889
1930
  데스크톱 미디어(화면/윈도우) 선택 핸들러를 설정합니다.
1890
-
1931
+
1891
1932
  Parameters
1892
1933
  ----------
1893
1934
  handler : callable
1894
- 요청을 처리할 핸들러 함수. QWebEngineDesktopMediaRequest��� 인자로 받습니다.
1895
-
1935
+ 요청을 처리할 핸들러 함수. QWebEngineDesktopMediaRequest 인자로 받습니다.
1936
+
1896
1937
  Examples
1897
1938
  --------
1898
1939
  ```python
@@ -1900,15 +1941,15 @@ class BrowserWindow:
1900
1941
  # 사용 가능한 화면 목록 출력
1901
1942
  for screen in request.screenList():
1902
1943
  print(f"Screen: {screen.name}")
1903
-
1944
+
1904
1945
  # 사용 가능한 윈도우 목록 출력
1905
1946
  for window in request.windowList():
1906
1947
  print(f"Window: {window.name}")
1907
-
1948
+
1908
1949
  # 첫 번째 화면 선택
1909
1950
  if request.screenList():
1910
1951
  request.selectScreen(request.screenList()[0])
1911
-
1952
+
1912
1953
  window.set_desktop_media_handler(custom_media_handler)
1913
1954
  ```
1914
1955
  """
@@ -0,0 +1,81 @@
1
+ import json
2
+ from pathlib import Path
3
+ from PyInstaller.__main__ import run as pyinstaller_run
4
+ import shutil
5
+ import site
6
+ from pyloid.builder.spec import create_spec_from_json
7
+
8
+ __all__ = ['create_spec_from_json', 'cleanup_before_build', 'build_from_spec', 'get_site_packages']
9
+
10
+ def cleanup_before_build(json_path):
11
+ """Function to clean up unnecessary files before build"""
12
+ try:
13
+ with open(json_path, 'r', encoding='utf-8') as f:
14
+ config = json.load(f)
15
+
16
+ cleanup_patterns = config.get('before_build', {}).get('cleanup_patterns', [])
17
+ if not cleanup_patterns:
18
+ return Exception("Cannot find cleanup patterns.")
19
+
20
+ site_packages = get_site_packages()
21
+ if not site_packages:
22
+ raise Exception("Cannot find site-packages directory.")
23
+
24
+ dist_dir = Path(f'{site_packages}')
25
+ if not dist_dir.exists():
26
+ raise Exception(f"Cannot find directory to clean: {dist_dir}")
27
+
28
+ print("\033[1;34mCleaning up unnecessary files...\033[0m")
29
+ exclude_patterns = [p[1:] for p in cleanup_patterns if p.startswith('!')]
30
+ include_patterns = [p for p in cleanup_patterns if not p.startswith('!')]
31
+
32
+ for pattern in include_patterns:
33
+ matching_files = list(dist_dir.glob(pattern))
34
+ for file_path in matching_files:
35
+ if any(file_path.match(p) for p in exclude_patterns):
36
+ print(f"\033[33mSkipping: {file_path}\033[0m")
37
+ continue
38
+ print(f"\033[33mRemoving: {file_path}\033[0m")
39
+ if file_path.is_dir():
40
+ shutil.rmtree(file_path)
41
+ else:
42
+ file_path.unlink()
43
+ print(f"\033[32mRemoved: {file_path}\033[0m")
44
+
45
+ print("\033[1;32mFile cleanup completed.\033[0m")
46
+
47
+ except Exception as e:
48
+ raise Exception(f"\033[1;31mError occurred during file cleanup: {e}\033[0m")
49
+
50
+ def build_from_spec(spec_path):
51
+ try:
52
+ pyinstaller_run([
53
+ '--clean', # Clean temporary files
54
+ spec_path # Spec file path
55
+ ])
56
+ print("Build completed.")
57
+
58
+ except Exception as e:
59
+ raise Exception(f"Error occurred during build: {e}")
60
+
61
+ def get_site_packages():
62
+ """
63
+ Returns the path to the site-packages directory.
64
+ Raises an exception if the directory is not found.
65
+ """
66
+ for path in site.getsitepackages():
67
+ if 'site-packages' in path:
68
+ return path
69
+ raise Exception("Site-packages directory not found.")
70
+
71
+ def main():
72
+ spec_path = create_spec_from_json('build_config.json')
73
+
74
+ cleanup_before_build('build_config.json')
75
+
76
+ # build_from_spec(spec_path)
77
+
78
+
79
+
80
+ if __name__ == "__main__":
81
+ main()
@@ -0,0 +1,73 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "required": ["before_build", "name", "main_script", "datas", "excludes", "icon", "bundle"],
5
+ "properties": {
6
+ "before_build": {
7
+ "type": "object",
8
+ "properties": {
9
+ "cleanup_patterns": {
10
+ "type": "array",
11
+ "items": {
12
+ "type": "string"
13
+ },
14
+ "description": "List of file patterns to clean up in the library folder before building (used to remove files that cannot be excluded through pyinstaller) - default path is site-packages"
15
+ }
16
+ }
17
+ },
18
+ "name": {
19
+ "type": "string",
20
+ "description": "Application name"
21
+ },
22
+ "main_script": {
23
+ "type": "string",
24
+ "description": "Path to the main Python script"
25
+ },
26
+ "datas": {
27
+ "type": "array",
28
+ "items": {
29
+ "type": "array",
30
+ "minItems": 2,
31
+ "maxItems": 2,
32
+ "items": [
33
+ { "type": "string" },
34
+ { "type": "string" }
35
+ ]
36
+ },
37
+ "description": "List of data files to include [source path, destination path]"
38
+ },
39
+ "excludes": {
40
+ "type": "array",
41
+ "items": {
42
+ "type": "string"
43
+ },
44
+ "description": "List of modules to exclude during build with pyinstaller"
45
+ },
46
+ "icon": {
47
+ "type": "string",
48
+ "description": "Path to the application icon file"
49
+ },
50
+ "bundle": {
51
+ "type": "object",
52
+ "properties": {
53
+ "windows": {
54
+ "type": "string",
55
+ "enum": ["onefile", "directory"],
56
+ "description": "Windows build type"
57
+ },
58
+ "macos": {
59
+ "type": "string",
60
+ "enum": ["app"],
61
+ "description": "macOS build type"
62
+ },
63
+ "linux": {
64
+ "type": "string",
65
+ "enum": ["onefile", "directory"],
66
+ "description": "Linux build type"
67
+ }
68
+ },
69
+ "required": ["windows", "macos", "linux"],
70
+ "description": "Build settings for each OS"
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,266 @@
1
+ from pyloid.utils import get_platform
2
+ import json
3
+ from pathlib import Path
4
+
5
+
6
+ def create_spec_from_json(json_path):
7
+ with open(json_path, "r", encoding="utf-8") as f:
8
+ config = json.load(f)
9
+
10
+ os_type = get_platform()
11
+
12
+ if os_type == "macos":
13
+ spec_content = _create_macos_spec(config)
14
+ elif os_type == "linux":
15
+ spec_content = _create_linux_spec(config)
16
+ else: # windows
17
+ spec_content = _create_windows_spec(config)
18
+
19
+ spec_path = Path(f"build-{os_type}.spec")
20
+ spec_path.write_text(spec_content, encoding="utf-8")
21
+
22
+ return str(spec_path)
23
+
24
+
25
+ def _create_windows_spec(config):
26
+ bundle_type = config.get("bundle", {}).get("windows", "directory")
27
+ console = config.get("console", False)
28
+
29
+ base_spec = f"""# -*- mode: python ; coding: utf-8 -*-
30
+
31
+ block_cipher = None
32
+
33
+ a = Analysis(
34
+ ['{config['main_script']}'],
35
+ pathex={config.get('pathex', [])},
36
+ binaries={config.get('binaries', [])},
37
+ datas={config.get('datas', [])},
38
+ hiddenimports={config.get('hiddenimports', [])},
39
+ hookspath={config.get('hookspath', [])},
40
+ hooksconfig={config.get('hooksconfig', {})},
41
+ runtime_hooks={config.get('runtime_hooks', [])},
42
+ excludes={config.get('excludes', [])},
43
+ win_no_prefer_redirects=False,
44
+ win_private_assemblies=False,
45
+ cipher=block_cipher,
46
+ noarchive=False
47
+ )
48
+
49
+ pyz = PYZ(a.pure, a.zipped_data,
50
+ cipher=block_cipher)
51
+ """
52
+
53
+ if bundle_type == "onefile":
54
+ return (
55
+ base_spec
56
+ + f"""
57
+ exe = EXE(
58
+ pyz,
59
+ a.scripts,
60
+ a.binaries,
61
+ a.zipfiles,
62
+ a.datas,
63
+ [],
64
+ name='{config.get("name", "pyloid-app")}',
65
+ debug=False,
66
+ bootloader_ignore_signals=False,
67
+ strip=False,
68
+ upx=True,
69
+ upx_exclude=[],
70
+ runtime_tmpdir=None,
71
+ console={console},
72
+ disable_windowed_traceback=False,
73
+ argv_emulation=False,
74
+ target_arch=None,
75
+ codesign_identity=None,
76
+ entitlements_file=None,
77
+ icon='{config.get('icon', 'src-pyloid/icons/icon.ico')}'
78
+ )
79
+ """
80
+ )
81
+ else:
82
+ return (
83
+ base_spec
84
+ + f"""
85
+ exe = EXE(
86
+ pyz,
87
+ a.scripts,
88
+ [],
89
+ exclude_binaries=True,
90
+ name='{config.get("name", "pyloid-app")}',
91
+ debug=False,
92
+ bootloader_ignore_signals=False,
93
+ strip=False,
94
+ upx=True,
95
+ console={console},
96
+ disable_windowed_traceback=False,
97
+ argv_emulation=False,
98
+ target_arch=None,
99
+ codesign_identity=None,
100
+ entitlements_file=None,
101
+ icon='{config.get('icon', 'src-pyloid/icons/icon.ico')}'
102
+ )
103
+
104
+ coll = COLLECT(
105
+ exe,
106
+ a.binaries,
107
+ a.zipfiles,
108
+ a.datas,
109
+ strip=False,
110
+ upx=True,
111
+ upx_exclude=[],
112
+ name='{config.get("name", "pyloid-app")}'
113
+ )
114
+ """
115
+ )
116
+
117
+
118
+ def _create_macos_spec(config):
119
+ console = config.get("console", False)
120
+ return f"""# -*- mode: python ; coding: utf-8 -*-
121
+
122
+ a = Analysis(
123
+ ['{config['main_script']}'],
124
+ pathex={config.get('pathex', [])},
125
+ binaries={config.get('binaries', [])},
126
+ datas={config.get('datas', [])},
127
+ hiddenimports={config.get('hiddenimports', [])},
128
+ hookspath={config.get('hookspath', [])},
129
+ hooksconfig={config.get('hooksconfig', {})},
130
+ runtime_hooks={config.get('runtime_hooks', [])},
131
+ excludes={config.get('excludes', [])},
132
+ noarchive=False,
133
+ optimize=0
134
+ )
135
+
136
+ pyz = PYZ(a.pure)
137
+
138
+ exe = EXE(
139
+ pyz,
140
+ a.scripts,
141
+ [],
142
+ exclude_binaries=True,
143
+ name='{config.get("name", "pyloid-app")}',
144
+ debug=False,
145
+ bootloader_ignore_signals=False,
146
+ strip=False,
147
+ upx=True,
148
+ console={console},
149
+ disable_windowed_traceback=False,
150
+ argv_emulation=False,
151
+ target_arch=None,
152
+ codesign_identity=None,
153
+ entitlements_file=None,
154
+ icon='{config.get('icon', 'src-pyloid/icons/icon.ico')}'
155
+ )
156
+
157
+ coll = COLLECT(
158
+ exe,
159
+ a.binaries,
160
+ a.datas,
161
+ strip=False,
162
+ upx=True,
163
+ upx_exclude=[],
164
+ name='{config.get("name", "pyloid-app")}'
165
+ )
166
+
167
+ app = BUNDLE(
168
+ coll,
169
+ name='{config.get("name", "pyloid-app")}.app',
170
+ icon='{config.get('icon', 'src-pyloid/icons/icon.icns')}',
171
+ bundle_identifier=None
172
+ )
173
+ """
174
+
175
+
176
+ def _create_linux_spec(config):
177
+ bundle_type = config.get("bundle", {}).get("linux", "directory")
178
+ console = config.get("console", False)
179
+
180
+ base_spec = f"""# -*- mode: python ; coding: utf-8 -*-
181
+
182
+ block_cipher = None
183
+
184
+ a = Analysis(
185
+ ['{config['main_script']}'],
186
+ pathex={config.get('pathex', [])},
187
+ binaries={config.get('binaries', [])},
188
+ datas={config.get('datas', [])},
189
+ hiddenimports={config.get('hiddenimports', [])},
190
+ hookspath={config.get('hookspath', [])},
191
+ hooksconfig={config.get('hooksconfig', {})},
192
+ runtime_hooks={config.get('runtime_hooks', [])},
193
+ excludes={config.get('excludes', [])},
194
+ win_no_prefer_redirects=False,
195
+ win_private_assemblies=False,
196
+ cipher=block_cipher,
197
+ noarchive=False
198
+ )
199
+
200
+ pyz = PYZ(a.pure, a.zipped_data,
201
+ cipher=block_cipher)
202
+ """
203
+
204
+ if bundle_type == "onefile":
205
+ return (
206
+ base_spec
207
+ + f"""
208
+ exe = EXE(
209
+ pyz,
210
+ a.scripts,
211
+ a.binaries,
212
+ a.zipfiles,
213
+ a.datas,
214
+ [],
215
+ name='{config.get("name", "pyloid-app")}',
216
+ debug=False,
217
+ bootloader_ignore_signals=False,
218
+ strip=False,
219
+ upx=True,
220
+ upx_exclude=[],
221
+ runtime_tmpdir=None,
222
+ console={console},
223
+ disable_windowed_traceback=False,
224
+ argv_emulation=False,
225
+ target_arch=None,
226
+ codesign_identity=None,
227
+ entitlements_file=None,
228
+ icon='{config.get('icon', 'src-pyloid/icons/icon.png')}'
229
+ )
230
+ """
231
+ )
232
+ else:
233
+ return (
234
+ base_spec
235
+ + f"""
236
+ exe = EXE(
237
+ pyz,
238
+ a.scripts,
239
+ [],
240
+ exclude_binaries=True,
241
+ name='{config.get("name", "pyloid-app")}',
242
+ debug=False,
243
+ bootloader_ignore_signals=False,
244
+ strip=False,
245
+ upx=True,
246
+ console={console},
247
+ disable_windowed_traceback=False,
248
+ argv_emulation=False,
249
+ target_arch=None,
250
+ codesign_identity=None,
251
+ entitlements_file=None,
252
+ icon='{config.get('icon', 'src-pyloid/icons/icon.png')}'
253
+ )
254
+
255
+ coll = COLLECT(
256
+ exe,
257
+ a.binaries,
258
+ a.zipfiles,
259
+ a.datas,
260
+ strip=False,
261
+ upx=True,
262
+ upx_exclude=[],
263
+ name='{config.get("name", "pyloid-app")}'
264
+ )
265
+ """
266
+ )
@@ -25,17 +25,20 @@ def get_production_path(path: Optional[str] = None) -> Optional[str]:
25
25
  >>> print("Not in a production environment.")
26
26
  """
27
27
  if is_production():
28
- # Nuitka
29
28
  if hasattr(sys, '_MEIPASS'):
30
29
  # PyInstaller
31
30
  base_path = sys._MEIPASS
32
31
  else:
33
- # Nuitka의 경우 실행 파일이 있는 디렉토리를 기준으로 함
34
- base_path = os.path.dirname(os.path.abspath(sys.argv[0]))
32
+ # Nuitka
33
+ base_path = os.path.dirname(sys.executable)
34
+
35
+ if base_path is None:
36
+ # 환경변수가 없는 경우 실행 파일 디렉토리 사용
37
+ base_path = os.path.dirname(os.path.abspath(sys.argv[0]))
38
+
35
39
 
36
40
  return os.path.join(base_path, path) if path else base_path
37
41
  else:
38
- # 일반 Python 스크립트로 실행 중일 때
39
42
  return None
40
43
 
41
44
 
@@ -71,19 +74,28 @@ def get_platform() -> str:
71
74
 
72
75
  Returns
73
76
  -------
74
- "Windows" | "Darwin" | "Linux"
75
- - "Windows" for Windows systems
76
- - "Darwin" for macOS systems
77
- - "Linux" for Linux systems
77
+ "windows" | "macos" | "linux"
78
+ - "windows" for Windows systems
79
+ - "macos" for macOS systems
80
+ - "linux" for Linux systems
78
81
 
79
82
  Examples
80
83
  --------
81
84
  >>> from pyloid.utils import get_platform
82
85
  >>> platform_name = get_platform()
83
86
  >>> print(platform_name)
84
- Windows
87
+ windows
85
88
  """
86
- return platform.system()
89
+ os_name = platform.system().lower()
90
+ os_type = {
91
+ 'darwin': 'macos',
92
+ 'linux': 'linux',
93
+ 'windows': 'windows'
94
+ }.get(os_name)
95
+ if os_type is None:
96
+ raise ValueError(f"Unsupported platform: {os_name}")
97
+
98
+ return os_type
87
99
 
88
100
  def get_absolute_path(path: str) -> str:
89
101
  """
@@ -1,7 +0,0 @@
1
- from .pyloid import Pyloid
2
- from .api import PyloidAPI, Bridge
3
- from .utils import get_production_path, is_production
4
- from .tray import TrayEvent
5
- from .timer import PyloidTimer
6
-
7
- __all__ = ['Pyloid', 'PyloidAPI', 'Bridge', 'get_production_path', 'is_production', 'TrayEvent', 'PyloidTimer']
File without changes
File without changes