webview-python 1.1.3__cp314-cp314-win_amd64.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.
- webview/__init__.py +1 -0
- webview/_dummy_holder.c +4506 -0
- webview/_dummy_holder.cp314-win_amd64.pyd +0 -0
- webview/_webview_ffi.py +184 -0
- webview/webview.py +109 -0
- webview_python-1.1.3.dist-info/METADATA +421 -0
- webview_python-1.1.3.dist-info/RECORD +10 -0
- webview_python-1.1.3.dist-info/WHEEL +5 -0
- webview_python-1.1.3.dist-info/licenses/LICENSE +21 -0
- webview_python-1.1.3.dist-info/top_level.txt +1 -0
|
Binary file
|
webview/_webview_ffi.py
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
import urllib.request
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from ctypes import c_int, c_char_p, c_void_p, CFUNCTYPE
|
|
8
|
+
import ctypes.util
|
|
9
|
+
import shutil
|
|
10
|
+
import logging
|
|
11
|
+
def _encode_c_string(s: str) -> bytes:
|
|
12
|
+
return s.encode("utf-8")
|
|
13
|
+
|
|
14
|
+
def _get_webview_version():
|
|
15
|
+
"""Get webview version from environment variable or use default"""
|
|
16
|
+
return os.getenv("WEBVIEW_VERSION", "0.9.0")
|
|
17
|
+
|
|
18
|
+
def _get_download_base():
|
|
19
|
+
"""Get download base URL or path from environment variable or use default.
|
|
20
|
+
|
|
21
|
+
This allows users to specify a custom location (URL or file path) to download
|
|
22
|
+
libraries from, which is particularly useful for internal deployments or
|
|
23
|
+
offline environments.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
str: The base URL or path for downloading libraries
|
|
27
|
+
"""
|
|
28
|
+
return os.getenv(
|
|
29
|
+
"WEBVIEW_DOWNLOAD_BASE",
|
|
30
|
+
"https://github.com/webview/webview_deno/releases/download"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def _get_lib_names():
|
|
34
|
+
"""Get platform-specific library names."""
|
|
35
|
+
system = platform.system().lower()
|
|
36
|
+
machine = platform.machine().lower()
|
|
37
|
+
|
|
38
|
+
if system == "windows":
|
|
39
|
+
if machine == "amd64" or machine == "x86_64":
|
|
40
|
+
return ["webview.dll", "WebView2Loader.dll"]
|
|
41
|
+
elif machine == "arm64":
|
|
42
|
+
raise Exception("arm64 is not supported on Windows")
|
|
43
|
+
elif system == "darwin":
|
|
44
|
+
if machine == "arm64":
|
|
45
|
+
return ["libwebview.aarch64.dylib"]
|
|
46
|
+
else:
|
|
47
|
+
return ["libwebview.x86_64.dylib"]
|
|
48
|
+
else: # linux
|
|
49
|
+
if machine == "aarch64" or machine == "arm64":
|
|
50
|
+
return ["libwebview.aarch64.so"]
|
|
51
|
+
else:
|
|
52
|
+
return ["libwebview.x86_64.so"]
|
|
53
|
+
|
|
54
|
+
def _get_download_urls():
|
|
55
|
+
"""Get the appropriate download URLs based on the platform."""
|
|
56
|
+
version = _get_webview_version()
|
|
57
|
+
base_url = _get_download_base()
|
|
58
|
+
|
|
59
|
+
# Handle both URL and file path formats
|
|
60
|
+
if base_url.startswith(("http://", "https://", "file://")):
|
|
61
|
+
# For URLs, use / separator
|
|
62
|
+
return [f"{base_url}/{version}/{lib_name}"
|
|
63
|
+
for lib_name in _get_lib_names()]
|
|
64
|
+
else:
|
|
65
|
+
# For file paths, use OS-specific path handling
|
|
66
|
+
base_path = Path(base_url)
|
|
67
|
+
return [str(base_path / version / lib_name)
|
|
68
|
+
for lib_name in _get_lib_names()]
|
|
69
|
+
|
|
70
|
+
def _be_sure_libraries():
|
|
71
|
+
"""Ensure libraries exist and return paths."""
|
|
72
|
+
if getattr(sys, 'frozen', False):
|
|
73
|
+
# if hasattr(sys, '_MEIPASS'):
|
|
74
|
+
# base_dir = Path(sys._MEIPASS)
|
|
75
|
+
# else:
|
|
76
|
+
# base_dir = Path(sys.executable).parent / '_internal'/'webview'
|
|
77
|
+
base_dir = Path(sys.executable).parent / '_internal'/'webview'
|
|
78
|
+
else:
|
|
79
|
+
base_dir = Path(__file__).parent
|
|
80
|
+
|
|
81
|
+
#lib_dir = base_dir / "lib"
|
|
82
|
+
lib_dir = base_dir
|
|
83
|
+
logging.info(f"webview lib_dir: {lib_dir}")
|
|
84
|
+
lib_names = _get_lib_names()
|
|
85
|
+
logging.info(f"webview lib_names: {lib_names}")
|
|
86
|
+
lib_paths = [lib_dir / lib_name for lib_name in lib_names]
|
|
87
|
+
logging.info(f"webview lib_paths: {lib_paths}")
|
|
88
|
+
# Check if any library is missing
|
|
89
|
+
missing_libs = [path for path in lib_paths if not path.exists()]
|
|
90
|
+
if not missing_libs:
|
|
91
|
+
return lib_paths
|
|
92
|
+
else:
|
|
93
|
+
logging.warning(f"Missing libraries: {missing_libs}")
|
|
94
|
+
# Download or copy missing libraries
|
|
95
|
+
download_urls = _get_download_urls()
|
|
96
|
+
|
|
97
|
+
lib_dir.mkdir(parents=True, exist_ok=True)
|
|
98
|
+
|
|
99
|
+
for url, lib_path in zip(download_urls, lib_paths):
|
|
100
|
+
if lib_path.exists():
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
print(f"Getting library from {url}, writing to {lib_path}")
|
|
104
|
+
logging.warning(f"Getting library from {url}, writing to {lib_path}")
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
# Handle different URL types
|
|
108
|
+
if url.startswith(("http://", "https://")):
|
|
109
|
+
# Web URL - download
|
|
110
|
+
req = urllib.request.Request(
|
|
111
|
+
url,
|
|
112
|
+
headers={'User-Agent': 'Mozilla/5.0'}
|
|
113
|
+
)
|
|
114
|
+
with urllib.request.urlopen(req) as response, open(lib_path, 'wb') as out_file:
|
|
115
|
+
out_file.write(response.read())
|
|
116
|
+
elif url.startswith("file://"):
|
|
117
|
+
# File URL - copy from local filesystem
|
|
118
|
+
source_path = url[7:] # Strip 'file://' prefix
|
|
119
|
+
shutil.copy2(source_path, lib_path)
|
|
120
|
+
else:
|
|
121
|
+
# Assume it's a filesystem path
|
|
122
|
+
source_path = url
|
|
123
|
+
if os.path.exists(source_path):
|
|
124
|
+
shutil.copy2(source_path, lib_path)
|
|
125
|
+
else:
|
|
126
|
+
raise FileNotFoundError(f"Could not find library at {source_path}")
|
|
127
|
+
except Exception as e:
|
|
128
|
+
raise RuntimeError(f"Failed to get library from {url}: {e}")
|
|
129
|
+
|
|
130
|
+
return lib_paths
|
|
131
|
+
|
|
132
|
+
class _WebviewLibrary:
|
|
133
|
+
def __init__(self):
|
|
134
|
+
lib_names=_get_lib_names()
|
|
135
|
+
try:
|
|
136
|
+
library_path = ctypes.util.find_library(lib_names[0])
|
|
137
|
+
if not library_path:
|
|
138
|
+
library_paths = _be_sure_libraries()
|
|
139
|
+
library_path = library_paths[0]
|
|
140
|
+
self.lib = ctypes.cdll.LoadLibrary(str(library_path))
|
|
141
|
+
except Exception as e:
|
|
142
|
+
print(f"Failed to load webview library: {e}")
|
|
143
|
+
raise
|
|
144
|
+
# Define FFI functions
|
|
145
|
+
self.webview_create = self.lib.webview_create
|
|
146
|
+
self.webview_create.argtypes = [c_int, c_void_p]
|
|
147
|
+
self.webview_create.restype = c_void_p
|
|
148
|
+
|
|
149
|
+
self.webview_destroy = self.lib.webview_destroy
|
|
150
|
+
self.webview_destroy.argtypes = [c_void_p]
|
|
151
|
+
|
|
152
|
+
self.webview_run = self.lib.webview_run
|
|
153
|
+
self.webview_run.argtypes = [c_void_p]
|
|
154
|
+
|
|
155
|
+
self.webview_terminate = self.lib.webview_terminate
|
|
156
|
+
self.webview_terminate.argtypes = [c_void_p]
|
|
157
|
+
|
|
158
|
+
self.webview_set_title = self.lib.webview_set_title
|
|
159
|
+
self.webview_set_title.argtypes = [c_void_p, c_char_p]
|
|
160
|
+
|
|
161
|
+
self.webview_set_size = self.lib.webview_set_size
|
|
162
|
+
self.webview_set_size.argtypes = [c_void_p, c_int, c_int, c_int]
|
|
163
|
+
|
|
164
|
+
self.webview_navigate = self.lib.webview_navigate
|
|
165
|
+
self.webview_navigate.argtypes = [c_void_p, c_char_p]
|
|
166
|
+
|
|
167
|
+
self.webview_init = self.lib.webview_init
|
|
168
|
+
self.webview_init.argtypes = [c_void_p, c_char_p]
|
|
169
|
+
|
|
170
|
+
self.webview_eval = self.lib.webview_eval
|
|
171
|
+
self.webview_eval.argtypes = [c_void_p, c_char_p]
|
|
172
|
+
|
|
173
|
+
self.webview_bind = self.lib.webview_bind
|
|
174
|
+
self.webview_bind.argtypes = [c_void_p, c_char_p, c_void_p, c_void_p]
|
|
175
|
+
|
|
176
|
+
self.webview_unbind = self.lib.webview_unbind
|
|
177
|
+
self.webview_unbind.argtypes = [c_void_p, c_char_p]
|
|
178
|
+
|
|
179
|
+
self.webview_return = self.lib.webview_return
|
|
180
|
+
self.webview_return.argtypes = [c_void_p, c_char_p, c_int, c_char_p]
|
|
181
|
+
|
|
182
|
+
self.CFUNCTYPE = CFUNCTYPE
|
|
183
|
+
|
|
184
|
+
_webview_lib = _WebviewLibrary()
|
webview/webview.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from enum import IntEnum
|
|
2
|
+
from typing import Optional, Callable, Any
|
|
3
|
+
import json
|
|
4
|
+
import ctypes
|
|
5
|
+
import asyncio
|
|
6
|
+
import inspect
|
|
7
|
+
from ._webview_ffi import _webview_lib, _encode_c_string
|
|
8
|
+
|
|
9
|
+
class SizeHint(IntEnum):
|
|
10
|
+
NONE = 0
|
|
11
|
+
MIN = 1
|
|
12
|
+
MAX = 2
|
|
13
|
+
FIXED = 3
|
|
14
|
+
|
|
15
|
+
class Size:
|
|
16
|
+
def __init__(self, width: int, height: int, hint: SizeHint):
|
|
17
|
+
self.width = width
|
|
18
|
+
self.height = height
|
|
19
|
+
self.hint = hint
|
|
20
|
+
|
|
21
|
+
class Webview:
|
|
22
|
+
def __init__(self, debug: bool = False, size: Optional[Size] = None, window: Optional[int] = None):
|
|
23
|
+
self._handle = _webview_lib.webview_create(int(debug), window)
|
|
24
|
+
self._callbacks = {}
|
|
25
|
+
|
|
26
|
+
if size:
|
|
27
|
+
self.size = size
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def size(self) -> Size:
|
|
31
|
+
return self._size
|
|
32
|
+
|
|
33
|
+
@size.setter
|
|
34
|
+
def size(self, value: Size):
|
|
35
|
+
_webview_lib.webview_set_size(self._handle, value.width, value.height, value.hint)
|
|
36
|
+
self._size = value
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def title(self) -> str:
|
|
40
|
+
return self._title
|
|
41
|
+
|
|
42
|
+
@title.setter
|
|
43
|
+
def title(self, value: str):
|
|
44
|
+
_webview_lib.webview_set_title(self._handle, _encode_c_string(value))
|
|
45
|
+
self._title = value
|
|
46
|
+
|
|
47
|
+
def destroy(self):
|
|
48
|
+
for name in list(self._callbacks.keys()):
|
|
49
|
+
self.unbind(name)
|
|
50
|
+
_webview_lib.webview_terminate(self._handle)
|
|
51
|
+
_webview_lib.webview_destroy(self._handle)
|
|
52
|
+
self._handle = None
|
|
53
|
+
|
|
54
|
+
def navigate(self, url: str):
|
|
55
|
+
_webview_lib.webview_navigate(self._handle, _encode_c_string(url))
|
|
56
|
+
|
|
57
|
+
def run(self):
|
|
58
|
+
_webview_lib.webview_run(self._handle)
|
|
59
|
+
self.destroy()
|
|
60
|
+
|
|
61
|
+
def bind(self, name: str, callback: Callable[..., Any]):
|
|
62
|
+
def wrapper(seq: bytes, req: bytes, arg: int):
|
|
63
|
+
args = json.loads(req.decode())
|
|
64
|
+
seq_str = seq.decode()
|
|
65
|
+
|
|
66
|
+
if inspect.iscoroutinefunction(callback):
|
|
67
|
+
# Handle async function
|
|
68
|
+
async def handle_async():
|
|
69
|
+
try:
|
|
70
|
+
result = await callback(*args)
|
|
71
|
+
success = True
|
|
72
|
+
except Exception as e:
|
|
73
|
+
result = str(e)
|
|
74
|
+
success = False
|
|
75
|
+
self.return_(seq_str, 0 if success else 1, json.dumps(result))
|
|
76
|
+
# Schedule the coroutine to run
|
|
77
|
+
asyncio.ensure_future(handle_async())
|
|
78
|
+
else:
|
|
79
|
+
try:
|
|
80
|
+
result = callback(*args)
|
|
81
|
+
success = True
|
|
82
|
+
except Exception as e:
|
|
83
|
+
result = str(e)
|
|
84
|
+
success = False
|
|
85
|
+
self.return_(seq_str, 0 if success else 1, json.dumps(result))
|
|
86
|
+
|
|
87
|
+
c_callback = _webview_lib.CFUNCTYPE(None, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p)(wrapper)
|
|
88
|
+
self._callbacks[name] = c_callback
|
|
89
|
+
_webview_lib.webview_bind(self._handle, _encode_c_string(name), c_callback, None)
|
|
90
|
+
|
|
91
|
+
def unbind(self, name: str):
|
|
92
|
+
if name in self._callbacks:
|
|
93
|
+
_webview_lib.webview_unbind(self._handle, _encode_c_string(name))
|
|
94
|
+
del self._callbacks[name]
|
|
95
|
+
|
|
96
|
+
def return_(self, seq: str, status: int, result: str):
|
|
97
|
+
_webview_lib.webview_return(self._handle, _encode_c_string(seq), status, _encode_c_string(result))
|
|
98
|
+
|
|
99
|
+
def eval(self, source: str):
|
|
100
|
+
_webview_lib.webview_eval(self._handle, _encode_c_string(source))
|
|
101
|
+
|
|
102
|
+
def init(self, source: str):
|
|
103
|
+
_webview_lib.webview_init(self._handle, _encode_c_string(source))
|
|
104
|
+
|
|
105
|
+
if __name__ == "__main__":
|
|
106
|
+
wv = Webview()
|
|
107
|
+
wv.title = "Hello, World!"
|
|
108
|
+
wv.navigate("https://www.google.com")
|
|
109
|
+
wv.run()
|