Files
aufbau2csv/venv3_12/Lib/site-packages/cx_Freeze/executable.py

266 lines
8.6 KiB
Python

"""Module for the Executable base class."""
from __future__ import annotations
import os
import string
import sys
from collections.abc import Mapping
from pathlib import Path
from sysconfig import get_config_var, get_platform
from typing import TYPE_CHECKING
from cx_Freeze._compat import EXE_SUFFIX, IS_MACOS, IS_MINGW, IS_WINDOWS
from cx_Freeze.common import get_resource_file_path
from cx_Freeze.exception import OptionError, SetupError
if TYPE_CHECKING:
from setuptools import Distribution
STRINGREPLACE = list(
string.whitespace + string.punctuation.replace(".", "").replace("_", "")
)
__all__ = ["Executable", "validate_executables"]
class Executable:
"""Base Executable class."""
def __init__(
self,
script: str | Path,
init_script: str | Path | None = None,
base: str | Path | None = None,
target_name: str | None = None,
icon: str | Path | None = None,
shortcut_name: str | None = None,
shortcut_dir: str | Path | None = None,
copyright: str | None = None, # noqa: A002
trademarks: str | None = None,
manifest: str | Path | None = None,
uac_admin: bool = False,
uac_uiaccess: bool = False,
) -> None:
self.main_script = script
self.init_script = init_script
self.base = base
self.target_name = target_name
self.icon = icon
self.shortcut_name = shortcut_name
self.shortcut_dir = shortcut_dir
self.copyright = copyright
self.trademarks = trademarks
self.manifest = manifest
self.uac_admin = uac_admin
self.uac_uiaccess = uac_uiaccess
def __repr__(self) -> str:
return f"<Executable script={self.main_script}>"
@property
def base(self) -> Path:
""":return: the name of the base executable
:rtype: Path
"""
return self._base
@base.setter
def base(self, name: str | Path | None) -> None:
name = name or "console"
if name == "gui":
name = "Win32GUI" if IS_WINDOWS or IS_MINGW else "console"
elif name == "service":
name = "Win32Service" if IS_WINDOWS or IS_MINGW else "console"
if IS_WINDOWS or IS_MINGW:
platform_nodot = get_platform().replace(".", "").replace("-", "_")
soabi = f"{sys.implementation.cache_tag}-{platform_nodot}"
else:
soabi = get_config_var("SOABI")
suffix = EXE_SUFFIX
name_base = f"{name}-{soabi}"
self._base: Path = get_resource_file_path("bases", name_base, suffix)
if self._base is None:
msg = f"no base named {name!r} ({name_base!r})"
raise OptionError(msg)
self._ext: str = suffix
@property
def icon(self) -> Path | None:
""":return: the path of the icon
:rtype: Path
"""
return self._icon
@icon.setter
def icon(self, name: str | Path | None) -> None:
iconfile: Path = Path(name) if name else None
if iconfile and not iconfile.suffix:
# add an extension
valid_extensions = [".png", ".svg"]
if IS_WINDOWS or IS_MINGW:
valid_extensions.insert(0, ".ico")
elif IS_MACOS:
valid_extensions.insert(0, ".icns")
for ext in valid_extensions:
iconfile = iconfile.with_suffix(ext)
if iconfile.exists():
break
self._icon: Path | None = iconfile
@property
def init_module_name(self) -> str:
""":return: the name of the init module in zip file
:rtype: str
"""
return f"__init__{self._internal_name}"
@property
def init_script(self) -> Path:
""":return: the name of the initialization script that will be executed
before the main script is executed
:rtype: Path
"""
return self._init_script
@init_script.setter
def init_script(self, name: str | Path | None) -> None:
name = name or "console"
self._init_script: Path = get_resource_file_path(
"initscripts", name, ".py"
)
if self._init_script is None:
msg = f"no init_script named {name}"
raise OptionError(msg)
@property
def main_module_name(self) -> str:
""":return: the name of the main module in zip file
:rtype: str
"""
return f"__main__{self._internal_name}"
@property
def main_script(self) -> Path:
""":return: the path of the file containing the script which is to be
frozen
:rtype: Path
"""
return self._main_script
@main_script.setter
def main_script(self, name: str | Path) -> None:
self._main_script: Path = Path(name)
@property
def manifest(self) -> str | None:
""":return: the XML schema of the manifest which is to be included in
the frozen executable
:rtype: str
"""
return self._manifest
@manifest.setter
def manifest(self, name: str | Path | None) -> None:
self._manifest: str | None = None
if name is None:
return
if isinstance(name, str):
name = Path(name)
self._manifest = name.read_text(encoding="utf-8")
@property
def shortcut_name(self) -> str:
""":return: the name to give a shortcut for the executable when
included in an MSI package (Windows only).
:rtype: str
"""
return self._shortcut_name
@shortcut_name.setter
def shortcut_name(self, name: str) -> None:
self._shortcut_name: str = name
@property
def shortcut_dir(self) -> Path:
""":return: tthe directory in which to place the shortcut when being
installed by an MSI package; see the MSI Shortcut table documentation
for more information on what values can be placed here (Windows only).
:rtype: Path
"""
return self._shortcut_dir
@shortcut_dir.setter
def shortcut_dir(self, name: str | Path) -> None:
self._shortcut_dir: Path = Path(name) if name else None
@property
def target_name(self) -> str:
""":return: the name of the target executable
:rtype: str
"""
return self._name + self._ext
@target_name.setter
def target_name(self, name: str | None) -> None:
if name is None:
name = self.main_script.stem
else:
pathname = Path(name)
if name != pathname.name:
msg = (
"target_name cannot contain the path, only the filename: "
f"{pathname.name}"
)
raise OptionError(msg)
if sys.platform == "win32" and pathname.suffix.lower() == ".exe":
name = pathname.stem
self._name: str = name
name = name.partition(".")[0]
if not name.isidentifier():
for invalid in STRINGREPLACE:
name = name.replace(invalid, "_")
name = os.path.normcase(name)
self._internal_name: str = name
if not self.init_module_name.isidentifier():
msg = f"target_name is invalid: {self._name!r}"
raise OptionError(msg)
def validate_executables(dist: Distribution, attr: str, value) -> None:
"""Verify that value is a valid executables attribute, which could be an
Executable list, a mapping list or a string list.
"""
try:
# verify that value is a list or tuple to exclude unordered
# or single-use iterables
assert isinstance(value, (list, tuple)) # noqa: S101
assert value # noqa: S101
# verify that elements of value are Executable, Dict or string
for executable in value:
assert isinstance(executable, (Executable, Mapping, str)) # noqa: S101
except (TypeError, ValueError, AttributeError, AssertionError) as exc:
msg = f"{attr!r} must be a list of Executable (got {value!r})"
raise SetupError(msg) from exc
# Returns valid Executable list
if dist.executables == value:
dist.executables = []
executables = list(value)
for i, executable in enumerate(executables):
if isinstance(executable, str):
executables[i] = Executable(executable)
elif isinstance(executable, Mapping):
executables[i] = Executable(**executable)
dist.executables.extend(executables)