Commit 7a2ff30d authored by Lisa's avatar Lisa

Fix: update .gitignore to exclude build artifacts

parent cabbf17b

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

......@@ -45,3 +45,11 @@ htmlcov/
# Security-sensitive deploy scripts (contains tokens)
install-on-sissy.sh
windows/python-win/
release/hermes-node-agent-installer.exe
deploy/windows/hermes-node-agent-installer.exe
chrome-extension.zip
*.zip
*.exe
*.pyc
\ No newline at end of file
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
/* Greenlet object interface */
#ifndef Py_GREENLETOBJECT_H
#define Py_GREENLETOBJECT_H
#include <Python.h>
#ifdef __cplusplus
extern "C" {
#endif
/* This is deprecated and undocumented. It does not change. */
#define GREENLET_VERSION "1.0.0"
#ifndef GREENLET_MODULE
#define implementation_ptr_t void*
#endif
typedef struct _greenlet {
PyObject_HEAD
PyObject* weakreflist;
PyObject* dict;
implementation_ptr_t pimpl;
} PyGreenlet;
#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type))
/* C API functions */
/* Total number of symbols that are exported */
#define PyGreenlet_API_pointers 12
#define PyGreenlet_Type_NUM 0
#define PyExc_GreenletError_NUM 1
#define PyExc_GreenletExit_NUM 2
#define PyGreenlet_New_NUM 3
#define PyGreenlet_GetCurrent_NUM 4
#define PyGreenlet_Throw_NUM 5
#define PyGreenlet_Switch_NUM 6
#define PyGreenlet_SetParent_NUM 7
#define PyGreenlet_MAIN_NUM 8
#define PyGreenlet_STARTED_NUM 9
#define PyGreenlet_ACTIVE_NUM 10
#define PyGreenlet_GET_PARENT_NUM 11
#ifndef GREENLET_MODULE
/* This section is used by modules that uses the greenlet C API */
static void** _PyGreenlet_API = NULL;
# define PyGreenlet_Type \
(*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM])
# define PyExc_GreenletError \
((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM])
# define PyExc_GreenletExit \
((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM])
/*
* PyGreenlet_New(PyObject *args)
*
* greenlet.greenlet(run, parent=None)
*/
# define PyGreenlet_New \
(*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \
_PyGreenlet_API[PyGreenlet_New_NUM])
/*
* PyGreenlet_GetCurrent(void)
*
* greenlet.getcurrent()
*/
# define PyGreenlet_GetCurrent \
(*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM])
/*
* PyGreenlet_Throw(
* PyGreenlet *greenlet,
* PyObject *typ,
* PyObject *val,
* PyObject *tb)
*
* g.throw(...)
*/
# define PyGreenlet_Throw \
(*(PyObject * (*)(PyGreenlet * self, \
PyObject * typ, \
PyObject * val, \
PyObject * tb)) \
_PyGreenlet_API[PyGreenlet_Throw_NUM])
/*
* PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args)
*
* g.switch(*args, **kwargs)
*/
# define PyGreenlet_Switch \
(*(PyObject * \
(*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \
_PyGreenlet_API[PyGreenlet_Switch_NUM])
/*
* PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent)
*
* g.parent = new_parent
*/
# define PyGreenlet_SetParent \
(*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \
_PyGreenlet_API[PyGreenlet_SetParent_NUM])
/*
* PyGreenlet_GetParent(PyObject* greenlet)
*
* return greenlet.parent;
*
* This could return NULL even if there is no exception active.
* If it does not return NULL, you are responsible for decrementing the
* reference count.
*/
# define PyGreenlet_GetParent \
(*(PyGreenlet* (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_GET_PARENT_NUM])
/*
* deprecated, undocumented alias.
*/
# define PyGreenlet_GET_PARENT PyGreenlet_GetParent
# define PyGreenlet_MAIN \
(*(int (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_MAIN_NUM])
# define PyGreenlet_STARTED \
(*(int (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_STARTED_NUM])
# define PyGreenlet_ACTIVE \
(*(int (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_ACTIVE_NUM])
/* Macro that imports greenlet and initializes C API */
/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we
keep the older definition to be sure older code that might have a copy of
the header still works. */
# define PyGreenlet_Import() \
{ \
_PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \
}
#endif /* GREENLET_MODULE */
#ifdef __cplusplus
}
#endif
#endif /* !Py_GREENLETOBJECT_H */
This diff is collapsed.
#-----------------------------------------------------------------------------
# Copyright (c) 2005-2023, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
#-----------------------------------------------------------------------------
__all__ = ('HOMEPATH', 'PLATFORM', '__version__', 'DEFAULT_DISTPATH', 'DEFAULT_SPECPATH', 'DEFAULT_WORKPATH')
import os
from PyInstaller import compat
# Note: Keep this variable as plain string so it could be updated automatically when doing a release.
__version__ = '6.20.0'
# Absolute path of this package's directory. Save this early so all submodules can use the absolute path. This is
# required for example if the current directory changes prior to loading the hooks.
PACKAGEPATH = os.path.abspath(os.path.dirname(__file__))
HOMEPATH = os.path.dirname(PACKAGEPATH)
# Default values of paths where to put files created by PyInstaller. If changing these, do not forget to update the
# help text for corresponding command-line options, defined in build_main.
# Where to put created .spec file.
DEFAULT_SPECPATH = os.getcwd()
# Where to put the final frozen application.
DEFAULT_DISTPATH = os.path.join(os.getcwd(), 'dist')
# Where to put all the temporary files; .log, .pyz, etc.
DEFAULT_WORKPATH = os.path.join(os.getcwd(), 'build')
PLATFORM = compat.system + '-' + compat.architecture
# Include machine name in path to bootloader for some machines (e.g., 'arm'). Explicitly avoid doing this on macOS,
# where we keep universal2 bootloaders in Darwin-64bit folder regardless of whether we are on x86_64 or arm64.
if compat.machine and not compat.is_darwin:
PLATFORM += '-' + compat.machine
# Similarly, disambiguate musl Linux from glibc Linux.
if compat.is_musl:
PLATFORM += '-musl'
#-----------------------------------------------------------------------------
# Copyright (c) 2013-2023, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
#-----------------------------------------------------------------------------
msg = """
=============================================================
A RecursionError (maximum recursion depth exceeded) occurred.
For working around please follow these instructions
=============================================================
1. In your program's .spec file add this line near the top::
import sys ; sys.setrecursionlimit(sys.getrecursionlimit() * 5)
2. Build your program by running PyInstaller with the .spec file as
argument::
pyinstaller myprog.spec
3. If this fails, you most probably hit an endless recursion in
PyInstaller. Please try to track this down as far as possible,
create a minimal example so we can reproduce and open an issue at
https://github.com/pyinstaller/pyinstaller/issues following the
instructions in the issue template. Many thanks.
Explanation: Python's stack-limit is a safety-belt against endless recursion,
eating up memory. PyInstaller imports modules recursively. If the structure
how modules are imported within your program is awkward, this leads to the
nesting being too deep and hitting Python's stack-limit.
With the default recursion limit (1000), the recursion error occurs at about
115 nested imported, with limit 2000 at about 240, with limit 5000 at about
660.
"""
def raise_with_msg():
raise SystemExit(msg)
#-----------------------------------------------------------------------------
# Copyright (c) 2005-2023, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
#-----------------------------------------------------------------------------
"""
Code to be shared by PyInstaller and the bootloader/wscript file.
This code must not assume that either PyInstaller or any of its dependencies installed. I.e., the only imports allowed
in here are standard library ones. Within reason, it is preferable that this file should still run under Python 2.7 as
many compiler docker images still have only Python 2 installed.
"""
import re
def _pyi_machine(machine, system):
# type: (str, str) -> str
"""
Choose an intentionally simplified architecture identifier to be used in the bootloader's directory name.
Args:
machine:
The output of ``platform.machine()`` or any known architecture alias or shorthand that may be used by a
C compiler.
system:
The output of ``platform.system()`` on the target machine.
Returns:
Either a string tag or, on platforms that don't need an architecture tag, ``None``.
Ideally, we would just use ``platform.machine()`` directly, but that makes cross-compiling the bootloader almost
impossible, because you need to know at compile time exactly what ``platform.machine()`` will be at run time, based
only on the machine name alias or shorthand reported by the C compiler at the build time. Rather, use a loose
differentiation, and trust that anyone mixing armv6l with armv6h knows what they are doing.
"""
# See the corresponding tests in tests/unit/test_compat.py for examples.
if system == "Windows":
if machine.lower().startswith("arm"):
return "arm"
else:
return "intel"
if system == "SunOS":
if machine.lower() in ("x86_64", "x86", "i86pc"):
return "intel"
else:
return "sparc"
# Fold Android back into Linux. Currently, Termux environment is the only way to run PyInstaller on Android.
# Starting with python 3.13, `platform.system()` reports 'Android' (see PEP-738); earlier versions reported 'Linux'.
# The compiler-based target platform identification in `waf`, however, identifies target platform as Linux on all
# python versions.
if system == "Android":
system = "Linux"
if system != "Linux":
# No architecture specifier for anything par Linux.
# - macOS is on two 64 bit architectures, but they are merged into one "universal2" bootloader.
# - BSD supports a wide range of architectures, but according to PyPI's download statistics, every one of our
# BSD users are on x86_64. This may change in the distant future.
return
if machine.startswith(("arm", "aarch")):
# ARM has a huge number of similar and aliased sub-versions, such as armv5, armv6l armv8h, aarch64.
return "arm"
if machine in ("thumb"):
# Reported by waf/gcc when Thumb instruction set is enabled on 32-bit ARM. The platform.machine() returns "arm"
# regardless of the instruction set.
return "arm"
if machine in ("x86_64", "x64", "x86"):
return "intel"
if re.fullmatch("i[1-6]86", machine):
return "intel"
if machine.startswith(("ppc", "powerpc")):
# PowerPC comes in 64 vs 32 bit and little vs big endian variants.
return "ppc"
if machine in ("mips64", "mips"):
return "mips"
if machine.startswith("riscv"):
return "riscv"
if machine.startswith(("sw_", "sunway")):
return "sunway"
if machine.startswith("loongarch"):
return "loongarch"
# Machines with no known aliases :)
if machine in ("s390x",):
return machine
# Unknown architectures are allowed by default, but will all be placed under one directory. In theory, trying to
# have multiple unknown architectures in one copy of PyInstaller will not work, but that should be sufficiently
# unlikely to ever happen.
return "unknown"
#-----------------------------------------------------------------------------
# Copyright (c) 2005-2023, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
#-----------------------------------------------------------------------------
class PyiBlockCipher:
def __init__(self, key=None):
from PyInstaller.exceptions import RemovedCipherFeatureError
raise RemovedCipherFeatureError("Please remove cipher and block_cipher parameters from your spec file.")
#-----------------------------------------------------------------------------
# Copyright (c) 2013-2023, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
#-----------------------------------------------------------------------------
"""
Python-based CArchive (PKG) reader implementation. Used only in the archive_viewer utility.
"""
import os
import struct
from PyInstaller.loader.pyimod01_archive import ZlibArchiveReader, ArchiveReadError
class NotAnArchiveError(TypeError):
pass
# Type codes for CArchive TOC entries
PKG_ITEM_BINARY = 'b' # binary
PKG_ITEM_DEPENDENCY = 'd' # runtime option
PKG_ITEM_PYZ = 'z' # zlib (pyz) - frozen Python code
PKG_ITEM_ZIPFILE = 'Z' # zlib (pyz) - frozen Python code
PKG_ITEM_PYPACKAGE = 'M' # Python package (__init__.py)
PKG_ITEM_PYMODULE = 'm' # Python module
PKG_ITEM_PYSOURCE = 's' # Python script (v3)
PKG_ITEM_DATA = 'x' # data
PKG_ITEM_RUNTIME_OPTION = 'o' # runtime option
PKG_ITEM_SPLASH = 'l' # splash resources
class CArchiveReader:
"""
Reader for PyInstaller's CArchive (PKG) archive.
"""
# Cookie - holds some information for the bootloader. C struct format definition. '!' at the beginning means network
# byte order. C struct looks like:
#
# typedef struct _archive_cookie
# {
# char magic[8];
# uint32_t pkg_length;
# uint32_t toc_offset;
# uint32_t toc_length;
# uint32_t python_version;
# char python_libname[64];
# } ARCHIVE_COOKIE;
#
_COOKIE_MAGIC_PATTERN = b'MEI\014\013\012\013\016'
_COOKIE_FORMAT = '!8sIIII64s'
_COOKIE_LENGTH = struct.calcsize(_COOKIE_FORMAT)
# TOC entry:
#
# typedef struct _toc_entry
# {
# uint32_t entry_length;
# uint32_t offset;
# uint32_t length;
# uint32_t uncompressed_length;
# unsigned char compression_flag;
# char typecode;
# char name[1]; /* Variable-length name, padded to multiple of 16 */
# } TOC_ENTRY;
#
_TOC_ENTRY_FORMAT = '!IIIIBc'
_TOC_ENTRY_LENGTH = struct.calcsize(_TOC_ENTRY_FORMAT)
def __init__(self, filename):
self._filename = filename
self._start_offset = 0
self._end_offset = 0
self._toc_offset = 0
self._toc_length = 0
self.toc = {}
self.options = []
# Load TOC
with open(self._filename, "rb") as fp:
# Find cookie MAGIC pattern
cookie_start_offset = self._find_magic_pattern(fp, self._COOKIE_MAGIC_PATTERN)
if cookie_start_offset == -1:
raise ArchiveReadError("Could not find COOKIE magic pattern!")
# Read the whole cookie
fp.seek(cookie_start_offset, os.SEEK_SET)
cookie_data = fp.read(self._COOKIE_LENGTH)
magic, archive_length, toc_offset, toc_length, pyvers, pylib_name = \
struct.unpack(self._COOKIE_FORMAT, cookie_data)
# Compute start and end offset of the the archive
self._end_offset = cookie_start_offset + self._COOKIE_LENGTH
self._start_offset = self._end_offset - archive_length
# Verify that Python shared library name is set
if not pylib_name:
raise ArchiveReadError("Python shared library name not set in the archive!")
# Read whole toc
fp.seek(self._start_offset + toc_offset)
toc_data = fp.read(toc_length)
self.toc, self.options = self._parse_toc(toc_data)
@staticmethod
def _find_magic_pattern(fp, magic_pattern):
# Start at the end of file, and scan back-to-start
fp.seek(0, os.SEEK_END)
end_pos = fp.tell()
# Scan from back
SEARCH_CHUNK_SIZE = 8192
magic_offset = -1
while end_pos >= len(magic_pattern):
start_pos = max(end_pos - SEARCH_CHUNK_SIZE, 0)
chunk_size = end_pos - start_pos
# Is the remaining chunk large enough to hold the pattern?
if chunk_size < len(magic_pattern):
break
# Read and scan the chunk
fp.seek(start_pos, os.SEEK_SET)
buf = fp.read(chunk_size)
pos = buf.rfind(magic_pattern)
if pos != -1:
magic_offset = start_pos + pos
break
# Adjust search location for next chunk; ensure proper overlap
end_pos = start_pos + len(magic_pattern) - 1
return magic_offset
@classmethod
def _parse_toc(cls, data):
options = []
toc = {}
cur_pos = 0
while cur_pos < len(data):
# Read and parse the fixed-size TOC entry header
entry_length, entry_offset, data_length, uncompressed_length, compression_flag, typecode = \
struct.unpack(cls._TOC_ENTRY_FORMAT, data[cur_pos:(cur_pos + cls._TOC_ENTRY_LENGTH)])
cur_pos += cls._TOC_ENTRY_LENGTH
# Read variable-length name
name_length = entry_length - cls._TOC_ENTRY_LENGTH
name, *_ = struct.unpack(f'{name_length}s', data[cur_pos:(cur_pos + name_length)])
cur_pos += name_length
# Name string may contain up to 15 bytes of padding
name = name.rstrip(b'\0').decode('utf-8')
typecode = typecode.decode('ascii')
# The TOC should not contain duplicates, except for OPTION entries. Therefore, keep those
# in a separate list. With options, the rest of the entries do not make sense, anyway.
if typecode == 'o':
options.append(name)
else:
toc[name] = (entry_offset, data_length, uncompressed_length, compression_flag, typecode)
return toc, options
def extract(self, name):
"""
Extract data for the given entry name.
"""
entry = self.toc.get(name)
if entry is None:
raise KeyError(f"No entry named {name!r} found in the archive!")
entry_offset, data_length, uncompressed_length, compression_flag, typecode = entry
with open(self._filename, "rb") as fp:
fp.seek(self._start_offset + entry_offset, os.SEEK_SET)
data = fp.read(data_length)
if compression_flag:
import zlib
data = zlib.decompress(data)
return data
def raw_pkg_data(self):
"""
Extract complete PKG/CArchive archive from the parent file (executable).
"""
total_length = self._end_offset - self._start_offset
with open(self._filename, "rb") as fp:
fp.seek(self._start_offset, os.SEEK_SET)
return fp.read(total_length)
def open_embedded_archive(self, name):
"""
Open new archive reader for the embedded archive.
"""
entry = self.toc.get(name)
if entry is None:
raise KeyError(f"No entry named {name!r} found in the archive!")
entry_offset, data_length, uncompressed_length, compression_flag, typecode = entry
if typecode == PKG_ITEM_PYZ:
# Open as embedded archive, without extraction.
return ZlibArchiveReader(self._filename, self._start_offset + entry_offset)
elif typecode == PKG_ITEM_ZIPFILE:
raise NotAnArchiveError("Zipfile archives not supported yet!")
else:
raise NotAnArchiveError(f"Entry {name!r} is not a supported embedded archive!")
def pkg_archive_contents(filename, recursive=True):
"""
List the contents of the PKG / CArchive. If `recursive` flag is set (the default), the contents of the embedded PYZ
archive is included as well.
Used by the tests.
"""
contents = []
pkg_archive = CArchiveReader(filename)
for name, toc_entry in pkg_archive.toc.items():
*_, typecode = toc_entry
contents.append(name)
if typecode == PKG_ITEM_PYZ and recursive:
pyz_archive = pkg_archive.open_embedded_archive(name)
for name in pyz_archive.toc.keys():
contents.append(name)
return contents
#-----------------------------------------------------------------------------
# Copyright (c) 2022-2023, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
#-----------------------------------------------------------------------------
from typing import Tuple
import os
import hashlib
def normalize_icon_type(icon_path: str, allowed_types: Tuple[str], convert_type: str, workpath: str) -> str:
"""
Returns a valid icon path or raises an Exception on error.
Ensures that the icon exists, and, if necessary, attempts to convert it to correct OS-specific format using Pillow.
Takes:
icon_path - the icon given by the user
allowed_types - a tuple of icon formats that should be allowed through
EX: ("ico", "exe")
convert_type - the type to attempt conversion too if necessary
EX: "icns"
workpath - the temp directory to save any newly generated image files
"""
# explicitly error if file not found
if not os.path.exists(icon_path):
raise FileNotFoundError(f"Icon input file {icon_path} not found")
_, extension = os.path.splitext(icon_path)
extension = extension[1:] # get rid of the "." in ".whatever"
# if the file is already in the right format, pass it back unchanged
if extension in allowed_types:
# Check both the suffix and the header of the file to guard against the user confusing image types.
signatures = hex_signatures[extension]
with open(icon_path, "rb") as f:
header = f.read(max(len(s) for s in signatures))
if any(list(header)[:len(s)] == s for s in signatures):
return icon_path
# The icon type is wrong! Let's try and import PIL
try:
from PIL import Image as PILImage
import PIL
except ImportError:
raise ValueError(
f"Received icon image '{icon_path}' which exists but is not in the correct format. On this platform, "
f"only {allowed_types} images may be used as icons. If Pillow is installed, automatic conversion will "
f"be attempted. Please install Pillow or convert your '{extension}' file to one of {allowed_types} "
f"and try again."
)
# Let's try to use PIL to convert the icon file type
try:
_generated_name = f"generated-{hashlib.sha256(icon_path.encode()).hexdigest()}.{convert_type}"
generated_icon = os.path.join(workpath, _generated_name)
with PILImage.open(icon_path) as im:
# If an image uses a custom palette + transparency, convert it to RGBA for a better alpha mask depth.
if im.mode == "P" and im.info.get("transparency", None) is not None:
# The bit depth of the alpha channel will be higher, and the images will look better when eventually
# scaled to multiple sizes (16,24,32,..) for the ICO format for example.
im = im.convert("RGBA")
im.save(generated_icon)
icon_path = generated_icon
except PIL.UnidentifiedImageError:
raise ValueError(
f"Something went wrong converting icon image '{icon_path}' to '.{convert_type}' with Pillow, "
f"perhaps the image format is unsupported. Try again with a different file or use a file that can "
f"be used without conversion on this platform: {allowed_types}"
)
return icon_path
# Possible initial bytes of icon types PyInstaller needs to be able to recognise.
# Taken from: https://en.wikipedia.org/wiki/List_of_file_signatures
hex_signatures = {
"png": [[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]],
"exe": [[0x4D, 0x5A], [0x5A, 0x4D]],
"ico": [[0x00, 0x00, 0x01, 0x00]],
"icns": [[0x69, 0x63, 0x6e, 0x73]],
}
# -----------------------------------------------------------------------------
# Copyright (c) 2005-2023, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
# -----------------------------------------------------------------------------
"""
Templates for the splash screen tcl script.
"""
from PyInstaller.compat import is_cygwin, is_darwin, is_win
ipc_script = r"""
proc _ipc_server {channel clientaddr clientport} {
# This function is called if a new client connects to
# the server. This creates a channel, which calls
# _ipc_caller if data was send through the connection
set client_name [format <%s:%d> $clientaddr $clientport]
chan configure $channel \
-buffering none \
-encoding utf-8 \
-eofchar \x04 \
-translation cr
chan event $channel readable [list _ipc_caller $channel $client_name]
}
proc _ipc_caller {channel client_name} {
# This function is called if a command was sent through
# the tcp connection. The current implementation supports
# two commands: update_text and exit, although exit
# is implemented to be called if the connection gets
# closed (from python) or the character 0x04 was received
chan gets $channel cmd
if {[chan eof $channel]} {
# This is entered if either the connection was closed
# or the char 0x04 was send
chan close $channel
exit
} elseif {![chan blocked $channel]} {
# RPC methods
# update_text command
if {[string match "update_text*" $cmd]} {
global status_text
set first [expr {[string first "(" $cmd] + 1}]
set last [expr {[string last ")" $cmd] - 1}]
set status_text [string range $cmd $first $last]
}
# Implement other procedures here
}
}
# By setting the port to 0 the os will assign a free port
set server_socket [socket -server _ipc_server -myaddr localhost 0]
set server_port [fconfigure $server_socket -sockname]
# This environment variable is shared between the python and the tcl
# interpreter and publishes the port the tcp server socket is available
set env(_PYI_SPLASH_IPC) [lindex $server_port 2]
"""
image_script = r"""
# The variable $_image_data, which holds the data for the splash
# image is created by the bootloader.
image create photo splash_image
splash_image put $_image_data
# delete the variable, because the image now holds the data
unset _image_data
proc canvas_text_update {canvas tag _var - -} {
# This function is rigged to be called if the a variable
# status_text gets changed. This updates the text on
# the canvas
upvar $_var var
$canvas itemconfigure $tag -text $var
}
"""
splash_canvas_setup = r"""
package require Tk
set image_width [image width splash_image]
set image_height [image height splash_image]
set display_width [winfo screenwidth .]
set display_height [winfo screenheight .]
set x_position [expr {int(0.5*($display_width - $image_width))}]
set y_position [expr {int(0.5*($display_height - $image_height))}]
# Toplevel frame in which all widgets should be positioned
frame .root
# Configure the canvas on which the splash
# screen will be drawn
canvas .root.canvas \
-width $image_width \
-height $image_height \
-borderwidth 0 \
-highlightthickness 0
# Draw the image into the canvas, filling it.
.root.canvas create image \
[expr {$image_width / 2}] \
[expr {$image_height / 2}] \
-image splash_image
"""
splash_canvas_text = r"""
# Create a text on the canvas, which tracks the local
# variable status_text. status_text is changed via C to
# update the progress on the splash screen.
# We cannot use the default label, because it has a
# default background, which cannot be turned transparent
.root.canvas create text \
%(pad_x)d \
%(pad_y)d \
-fill %(color)s \
-justify center \
-font myFont \
-tag vartext \
-anchor sw
trace add variable status_text write \
[list canvas_text_update .root.canvas vartext]
set status_text "%(default_text)s"
"""
splash_canvas_default_font = r"""
font create myFont {*}[font actual TkDefaultFont]
font configure myFont -size %(font_size)d
"""
splash_canvas_custom_font = r"""
font create myFont -family %(font)s -size %(font_size)d
"""
if is_win or is_cygwin:
transparent_setup = r"""
# If the image is transparent, the background will be filled
# with magenta. The magenta background is later replaced with transparency.
# Here is the limitation of this implementation, that only
# sharp transparent image corners are possible
wm attributes . -transparentcolor magenta
.root.canvas configure -background magenta
"""
elif is_darwin:
# This is untested, but should work following: https://stackoverflow.com/a/44296157/5869139
transparent_setup = r"""
wm attributes . -transparent 1
. configure -background systemTransparent
.root.canvas configure -background systemTransparent
"""
else:
# For Linux there is no common way to create a transparent window
transparent_setup = r""
pack_widgets = r"""
# Position all widgets in the window
pack .root
grid .root.canvas -column 0 -row 0 -columnspan 1 -rowspan 2
"""
# Enable always-on-top behavior, by setting overrideredirect and the topmost attribute.
position_window_on_top = r"""
# Set position and mode of the window - always-on-top behavior
wm overrideredirect . 1
wm geometry . +${x_position}+${y_position}
wm attributes . -topmost 1
"""
# Disable always-on-top behavior
if is_win or is_cygwin or is_darwin:
# On Windows, we disable the always-on-top behavior while still setting overrideredirect
# (to disable window decorations), but set topmost attribute to 0.
position_window = r"""
# Set position and mode of the window
wm overrideredirect . 1
wm geometry . +${x_position}+${y_position}
wm attributes . -topmost 0
"""
else:
# On Linux, we must not use overrideredirect; instead, we set X11-specific type attribute to splash,
# which lets the window manager to properly handle the splash screen (without window decorations
# but allowing other windows to be brought to front).
position_window = r"""
# Set position and mode of the window
wm geometry . +${x_position}+${y_position}
wm attributes . -type splash
"""
raise_window = r"""
raise .
"""
def build_script(text_options=None, always_on_top=False):
"""
This function builds the tcl script for the splash screen.
"""
# Order is important!
script = [
ipc_script,
image_script,
splash_canvas_setup,
]
if text_options:
# If the default font is used we need a different syntax
if text_options['font'] == "TkDefaultFont":
script.append(splash_canvas_default_font % text_options)
else:
script.append(splash_canvas_custom_font % text_options)
script.append(splash_canvas_text % text_options)
script.append(transparent_setup)
script.append(pack_widgets)
script.append(position_window_on_top if always_on_top else position_window)
script.append(raise_window)
return '\n'.join(script)
#-----------------------------------------------------------------------------
# Copyright (c) 2005-2023, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
#-----------------------------------------------------------------------------
# Only used if installed, not mean to pull in numpy.
excludedimports = ["numpy"]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment