This commit is contained in:
Alexandr 2026-04-02 15:03:43 +07:00 committed by GitHub
commit 9ceaea8002
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 70 additions and 8 deletions

View File

@ -10,7 +10,13 @@ block_cipher = None
# customtkinter ships JSON themes + assets that must be bundled # customtkinter ships JSON themes + assets that must be bundled
import customtkinter import customtkinter
import setuptools
ctk_path = os.path.dirname(customtkinter.__file__) ctk_path = os.path.dirname(customtkinter.__file__)
_vendor = os.path.join(os.path.dirname(setuptools.__file__), '_vendor')
_setuptools_vendor = (
[(_vendor, os.path.join('setuptools', '_vendor'))]
if os.path.isdir(_vendor) else [])
# Collect gi (PyGObject) submodules and data so pystray._appindicator works # Collect gi (PyGObject) submodules and data so pystray._appindicator works
gi_hiddenimports = collect_submodules('gi') gi_hiddenimports = collect_submodules('gi')
@ -26,7 +32,7 @@ a = Analysis(
[os.path.join(os.path.dirname(SPEC), os.pardir, 'linux.py')], [os.path.join(os.path.dirname(SPEC), os.pardir, 'linux.py')],
pathex=[], pathex=[],
binaries=[], binaries=[],
datas=[(ctk_path, 'customtkinter/')] + gi_datas + typelib_datas, datas=[(ctk_path, 'customtkinter/')] + _setuptools_vendor + gi_datas + typelib_datas,
hiddenimports=[ hiddenimports=[
'pystray._appindicator', 'pystray._appindicator',
'PIL._tkinter_finder', 'PIL._tkinter_finder',
@ -35,6 +41,7 @@ a = Analysis(
'cryptography.hazmat.primitives.ciphers.algorithms', 'cryptography.hazmat.primitives.ciphers.algorithms',
'cryptography.hazmat.primitives.ciphers.modes', 'cryptography.hazmat.primitives.ciphers.modes',
'cryptography.hazmat.backends.openssl', 'cryptography.hazmat.backends.openssl',
'platformdirs',
'gi', 'gi',
'_gi', '_gi',
'gi.repository.GLib', 'gi.repository.GLib',

View File

@ -5,11 +5,18 @@ import os
block_cipher = None block_cipher = None
import setuptools
_vendor = os.path.join(os.path.dirname(setuptools.__file__), '_vendor')
_macos_datas = (
[(_vendor, os.path.join('setuptools', '_vendor'))]
if os.path.isdir(_vendor) else [])
a = Analysis( a = Analysis(
[os.path.join(os.path.dirname(SPEC), os.pardir, 'macos.py')], [os.path.join(os.path.dirname(SPEC), os.pardir, 'macos.py')],
pathex=[], pathex=[],
binaries=[], binaries=[],
datas=[], datas=_macos_datas,
hiddenimports=[ hiddenimports=[
'rumps', 'rumps',
'objc', 'objc',
@ -21,6 +28,7 @@ a = Analysis(
'cryptography.hazmat.primitives.ciphers.algorithms', 'cryptography.hazmat.primitives.ciphers.algorithms',
'cryptography.hazmat.primitives.ciphers.modes', 'cryptography.hazmat.primitives.ciphers.modes',
'cryptography.hazmat.backends.openssl', 'cryptography.hazmat.backends.openssl',
'platformdirs',
], ],
hookspath=[], hookspath=[],
hooksconfig={}, hooksconfig={},

View File

@ -7,13 +7,21 @@ block_cipher = None
# customtkinter ships JSON themes + assets that must be bundled # customtkinter ships JSON themes + assets that must be bundled
import customtkinter import customtkinter
import setuptools
ctk_path = os.path.dirname(customtkinter.__file__) ctk_path = os.path.dirname(customtkinter.__file__)
# pkg_resources (pyi_rth_pkgres) prepends setuptools/_vendor to sys.path and
# imports jaraco.* from there; frozen builds need the tree on disk.
_vendor = os.path.join(os.path.dirname(setuptools.__file__), '_vendor')
_bundle_datas = [(ctk_path, 'customtkinter/')]
if os.path.isdir(_vendor):
_bundle_datas.append((_vendor, os.path.join('setuptools', '_vendor')))
a = Analysis( a = Analysis(
[os.path.join(os.path.dirname(SPEC), os.pardir, 'windows.py')], [os.path.join(os.path.dirname(SPEC), os.pardir, 'windows.py')],
pathex=[], pathex=[],
binaries=[], binaries=[],
datas=[(ctk_path, 'customtkinter/')], datas=_bundle_datas,
hiddenimports=[ hiddenimports=[
'pystray._win32', 'pystray._win32',
'PIL._tkinter_finder', 'PIL._tkinter_finder',
@ -22,6 +30,7 @@ a = Analysis(
'cryptography.hazmat.primitives.ciphers.algorithms', 'cryptography.hazmat.primitives.ciphers.algorithms',
'cryptography.hazmat.primitives.ciphers.modes', 'cryptography.hazmat.primitives.ciphers.modes',
'cryptography.hazmat.backends.openssl', 'cryptography.hazmat.backends.openssl',
'platformdirs',
], ],
hookspath=[], hookspath=[],
hooksconfig={}, hooksconfig={},

View File

@ -234,8 +234,15 @@ class RawWebSocket:
await self.writer.drain() await self.writer.drain()
async def recv(self) -> Optional[bytes]: async def recv(self) -> Optional[bytes]:
"""
Return one complete WebSocket data message (RFC 6455), reassembling
fragmented BINARY/TEXT frames. Control frames may appear between
fragments and are handled without breaking reassembly.
"""
fragments: Optional[List[bytes]] = None
while not self._closed: while not self._closed:
opcode, payload = await self._read_frame() opcode, payload, fin = await self._read_frame()
if opcode == self.OP_CLOSE: if opcode == self.OP_CLOSE:
self._closed = True self._closed = True
@ -260,8 +267,38 @@ class RawWebSocket:
if opcode == self.OP_PONG: if opcode == self.OP_PONG:
continue continue
# Continuation — only valid inside a fragmented message
if opcode == 0:
if fragments is None:
self._closed = True
try:
self.writer.close()
await self.writer.wait_closed()
except Exception:
pass
return None
fragments.append(payload)
if fin:
out = b''.join(fragments)
fragments = None
return out
continue
if opcode in (0x1, 0x2): if opcode in (0x1, 0x2):
return payload if fragments is not None:
self._closed = True
try:
self.writer.close()
await self.writer.wait_closed()
except Exception:
pass
return None
if fin:
return payload
fragments = [payload]
continue
# Reserved / unknown data opcodes — skip frame, keep reading
continue continue
return None return None
@ -300,8 +337,9 @@ class RawWebSocket:
return _st_BBH4s.pack(fb, 0x80 | 126, length, mask_key) + masked return _st_BBH4s.pack(fb, 0x80 | 126, length, mask_key) + masked
return _st_BBQ4s.pack(fb, 0x80 | 127, length, mask_key) + masked return _st_BBQ4s.pack(fb, 0x80 | 127, length, mask_key) + masked
async def _read_frame(self) -> Tuple[int, bytes]: async def _read_frame(self) -> Tuple[int, bytes, bool]:
hdr = await self.reader.readexactly(2) hdr = await self.reader.readexactly(2)
fin = bool(hdr[0] & 0x80)
opcode = hdr[0] & 0x0F opcode = hdr[0] & 0x0F
length = hdr[1] & 0x7F length = hdr[1] & 0x7F
if length == 126: if length == 126:
@ -311,9 +349,9 @@ class RawWebSocket:
if hdr[1] & 0x80: if hdr[1] & 0x80:
mask_key = await self.reader.readexactly(4) mask_key = await self.reader.readexactly(4)
payload = await self.reader.readexactly(length) payload = await self.reader.readexactly(length)
return opcode, _xor_mask(payload, mask_key) return opcode, _xor_mask(payload, mask_key), fin
payload = await self.reader.readexactly(length) payload = await self.reader.readexactly(length)
return opcode, payload return opcode, payload, fin
def _human_bytes(n: int) -> str: def _human_bytes(n: int) -> str: