Compare commits

...

27 commits

Author SHA1 Message Date
timoxa0 ac1f25ccbc Add unexpected error handler 2024-05-20 08:48:59 +05:00
timoxa0 f4c68e6bbb Convert path to str 2024-05-18 07:33:00 -07:00
timoxa0 b3aa49211f Migrate to self-hosted forgejo 2024-05-18 06:22:28 -07:00
timoxa0 6a74023ddc Add UnauthorizedBootImage error code 2024-05-16 23:31:42 +05:00
timoxa0 00055c8bd4 Add UnauthorizedBootImage exception. Add exit counter switch. 2024-05-16 23:31:28 +05:00
timoxa0 0e38f492da Optimize imports 2024-05-16 23:30:02 +05:00
timoxa0 dd4ccc5317 Add check_roofs function 2024-05-16 23:29:49 +05:00
timoxa0 c97de2f2e4 Do not use shell=True. Change FastbootException to UnauthorizedBootImage exception 2024-05-16 23:28:42 +05:00
timoxa0 fdad3b80b2 Add UnauthorizedBootImage and UnsupportedPlatform exceptions 2024-05-16 23:27:44 +05:00
timoxa0 36773eab2d Remove poetry.lock 2024-05-13 21:13:42 +05:00
timoxa0 5fe495e2b4 run -> main 2024-05-13 21:12:06 +05:00
timoxa0 83f6ba25fe Fix error codes 2024-05-13 21:09:23 +05:00
timoxa0 206a3c7c70 Verify device before getting user info 2024-05-13 21:08:50 +05:00
timoxa0 5ee96e2c39 Move exceptions to exceptions.py 2024-05-13 21:07:15 +05:00
timoxa0 e940c13019 Add exit handler 2024-05-13 19:40:05 +05:00
timoxa0 1160849b3e Rename Files to files 2024-05-13 19:38:02 +05:00
timoxa0 520c99d109 Move fastboot funcs to fastboot.py 2024-05-13 19:36:14 +05:00
timoxa0 6524b921eb Move fastboot funcs to fastboot.py 2024-05-13 19:30:34 +05:00
timoxa0 105aacf527 Fix partitions detection 2024-05-10 14:14:06 +05:00
timoxa0 4f7afad4e3 Add argsparse formatter 2024-05-10 14:12:58 +05:00
timoxa0 d12b3678b0 Remove unused imports 2024-05-10 14:12:40 +05:00
timoxa0 fb68dd8469 Fix builder 2024-05-10 14:12:14 +05:00
timoxa0 5a4afebf32 Add lon_deployer/_version.py 2024-05-10 13:28:41 +05:00
timoxa0 16e1232d77 Add debug output 2024-05-10 13:28:30 +05:00
timoxa0 8a69d6da77 Add version script 2024-05-10 13:28:19 +05:00
timoxa0 d0621cd60c Add debug and version flags 2024-05-10 13:28:08 +05:00
timoxa0 74fcd90daf Improved build system 2024-05-10 13:27:51 +05:00
13 changed files with 380 additions and 919 deletions

4
.gitignore vendored
View file

@ -6,4 +6,6 @@ lon_deployer/__pycache__/
tests/__pycache__/
*.zip
*.spec
*.spec
lon_deployer/_version.py
poetry.lock

View file

