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.
@@ -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()