mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-06-13 04:05:04 +03:00
Merge commit from fork
Builder Win / Build PluginLoader for Win (push) Has been cancelled
Builder / Build PluginLoader (push) Has been cancelled
Push Updated Plugin Stub to Template / copy-stub (push) Has been cancelled
Lint / Run linters (push) Has been cancelled
Type Check / Run type checkers (push) Has been cancelled
Builder Win / Build PluginLoader for Win (push) Has been cancelled
Builder / Build PluginLoader (push) Has been cancelled
Push Updated Plugin Stub to Template / copy-stub (push) Has been cancelled
Lint / Run linters (push) Has been cancelled
Type Check / Run type checkers (push) Has been cancelled
* fix incorrect permissions on plugin directories * chown plugin dirs too * fix the stupid * cleanup useless comments
This commit is contained in:
@@ -18,9 +18,10 @@ from enum import IntEnum
|
|||||||
from typing import Dict, List, TypedDict
|
from typing import Dict, List, TypedDict
|
||||||
|
|
||||||
# Local modules
|
# Local modules
|
||||||
from .localplatform.localplatform import chown, chmod
|
from .localplatform.localplatform import chown, chmod, get_chown_plugin_path
|
||||||
from .loader import Loader, Plugins
|
from .loader import Loader, Plugins
|
||||||
from .helpers import get_ssl_context, download_remote_binary_to_path
|
from .helpers import get_ssl_context, download_remote_binary_to_path
|
||||||
|
from .enums import UserType
|
||||||
from .settings import SettingsManager
|
from .settings import SettingsManager
|
||||||
|
|
||||||
logger = getLogger("Browser")
|
logger = getLogger("Browser")
|
||||||
@@ -60,13 +61,6 @@ class PluginBrowser:
|
|||||||
return False
|
return False
|
||||||
zip_file = ZipFile(zip)
|
zip_file = ZipFile(zip)
|
||||||
zip_file.extractall(self.plugin_path)
|
zip_file.extractall(self.plugin_path)
|
||||||
plugin_folder = self.find_plugin_folder(name)
|
|
||||||
assert plugin_folder is not None
|
|
||||||
plugin_dir = path.join(self.plugin_path, plugin_folder)
|
|
||||||
|
|
||||||
if not chown(plugin_dir) or not chmod(plugin_dir, 555):
|
|
||||||
logger.error(f"chown/chmod exited with a non-zero exit code")
|
|
||||||
return False
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _download_remote_binaries_for_plugin_with_name(self, pluginBasePath: str):
|
async def _download_remote_binaries_for_plugin_with_name(self, pluginBasePath: str):
|
||||||
@@ -101,8 +95,6 @@ class PluginBrowser:
|
|||||||
rv = False
|
rv = False
|
||||||
raise Exception(f"Error Downloading Remote Binary {binName}@{binURL} with hash {binHash} to {path.join(pluginBinPath, binName)}")
|
raise Exception(f"Error Downloading Remote Binary {binName}@{binURL} with hash {binHash} to {path.join(pluginBinPath, binName)}")
|
||||||
|
|
||||||
chown(self.plugin_path)
|
|
||||||
chmod(pluginBasePath, 555)
|
|
||||||
else:
|
else:
|
||||||
rv = True
|
rv = True
|
||||||
logger.info(f"No Remote Binaries to Download")
|
logger.info(f"No Remote Binaries to Download")
|
||||||
@@ -124,6 +116,25 @@ class PluginBrowser:
|
|||||||
return folder
|
return folder
|
||||||
except:
|
except:
|
||||||
logger.debug(f"skipping {folder}")
|
logger.debug(f"skipping {folder}")
|
||||||
|
|
||||||
|
def set_plugin_dir_permissions(self, plugin_dir: str) -> bool:
|
||||||
|
plugin_json_path = path.join(plugin_dir, 'plugin.json')
|
||||||
|
logger.debug(f"Checking plugin.json at {plugin_json_path}")
|
||||||
|
|
||||||
|
root_plugin = False
|
||||||
|
|
||||||
|
if access(plugin_json_path, R_OK):
|
||||||
|
with open(plugin_json_path, "r", encoding="utf-8") as f:
|
||||||
|
plugin_json = json.load(f)
|
||||||
|
if "flags" in plugin_json and "root" in plugin_json["flags"]:
|
||||||
|
root_plugin = True
|
||||||
|
|
||||||
|
logger.debug("root_plugin %d, dir %s", root_plugin, plugin_dir)
|
||||||
|
if get_chown_plugin_path():
|
||||||
|
return chown(plugin_dir, UserType.EFFECTIVE_USER if root_plugin else UserType.HOST_USER, True) and chown(plugin_dir, UserType.EFFECTIVE_USER, False) and chmod(plugin_dir, 755) and chown(plugin_json_path, UserType.EFFECTIVE_USER, False) and chmod(plugin_json_path, 755)
|
||||||
|
else:
|
||||||
|
logger.debug("chown disabled by environment")
|
||||||
|
return True
|
||||||
|
|
||||||
async def uninstall_plugin(self, name: str):
|
async def uninstall_plugin(self, name: str):
|
||||||
if self.loader.watcher:
|
if self.loader.watcher:
|
||||||
@@ -266,6 +277,7 @@ class PluginBrowser:
|
|||||||
plugin_dir = path.join(self.plugin_path, plugin_folder)
|
plugin_dir = path.join(self.plugin_path, plugin_folder)
|
||||||
await self.loader.ws.emit("loader/plugin_download_info", 95, "Store.download_progress_info.download_remote")
|
await self.loader.ws.emit("loader/plugin_download_info", 95, "Store.download_progress_info.download_remote")
|
||||||
ret = await self._download_remote_binaries_for_plugin_with_name(plugin_dir)
|
ret = await self._download_remote_binaries_for_plugin_with_name(plugin_dir)
|
||||||
|
chown_ret = self.set_plugin_dir_permissions(plugin_dir)
|
||||||
if ret:
|
if ret:
|
||||||
logger.info(f"Installed {name} (Version: {version})")
|
logger.info(f"Installed {name} (Version: {version})")
|
||||||
if name in self.loader.plugins:
|
if name in self.loader.plugins:
|
||||||
@@ -278,6 +290,9 @@ class PluginBrowser:
|
|||||||
self.settings.setSetting("pluginOrder", current_plugin_order)
|
self.settings.setSetting("pluginOrder", current_plugin_order)
|
||||||
logger.debug("Plugin %s was added to the pluginOrder setting", name)
|
logger.debug("Plugin %s was added to the pluginOrder setting", name)
|
||||||
await self.loader.import_plugin(path.join(plugin_dir, "main.py"), plugin_folder)
|
await self.loader.import_plugin(path.join(plugin_dir, "main.py"), plugin_folder)
|
||||||
|
elif not chown_ret:
|
||||||
|
logger.error("Could not chown plugin")
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
logger.error("Could not download remote binaries")
|
logger.error("Could not download remote binaries")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
|
|
||||||
class UserType(IntEnum):
|
class UserType(IntEnum):
|
||||||
HOST_USER = 1
|
HOST_USER = 1 # usually deck
|
||||||
EFFECTIVE_USER = 2
|
EFFECTIVE_USER = 2 # usually root
|
||||||
ROOT = 3
|
|
||||||
|
|
||||||
class PluginLoadType(IntEnum):
|
class PluginLoadType(IntEnum):
|
||||||
LEGACY_EVAL_IIFE = 0 # legacy, uses legacy serverAPI
|
LEGACY_EVAL_IIFE = 0 # legacy, uses legacy serverAPI
|
||||||
|
|||||||
@@ -181,7 +181,8 @@ def get_user_group_id() -> int:
|
|||||||
|
|
||||||
# Get the default home path unless a user is specified
|
# Get the default home path unless a user is specified
|
||||||
def get_home_path(username: str | None = None) -> str:
|
def get_home_path(username: str | None = None) -> str:
|
||||||
return localplatform.get_home_path(UserType.ROOT if username == "root" else UserType.HOST_USER)
|
# TODO hardcoded root is kinda a hack
|
||||||
|
return localplatform.get_home_path(UserType.EFFECTIVE_USER if username == "root" else UserType.HOST_USER)
|
||||||
|
|
||||||
async def is_systemd_unit_active(unit_name: str) -> bool:
|
async def is_systemd_unit_active(unit_name: str) -> bool:
|
||||||
return await localplatform.service_active(unit_name)
|
return await localplatform.service_active(unit_name)
|
||||||
|
|||||||
@@ -59,8 +59,6 @@ def chown(path : str, user : UserType = UserType.HOST_USER, recursive : bool =
|
|||||||
user_str = _get_user()+":"+_get_user_group()
|
user_str = _get_user()+":"+_get_user_group()
|
||||||
elif user == UserType.EFFECTIVE_USER:
|
elif user == UserType.EFFECTIVE_USER:
|
||||||
user_str = _get_effective_user()+":"+_get_effective_user_group()
|
user_str = _get_effective_user()+":"+_get_effective_user_group()
|
||||||
elif user == UserType.ROOT:
|
|
||||||
user_str = "root:root"
|
|
||||||
else:
|
else:
|
||||||
raise Exception("Unknown User Type")
|
raise Exception("Unknown User Type")
|
||||||
|
|
||||||
@@ -87,7 +85,7 @@ def chmod(path : str, permissions : int, recursive : bool = True) -> bool:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def folder_owner(path : str) -> UserType|None:
|
def file_owner(path : str) -> UserType|None:
|
||||||
user_owner = _get_user_owner(path)
|
user_owner = _get_user_owner(path)
|
||||||
|
|
||||||
if (user_owner == _get_user()):
|
if (user_owner == _get_user()):
|
||||||
@@ -106,13 +104,14 @@ def get_home_path(user : UserType = UserType.HOST_USER) -> str:
|
|||||||
user_name = _get_user()
|
user_name = _get_user()
|
||||||
elif user == UserType.EFFECTIVE_USER:
|
elif user == UserType.EFFECTIVE_USER:
|
||||||
user_name = _get_effective_user()
|
user_name = _get_effective_user()
|
||||||
elif user == UserType.ROOT:
|
|
||||||
pass
|
|
||||||
else:
|
else:
|
||||||
raise Exception("Unknown User Type")
|
raise Exception("Unknown User Type")
|
||||||
|
|
||||||
return pwd.getpwnam(user_name).pw_dir
|
return pwd.getpwnam(user_name).pw_dir
|
||||||
|
|
||||||
|
def get_effective_username() -> str:
|
||||||
|
return _get_effective_user()
|
||||||
|
|
||||||
def get_username() -> str:
|
def get_username() -> str:
|
||||||
return _get_user()
|
return _get_user()
|
||||||
|
|
||||||
@@ -121,8 +120,8 @@ def setgid(user : UserType = UserType.HOST_USER):
|
|||||||
|
|
||||||
if user == UserType.HOST_USER:
|
if user == UserType.HOST_USER:
|
||||||
user_id = _get_user_group_id()
|
user_id = _get_user_group_id()
|
||||||
elif user == UserType.ROOT:
|
elif user == UserType.EFFECTIVE_USER:
|
||||||
pass
|
pass # we already are
|
||||||
else:
|
else:
|
||||||
raise Exception("Unknown user type")
|
raise Exception("Unknown user type")
|
||||||
|
|
||||||
@@ -133,8 +132,8 @@ def setuid(user : UserType = UserType.HOST_USER):
|
|||||||
|
|
||||||
if user == UserType.HOST_USER:
|
if user == UserType.HOST_USER:
|
||||||
user_id = _get_user_id()
|
user_id = _get_user_id()
|
||||||
elif user == UserType.ROOT:
|
elif user == UserType.EFFECTIVE_USER:
|
||||||
pass
|
pass # we already are
|
||||||
else:
|
else:
|
||||||
raise Exception("Unknown user type")
|
raise Exception("Unknown user type")
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ def chown(path : str, user : UserType = UserType.HOST_USER, recursive : bool =
|
|||||||
def chmod(path : str, permissions : int, recursive : bool = True) -> bool:
|
def chmod(path : str, permissions : int, recursive : bool = True) -> bool:
|
||||||
return True # Stubbed
|
return True # Stubbed
|
||||||
|
|
||||||
def folder_owner(path : str) -> UserType|None:
|
def file_owner(path : str) -> UserType|None:
|
||||||
return UserType.HOST_USER # Stubbed
|
return UserType.HOST_USER # Stubbed
|
||||||
|
|
||||||
def get_home_path(user : UserType = UserType.HOST_USER) -> str:
|
def get_home_path(user : UserType = UserType.HOST_USER) -> str:
|
||||||
@@ -34,6 +34,9 @@ async def service_restart(service_name : str, block : bool = True) -> bool:
|
|||||||
|
|
||||||
return True # Stubbed
|
return True # Stubbed
|
||||||
|
|
||||||
|
def get_effective_username() -> str:
|
||||||
|
return os.getlogin()
|
||||||
|
|
||||||
def get_username() -> str:
|
def get_username() -> str:
|
||||||
return os.getlogin()
|
return os.getlogin()
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ def chown_plugin_dir():
|
|||||||
if not path.exists(plugin_path): # For safety, create the folder before attempting to do anything with it
|
if not path.exists(plugin_path): # For safety, create the folder before attempting to do anything with it
|
||||||
mkdir_as_user(plugin_path)
|
mkdir_as_user(plugin_path)
|
||||||
|
|
||||||
if not chown(plugin_path, UserType.HOST_USER) or not chmod(plugin_path, 555):
|
if not chown(plugin_path, UserType.EFFECTIVE_USER, False) or not chmod(plugin_path, 755, False):
|
||||||
logger.error(f"chown/chmod exited with a non-zero exit code")
|
logger.error(f"chown/chmod exited with a non-zero exit code")
|
||||||
|
|
||||||
if get_chown_plugin_path() == True:
|
if get_chown_plugin_path() == True:
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ from traceback import format_exc
|
|||||||
|
|
||||||
from .sandboxed_plugin import SandboxedPlugin
|
from .sandboxed_plugin import SandboxedPlugin
|
||||||
from .messages import MethodCallRequest, SocketMessageType
|
from .messages import MethodCallRequest, SocketMessageType
|
||||||
from ..enums import PluginLoadType
|
from ..enums import PluginLoadType, UserType
|
||||||
|
from ..localplatform.localplatform import file_owner, chown, chmod, get_chown_plugin_path
|
||||||
from ..localplatform.localsocket import LocalSocket
|
from ..localplatform.localsocket import LocalSocket
|
||||||
from ..helpers import get_homebrew_path, mkdir_as_user
|
from ..helpers import get_homebrew_path, mkdir_as_user
|
||||||
|
|
||||||
@@ -26,9 +27,12 @@ class PluginWrapper:
|
|||||||
|
|
||||||
self.load_type = PluginLoadType.LEGACY_EVAL_IIFE.value
|
self.load_type = PluginLoadType.LEGACY_EVAL_IIFE.value
|
||||||
|
|
||||||
json = load(open(path.join(plugin_path, plugin_directory, "plugin.json"), "r", encoding="utf-8"))
|
plugin_dir_path = path.join(plugin_path, plugin_directory)
|
||||||
if path.isfile(path.join(plugin_path, plugin_directory, "package.json")):
|
plugin_json_path = path.join(plugin_dir_path, "plugin.json")
|
||||||
package_json = load(open(path.join(plugin_path, plugin_directory, "package.json"), "r", encoding="utf-8"))
|
|
||||||
|
json = load(open(plugin_json_path, "r", encoding="utf-8"))
|
||||||
|
if path.isfile(path.join(plugin_dir_path, "package.json")):
|
||||||
|
package_json = load(open(path.join(plugin_dir_path, "package.json"), "r", encoding="utf-8"))
|
||||||
self.version = package_json["version"]
|
self.version = package_json["version"]
|
||||||
if ("type" in package_json and package_json["type"] == "module"):
|
if ("type" in package_json and package_json["type"] == "module"):
|
||||||
self.load_type = PluginLoadType.ESMODULE_V1.value
|
self.load_type = PluginLoadType.ESMODULE_V1.value
|
||||||
@@ -42,6 +46,17 @@ class PluginWrapper:
|
|||||||
|
|
||||||
self.log = getLogger("plugin")
|
self.log = getLogger("plugin")
|
||||||
|
|
||||||
|
if get_chown_plugin_path():
|
||||||
|
# ensure plugin folder ownership
|
||||||
|
if file_owner(plugin_dir_path) != UserType.EFFECTIVE_USER:
|
||||||
|
chown(plugin_dir_path, UserType.EFFECTIVE_USER if "root" in self.flags else UserType.HOST_USER, True)
|
||||||
|
chown(plugin_dir_path, UserType.EFFECTIVE_USER, False)
|
||||||
|
chmod(plugin_dir_path, 755, True)
|
||||||
|
# fix plugin.json permissions
|
||||||
|
if file_owner(plugin_json_path) != UserType.EFFECTIVE_USER:
|
||||||
|
chown(plugin_json_path, UserType.EFFECTIVE_USER, False)
|
||||||
|
chmod(plugin_json_path, 755, False)
|
||||||
|
|
||||||
self.sandboxed_plugin = SandboxedPlugin(self.name, self.passive, self.flags, self.file, self.plugin_directory, self.plugin_path, self.version, self.author, self.api_version)
|
self.sandboxed_plugin = SandboxedPlugin(self.name, self.passive, self.flags, self.file, self.plugin_directory, self.plugin_path, self.version, self.author, self.api_version)
|
||||||
self.proc: Process | None = None
|
self.proc: Process | None = None
|
||||||
self._socket = LocalSocket()
|
self._socket = LocalSocket()
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from .messages import SocketResponseDict, SocketMessageType
|
|||||||
from ..localplatform.localsocket import LocalSocket
|
from ..localplatform.localsocket import LocalSocket
|
||||||
from ..localplatform.localplatform import setgid, setuid, get_username, get_home_path, ON_LINUX
|
from ..localplatform.localplatform import setgid, setuid, get_username, get_home_path, ON_LINUX
|
||||||
from ..enums import UserType
|
from ..enums import UserType
|
||||||
from .. import helpers, settings, injector # pyright: ignore [reportUnusedImport]
|
from .. import helpers
|
||||||
|
|
||||||
from typing import List, TypeVar, Any
|
from typing import List, TypeVar, Any
|
||||||
|
|
||||||
@@ -61,10 +61,10 @@ class SandboxedPlugin:
|
|||||||
if self.passive:
|
if self.passive:
|
||||||
return
|
return
|
||||||
|
|
||||||
setgid(UserType.ROOT if "root" in self.flags else UserType.HOST_USER)
|
setgid(UserType.EFFECTIVE_USER if "root" in self.flags else UserType.HOST_USER)
|
||||||
setuid(UserType.ROOT if "root" in self.flags else UserType.HOST_USER)
|
setuid(UserType.EFFECTIVE_USER if "root" in self.flags else UserType.HOST_USER)
|
||||||
# export a bunch of environment variables to help plugin developers
|
# export a bunch of environment variables to help plugin developers
|
||||||
environ["HOME"] = get_home_path(UserType.ROOT if "root" in self.flags else UserType.HOST_USER)
|
environ["HOME"] = get_home_path(UserType.EFFECTIVE_USER if "root" in self.flags else UserType.HOST_USER)
|
||||||
environ["USER"] = "root" if "root" in self.flags else get_username()
|
environ["USER"] = "root" if "root" in self.flags else get_username()
|
||||||
environ["DECKY_VERSION"] = helpers.get_loader_version()
|
environ["DECKY_VERSION"] = helpers.get_loader_version()
|
||||||
environ["DECKY_USER"] = get_username()
|
environ["DECKY_USER"] = get_username()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from json import dump, load
|
from json import dump, load
|
||||||
from os import mkdir, path, listdir, rename
|
from os import mkdir, path, listdir, rename
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
from .localplatform.localplatform import chown, folder_owner, get_chown_plugin_path
|
from .localplatform.localplatform import chown, file_owner, get_chown_plugin_path
|
||||||
from .enums import UserType
|
from .enums import UserType
|
||||||
|
|
||||||
from .helpers import get_homebrew_path
|
from .helpers import get_homebrew_path
|
||||||
@@ -28,8 +28,8 @@ class SettingsManager:
|
|||||||
|
|
||||||
|
|
||||||
#If the owner of the settings directory is not the user, then set it as the user:
|
#If the owner of the settings directory is not the user, then set it as the user:
|
||||||
expected_user = UserType.HOST_USER if get_chown_plugin_path() else UserType.ROOT
|
expected_user = UserType.HOST_USER if get_chown_plugin_path() else UserType.EFFECTIVE_USER
|
||||||
if folder_owner(settings_directory) != expected_user:
|
if file_owner(settings_directory) != expected_user:
|
||||||
chown(settings_directory, expected_user, False)
|
chown(settings_directory, expected_user, False)
|
||||||
|
|
||||||
self.settings: Dict[str, Any] = {}
|
self.settings: Dict[str, Any] = {}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from aiohttp.web import Application, WebSocketResponse, Request, Response, get
|
|||||||
|
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
|
|
||||||
from typing import Callable, Coroutine, Dict, Any, cast, TypeVar
|
from typing import Callable, Coroutine, Dict, Any, cast
|
||||||
|
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
|
|
||||||
@@ -29,8 +29,6 @@ class WSMessageExtra(WSMessage):
|
|||||||
|
|
||||||
# see wsrouter.ts for typings
|
# see wsrouter.ts for typings
|
||||||
|
|
||||||
DataType = TypeVar("DataType")
|
|
||||||
|
|
||||||
Route = Callable[..., Coroutine[Any, Any, Any]]
|
Route = Callable[..., Coroutine[Any, Any, Any]]
|
||||||
|
|
||||||
class WSRouter:
|
class WSRouter:
|
||||||
|
|||||||
Generated
+24
-24
@@ -1,4 +1,4 @@
|
|||||||
# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiohappyeyeballs"
|
name = "aiohappyeyeballs"
|
||||||
@@ -190,7 +190,7 @@ description = "Timeout context manager for asyncio programs"
|
|||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
groups = ["main"]
|
groups = ["main"]
|
||||||
markers = "python_version < \"3.11\""
|
markers = "python_version == \"3.10\""
|
||||||
files = [
|
files = [
|
||||||
{file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
|
{file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
|
||||||
{file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
|
{file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
|
||||||
@@ -678,32 +678,32 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyinstaller"
|
name = "pyinstaller"
|
||||||
version = "6.8.0"
|
version = "6.14.2"
|
||||||
description = "PyInstaller bundles a Python application and all its dependencies into a single package."
|
description = "PyInstaller bundles a Python application and all its dependencies into a single package."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<3.13,>=3.8"
|
python-versions = "<3.14,>=3.8"
|
||||||
groups = ["dev"]
|
groups = ["dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "pyinstaller-6.8.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:5ff6bc2784c1026f8e2f04aa3760cbed41408e108a9d4cf1dd52ee8351a3f6e1"},
|
{file = "pyinstaller-6.14.2-py3-none-macosx_10_13_universal2.whl", hash = "sha256:d77d18bf5343a1afef2772393d7a489d4ec2282dee5bca549803fc0d74b78330"},
|
||||||
{file = "pyinstaller-6.8.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:39ac424d2ee2457d2ab11a5091436e75a0cccae207d460d180aa1fcbbafdd528"},
|
{file = "pyinstaller-6.14.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:3fa0c391e1300a9fd7752eb1ffe2950112b88fba9d2743eee2ef218a15f4705f"},
|
||||||
{file = "pyinstaller-6.8.0-py3-none-manylinux2014_i686.whl", hash = "sha256:355832a3acc7de90a255ecacd4b9f9e166a547a79c8905d49f14e3a75c1acdb9"},
|
{file = "pyinstaller-6.14.2-py3-none-manylinux2014_i686.whl", hash = "sha256:077efb2d01d16d9c8fdda3ad52788f0fead2791c5cec9ed6ce058af7e26eb74b"},
|
||||||
{file = "pyinstaller-6.8.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:6303c7a009f47e6a96ef65aed49f41e36ece8d079b9193ca92fe807403e5fe80"},
|
{file = "pyinstaller-6.14.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:fdd2bd020a18736806a6bd5d3c4352f1209b427a96ad6c459d88aec1d90c4f21"},
|
||||||
{file = "pyinstaller-6.8.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2b71509468c811968c0b5decb5bbe85b6292ea52d7b1f26313d2aabb673fa9a5"},
|
{file = "pyinstaller-6.14.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:03862c6b3cf7b16843d24b529f89cd4077cbe467883cd54ce7a81940d6da09d3"},
|
||||||
{file = "pyinstaller-6.8.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ff31c5b99e05a4384bbe2071df67ec8b2b347640a375eae9b40218be2f1754c6"},
|
{file = "pyinstaller-6.14.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:78827a21ada2a848e98671852d20d74b2955b6e2aaf2359ed13a462e1a603d84"},
|
||||||
{file = "pyinstaller-6.8.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:000c36b13fe4cd8d0d8c2bc855b1ddcf39867b5adf389e6b5ca45b25fa3e619d"},
|
{file = "pyinstaller-6.14.2-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:185710ab1503dfdfa14c43237d394d96ac183422d588294be42531480dfa6c38"},
|
||||||
{file = "pyinstaller-6.8.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:fe0af018d7d5077180e3144ada89a4da5df8d07716eb7e9482834a56dc57a4e8"},
|
{file = "pyinstaller-6.14.2-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:6c673a7e761bd4a2560cfd5dbe1ccdcfe2dff304b774e6e5242fc5afed953661"},
|
||||||
{file = "pyinstaller-6.8.0-py3-none-win32.whl", hash = "sha256:d257f6645c7334cbd66f38a4fac62c3ad614cc46302b2b5d9f8cc48c563bce0e"},
|
{file = "pyinstaller-6.14.2-py3-none-win32.whl", hash = "sha256:1697601aa788e3a52f0b5e620b4741a34b82e6f222ec6e1318b3a1349f566bb2"},
|
||||||
{file = "pyinstaller-6.8.0-py3-none-win_amd64.whl", hash = "sha256:81cccfa9b16699b457f4788c5cc119b50f3cd4d0db924955f15c33f2ad27a50d"},
|
{file = "pyinstaller-6.14.2-py3-none-win_amd64.whl", hash = "sha256:e10e0e67288d6dcb5898a917dd1d4272aa0ff33f197ad49a0e39618009d63ed9"},
|
||||||
{file = "pyinstaller-6.8.0-py3-none-win_arm64.whl", hash = "sha256:1c3060a263758cf7f0144ab4c016097b20451b2469d468763414665db1bb743d"},
|
{file = "pyinstaller-6.14.2-py3-none-win_arm64.whl", hash = "sha256:69fd11ca57e572387826afaa4a1b3d4cb74927d76f231f0308c0bd7872ca5ac1"},
|
||||||
{file = "pyinstaller-6.8.0.tar.gz", hash = "sha256:3f4b6520f4423fe19bcc2fd63ab7238851ae2bdcbc98f25bc5d2f97cc62012e9"},
|
{file = "pyinstaller-6.14.2.tar.gz", hash = "sha256:142cce0719e79315f0cc26400c2e5c45d9b6b17e7e0491fee444a9f8f16f4917"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
altgraph = "*"
|
altgraph = "*"
|
||||||
macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""}
|
macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""}
|
||||||
packaging = ">=22.0"
|
packaging = ">=22.0"
|
||||||
pefile = {version = ">=2022.5.30", markers = "sys_platform == \"win32\""}
|
pefile = {version = ">=2022.5.30,<2024.8.26 || >2024.8.26", markers = "sys_platform == \"win32\""}
|
||||||
pyinstaller-hooks-contrib = ">=2024.6"
|
pyinstaller-hooks-contrib = ">=2025.5"
|
||||||
pywin32-ctypes = {version = ">=0.2.1", markers = "sys_platform == \"win32\""}
|
pywin32-ctypes = {version = ">=0.2.1", markers = "sys_platform == \"win32\""}
|
||||||
setuptools = ">=42.0.0"
|
setuptools = ">=42.0.0"
|
||||||
|
|
||||||
@@ -713,14 +713,14 @@ hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyinstaller-hooks-contrib"
|
name = "pyinstaller-hooks-contrib"
|
||||||
version = "2024.7"
|
version = "2025.8"
|
||||||
description = "Community maintained hooks for PyInstaller"
|
description = "Community maintained hooks for PyInstaller"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.8"
|
||||||
groups = ["dev"]
|
groups = ["dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "pyinstaller_hooks_contrib-2024.7-py2.py3-none-any.whl", hash = "sha256:8bf0775771fbaf96bcd2f4dfd6f7ae6c1dd1b1efe254c7e50477b3c08e7841d8"},
|
{file = "pyinstaller_hooks_contrib-2025.8-py3-none-any.whl", hash = "sha256:8d0b8cfa0cb689a619294ae200497374234bd4e3994b3ace2a4442274c899064"},
|
||||||
{file = "pyinstaller_hooks_contrib-2024.7.tar.gz", hash = "sha256:fd5f37dcf99bece184e40642af88be16a9b89613ecb958a8bd1136634fc9fac5"},
|
{file = "pyinstaller_hooks_contrib-2025.8.tar.gz", hash = "sha256:3402ad41dfe9b5110af134422e37fc5d421ba342c6cb980bd67cb30b7415641c"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -1041,5 +1041,5 @@ propcache = ">=0.2.0"
|
|||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.1"
|
lock-version = "2.1"
|
||||||
python-versions = ">=3.10,<3.13"
|
python-versions = ">=3.10,<3.14"
|
||||||
content-hash = "3c9488709e61f3aa21ab47ceb9c677927ce770d8e1e327602a1a6afb09164475"
|
content-hash = "9a331b42c52134230384c1a7348c2856903d82d6007e06cd75eed13842aa21ea"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ include = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = ">=3.10,<3.13"
|
python = ">=3.10,<3.14"
|
||||||
|
|
||||||
aiohttp = "^3.10.11"
|
aiohttp = "^3.10.11"
|
||||||
aiohttp-jinja2 = "^1.5.1"
|
aiohttp-jinja2 = "^1.5.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user