@ -9,7 +9,7 @@ Expand-Archive -Path $platformToolsZip -DestinationPath $env:USERPROFILE -Force
Remove-Item -Path $platformToolsZip -Force
$platformToolsDir = Join-Path $env:USERPROFILE "platform-tools"
$lonDeployerExe = Join-Path $lonDeployerDir "lon-deployer.exe"
$latestRelease = Invoke-WebRequest -UseBasicParsing -Uri "https://api.github.com/repos/timoxa0/LoN-Deployer/releases/latest" | ConvertFrom-Json
$latestRelease = Invoke-WebRequest -UseBasicParsing -Uri "https://git.timoxa0.su/api/v1/repos/timoxa0/LoN-Deployer/releases/latest" | ConvertFrom-Json
foreach ($asset in $latestRelease.assets) {
if ($asset.name -eq "LoN-Deployer.exe") {
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile $lonDeployerExe

View file

@ -1,2 +1,22 @@
# Linux on Nabu Deployer
#### Python tool for installing linux on xiaomi pad 5
#### Exit codes:
| Code | Description |
|:----:|:-----------------------------|
| 166 | Invalid RootFS image |
| 167 | RootFS image not found! |
| 168 | Invalid args |
| 169 | Failed to start adb server |
| 170 | Device not found |
| 171 | More then one device |
| 172 | Fastboot device timed out |
| 173 | Recovery device timed out |
| 174 | Incompatible partition table |
| 175 | Postinstall failed |
| 176 | Failed to patch boot image |
| 177 | Failed to boot recovery |
| 178 | Platform is not supported |
| 179 | Unexpected error |
| 253 | User cancel |
| 254 | Is it nabu? |

31
lon_deployer/builder.py Normal file
View file

@ -0,0 +1,31 @@
import PyInstaller.__main__
from pathlib import Path
PROJECT_DIR = Path(__file__).parent.parent.absolute()
def get_git_revision(base_path):
git_dir = Path(base_path) / '.git'
with (git_dir / 'HEAD').open('r') as head:
ref = head.readline().split(' ')[-1].strip()
with (git_dir / ref).open('r') as git_hash:
return git_hash.readline().strip()
def create_version():
with open(Path(__file__).parent/"_version.py", "w") as vf:
vf.write(f"VERSION=\"{get_git_revision(PROJECT_DIR)[:7]}\"")
def build():
create_version()
PyInstaller.__main__.run([
str(PROJECT_DIR / "run.py"),
'--onefile',
'--name',
'LoN-Deployer',
"-i",
"NONE",
# other pyinstaller options...
])

View file

@ -0,0 +1,22 @@
class FastbootException(Exception):
def __init__(self, message, output):
super().__init__(message)
self.output = output
class UnauthorizedBootImage(FastbootException):
pass
class DeviceNotFound(Exception):
pass
class RepartitonError(Exception):
pass
class UnsupportedPlatform(Exception):
def __init__(self, platform):
super().__init__(f"{platform} is not supported")
self.platform = platform

87
lon_deployer/fastboot.py Normal file
View file

@ -0,0 +1,87 @@
import os
import subprocess
from . import files, exceptions
from .utils import logger, console
def _fastboot_run(command: list[str], serial: str | None = None) -> str:
try:
cmd = ["fastboot"]
if not serial:
cmd += command
else:
cmd += ["-s", serial] + command
logger.debug(f"fb-cmd: {cmd}")
fb_out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, timeout=60)
logger.debug(f"fb-out: {fb_out}")
except FileNotFoundError:
console.log("Fastboot binary not found")
console.log("Exiting")
exit(1)
except subprocess.TimeoutExpired:
raise exceptions.DeviceNotFound("Timed out")
else:
return fb_out.decode()
def list_devices() -> list[str]:
return list(
filter(
lambda x: x != "",
[x.split("\t ")[0] for x in _fastboot_run(["devices"]).split("\n")]
)
)
def check_device(serial: str) -> bool:
return "nabu" in _fastboot_run(["getvar", "product"], serial=serial)
def check_parts(serial: str) -> bool:
linux_response = _fastboot_run(["getvar", "partition-type:linux"], serial=serial)
esp_response = _fastboot_run(["getvar", "partition-type:esp"], serial=serial)
logger.debug({
"esp": 'FAILED' not in esp_response,
"linux": 'FAILED' not in linux_response
})
return not ("FAILED" in linux_response or "FAILED" in esp_response)
def reboot(serial: str) -> None:
_fastboot_run(["reboot"], serial=serial)
def boot_ofox(serial: str) -> None:
files.OrangeFox.get()
ofox = files.OrangeFox.filepath
with console.status("[cyan]Booting", spinner="line", spinner_style="white"):
out = _fastboot_run(["boot", ofox], serial)
if "Failed to load/authenticate boot image: Device Error" in out:
raise exceptions.UnauthorizedBootImage("Failed to load/authenticate boot image: Device Error", out)
def flash(serial: str, part: str, data: bytes) -> None:
with open("image.img", "wb") as file:
file.write(data)
with console.status(f"[cyan]Flashing {part}", spinner="line", spinner_style="white"):
_fastboot_run(["flash", part, "image.img"], serial=serial)
os.remove("image.img")
def restore_parts(serial: str) -> None:
gpt_both = files.GPT_Both0.get()
userdata = files.UserData_Empty.get()
flash(serial, "partition:0", gpt_both)
flash(serial, "userdata", userdata)
def clean_device(serial: str) -> None:
_fastboot_run(["erase", "linux"], serial=serial)
_fastboot_run(["erase", "esp"], serial=serial)
def wait_for_bootloader(serial: str) -> None:
_fastboot_run(["getvar", "product"], serial=serial)

View file

@ -5,7 +5,6 @@ import requests
import hashlib
from os import path as op
from os import getcwd as pwd
from sys import exit
from rich.console import Console
from .utils import get_progress

View file

@ -1,9 +1,12 @@
import argparse
import atexit
import logging
import pathlib
import platform
import re
import signal
import subprocess
import threading
import magic
from os import getcwd as pwd, remove
from os import path as op
from sys import exit
@ -11,74 +14,122 @@ from time import sleep
import adbutils
import adbutils.shell
from rich.console import Console
from rich.prompt import Prompt
from rich_argparse import RichHelpFormatter
from . import Files
from .utils import check_device, get_port, flash_boot, boot_ofox, clean_device, wait_for_bootloader, check_parts, \
restore_parts, repartition, get_progress, list_fb_devices, reboot_fb_device
console = Console(log_path=False)
from . import exceptions
from . import fastboot
from . import files
from ._version import VERSION
from .utils import get_port, repartition, get_progress, logger, console, check_rootfs
exit_counter = 0
exit_counter_needed = False
adb: adbutils.AdbClient | None = None
def handle_sigint(*_) -> None:
global exit_counter
if exit_counter == 2:
console.log("CTRL+C pressed 3 times. Exiting")
exit(1)
global exit_counter, exit_counter_needed
if exit_counter_needed:
if exit_counter == 2:
console.log("CTRL+C pressed 3 times. Exiting")
exit(1)
else:
console.log(f"Press CTRL+C {2 - exit_counter} more {'time' if exit_counter == 1 else 'times'} to exit")
exit_counter += 1
else:
console.log(f"Press CTRL+C {2 - exit_counter} more {'time' if exit_counter == 1 else 'times'} to exit")
exit_counter += 1
console.log("CTRL+C pressed. Exiting")
exit(1)
def exit_handler(*_) -> None:
global adb
if adb is not None:
with console.status("[cyan]Stopping adb server", spinner="line", spinner_style="white"):
adb.server_kill()
def main() -> int:
global adb
global adb, exit_counter_needed
signal.signal(signal.SIGINT, handle_sigint)
atexit.register(exit_handler)
logger.debug(f"Running on {platform.system()}")
parser = argparse.ArgumentParser(
description="Linux on Nabu deployer"
description="Linux on Nabu deployer",
formatter_class=lambda prog: RichHelpFormatter(
prog,
max_help_position=37
)
)
parser.add_argument(
"-v", "--version",
help="show version and exit",
action="store_true"
)
parser.add_argument(
"-d", "--device-serial",
help="Device serial"
help="device serial"
)
parser.add_argument(
"-u", "--username",
help="User name"
help="linux user name"
)
parser.add_argument(
"-p", "--password",
help="User password"
help="linux user password"
)
parser.add_argument(
"RootFS",
help="RootFS image"
help="root fs image",
default=None, nargs="?"
)
parser.add_argument(
"-S", "--part-size",
help="linux partition size in percents"
)
parser.add_argument(
"--debug",
help="enable debug output",
action="store_true"
)
args = parser.parse_args()
rootfs = op.abspath(args.RootFS)
try:
if magic.Magic(mime=True).from_file(rootfs) not in ["application/octet-stream", "inode/blockdevice"]:
console.log("Invalid RootFS image")
return 1
except FileNotFoundError:
console.log("RootFS image not found!")
return 1
if args.version:
console.log(f"Version: {VERSION}")
return 0
if args.debug:
logger.setLevel(logging.DEBUG)
else:
logger.setLevel(logging.INFO)
if args.RootFS:
rootfs = pathlib.Path(args.RootFS)
try:
if not check_rootfs(rootfs):
console.log("Invalid RootFS image")
return 166
except exceptions.UnsupportedPlatform as e:
console.log(f"{e.platform} is not supported")
return 178
except FileNotFoundError:
console.log("RootFS image not found!")
return 167
else:
console.log(parser.parse_args("-h".split()))
return 168
while True:
try:
@ -95,16 +146,62 @@ def main() -> int:
console.log("Failed to start adb server")
console.log("Adb binary not found in path")
adb = None
return 1
return 169
else:
if proc.wait() != 0:
console.log("Failed to start adb server")
console.log(stdout)
adb = None
return 1
return 169
else:
break
fb_list = fastboot.list_devices()
adb_list = list(map(lambda x: x.serial, adb.list()))
if args.device_serial:
if args.device_serial in fb_list or args.device_serial in adb_list:
serial = args.device_serial
else:
console.log(f"Device with serial {args.device_serial} not found")
return 170
elif len(fb_list) == 1 and len(adb_list) == 0:
serial = fb_list[0]
elif len(adb_list) == 1 and len(fb_list) == 0:
serial = adb_list[0]
elif len(adb_list + fb_list) == 0:
console.log("No devices available. Please check your device connection")
return 170
else:
console.log("More then one device detected. Use -d flag to set device")
return 171
if serial not in fb_list:
console.log("ADB Device detected. Rebooting it to bootloader")
adb.device(serial).shell("reboot bootloader")
with console.status("[cyan]Waiting for fastboot device", spinner="line", spinner_style="white"):
try:
fastboot.wait_for_bootloader(serial)
except exceptions.DeviceNotFound:
console.log("Device timed out! Exiting")
return 172
console.log("Device connected")
else:
console.log("Device connected")
with console.status("[cyan]Getting info from device", spinner="line", spinner_style="white"):
try:
if not fastboot.check_device(serial):
console.log("Is it nabu?")
fastboot.reboot(serial)
return 254
except exceptions.DeviceNotFound:
console.log("Device timed out! Exiting")
return 172
parts_status = fastboot.check_parts(serial)
console.log("Device verified")
username = args.username
while username is None:
username_pattern = r"^[a-z0-9](?!.*[-._?])[a-z0-9]{1,18}[a-z0-9]$"
@ -122,38 +219,16 @@ def main() -> int:
password = None
linux_part_size = args.part_size
while linux_part_size is None:
while linux_part_size is not None or not parts_status:
if (linux_part_size is not None and
re.match(r"^\d+%$", linux_part_size) and 20 <= int(linux_part_size[:-1]) <= 90):
break
else:
console.log("Incorrect linux partition size. It can be [20; 90]%")
linux_part_size = Prompt.ask(
"Size of linux partition (leave empty to skip if possible)",
default="", show_default=False
)
if linux_part_size == "":
linux_part_size = None
break
elif re.match(r"^\d+%$", linux_part_size) and 20 <= int(linux_part_size[:-1]) <= 90:
break
else:
console.log("Incorrect linux partition size. It can be [20; 90]%")
linux_part_size = None
fb_list = list_fb_devices()
adb_list = list(map(lambda x: x.serial, adb.list()))
if args.device_serial:
if args.device_serial in fb_list or args.device_serial in adb_list:
serial = args.device_serial
else:
console.log(f"Device with serial {args.device_serial} not found")
return 1
elif len(fb_list) == 1 and len(adb_list) == 0:
serial = fb_list[0]
elif len(adb_list) == 1 and len(fb_list) == 0:
serial = adb_list[0]
elif len(adb_list + fb_list) == 0:
console.log("No devices available. Please check your device connection")
return 1
else:
console.log("More then one device detected. Use -d flag to set device")
return 1
for msg in [
f"Username: {username}",
@ -164,78 +239,78 @@ def main() -> int:
console.log(msg)
if Prompt.ask("Is it ok?", default="n", choices=["y", "n"]) == "n":
return 1
return 253
if serial not in fb_list:
console.log("ADB Device detected. Rebooting it to bootloader")
adb.device(serial).shell("reboot bootloader")
with console.status("[cyan]Waiting for fastboot device", spinner="line", spinner_style="white"):
wait_for_bootloader(serial)
console.log("Device connected")
else:
console.log("Device connected")
if not check_device(serial):
console.log("Is it nabu?")
reboot_fb_device(serial)
return 2
parts_status = check_parts(serial)
if linux_part_size:
if Prompt.ask(
f"Repartition {'requested' if parts_status else 'needed'}. All data will be ERASED",
default="n", choices=["y", "n"]) == "y":
exit_counter_needed = True
console.log("Restoring stock partition table")
restore_parts(serial)
fastboot.restore_parts(serial)
console.log("Booting OrangeFox recovery")
boot_ofox(serial)
try:
fastboot.boot_ofox(serial)
except exceptions.UnauthorizedBootImage:
console.log("Unable to start orangefox recovery")
console.log("Reflash your rom and try again")
fastboot.reboot(serial)
return 177
except subprocess.CalledProcessError as e:
console.log("Fastboot error. Please contact developer")
console.log("Executed command", e.cmd)
return 179
with console.status("[cyan]Waiting for device", spinner="line", spinner_style="white"):
try:
adb.wait_for(serial, state="recovery")
except adbutils.errors.AdbTimeout():
console.log("Could not detect recovery device")
return 1
console.log("Device timed out! Exiting")
return 173
repartition(serial, int(linux_part_size.replace("%", "")), percents=True)
console.log("Repartition complete")
console.log("To boot android you need to manually format data in your ROM recovery")
adbutils.device(serial).shell("reboot bootloader")
console.log("Rebooting into bootloader")
wait_for_bootloader(serial)
console.log("Booting OrangeFox recovery")
boot_ofox(serial)
with console.status("[cyan]Waiting for device", spinner="line", spinner_style="white"):
try:
adb.wait_for(serial, state="recovery")
except adbutils.errors.AdbTimeout():
console.log("Could not detect recovery device")
return 1
with console.status("[cyan]Formating userdata partition", spinner="line", spinner_style="white"):
adbutils.device(serial).shell("twrp format data")
console.log("Userdata partition formated")
adbutils.device(serial).shell("reboot bootloader")
console.log("Rebooting into bootloader")
with console.status("[cyan]Waiting for device", spinner="line", spinner_style="white"):
wait_for_bootloader(serial)
fastboot.wait_for_bootloader(serial)
except exceptions.DeviceNotFound:
console.log("Device timed out! Exiting")
return 172
else:
console.log("Repartition canceled. Exiting")
return 1
return 253
exit_counter_needed = True
if not parts_status and not linux_part_size:
console.log("Incompatible partition table detected. Repartition needed. Exiting")
return 1
return 174
console.log("Cleaning linux and esp")
clean_device(serial)
fastboot.clean_device(serial)
console.log("Booting OrangeFox recovery")
boot_ofox(serial)
try:
fastboot.boot_ofox(serial)
except exceptions.UnauthorizedBootImage:
console.log("Unable to start orangefox recovery")
console.log("Reflash your rom and try again")
fastboot.reboot(serial)
return 177
except subprocess.CalledProcessError as e:
console.log("Fastboot error. Please contact developer")
console.log("Executed command", e.cmd)
return 179
with console.status("[cyan]Waiting for device", spinner="line", spinner_style="white"):
try:
adb.wait_for(serial, state="recovery")
except adbutils.errors.AdbTimeout():
console.log("Could not detect recovery device")
return 1
console.log("Device timed out! Exiting")
return 173
adbd = adb.device(serial)
@ -250,8 +325,9 @@ def main() -> int:
)
nc_thread.start()
sleep(3)
console.log("Flashing RootFS")
with adbd.create_connection("tcp", server_port) as conn:
with adbd.create_connection(adbutils.Network.TCP, server_port) as conn:
with get_progress() as pbar:
task = pbar.add_task("[cyan]Uploading RootFS", total=op.getsize(rootfs))
with open(rootfs, "rb") as rootfs:
@ -272,23 +348,25 @@ def main() -> int:
else:
console.log("Postinstall failed. Rebooting to system")
adbd.reboot()
return 4
return 174
console.log("Installing UEFI")
bootshim = Files.BootShim.get()
payload = Files.UEFI_Payload.get()
bootshim = files.BootShim.get()
payload = files.UEFI_Payload.get()
adbd.shell("mkdir /tmp/uefi-install")
with get_progress() as pbar:
task = pbar.add_task("[cyan]Pushing uefi files", total=2)
adbd.sync.push(bootshim, f"/tmp/uefi-install/{Files.BootShim.name}")
adbd.sync.push(bootshim, f"/tmp/uefi-install/{files.BootShim.name}")
pbar.update(task, advance=1)
adbd.sync.push(payload, f"/tmp/uefi-install/{Files.UEFI_Payload.name}")
adbd.sync.push(payload, f"/tmp/uefi-install/{files.UEFI_Payload.name}")
pbar.update(task, advance=1)
console.log("Patching boot image")
match adbd.shell2("uefi-patch").returncode:
case 1:
console.log("Failed to patch boot")
return 1
console.log("Failed to patch boot. Rebooting")
adbd.reboot()
return 176
case 2:
console.log("Boot image already patched. Skipping")
adbd.reboot()
@ -303,7 +381,7 @@ def main() -> int:
for chunk in adbd.sync.iter_content("/tmp/uefi-install/new-boot.img"):
file.write(chunk)
pbar.update(task, advance=len(chunk))
console.log(f"Pathed boot loaded to {boot_uefi_path}")
console.log(f"Pathed boot saved to {boot_uefi_path}")
backup_size = int(adbd.shell("stat -c%s /tmp/uefi-install/boot.img"))
with get_progress() as pbar:
@ -319,25 +397,15 @@ def main() -> int:
console.log("Rebooting to bootloader")
adbd.shell("reboot bootloader")
wait_for_bootloader(serial)
fastboot.wait_for_bootloader(serial)
console.log("Flashing patched boot")
with open(boot_uefi_path, "rb") as file:
flash_boot(serial, file.read())
reboot_fb_device(serial)
fastboot.flash(serial, "boot", file.read())
fastboot.reboot(serial)
console.log("Done!")
return 0
def run() -> None:
global adb
status = main()
with console.status("[cyan]Stopping adb server", spinner="line", spinner_style="white"):
sleep(1)
if adb is not None:
adb.server_kill()
exit(status)
if "__main__" == __name__:
run()
exit(main())

View file

@ -1,17 +0,0 @@
import PyInstaller.__main__
from pathlib import Path
HERE = Path(__file__).parent.parent.absolute()
path_to_main = str(HERE / "run.py")
def install():
PyInstaller.__main__.run([
path_to_main,
'--onefile',
'--collect-submodules',
'--name LoN Deployer',
"-i",
"NONE",
# other pyinstaller options...
])

View file

@ -1,5 +1,6 @@
import logging
import os
import pathlib
import platform
import re
import socket
import subprocess
@ -7,8 +8,9 @@ from random import randint
from time import sleep
import adbutils
from rich.logging import RichHandler
from magic import Magic
from rich.console import Console
from rich.logging import RichHandler
from rich.progress import (
BarColumn,
DownloadColumn,
@ -18,8 +20,7 @@ from rich.progress import (
TransferSpeedColumn,
)
from . import Files
from . import exceptions
console = Console(log_path=False)
@ -36,10 +37,6 @@ logging.basicConfig(
logger = logging.getLogger("Deployer")
class DeviceNotFound(Exception):
pass
def get_progress() -> Progress:
return Progress(
TextColumn("[bold blue]{task.description}", justify="right"),
@ -54,42 +51,6 @@ def get_progress() -> Progress:
)
def fastboot_run(command: [str], serial: str = None) -> str:
try:
if not serial:
cmd = f"fastboot {' '.join(command)}"
else:
cmd = f"fastboot -s {serial} {' '.join(command)}"
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
except FileNotFoundError:
console.log("Fastboot binary not found")
console.log("Exiting")
exit(1)
else:
stdout, stderr = proc.communicate()
proc.wait()
return (stdout if stdout else stderr).decode()
def list_fb_devices() -> [str]:
return [x.split("\t ")[0] for x in fastboot_run(["devices"]).split("\n")[:-1]]
def check_device(serial: str) -> bool:
return "nabu" in fastboot_run(["getvar", "product"], serial=serial)
def check_parts(serial: str) -> bool:
linux_response = fastboot_run(["getvar", "partition-type:linux"], serial=serial)
esp_response = fastboot_run(["getvar", "partition-type:esp"], serial=serial)
return linux_response != "FAILED" and esp_response != "FAILED"
def reboot_fb_device(serial: str) -> None:
fastboot_run(["reboot"], serial=serial)
def check_port(tcp_port: int) -> bool:
s = socket.socket()
try:
@ -107,39 +68,6 @@ def get_port() -> int:
return tcp_port
def boot_ofox(serial: str) -> None:
Files.OrangeFox.get()
ofox = Files.OrangeFox.filepath
with console.status("[cyan]Booting", spinner="line", spinner_style="white"):
fastboot_run(["boot", ofox])
def flash_boot(serial: str, boot_data: bytes) -> None:
proper_flash(serial, "boot", boot_data)
def proper_flash(serial: str, part: str, data: bytes) -> None:
with open("image.img", "wb") as file:
file.write(data)
with console.status(f"[cyan]Flashing {part}", spinner="line", spinner_style="white"):
fastboot_run(["flash", part, "image.img"], serial=serial)
os.remove("image.img")
def restore_parts(serial: str) -> None:
gpt_both = Files.GPT_Both0.get()
userdata = Files.UserData_Empty.get()
proper_flash(serial, "partition:0", gpt_both)
proper_flash(serial, "userdata", userdata)
def clean_device(serial: str) -> None:
fastboot_run(["erase", "linux"], serial=serial)
fastboot_run(["erase", "esp"], serial=serial)
def repartition(serial: str, size: int, percents=False) -> None:
device = adbutils.adb.device(serial)
block_size = device.shell("blockdev --getsize64 /dev/block/sda")
@ -149,7 +77,7 @@ def repartition(serial: str, size: int, percents=False) -> None:
maxsize = 254
else:
logger.error("Weird block size. Is it nabu?")
exit(4)
raise exceptions.RepartitonError()
linux_max = maxsize - 12
if percents:
size = round(linux_max / 100 * size, 2)
@ -163,13 +91,24 @@ def repartition(serial: str, size: int, percents=False) -> None:
f"parted -s /dev/block/sda rm 31",
f"parted -s /dev/block/sda mkpart userdata ext4 10.9GB {userdata_end}GB",
f"parted -s /dev/block/sda mkpart linux ext4 {userdata_end}GB {linux_end}GB",
f"parted -s /dev/block/sda mkpart esp fat32 {linux_end}GB {maxsize}GB",
f"parted -s /dev/block/sda set 33 esp on"
f"parted -s /dev/block/sda mkpart esp fat32 {linux_end}GB {maxsize}GB"
]
for cmd in cmds:
device.shell(cmd)
sleep(3)
sleep(1)
def wait_for_bootloader(serial: str) -> None:
fastboot_run(["getvar", "product"], serial=serial)
def check_rootfs(filepath: pathlib.Path) -> bool:
osname = platform.system()
if osname == "Linux":
magic_file = subprocess.check_output(["file", "--version"]) \
.decode().splitlines()[1].split()[-1].split(":")[-1] + ".mgc"
logger.debug(f"Magic file: {magic_file}")
magic = Magic(mime=True, magic_file=magic_file)
elif osname == "Windows":
magic = Magic(mime=True)
else:
raise exceptions.UnsupportedPlatform(osname)
filetype = magic.from_file(str(filepath.absolute()))
logger.debug(f"RootFS MIME type: {filetype}")
return filetype in ["application/octet-stream", "inode/blockdevice"]

692
poetry.lock generated
View file

@ -1,692 +0,0 @@
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
[[package]]
name = "adbutils"
version = "2.5.0"
description = "Pure Python Adb Library"
optional = false
python-versions = ">=3.8"
files = [
{file = "adbutils-2.5.0-py3-none-macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:d6dfeb35ad1b13cc0e135153cb9cb2eccf9ce03073f73b23426bd09b0058ef69"},
{file = "adbutils-2.5.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:2054ae98d8651c0b03eb291768c667198cc222df26a545de55627d520f77e777"},
{file = "adbutils-2.5.0-py3-none-win32.whl", hash = "sha256:899d26300281f9cebdd6a4a64084f27cfadefe22c4552307f3d88852a3e3b89a"},
{file = "adbutils-2.5.0-py3-none-win_amd64.whl", hash = "sha256:134b6eb32660f0123bdedafa94d2f3fbff6f9f0f11ff2395b7c37013c73754ac"},
{file = "adbutils-2.5.0.tar.gz", hash = "sha256:0906bbd0b0952cdad298320556345816c7db68e0262fa78d8a7b20fde8e7f830"},
]
[package.dependencies]
apkutils2 = ">=1.0.0,<2.0"
deprecation = ">=2.0.6,<3.0"
Pillow = "*"
requests = "*"
retry = ">=0.9"
[[package]]
name = "altgraph"
version = "0.17.4"
description = "Python graph (network) package"
optional = false
python-versions = "*"
files = [
{file = "altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff"},
{file = "altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406"},
]
[[package]]
name = "apkutils2"
version = "1.0.0"
description = "Utils for parsing apk."
optional = false
python-versions = "*"
files = [
{file = "apkutils2-1.0.0.tar.gz", hash = "sha256:c5ae8f86d3ebee6a59fc014d88507741d7f3f9ab183bab34b44d011fe878660b"},
]
[package.dependencies]
cigam = "*"
pyelftools = "*"
xmltodict = "*"
[[package]]
name = "certifi"
version = "2024.2.2"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
files = [
{file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"},
{file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"},
]
[[package]]
name = "charset-normalizer"
version = "3.3.2"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
optional = false
python-versions = ">=3.7.0"
files = [
{file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
{file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
{file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
{file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
{file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
{file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
{file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
{file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
{file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
{file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
{file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
{file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
]
[[package]]
name = "cigam"
version = "0.0.3"
description = "magic"
optional = false
python-versions = "*"
files = [
{file = "cigam-0.0.3-py3-none-any.whl", hash = "sha256:8fcf65d7361f0372c53780e861307abd1f11a94b6204fa653ba3f38277822783"},
]
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "decorator"
version = "5.1.1"
description = "Decorators for Humans"
optional = false
python-versions = ">=3.5"
files = [
{file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
]
[[package]]
name = "deprecation"
version = "2.1.0"
description = "A library to handle automated deprecations"
optional = false
python-versions = "*"
files = [
{file = "deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a"},
{file = "deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff"},
]
[package.dependencies]
packaging = "*"
[[package]]
name = "idna"
version = "3.7"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.5"
files = [
{file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
{file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
]
[[package]]
name = "iniconfig"
version = "2.0.0"
description = "brain-dead simple config-ini parsing"
optional = false
python-versions = ">=3.7"
files = [
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
[[package]]
name = "libusb"
version = "1.0.27"
description = "Python binding for the libusb C library."
optional = false
python-versions = "<4.0.0,>=3.8.1"
files = [
{file = "libusb-1.0.27-py3-none-any.whl", hash = "sha256:cf547ebd01044014f2073ffea58a1b4c0172b958e3aed36c1d42e683eed71114"},
{file = "libusb-1.0.27.zip", hash = "sha256:3f8055df52d797d2da70ff3b9983e51ab6299c915afdea26988146ec7f6a4170"},
]
[package.dependencies]
pkg-about = ">=1.1.5"
setuptools = ">=68.2.2"
[package.extras]
doc = ["Sphinx (>=7.1.2)", "nbsphinx (>=0.8.10)", "restructuredtext-lint (>=1.4.0)", "sphinx-copybutton (>=0.5.1)", "sphinx-lint (>=0.6.7)", "sphinx-tabs (>=3.4.1)", "sphinx-toolbox (>=3.5.0)", "sphinxcontrib-spelling (>=7.7.0)"]
test = ["deepdiff (>=6.7.1)", "rich (>=13.7.0)"]
[[package]]
name = "macholib"
version = "1.16.3"
description = "Mach-O header analysis and editing"
optional = false
python-versions = "*"
files = [
{file = "macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c"},
{file = "macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30"},
]
[package.dependencies]
altgraph = ">=0.17"
[[package]]
name = "markdown-it-py"
version = "3.0.0"
description = "Python port of markdown-it. Markdown parsing, done right!"
optional = false
python-versions = ">=3.8"
files = [
{file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
{file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
]
[package.dependencies]
mdurl = ">=0.1,<1.0"
[package.extras]
benchmarking = ["psutil", "pytest", "pytest-benchmark"]
code-style = ["pre-commit (>=3.0,<4.0)"]
compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
linkify = ["linkify-it-py (>=1,<3)"]
plugins = ["mdit-py-plugins"]
profiling = ["gprof2dot"]
rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
[[package]]
name = "mdurl"
version = "0.1.2"
description = "Markdown URL utilities"
optional = false
python-versions = ">=3.7"
files = [
{file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
]
[[package]]
name = "packaging"
version = "24.0"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.7"
files = [
{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
]
[[package]]
name = "pefile"
version = "2023.2.7"
description = "Python PE parsing module"
optional = false
python-versions = ">=3.6.0"
files = [
{file = "pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"},
{file = "pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc"},
]
[[package]]
name = "pillow"
version = "10.3.0"
description = "Python Imaging Library (Fork)"
optional = false
python-versions = ">=3.8"
files = [
{file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"},
{file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"},
{file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"},
{file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"},
{file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"},
{file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"},
{file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"},
{file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"},
{file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"},
{file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"},
{file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"},
{file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"},
{file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"},
{file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"},
{file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"},
{file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"},
{file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"},
{file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"},
{file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"},
{file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"},
{file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"},
{file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"},
{file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"},
{file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"},
{file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"},
{file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"},
{file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"},
{file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"},
{file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"},
{file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"},
{file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"},
{file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"},
{file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"},
{file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"},
{file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"},
{file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"},
{file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"},
{file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"},
{file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"},
{file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"},
{file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"},
{file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"},
{file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"},
{file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"},
{file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"},
{file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"},
{file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"},
{file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"},
{file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"},
{file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"},
{file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"},
{file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"},
{file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"},
{file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"},
{file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"},
{file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"},
{file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"},
{file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"},
{file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"},
{file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"},
{file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"},
{file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"},
{file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"},
{file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"},
{file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"},
{file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"},
{file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"},
{file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"},
{file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"},
]
[package.extras]
docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"]
fpx = ["olefile"]
mic = ["olefile"]
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
typing = ["typing-extensions"]
xmp = ["defusedxml"]
[[package]]
name = "pkg-about"
version = "1.1.5"
description = "Shares Python package metadata at runtime."
optional = false
python-versions = "<4.0.0,>=3.8.1"
files = [
{file = "pkg-about-1.1.5.zip", hash = "sha256:079bbe889baa1edbf80651a1756a98c417d2f3dfd2d3539798bc83390ada1dfa"},
{file = "pkg_about-1.1.5-py3-none-any.whl", hash = "sha256:09caff4ae4cbec14b28579199106f295a62472c20c0621e358b9b12395e23ee4"},
]
[package.dependencies]
packaging = ">=23.2.0"
setuptools = ">=68.2.2"
[package.extras]
doc = ["Sphinx (>=7.1.2)", "nbsphinx (>=0.8.10)", "restructuredtext-lint (>=1.4.0)", "sphinx-copybutton (>=0.5.1)", "sphinx-lint (>=0.6.7)", "sphinx-tabs (>=3.4.1)", "sphinx-toolbox (>=3.5.0)", "sphinxcontrib-spelling (>=7.7.0)"]
test = ["deepdiff (>=6.7.1)", "rich (>=13.7.0)"]
[[package]]
name = "pluggy"
version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
files = [
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "py"
version = "1.11.0"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
]
[[package]]
name = "pyelftools"
version = "0.31"
description = "Library for analyzing ELF files and DWARF debugging information"
optional = false
python-versions = "*"
files = [
{file = "pyelftools-0.31-py3-none-any.whl", hash = "sha256:f52de7b3c7e8c64c8abc04a79a1cf37ac5fb0b8a49809827130b858944840607"},
{file = "pyelftools-0.31.tar.gz", hash = "sha256:c774416b10310156879443b81187d182d8d9ee499660380e645918b50bc88f99"},
]
[[package]]
name = "pygments"
version = "2.18.0"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.8"
files = [
{file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
{file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
]
[package.extras]
windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pyinstaller"
version = "6.6.0"
description = "PyInstaller bundles a Python application and all its dependencies into a single package."
optional = false
python-versions = "<3.13,>=3.8"
files = [
{file = "pyinstaller-6.6.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:d2705efe79f8749526f65c4bce70ae88eea8b6adfb051f123122e86542fe3802"},
{file = "pyinstaller-6.6.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:2aa771693ee3e0a899be3e9d946a24eab9896a98d0d4035f05a22f1193004cfb"},
{file = "pyinstaller-6.6.0-py3-none-manylinux2014_i686.whl", hash = "sha256:1fc15e8cebf76361568359a40926aa5746fc0a84ca365fb2ac6caeea014a2cd3"},
{file = "pyinstaller-6.6.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:7c4a55a5d872c118bc7a5e641c2df46ad18585c002d96adad129b4ee8c104463"},
{file = "pyinstaller-6.6.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:97197593344f11f3dd2bdadbab14c61fbc4cdf9cc692a89b047cb671764c1824"},
{file = "pyinstaller-6.6.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:00d81ddeee97710245a7ed03b0f9d5a4daf6c3a07adf978487b10991e1e20470"},
{file = "pyinstaller-6.6.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:b7cab21db6fcfbdab47ee960239d1b44cd95383a4463177bd592613941d67959"},
{file = "pyinstaller-6.6.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:00996d2090734d9ae4a1e53ed40351b07d593c37118d3e0d435bbcfa8db9edee"},
{file = "pyinstaller-6.6.0-py3-none-win32.whl", hash = "sha256:cfe3ed214601de0723cb660994b44934efacb77a1cf0e4cc5133da996bcf36ce"},
{file = "pyinstaller-6.6.0-py3-none-win_amd64.whl", hash = "sha256:e2f55fbbdf8a99ea84b39bc5669a68624473c303486d7eb2cd9063b339f0aa28"},
{file = "pyinstaller-6.6.0-py3-none-win_arm64.whl", hash = "sha256:abbd591967593dab264bcc3bcb2466c0a1582f19a112e37e916c4212069c7933"},
{file = "pyinstaller-6.6.0.tar.gz", hash = "sha256:be6bc2c3073d3e84fb7148d3af33ce9b6a7f01cfb154e06314cd1d4c05798a32"},
]
[package.dependencies]
altgraph = "*"
macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""}
packaging = ">=22.0"
pefile = {version = ">=2022.5.30", markers = "sys_platform == \"win32\""}
pyinstaller-hooks-contrib = ">=2024.3"
pywin32-ctypes = {version = ">=0.2.1", markers = "sys_platform == \"win32\""}
setuptools = ">=42.0.0"
[package.extras]
completion = ["argcomplete"]
hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"]
[[package]]
name = "pyinstaller-hooks-contrib"
version = "2024.5"
description = "Community maintained hooks for PyInstaller"
optional = false
python-versions = ">=3.7"
files = [
{file = "pyinstaller_hooks_contrib-2024.5-py2.py3-none-any.whl", hash = "sha256:0852249b7fb1e9394f8f22af2c22fa5294c2c0366157969f98c96df62410c4c6"},
{file = "pyinstaller_hooks_contrib-2024.5.tar.gz", hash = "sha256:aa5dee25ea7ca317ad46fa16b5afc8dba3b0e43f2847e498930138885efd3cab"},
]
[package.dependencies]
packaging = ">=22.0"
setuptools = ">=42.0.0"
[[package]]
name = "pytest"
version = "8.2.0"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "pytest-8.2.0-py3-none-any.whl", hash = "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233"},
{file = "pytest-8.2.0.tar.gz", hash = "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f"},
]
[package.dependencies]
colorama = {version = "*", markers = "sys_platform == \"win32\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=1.5,<2.0"
[package.extras]
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
[[package]]
name = "python-magic"
version = "0.4.27"
description = "File type identification using libmagic"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
{file = "python-magic-0.4.27.tar.gz", hash = "sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b"},
{file = "python_magic-0.4.27-py2.py3-none-any.whl", hash = "sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3"},
]
[[package]]
name = "python-magic-bin"
version = "0.4.14"
description = "File type identification using libmagic binary package"
optional = false
python-versions = "*"
files = [
{file = "python_magic_bin-0.4.14-py2.py3-none-macosx_10_6_intel.whl", hash = "sha256:7b1743b3dbf16601d6eedf4e7c2c9a637901b0faaf24ad4df4d4527e7d8f66a4"},
{file = "python_magic_bin-0.4.14-py2.py3-none-win32.whl", hash = "sha256:34a788c03adde7608028203e2dbb208f1f62225ad91518787ae26d603ae68892"},
{file = "python_magic_bin-0.4.14-py2.py3-none-win_amd64.whl", hash = "sha256:90be6206ad31071a36065a2fc169c5afb5e0355cbe6030e87641c6c62edc2b69"},
]
[[package]]
name = "pywin32-ctypes"
version = "0.2.2"
description = "A (partial) reimplementation of pywin32 using ctypes/cffi"
optional = false
python-versions = ">=3.6"
files = [
{file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"},
{file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"},
]
[[package]]
name = "readchar"
version = "4.0.6"
description = "Library to easily read single chars and key strokes"
optional = false
python-versions = ">=3.8"
files = [
{file = "readchar-4.0.6-py3-none-any.whl", hash = "sha256:b4b31dd35de4897be738f27e8f9f62426b5fedb54b648364987e30ae534b71bc"},
{file = "readchar-4.0.6.tar.gz", hash = "sha256:e0dae942d3a746f8d5423f83dbad67efe704004baafe31b626477929faaee472"},
]
[package.dependencies]
setuptools = ">=41.0"
[[package]]
name = "requests"
version = "2.31.0"
description = "Python HTTP for Humans."
optional = false
python-versions = ">=3.7"
files = [
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
]
[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = ">=2,<4"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<3"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "retry"
version = "0.9.2"
description = "Easy to use retry decorator."
optional = false
python-versions = "*"
files = [
{file = "retry-0.9.2-py2.py3-none-any.whl", hash = "sha256:ccddf89761fa2c726ab29391837d4327f819ea14d244c232a1d24c67a2f98606"},
{file = "retry-0.9.2.tar.gz", hash = "sha256:f8bfa8b99b69c4506d6f5bd3b0aabf77f98cdb17f3c9fc3f5ca820033336fba4"},
]
[package.dependencies]
decorator = ">=3.4.2"
py = ">=1.4.26,<2.0.0"
[[package]]
name = "rich"
version = "13.7.1"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
optional = false
python-versions = ">=3.7.0"
files = [
{file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"},
{file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"},
]
[package.dependencies]
markdown-it-py = ">=2.2.0"
pygments = ">=2.13.0,<3.0.0"
[package.extras]
jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]]
name = "setuptools"
version = "69.5.1"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"},
{file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"},
]
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
[[package]]
name = "urllib3"
version = "2.2.1"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.8"
files = [
{file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
{file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "xmltodict"
version = "0.13.0"
description = "Makes working with XML feel like you are working with JSON"
optional = false
python-versions = ">=3.4"
files = [
{file = "xmltodict-0.13.0-py2.py3-none-any.whl", hash = "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852"},
{file = "xmltodict-0.13.0.tar.gz", hash = "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56"},
]
[metadata]
lock-version = "2.0"
python-versions = "3.11.*"
content-hash = "e2af14ef9b15c3b3cc9b0716da5276427808f431f8d5603541dfdf390656397b"

View file

@ -15,10 +15,12 @@ pyinstaller = "^6.6.0"
libusb = "^1.0.27"
python-magic = { version = "^0.4.27", platform="linux" }
python-magic-bin = { version = "0.4.14", platform="win32" }
rich-argparse = "^1.4.0"
[tool.poetry.scripts]
lon-deployer = "lon_deployer.main:run"
build = "lon_deployer.pyinstaller:install"
lon-deployer = "lon_deployer.main:main"
build = "lon_deployer.builder:build"
version = "lon_deployer.builder:create_version"
[tool.poetry.group.dev.dependencies]

View file

@ -1,8 +1,8 @@
from lon_deployer import Files
from lon_deployer import files
def test_ofox() -> None:
file = Files.OrangeFox
file = files.OrangeFox
assert file.name == "orangefox.img"
assert file.md5sum() == "3edc8c32db0384006caf8cf066257811"