pyloid 0.21.0.dev1__py3-none-any.whl → 0.22.0.dev2__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/serve.py ADDED
@@ -0,0 +1,56 @@
1
+ import threading
2
+ from http.server import HTTPServer, SimpleHTTPRequestHandler
3
+ from typing import Optional
4
+ from .utils import get_free_port
5
+
6
+ class CustomStaticHandler(SimpleHTTPRequestHandler):
7
+ def __init__(self, *args, directory=None, **kwargs):
8
+ super().__init__(*args, directory=directory, **kwargs)
9
+
10
+ def log_message(self, format, *args):
11
+ pass
12
+
13
+ def pyloid_serve(
14
+ directory: str,
15
+ port: Optional[int] = None,
16
+ ) -> str:
17
+ """
18
+ Static file server starts.
19
+
20
+ Args
21
+ ----
22
+ directory (str): Path to the static file directory to serve
23
+ port (int, optional): Server port (default: None - will use a random free port)
24
+
25
+ Returns
26
+ -------
27
+ str
28
+ URL of the started server
29
+
30
+ Examples
31
+ --------
32
+ ```python
33
+ from pyloid import Pyloid
34
+ from pyloid.serve import pyloid_serve
35
+
36
+ app = Pyloid("Pyloid-App")
37
+ url = pyloid_serve("dist")
38
+ window = app.create_window("Pyloid-App")
39
+ window.load_url(url)
40
+ window.show_and_focus()
41
+ ```
42
+ """
43
+
44
+ if port is None:
45
+ port = get_free_port()
46
+
47
+ handler = lambda *args: CustomStaticHandler(*args, directory=directory)
48
+ server = HTTPServer(("127.0.0.1", port), handler)
49
+
50
+ thread = threading.Thread(
51
+ target=server.serve_forever,
52
+ daemon=True
53
+ )
54
+ thread.start()
55
+
56
+ return f"http://127.0.0.1:{port}"
pyloid/utils.py CHANGED
@@ -2,6 +2,7 @@ import sys
2
2
  import os
3
3
  import platform
4
4
  from typing import Optional
5
+ import socket
5
6
 
6
7
 
7
8
  def get_production_path(path: Optional[str] = None) -> Optional[str]:
@@ -120,3 +121,38 @@ def get_absolute_path(path: str) -> str:
120
121
  """
121
122
  return os.path.normpath(os.path.abspath(path))
122
123
 
124
+ def get_free_port() -> int:
125
+ """
126
+ Finds and returns an available random network port number from the operating system.
127
+
128
+ This function creates a socket and binds it to port '0', allowing the operating system
129
+ to allocate a random available port. It retrieves the port number and safely closes
130
+ the socket afterward.
131
+
132
+ Returns
133
+ -------
134
+ int
135
+ An available network port number (typically in the range 1024-65535)
136
+
137
+ Notes
138
+ -----
139
+ - Since this function closes the socket immediately after finding a port, there is a
140
+ possibility that the port could be reassigned to another process.
141
+ - It is recommended to use the port number quickly after receiving it.
142
+ - This function interacts with the operating system's network stack, so its behavior
143
+ may vary depending on firewall or network settings.
144
+
145
+ Examples
146
+ --------
147
+ >>> from pyloid.utils import get_free_port
148
+ >>> port = get_free_port()
149
+ >>> print(f"Found available port: {port}")
150
+ Found available port: 49152
151
+
152
+ >>> # Web server example
153
+ >>> import http.server
154
+ >>> server = http.server.HTTPServer(('localhost', port), http.server.SimpleHTTPRequestHandler)
155
+ """
156
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
157
+ s.bind(('', 0))
158
+ return s.getsockname()[1]
@@ -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
1
  Metadata-Version: 2.3
2
2
  Name: pyloid
3
- Version: 0.21.0.dev1
3
+ Version: 0.22.0.dev2
4
4
  Summary:
5
5
  Author: aesthetics-of-record
6
6
  Author-email: 111675679+aesthetics-of-record@users.noreply.github.com
@@ -11,7 +11,6 @@ Classifier: Programming Language :: Python :: 3.10
11
11
  Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
13
  Classifier: Programming Language :: Python :: 3.13
14
- Requires-Dist: pyinstaller (>=6.12.0,<7.0.0)
15
14
  Requires-Dist: pyside6 (>=6.8.2.1,<7.0.0.0)
16
15
  Description-Content-Type: text/markdown
17
16
 
@@ -0,0 +1,20 @@
1
+ pyloid/__init__.py,sha256=4xh7DHBMw2ciDFwI376xcIArhH8GaM4K_NdIa3N0BFo,335
2
+ pyloid/api.py,sha256=A61Kmddh8BlpT3LfA6NbPQNzFmD95vQ4WKX53oKsGYU,2419
3
+ pyloid/autostart.py,sha256=K7DQYl4LHItvPp0bt1V9WwaaZmVSTeGvadkcwG-KKrI,3899
4
+ pyloid/browser_window.py,sha256=Njwh1Wxu8cEmfqmMlat4-iQioxS8lgz1JD9UHzsfN_g,99683
5
+ pyloid/custom/titlebar.py,sha256=itzK9pJbZMQ7BKca9kdbuHMffurrw15UijR6OU03Xsk,3894
6
+ pyloid/filewatcher.py,sha256=3M5zWVUf1OhlkWJcDFC8ZA9agO4Q-U8WdgGpy6kaVz0,4601
7
+ pyloid/js_api/base.py,sha256=v2Su9errEY4oBRSXmLZps8dn9qCGbEqwp-hs-MduQRc,826
8
+ pyloid/js_api/event_api.py,sha256=w0z1DcmwcmseqfcoZWgsQmFC2iBCgTMVJubTaHeXI1c,957
9
+ pyloid/js_api/window_api.py,sha256=YnnniHEBGO1qfyziQiJ5ssYOTUxaHe3Wqj1sUZZdem0,8939
10
+ pyloid/monitor.py,sha256=1mXvHm5deohnNlTLcRx4sT4x-stnOIb0dUQnnxN50Uo,28295
11
+ pyloid/pyloid.py,sha256=tSSwyyqaxQ_j8gh9P4cGydT14BQ2rrKlB4-8jZ2QSQ4,72333
12
+ pyloid/serve.py,sha256=wJIBqiLr1-8FvBdV3yybeBtVXsu94FfWYKjHL0eQ68s,1444
13
+ pyloid/thread_pool.py,sha256=fKOBb8jMfZn_7crA_fJCno8dObBRZE31EIWaNQ759aw,14616
14
+ pyloid/timer.py,sha256=RqMsChFUd93cxMVgkHWiIKrci0QDTBgJSTULnAtYT8M,8712
15
+ pyloid/tray.py,sha256=D12opVEc2wc2T4tK9epaN1oOdeziScsIVNM2uCN7C-A,1710
16
+ pyloid/utils.py,sha256=e866N9uyAGHTMYsqRYY4JL0AEMRCOiY-k1c1zmEpDA4,4686
17
+ pyloid-0.22.0.dev2.dist-info/LICENSE,sha256=F96EzotgWhhpnQTW2TcdoqrMDir1jyEo6H915tGQ-QE,11524
18
+ pyloid-0.22.0.dev2.dist-info/METADATA,sha256=_9kw9vBfaJaPs4GkBPLL3hsAPqainhVHCb9PRIuVf1E,3071
19
+ pyloid-0.22.0.dev2.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
20
+ pyloid-0.22.0.dev2.dist-info/RECORD,,
@@ -1,81 +0,0 @@
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()
@@ -1,73 +0,0 @@
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
- }
pyloid/builder/spec.py DELETED
@@ -1,266 +0,0 @@
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
- )
@@ -1,21 +0,0 @@
1
- pyloid/__init__.py,sha256=t1_67LkSfP4F1TYq4-62z5Cc3Gx1jyWI1yXux7Ojaug,484
2
- pyloid/api.py,sha256=A61Kmddh8BlpT3LfA6NbPQNzFmD95vQ4WKX53oKsGYU,2419
3
- pyloid/autostart.py,sha256=K7DQYl4LHItvPp0bt1V9WwaaZmVSTeGvadkcwG-KKrI,3899
4
- pyloid/browser_window.py,sha256=bSxi8LnAIePF6-u7CFttgEatq6dKHLc405H2QBOQXh4,66702
5
- pyloid/builder/__init__.py,sha256=nw0r2RXqZ6eEbSbVF44sHD7NXovMShujxpTwygXXlrY,2889
6
- pyloid/builder/build_config.schema.json,sha256=Wj4_RCxXrQE9lq9Qxen1oy1Q0lhi2ojDkln8YX_LntM,2213
7
- pyloid/builder/spec.py,sha256=KuAnqO5CdJyzNbU5dmEGoq_dpHA9HW2U2Cj77Cd6uKI,6421
8
- pyloid/custom/titlebar.py,sha256=itzK9pJbZMQ7BKca9kdbuHMffurrw15UijR6OU03Xsk,3894
9
- pyloid/filewatcher.py,sha256=3M5zWVUf1OhlkWJcDFC8ZA9agO4Q-U8WdgGpy6kaVz0,4601
10
- pyloid/js_api/event_api.py,sha256=_52yyBonqecmMvJpFW7OMNi_jX8Nrteqw_kI6r-DGG0,951
11
- pyloid/js_api/window_api.py,sha256=BsAO4VBkqfUPib4f7AHxaMRXvN3FvzMXDBsqSUAay3c,8489
12
- pyloid/monitor.py,sha256=1mXvHm5deohnNlTLcRx4sT4x-stnOIb0dUQnnxN50Uo,28295
13
- pyloid/pyloid.py,sha256=YKE2a5yZFbBZrtMND2X5I7CVnFRK40NFzDp3kYN-oQg,44091
14
- pyloid/thread_pool.py,sha256=fKOBb8jMfZn_7crA_fJCno8dObBRZE31EIWaNQ759aw,14616
15
- pyloid/timer.py,sha256=RqMsChFUd93cxMVgkHWiIKrci0QDTBgJSTULnAtYT8M,8712
16
- pyloid/tray.py,sha256=D12opVEc2wc2T4tK9epaN1oOdeziScsIVNM2uCN7C-A,1710
17
- pyloid/utils.py,sha256=mAjuppRXlZAocggf8La00Ae0Qzi4IRL_ovG87x4wagI,3300
18
- pyloid-0.21.0.dev1.dist-info/LICENSE,sha256=MTYF-6xpRekyTUglRweWtbfbwBL1I_3Bgfbm_SNOuI8,11525
19
- pyloid-0.21.0.dev1.dist-info/METADATA,sha256=6bh7zmaYT4vlwI13WvqxaoGU8hxLlQc6V2I36EuOFog,3116
20
- pyloid-0.21.0.dev1.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
21
- pyloid-0.21.0.dev1.dist-info/RECORD,,