diff --git a/.justfile b/.justfile index cd0cae9b..0d1729ce 100644 --- a/.justfile +++ b/.justfile @@ -26,6 +26,24 @@ setup: clean_up uv run prek install --hook-type commit-msg uv run prek install --hook-type pre-push +dev_setup: clean_up + uv venv + uv run prek install + uv run prek install --hook-type commit-msg + uv run prek install --hook-type pre-push + if (Test-Path "../tempo-core") { Remove-Item "../tempo-core" -Recurse -Force } + if (Test-Path "../tempo-binary-tool-manager") { Remove-Item "../tempo-binary-tool-manager" -Recurse -Force } + if (Test-Path "../tempo-binary-tools") { Remove-Item "../tempo-binary-tools" -Recurse -Force } + if (Test-Path "../tempo-settings") { Remove-Item "../tempo-settings" -Recurse -Force } + git clone -b dev --single-branch https://github.com/Tempo-Organization/tempo-core.git ../tempo-core + git clone -b main --single-branch https://github.com/Tempo-Organization/tempo-binary-tool-manager.git ../tempo-binary-tool-manager + git clone -b master --single-branch https://github.com/Tempo-Organization/tempo-binary-tools.git ../tempo-binary-tools + git clone -b main --single-branch https://github.com/Tempo-Organization/tempo-settings.git ../tempo-settings + uv pip install -e ../tempo-core + uv pip install -e ../tempo-binary-tools + uv pip install -e ../tempo-settings + uv pip install -e ../tempo-binary-tool-manager + build: uv run pyinstaller --noconfirm --onefile --hidden-import=textual.widgets._tab --console --name tempo_cli --collect-data trogon src/tempo_cli/__main__.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c370d55d..a6f12c79 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/adhtruong/mirrors-typos - rev: v1.45.1 + rev: v1.47.2 hooks: - id: typos diff --git a/.zed/debug.json b/.zed/debug.json index 74c5146e..c5ee6355 100644 --- a/.zed/debug.json +++ b/.zed/debug.json @@ -30,7 +30,7 @@ "args": [ "run", "test_mods_all", - "--settings_json", + "--config-file", ".tempo.json" ], "cwd": "$ZED_WORKTREE_ROOT/../ZedfestModdingKit", @@ -57,7 +57,7 @@ "args": [ "run", "full_run_all", - "--settings_json", + "--config-file", ".tempo.json" ], "cwd": "$ZED_WORKTREE_ROOT/../PoppyPlaytimeChapter5", @@ -71,7 +71,7 @@ "args": [ "run", "test_mods_all", - "--settings_json", + "--config-file", ".tempo.json" ], "cwd": "$ZED_WORKTREE_ROOT/../PoppyPlaytimeChapter5", @@ -85,7 +85,7 @@ "args": [ "uproject", "build", - "--settings_json", + "--config-file", ".tempo.json" ], "cwd": "$ZED_WORKTREE_ROOT/../PoppyPlaytimeChapter5", diff --git a/CHANGELOG.md b/CHANGELOG.md index 884f7df0..62c702c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,42 @@ +## 5.2.0-dev.28308125112 (2026-06-28) + +### Fix + +- **uv.lock**: updated deps + +## 5.2.0-dev.26602054573 (2026-05-28) + +### Refactor + +- **All**: replaced --settings_json with --config-file + +## 5.2.0-dev.25458714664 (2026-05-06) + +### Feat + +- **list.py**: added list commands + +## 5.2.0-dev.25399196670 (2026-05-05) + +### Fix + +- **uv.lock**: updated tempo-core dep + +## 5.2.0-dev.25340886994 (2026-05-04) + +### Feat + +- **All**: Added entry points for tools + +## 5.2.0-dev.24855988894 (2026-04-23) + +### Refactor + +- **All**: changed from str to pathlib, added ruff rules and ty + +## 5.2.0-dev.24637340882 (2026-04-19) + ## 5.2.0 (2026-04-17) ## 5.2.0-dev.24591472908 (2026-04-17) diff --git a/docs/to_do_list.md b/docs/to_do_list.md index 4f3b8ac1..8afc421d 100644 --- a/docs/to_do_list.md +++ b/docs/to_do_list.md @@ -58,7 +58,6 @@ hide: - [ ] Diff game and file list, backup diff (for cleanup/restore) - [ ] Layout support command - [ ] Engine pak making compression variants (defaults to compressed) -- [ ] Switch to `pathlib` from strings - [ ] some sort of save hook thing or something --- @@ -94,21 +93,11 @@ hide: References: - [VS Code Variables Reference](https://code.visualstudio.com/docs/editor/variables-reference) - [VS Code Launch JSON Attributes](https://code.visualstudio.com/docs/editor/debugging#_launchjson-attributes) -- [ ] Fix it so all settings are loaded and saved from one place (currently several functions don’t follow this) -- [ ] Add TOML support - -### Config Types - -- Workspace config -- Project config -- Mod-specific config -- Game config -- Config versions - - - +settings_config -> settings_config +add support for toml alongside json +congregate settings loading/access diff --git a/pyproject.toml b/pyproject.toml index f43183bb..0782cd79 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "tempo-cli" -version = "5.2.0" +version = "5.2.0-dev.28308125112" description = "Unreal Engine modding tool, that covers project management, automated builds, and more" readme = "README.md" requires-python = ">=3.12" @@ -23,9 +23,11 @@ dependencies = [ "prek>=0.3.9", "questionary>=2.1.0", "rich-click>=1.9.4", + "tempo-binary-tool-manager", + "tempo-binary-tools", "tempo-core", + "tempo-settings", "tomlkit>=0.13.3", - "trogon>=0.6.0", "ue4ss-installer-core", ] @@ -34,12 +36,16 @@ requires = ["uv_build>=0.7.19,<0.8"] build-backend = "uv_build" [tool.uv.sources] -tempo-core = { git = "https://www.github.com/Tempo-Organization/tempo-core" } ue4ss-installer-core = { git = "https://www.github.com/Tempo-Organization/ue4ss-installer-core" } +tempo-settings = { git = "https://www.github.com/Tempo-Organization/tempo-settings" } +tempo-binary-tool-manager = { git = "https://www.github.com/Tempo-Organization/tempo-binary-tool-manager" } +tempo-binary-tools = { git = "https://www.github.com/Tempo-Organization/tempo-binary-tools" } +tempo-core = { git = "https://www.github.com/Tempo-Organization/tempo-core" } [dependency-groups] dev = [ "commitizen>=4.8.3", + "importtime>=1.0.3.2", "mkdocs-material>=9.6.15", "mkdocstrings-python>=1.16.12", "prek>=0.3.9", @@ -65,12 +71,36 @@ exclude = [ "src/tempo_cli/cli.py" ] +[tool.ruff.lint] +select = [ + "ANN", + "PTH", + "N", + "BLE", + "YTT", + "ASYNC", + "B", + "A", + "COM", + "C4", + "EXE", + "FA", + "INT", + "PIE", + "T20", + "PYI", + "UP047" +] + +[tool.ty.src] +include = ["src", "tests"] +exclude = [] + + [tool.typos] [tool.typos.default] extend-words = { ue = "ue" } -[tool.pyright] -extraPaths = [".venv/Lib/site-packages"] [tool.commitizen] name = "cz_conventional_commits" diff --git a/src/tempo_cli/cli.py b/src/tempo_cli/cli.py index 8136cd1a..4cfbaeef 100644 --- a/src/tempo_cli/cli.py +++ b/src/tempo_cli/cli.py @@ -1,8 +1,7 @@ from __future__ import annotations import os -import pathlib +from pathlib import Path -from trogon import tui import rich_click as click from tempo_core import ( @@ -26,15 +25,15 @@ from tempo_cli.commands.toml import toml from tempo_cli.commands.uplugin import uplugin from tempo_cli.commands.uproject import uproject +from tempo_cli.commands.list import list -default_logs_dir = os.path.normpath(f"{file_io.SCRIPT_DIR}/logs") +default_logs_dir = Path(f"{file_io.SCRIPT_DIR}/logs") rich_color_system_choices = tempo_cli_data_structures.get_enum_strings_from_enum( tempo_cli_data_structures.RichColorSystem ) -@tui(help='Open Textual TUI') @click.version_option() # @click.group(chain=True) disabled to allow easy groups @click.group() @@ -62,7 +61,7 @@ @click.option( "--logs_directory", default=default_logs_dir, - type=click.Path(exists=False, resolve_path=True, path_type=pathlib.Path), + type=click.Path(exists=False, resolve_path=True, path_type=Path), help="The directory you want your logs outputted to.", ) def cli( @@ -107,3 +106,5 @@ def cli( cli.add_command(uplugin) cli.add_command(uproject) + +cli.add_command(list) diff --git a/src/tempo_cli/commands/clean.py b/src/tempo_cli/commands/clean.py index 61236264..543de9f5 100644 --- a/src/tempo_cli/commands/clean.py +++ b/src/tempo_cli/commands/clean.py @@ -1,4 +1,4 @@ -import pathlib +from pathlib import Path import rich_click as click @@ -6,7 +6,7 @@ @click.group() -def clean(): +def clean() -> None: """Clean related commands""" @@ -15,19 +15,19 @@ def clean(): @clean.command(name="full", help=command_help, short_help=command_help) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) -def full(settings_json): +def full(config_file: Path) -> None: main_logic.cleanup_full() @@ -36,19 +36,19 @@ def full(settings_json): @clean.command(name="cooked", help=command_help, short_help=command_help) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) -def cooked(settings_json): +def cooked(config_file: Path) -> None: main_logic.cleanup_cooked() @@ -57,19 +57,19 @@ def cooked(settings_json): @clean.command(name="build", help=command_help, short_help=command_help) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) -def cleanup(settings_json): +def cleanup(config_file: Path) -> None: main_logic.cleanup_build() @@ -81,27 +81,27 @@ def cleanup(settings_json): @clean.command(name="game", help=command_help, short_help=command_help) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) @click.option( "--output_json", type=click.Path( resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), - help="Path to the output game file list json." + help="Path to the output game file list json.", ) -def game(settings_json, output_json): +def game(config_file: Path, output_json: Path) -> None: if output_json: main_logic.cleanup_game(output_json) else: @@ -123,7 +123,7 @@ def game(settings_json, output_json): dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), ) @click.argument( @@ -134,10 +134,10 @@ def game(settings_json, output_json): dir_okay=True, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), ) -def from_file_list(file_list, directory): +def from_file_list(file_list: Path, directory: Path) -> None: """ Arguments: file_list (str): Path to the file list you want to clean from. @@ -152,17 +152,17 @@ def from_file_list(file_list, directory): @clean.command(name="resync_dir_with_repo", help=command_help, short_help=command_help) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) -def resync_dir_with_repo(settings_json): +def resync_dir_with_repo(config_file: Path) -> None: main_logic.resync_dir_with_repo() diff --git a/src/tempo_cli/commands/close.py b/src/tempo_cli/commands/close.py index 336f5b2d..557bdf85 100644 --- a/src/tempo_cli/commands/close.py +++ b/src/tempo_cli/commands/close.py @@ -1,4 +1,4 @@ -import pathlib +from pathlib import Path import rich_click as click @@ -8,24 +8,24 @@ command_help = "Close the game." @click.group() -def close(): +def close() -> None: """Close related commands""" @close.command(name="game", help=command_help, short_help=command_help) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", ) -def game(settings_json): +def game(settings_config: Path) -> None: main_logic.close_game() @@ -33,19 +33,19 @@ def game(settings_json): @close.command(name="engine", help=command_help, short_help=command_help) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", ) -def engine(settings_json): +def engine(settings_config: Path) -> None: main_logic.close_engine() @@ -59,5 +59,5 @@ def engine(settings_json): required=True, help="Name of an executable to be closed, can be specified multiple times.", ) -def programs(exe_names): +def programs(exe_names: list[str]) -> None: process_management.close_programs(exe_names) diff --git a/src/tempo_cli/commands/collection.py b/src/tempo_cli/commands/collection.py index a2159267..c1b39498 100644 --- a/src/tempo_cli/commands/collection.py +++ b/src/tempo_cli/commands/collection.py @@ -1,5 +1,5 @@ import os -import pathlib +from pathlib import Path import rich_click as click @@ -7,25 +7,25 @@ file_content_options = data_structures.get_enum_strings_from_enum( - unreal_collections.UnrealContentLineType + unreal_collections.UnrealContentLineType, ) command_help = "Create Collection" default_create_collection_guid = unreal_collections.UnrealGuid.generate_unreal_guid() default_parent_guid = unreal_collections.get_blank_unreal_guid().to_uid() @click.group() -def collection(): +def collection() -> None: """Collection related commands""" @collection.command(name="create_collection", help=command_help, short_help=command_help) @click.option( "--collection_path", - type=click.Path(exists=False, resolve_path=True, path_type=pathlib.Path), + type=click.Path(exists=False, resolve_path=True, path_type=Path), help="The path to the collection file to edit.", required=True, ) @click.option( - "--file_version", type=int, default=2, help="The collection file version." + "--file_version", type=int, default=2, help="The collection file version.", ) @click.option( "--content_type", @@ -46,16 +46,16 @@ def collection(): help="The parent guid of the collection, if not provided no parent is assumed, and a blank one is given.", ) @click.option( - "--red", default=0.0, type=float, help="The value of the color, accepts 0.0-1.0." + "--red", default=0.0, type=float, help="The value of the color, accepts 0.0-1.0.", ) @click.option( - "--green", default=0.0, type=float, help="The value of the color, accepts 0.0-1.0." + "--green", default=0.0, type=float, help="The value of the color, accepts 0.0-1.0.", ) @click.option( - "--blue", default=0.0, type=float, help="The value of the color, accepts 0.0-1.0." + "--blue", default=0.0, type=float, help="The value of the color, accepts 0.0-1.0.", ) @click.option( - "--alpha", default=0.0, type=float, help="The value of the color, accepts 0.0-1.0." + "--alpha", default=0.0, type=float, help="The value of the color, accepts 0.0-1.0.", ) @click.option( "--file_paths", @@ -76,7 +76,7 @@ def collection(): help="A list of asset filter for the collection, for use with dynamic collections.", ) def create_collection( - collection_path: pathlib.Path, + collection_path: Path, file_version: int, content_type: str, guid: str, @@ -88,12 +88,12 @@ def create_collection( file_paths: list[str], unreal_asset_paths: list[str], filter_lines: list[str], -): +) -> None: type_of_content = data_structures.get_enum_from_val( - unreal_collections.UnrealContentLineType, content_type + unreal_collections.UnrealContentLineType, content_type, ) content_lines = [] - os.makedirs(os.path.dirname(collection_path), exist_ok=True) + collection_path.mkdir(parents=True, exist_ok=True) if type_of_content == unreal_collections.UnrealContentLineType.STATIC: for file_path in file_paths: content_lines.append(unreal_collections.UnrealAssetPath(path=file_path)) @@ -101,15 +101,15 @@ def create_collection( content_lines.append( unreal_collections.UnrealAssetPath( unreal_collections.UnrealAssetPath.static_from_asset_reference( - unreal_asset_path - ) - ) + unreal_asset_path, + ), + ), ) if type_of_content == unreal_collections.UnrealContentLineType.DYNAMIC: content_lines = filter_lines unreal_collections.create_collection( - collection_name=os.path.basename(collection_path), - collections_directory=pathlib.Path(os.path.dirname(collection_path)), + collection_name=collection_path.name, + collections_directory=Path(collection_path.name), file_version=file_version, collection_type=unreal_collections.UnrealContentLineType(type_of_content), guid=unreal_collections.UnrealGuid.from_uid(guid), @@ -124,7 +124,7 @@ def create_collection( @collection.command( - name="set_color_from_collection_path", help=command_help, short_help=command_help + name="set_color_from_collection_path", help=command_help, short_help=command_help, ) @click.option( "--collection_path", @@ -134,26 +134,26 @@ def create_collection( dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to the collection file to edit.", required=True, ) @click.option( - "--red", default=0.0, type=float, help="The value of the color, accepts 0.0-1.0." + "--red", default=0.0, type=float, help="The value of the color, accepts 0.0-1.0.", ) @click.option( - "--green", default=0.0, type=float, help="The value of the color, accepts 0.0-1.0." + "--green", default=0.0, type=float, help="The value of the color, accepts 0.0-1.0.", ) @click.option( - "--blue", default=0.0, type=float, help="The value of the color, accepts 0.0-1.0." + "--blue", default=0.0, type=float, help="The value of the color, accepts 0.0-1.0.", ) @click.option( - "--alpha", default=0.0, type=float, help="The value of the color, accepts 0.0-1.0." + "--alpha", default=0.0, type=float, help="The value of the color, accepts 0.0-1.0.", ) def set_color_from_collection_path( - collection_path: pathlib.Path, red: float, green: float, blue: float, alpha: float -): + collection_path: Path, red: float, green: float, blue: float, alpha: float, +) -> None: unreal_collections.set_color_from_collection_path( collection_path, unreal_collections.UnrealCollectionColor(r=red, g=green, b=blue, a=alpha), @@ -172,16 +172,16 @@ def set_color_from_collection_path( dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to the collection file to rename.", required=True, ) @click.option( - "--new_name", type=str, help="New name for the collection.", required=True + "--new_name", type=str, help="New name for the collection.", required=True, ) -def rename_collection(collection_path: str, new_name: str): - unreal_collections.rename_collection_from_collection_path(pathlib.Path(collection_path), new_name) +def rename_collection(collection_path: Path, new_name: str) -> None: + unreal_collections.rename_collection_from_collection_path(Path(collection_path), new_name) command_help = "Delete Collection" @@ -196,12 +196,12 @@ def rename_collection(collection_path: str, new_name: str): dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to the collection file to rename.", required=True, ) -def delete_collection(collection_path: str): +def delete_collection(collection_path: Path) -> None: unreal_collections.delete_collection(collection_path) @@ -217,12 +217,12 @@ def delete_collection(collection_path: str): dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to the collection file to rename.", required=True, ) -def disable_collection(collection_path: str): +def disable_collection(collection_path: Path) -> None: unreal_collections.disable_collection(collection_path) @@ -238,23 +238,23 @@ def disable_collection(collection_path: str): dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to the collection file to rename.", required=True, ) -def enable_collection(collection_path: str): - unreal_collections.enable_collection(unreal_collections.get_unreal_collection_from_unreal_collection_path(pathlib.Path(collection_path))) +def enable_collection(collection_path: Path) -> None: + unreal_collections.enable_collection(unreal_collections.get_unreal_collection_from_unreal_collection_path(Path(collection_path))) command_help = "Set Guid" default_set_guid_from_collection_path_guid = unreal_collections.UnrealGuid.to_uid( - unreal_collections.UnrealGuid(unreal_collections.UnrealGuid.generate_unreal_guid()) + unreal_collections.UnrealGuid(unreal_collections.UnrealGuid.generate_unreal_guid()), ) @collection.command( - name="set_guid_from_collection_path", help=command_help, short_help=command_help + name="set_guid_from_collection_path", help=command_help, short_help=command_help, ) @click.option( "--collection_path", @@ -264,7 +264,7 @@ def enable_collection(collection_path: str): dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to the collection file to rename.", required=True, @@ -275,9 +275,9 @@ def enable_collection(collection_path: str): type=str, help="The new guid in string form for the collection.", ) -def set_guid_from_collection_path(collection_path: str, guid: str): +def set_guid_from_collection_path(collection_path: Path, guid: str) -> None: unreal_collections.set_guid_from_collection_path( - collection_path=pathlib.Path(collection_path), + collection_path=Path(collection_path), guid=unreal_collections.UnrealGuid.from_uid(guid), ) @@ -298,7 +298,7 @@ def set_guid_from_collection_path(collection_path: str, guid: str): dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to the collection file to rename.", required=True, @@ -309,9 +309,9 @@ def set_guid_from_collection_path(collection_path: str, guid: str): type=str, help="The new parent guid in string form for the collection.", ) -def set_parent_guid_from_collection_path(collection_path: str, parent_guid: str): +def set_parent_guid_from_collection_path(collection_path: Path, parent_guid: str) -> None: unreal_collections.set_parent_guid_from_collection_path( - collection_path=pathlib.Path(collection_path), + collection_path=Path(collection_path), parent_guid=unreal_collections.UnrealGuid(parent_guid), ) @@ -332,7 +332,7 @@ def set_parent_guid_from_collection_path(collection_path: str, parent_guid: str) dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to the collection file to change the file version of.", required=True, @@ -343,12 +343,12 @@ def set_parent_guid_from_collection_path(collection_path: str, parent_guid: str) type=int, help="The new file version for the collection.", ) -def set_file_version_from_collection_path(collection_path: str, file_version: int): - unreal_collections.set_file_version_from_collection_path(collection_path=pathlib.Path(collection_path), file_version=file_version) +def set_file_version_from_collection_path(collection_path: Path, file_version: int) -> None: + unreal_collections.set_file_version_from_collection_path(collection_path=Path(collection_path), file_version=file_version) set_content_type_options = data_structures.get_enum_strings_from_enum( - unreal_collections.UnrealContentLineType + unreal_collections.UnrealContentLineType, ) command_help = "Set Collection Type" @@ -366,7 +366,7 @@ def set_file_version_from_collection_path(collection_path: str, file_version: in dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to the collection file to change the file version of.", required=True, @@ -378,13 +378,13 @@ def set_file_version_from_collection_path(collection_path: str, file_version: in help="The new content type for the collection, Static or Dynamic.", ) def set_collection_type_from_collection_path( - collection_path: str, collection_type: str -): + collection_path: Path, collection_type: str, +) -> None: type_to_pass = data_structures.get_enum_from_val( - unreal_collections.UnrealContentLineType, collection_type + unreal_collections.UnrealContentLineType, collection_type, ) unreal_collections.set_collection_type_from_collection_path( - collection_path=pathlib.Path(collection_path), collection_type=unreal_collections.UnrealContentLineType(type_to_pass) + collection_path=Path(collection_path), collection_type=unreal_collections.UnrealContentLineType(type_to_pass), ) @@ -392,7 +392,7 @@ def set_collection_type_from_collection_path( @collection.command( - name="add_content_lines_to_collection", help=command_help, short_help=command_help + name="add_content_lines_to_collection", help=command_help, short_help=command_help, ) @click.option( "--collection_path", @@ -402,7 +402,7 @@ def set_collection_type_from_collection_path( dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to the collection file to edit.", required=True, @@ -429,13 +429,13 @@ def set_collection_type_from_collection_path( default=[], ) def add_content_lines_to_collection( - collection_path: str, + collection_path: Path, content_path_lines: list[str], filter_lines: list[str], unreal_asset_paths: list[str], -): +) -> None: collection = unreal_collections.get_unreal_collection_from_unreal_collection_path( - pathlib.Path(collection_path) + Path(collection_path), ) collection_type = collection.content_type content_lines = [] @@ -448,12 +448,12 @@ def add_content_lines_to_collection( for unreal_asset_path in unreal_asset_paths: test_one = unreal_collections.UnrealAssetPath.static_from_asset_reference( - unreal_asset_path + unreal_asset_path, ) test_two = unreal_collections.UnrealAssetPath(test_one) content_lines.append(test_two) unreal_collections.add_content_lines_to_collection( - collection=collection, content_lines=content_lines + collection=collection, content_lines=content_lines, # type: ignore ) @@ -473,7 +473,7 @@ def add_content_lines_to_collection( dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to the collection file to edit.", required=True, @@ -500,13 +500,13 @@ def add_content_lines_to_collection( default=[], ) def remove_content_lines_from_collection( - collection_path: str, + collection_path: Path, content_path_lines: list[str], filter_lines: list[str], unreal_asset_paths: list[str], -): +) -> None: collection = unreal_collections.get_unreal_collection_from_unreal_collection_path( - pathlib.Path(collection_path) + Path(collection_path), ) content_lines = [] @@ -521,13 +521,13 @@ def remove_content_lines_from_collection( content_lines.append( unreal_collections.UnrealAssetPath( unreal_collections.UnrealAssetPath.static_from_asset_reference( - unreal_asset_path - ) - ) + unreal_asset_path, + ), + ), ) unreal_collections.remove_content_lines_from_collection( - collection=collection, content_lines=content_lines + collection=collection, content_lines=content_lines, # type: ignore ) @@ -535,17 +535,17 @@ def remove_content_lines_from_collection( @collection.command( - name="add_collections_to_mod_entry", help=command_help, short_help=command_help + name="add_collections_to_mod_entry", help=command_help, short_help=command_help, ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to the settings json, who's mod entry you want to add collections to.", required=True, @@ -564,24 +564,24 @@ def remove_content_lines_from_collection( dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to a collection to add to the settings json, can be specified multiple times.", default=[], multiple=True, ) def add_collections_to_mod_entry( - settings_json: pathlib.Path, mod_name: str, collection_paths: list[pathlib.Path] -): + settings_config: Path, mod_name: str, collection_paths: list[Path], +) -> None: collections_to_pass = [] for collection_path in collection_paths: collections_to_pass.append( unreal_collections.get_unreal_collection_from_unreal_collection_path( - collection_path - ) + collection_path, + ), ) unreal_collections.add_collections_to_mod_entry( - collections_to_pass, mod_name, settings_json + collections_to_pass, mod_name, settings_config, ) @@ -589,17 +589,17 @@ def add_collections_to_mod_entry( @collection.command( - name="remove_collections_from_mod_entry", help=command_help, short_help=command_help + name="remove_collections_from_mod_entry", help=command_help, short_help=command_help, ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to the settings json, who's mod entry you want to remove collections from.", required=True, @@ -618,22 +618,22 @@ def add_collections_to_mod_entry( dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to a collection to remove from the settings json, can be specified multiple times.", default=[], multiple=True, ) def remove_collections_from_mod_entry( - settings_json: pathlib.Path, mod_name: str, collection_paths: list[pathlib.Path] -): + settings_config: Path, mod_name: str, collection_paths: list[Path], +) -> None: collections_to_pass = [] for collection_path in collection_paths: collections_to_pass.append( unreal_collections.get_unreal_collection_from_unreal_collection_path( - collection_path - ) + collection_path, + ), ) unreal_collections.remove_collections_from_mod_entry( - collections_to_pass, mod_name, settings_json + collections_to_pass, mod_name, settings_config, ) diff --git a/src/tempo_cli/commands/dump.py b/src/tempo_cli/commands/dump.py index b357b7cc..a3ab1b7a 100644 --- a/src/tempo_cli/commands/dump.py +++ b/src/tempo_cli/commands/dump.py @@ -1,21 +1,21 @@ import os import time import json -import pathlib +from pathlib import Path -from tempo_core import main_logic, window_management, utilities, game_runner, logger +from tempo_core import main_logic, window_management, utilities, game_runner, logger, manager from tempo_core.programs import retoc, pattern_sleuth from tempo_core.programs import jmap as jmap_program from tempo_core.threads import game_monitor -from tempo_cache_tools import jmap as jmap_tool -from tempo_cache_tools import retoc as retoc_tool +from tempo_binary_tools import jmap as jmap_tool +from tempo_binary_tools import retoc as retoc_tool import rich_click as click @click.group() -def dump(): +def dump() -> None: """Dump related commands""" @dump.command( @@ -24,22 +24,22 @@ def dump(): short_help="Dumps the key(s) from the game in the provided settings json.", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) @click.option( "--directory", - default=os.getcwd(), - type=click.Path(exists=True, resolve_path=True, path_type=pathlib.Path, file_okay=False, dir_okay=True), + default=Path.cwd(), + type=click.Path(exists=True, resolve_path=True, path_type=Path, file_okay=False, dir_okay=True), help="The directory you want your aes key outputted to.", ) @click.option( @@ -48,22 +48,22 @@ def dump(): default=True, help="Whether the dumped info should be stored in the tempo config file or not.", ) -def aes_keys(settings_json, directory, dump_to_tempo_config): +def aes_keys(config_file: Path, directory: Path, dump_to_tempo_config: bool) -> None: aes_keys = [] for key in pattern_sleuth.run_patternsleuth_aes_key_scan_command(): logger.log_message(f"AES Key: {key}") if key not in aes_keys: aes_keys.append(key) - os.makedirs(directory, exist_ok=True) + directory.mkdir(parents=True, exist_ok=True) - output_path = os.path.join(directory, "aes_keys.json") + output_path = Path(directory / "aes_keys.json") data = { - "aes_keys": aes_keys + "aes_keys": aes_keys, } - with open(output_path, "w", encoding="utf-8") as f: + with Path.open(output_path, "w", encoding="utf-8") as f: json.dump(data, f, indent=4) logger.log_message(f'output path: {output_path}') @@ -71,17 +71,17 @@ def aes_keys(settings_json, directory, dump_to_tempo_config): if not dump_to_tempo_config: return - with open(settings_json, "r", encoding="utf-8") as f: + with Path.open(config_file, "r", encoding="utf-8") as f: settings = json.load(f) engine_info = settings.setdefault("engine_info", {}) engine_info["aes_keys"] = aes_keys - with open(settings_json, "w", encoding="utf-8") as f: + with Path.open(config_file, "w", encoding="utf-8") as f: json.dump(settings, f, indent=4) - logger.log_message(f"updated settings json: {settings_json}") + logger.log_message(f"updated settings json: {config_file}") @dump.command( @@ -90,22 +90,22 @@ def aes_keys(settings_json, directory, dump_to_tempo_config): short_help="Dumps the engine version from the game in the provided settings json.", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) @click.option( "--directory", - default=os.getcwd(), - type=click.Path(exists=True, resolve_path=True, path_type=pathlib.Path, file_okay=False, dir_okay=True), + default=Path.cwd(), + type=click.Path(exists=True, resolve_path=True, path_type=Path, file_okay=False, dir_okay=True), help="The directory you want your engine version outputted to.", ) @click.option( @@ -114,23 +114,23 @@ def aes_keys(settings_json, directory, dump_to_tempo_config): default=True, help="Whether the dumped info should be stored in the tempo config file or not.", ) -def engine_version(settings_json, directory, dump_to_tempo_config): +def engine_version(config_file: Path, directory: Path, dump_to_tempo_config: bool) -> None: info = pattern_sleuth.run_patternsleuth_engine_version_scan_command() if not info: raise RuntimeError('dump engine version command failed due to info being None.') - os.makedirs(directory, exist_ok=True) + directory.mkdir(parents=True, exist_ok=True) - output_path = os.path.join(directory, "engine_version.json") + output_path = Path(directory / "engine_version.json") data = { "engine_major_version": info["major"], - "engine_minor_version": info["minor"] + "engine_minor_version": info["minor"], } - with open(output_path, "w", encoding="utf-8") as f: + with Path.open(output_path, "w", encoding="utf-8") as f: json.dump(data, f, indent=4) logger.log_message(f'output path: {output_path}') @@ -138,7 +138,7 @@ def engine_version(settings_json, directory, dump_to_tempo_config): if not dump_to_tempo_config: return - with open(settings_json, "r", encoding="utf-8") as f: + with Path.open(config_file, "r", encoding="utf-8") as f: settings = json.load(f) engine_info = settings.setdefault("engine_info", {}) @@ -146,10 +146,10 @@ def engine_version(settings_json, directory, dump_to_tempo_config): engine_info["unreal_engine_major_version"] = info["major"] engine_info["unreal_engine_minor_version"] = info["minor"] - with open(settings_json, "w", encoding="utf-8") as f: + with Path.open(config_file, "w", encoding="utf-8") as f: json.dump(settings, f, indent=4) - logger.log_message(f"updated settings json: {settings_json}") + logger.log_message(f"updated settings json: {config_file}") @dump.command( @@ -158,22 +158,22 @@ def engine_version(settings_json, directory, dump_to_tempo_config): short_help="Dumps the build configuration from the game in the provided settings json.", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) @click.option( "--directory", - default=os.getcwd(), - type=click.Path(exists=True, resolve_path=True, path_type=pathlib.Path, file_okay=False, dir_okay=True), + default=Path.cwd(), + type=click.Path(exists=True, resolve_path=True, path_type=Path, file_okay=False, dir_okay=True), help="The directory you want your build configuration outputted to.", ) @click.option( @@ -182,22 +182,22 @@ def engine_version(settings_json, directory, dump_to_tempo_config): default=True, help="Whether the dumped info should be stored in the tempo config file or not.", ) -def build_configuration(settings_json, directory, dump_to_tempo_config): +def build_configuration(config_file: Path, directory: Path, dump_to_tempo_config: bool) -> None: info = pattern_sleuth.run_patternsleuth_build_configuration_scan_command() if not info: raise RuntimeError('dump build configuration command failed due to info being None.') - os.makedirs(directory, exist_ok=True) + directory.mkdir(parents=True, exist_ok=True) - output_path = os.path.join(directory, "build_configuration.json") + output_path = Path(directory / "build_configuration.json") data = { - "build_configuration": info + "build_configuration": info, } - with open(output_path, "w", encoding="utf-8") as f: + with Path.open(output_path, "w", encoding="utf-8") as f: json.dump(data, f, indent=4) logger.log_message(output_path) @@ -205,17 +205,17 @@ def build_configuration(settings_json, directory, dump_to_tempo_config): if not dump_to_tempo_config: return - with open(settings_json, "r", encoding="utf-8") as f: + with Path.open(config_file, "r", encoding="utf-8") as f: settings = json.load(f) engine_info = settings.setdefault("engine_info", {}) engine_info["build_configuration"] = info - with open(settings_json, "w", encoding="utf-8") as f: + with Path.open(config_file, "w", encoding="utf-8") as f: json.dump(settings, f, indent=4) - logger.log_message(f"updated settings json: {settings_json}") + logger.log_message(f"updated settings json: {config_file}") @dump.command( @@ -224,26 +224,29 @@ def build_configuration(settings_json, directory, dump_to_tempo_config): short_help="Dumps the jmap from the game in the provided settings json.", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) @click.option( "--output", - default=os.path.normpath(f'{os.getcwd()}/Modding/output.jmap'), - type=click.Path(resolve_path=True, path_type=pathlib.Path), + default=Path(f'{Path.cwd()}/Modding/output.jmap'), + type=click.Path(resolve_path=True, path_type=Path), help="The file location you want your jmap outputted to.", ) -def jmap(settings_json, output): - os.makedirs(os.path.dirname(output), exist_ok=True) +def jmap(config_file: Path, output: Path) -> None: + tool_info = jmap_tool.JmapToolInfo(cache=manager.tools_cache) + tool_info.ensure_tool_installed() + jmap_exe = Path(tool_info.get_executable_path()) + output.parent.mkdir(parents=True, exist_ok=True) game_runner.run_game() game_monitor.start_game_monitor_thread() @@ -277,9 +280,9 @@ def jmap(settings_json, output): time.sleep(3) jmap_program.run_dump_jmap_jmap_command( - jmap_executable=jmap_tool.JmapToolInfo().get_executable_path(), + jmap_executable=jmap_exe, game_pid=game_pid, - output_jmap_location=output + output_jmap_location=output, ) main_logic.close_game() @@ -291,17 +294,17 @@ def jmap(settings_json, output): short_help="Dumps the script objects from the game in the provided settings json.", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) @click.option( "--jmap_path", @@ -311,18 +314,18 @@ def jmap(settings_json, output): dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), - default=os.path.normpath(f'{os.getcwd()}/Modding/output.jmap'), - help="Path to the a jmap file dumped from the game in the provided settings JSON file", + default=Path(f'{Path.cwd()}/Modding/output.jmap'), + help="Path to the a jmap file dumped from the game in the provided tempo config file", ) @click.option( "--output", - default=os.path.normpath(f'{os.getcwd()}/Modding/output.utoc'), - type=click.Path(resolve_path=True, path_type=pathlib.Path), + default=Path(f'{Path.cwd()}/Modding/output.utoc'), + type=click.Path(resolve_path=True, path_type=Path), help="The file location you want your utoc outputted to.", ) -def generate_script_objects(settings_json, jmap_path, output): - os.makedirs(os.path.dirname(output), exist_ok=True) - retoc_exec_path = retoc_tool.RetocToolInfo().get_executable_path() - retoc.run_gen_script_objects_retoc_command(pathlib.Path(retoc_exec_path), jmap_path, output) +def generate_script_objects(config_file: Path, jmap_path: Path, output: Path) -> None: + output.parent.mkdir(parents=True, exist_ok=True) + retoc_exec_path = retoc_tool.RetocToolInfo(cache=manager.tools_cache).get_executable_path() + retoc.run_gen_script_objects_retoc_command(Path(retoc_exec_path), jmap_path, output) diff --git a/src/tempo_cli/commands/file_io.py b/src/tempo_cli/commands/file_io.py index 86151b40..4fe4a16f 100644 --- a/src/tempo_cli/commands/file_io.py +++ b/src/tempo_cli/commands/file_io.py @@ -1,5 +1,4 @@ -import os -import pathlib +from pathlib import Path import rich_click as click @@ -8,7 +7,7 @@ @click.group() -def file_io(): +def file_io() -> None: """File IO related commands""" @@ -24,21 +23,21 @@ def file_io(): dir_okay=True, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, ) @click.option( "--zip", help="Path to the output zip file.", - type=click.Path(resolve_path=True, path_type=pathlib.Path), + type=click.Path(resolve_path=True, path_type=Path), required=True, ) -def zip_directory_tree(directory, zip): +def zip_directory_tree(directory: Path, zip: Path) -> None: # noqa tempo_core_file_io.zip_directory_tree( input_dir=directory, - output_dir=os.path.dirname(zip), - zip_name=os.path.basename(zip), + output_dir=zip.parent, + zip_name=zip.name, ) @@ -50,17 +49,17 @@ def zip_directory_tree(directory, zip): "--output_directory", help="Path to the directory to unzip the zip to.", type=click.Path( - file_okay=False, dir_okay=True, resolve_path=True, path_type=pathlib.Path + file_okay=False, dir_okay=True, resolve_path=True, path_type=Path, ), required=True, ) @click.option( "--zip", help="Path to the zip.", - type=click.Path(resolve_path=True, path_type=pathlib.Path), + type=click.Path(resolve_path=True, path_type=Path), required=True, ) -def unzip(output_directory, input_zip): +def unzip(output_directory: Path, input_zip: Path) -> None: tempo_core_file_io.unzip_zip(zip_path=input_zip, output_location=output_directory) @@ -71,19 +70,19 @@ def unzip(output_directory, input_zip): @click.option( "--input_path", help="The input path, to a directory tree or file.", - type=click.Path(exists=True, resolve_path=True, path_type=pathlib.Path), + type=click.Path(exists=True, resolve_path=True, path_type=Path), required=True, ) @click.option( "--output_path", help="The output path, to a directory tree or file.", - type=click.Path(resolve_path=True, path_type=pathlib.Path), + type=click.Path(resolve_path=True, path_type=Path), required=True, ) @click.option( - "--overwrite", is_flag=True, help="Overwrite existing files if they already exist." + "--overwrite", is_flag=True, help="Overwrite existing files if they already exist.", ) -def move(input_path, output_path, overwrite): +def move(input_path: Path, output_path: Path, overwrite: bool) -> None: tempo_core_file_io.move(input_path, output_path, overwrite) @@ -94,19 +93,19 @@ def move(input_path, output_path, overwrite): @click.option( "--input_path", help="The input path, to a directory tree or file.", - type=click.Path(exists=True, resolve_path=True, path_type=pathlib.Path), + type=click.Path(exists=True, resolve_path=True, path_type=Path), required=True, ) @click.option( "--output_path", help="The output path, to a directory tree or file.", - type=click.Path(resolve_path=True, path_type=pathlib.Path), + type=click.Path(resolve_path=True, path_type=Path), required=True, ) @click.option( - "--overwrite", is_flag=True, help="Overwrite existing files if they already exist." + "--overwrite", is_flag=True, help="Overwrite existing files if they already exist.", ) -def copy(input_path, output_path, overwrite): +def copy(input_path: Path, output_path: Path, overwrite: bool) -> None: tempo_core_file_io.copy(input_path, output_path, overwrite=overwrite) @@ -117,13 +116,13 @@ def copy(input_path, output_path, overwrite): @click.option( "--input_path", help="The input path, to a directory tree or file.", - type=click.Path(exists=True, resolve_path=True, path_type=pathlib.Path), + type=click.Path(exists=True, resolve_path=True, path_type=Path), required=True, ) @click.option( "--output_path", help="The output path, to a directory tree or file.", - type=click.Path(resolve_path=True, path_type=pathlib.Path), + type=click.Path(resolve_path=True, path_type=Path), required=True, ) @click.option( @@ -131,7 +130,7 @@ def copy(input_path, output_path, overwrite): is_flag=True, help="Overwrite existing files if they already exist.", ) -def symlink(input_path, output_path, overwrite): +def symlink(input_path: Path, output_path: Path, overwrite: bool) -> None: tempo_core_file_io.symlink(input_path, output_path, overwrite) @@ -142,11 +141,11 @@ def symlink(input_path, output_path, overwrite): @click.option( "--input_paths", help="The input path, to a directory tree or file to delete, can be specified multiple times.", - type=click.Path(exists=True, resolve_path=True, path_type=pathlib.Path), + type=click.Path(exists=True, resolve_path=True, path_type=Path), required=True, multiple=True, ) -def delete(input_paths): +def delete(input_paths: list[Path]) -> None: tempo_core_file_io.delete(input_paths) @@ -156,36 +155,36 @@ def delete(input_paths): @file_io.command(name="open_latest_log", help=command_help, short_help=command_help) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", ) -def open_latest_log(settings_json): +def open_latest_log(settings_config: Path) -> None: main_logic.open_latest_log() command_help = "Generates a JSON file containing all of the files in the game directory, from the game exe specified within the settings JSON." @file_io.command( - name="generate_game_file_list_json", help=command_help, short_help=command_help + name="generate_game_file_list_json", help=command_help, short_help=command_help, ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", @@ -194,11 +193,11 @@ def open_latest_log(settings_json): "--output_json", type=click.Path( resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), - help="Path to the output game file list json." + help="Path to the output game file list json.", ) -def generate_game_file_list_json(settings_json, output_json): +def generate_game_file_list_json(settings_config: Path, output_json: Path) -> None: if output_json: main_logic.generate_game_file_list_json(output_json) else: @@ -219,7 +218,7 @@ def generate_game_file_list_json(settings_json, output_json): dir_okay=True, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), ) @click.argument( @@ -230,10 +229,10 @@ def generate_game_file_list_json(settings_json, output_json): dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), ) -def generate_file_list(directory, file_list): +def generate_file_list(directory: Path, file_list: Path) -> None: """ Arguments: directory (str): Path to the directory tree you want to generate the file list from. diff --git a/src/tempo_cli/commands/ini.py b/src/tempo_cli/commands/ini.py index 343766f0..a5ba85e7 100644 --- a/src/tempo_cli/commands/ini.py +++ b/src/tempo_cli/commands/ini.py @@ -1,4 +1,4 @@ -import pathlib +from pathlib import Path import rich_click as click @@ -6,7 +6,7 @@ @click.group() -def ini(): +def ini() -> None: """Ini related commands""" @@ -27,7 +27,7 @@ def ini(): dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to the ini file to edit.", required=True, @@ -40,10 +40,10 @@ def ini(): default=[], ) def add_meta_data_tags_for_asset_registry_to_unreal_ini( - ini_path: pathlib.Path, tags: list[str] -): + ini_path: Path, tags: list[str], +) -> None: unreal_inis.add_meta_data_tags_for_asset_registry_to_unreal_ini( - ini=ini_path, tags=tags + ini=ini_path, tags=tags, ) @@ -64,7 +64,7 @@ def add_meta_data_tags_for_asset_registry_to_unreal_ini( dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The path to the ini file to edit.", required=True, @@ -77,8 +77,8 @@ def add_meta_data_tags_for_asset_registry_to_unreal_ini( default=[], ) def remove_meta_data_tags_for_asset_registry_from_unreal_ini( - ini_path: pathlib.Path, tags: list[str] -): + ini_path: Path, tags: list[str], +) -> None: unreal_inis.remove_meta_data_tags_for_asset_registry_from_unreal_ini( - ini=ini_path, tags=tags + ini=ini_path, tags=tags, ) diff --git a/src/tempo_cli/commands/init.py b/src/tempo_cli/commands/init.py index 3546211d..4e8bd03a 100644 --- a/src/tempo_cli/commands/init.py +++ b/src/tempo_cli/commands/init.py @@ -1,15 +1,19 @@ import os +import re import json -import pathlib +from pathlib import Path import subprocess from dataclasses import dataclass, field +from prompt_toolkit.validation import Validator, ValidationError +import shutil import tomlkit import questionary import rich_click as click -from tempo_core import logger +from tempo_core import logger, manager from tempo_core.main_logic import generate_uproject +from tempo_binary_tools import tempo_shell_scripts, tempo_gitignores, tempo_mod_docs_template from tempo_cli import file_io as tc_file_io from tempo_cli import validators, checks, unreal, git @@ -33,10 +37,10 @@ @dataclass class SetupInformation: - working_directory: pathlib.Path - tempo_config: pathlib.Path | None = None + working_directory: Path + tempo_config: Path | None = None tempo_config_contents: dict = field(default_factory=dict) - git_repo_dir: pathlib.Path | None = None + git_repo_dir: Path | None = None should_use_pre_commit: bool = False should_make_docs: bool = False should_download_easy_scripts: bool = False @@ -45,12 +49,35 @@ class SetupInformation: should_close_fmodel_and_umodel: bool = False -def project_init(directory: pathlib.Path): +class PackageNameValidator(Validator): + pattern = re.compile(r"^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$") + + def validate(self, document): #noqa + text = document.text.strip() + + if not text: + raise ValidationError( + message="Project name cannot be empty.", + cursor_position=0, + ) + + if not self.pattern.fullmatch(text): + raise ValidationError( + message=( + "Not a valid package or extra name. " + "Names must start and end with a letter or digit " + "and may only contain -, _, ., and alphanumeric characters." + ), + cursor_position=len(document.text), + ) + + +def project_init(directory: Path) -> None: # add initial select multi option thing ifg possible for features omn onitial step, using checkbox thing setup_information = SetupInformation(working_directory=directory) logger.log_message(f"project directory: {setup_information.working_directory}") - setup_information.git_repo_dir = pathlib.Path(f"{setup_information.working_directory}/.git") - setup_information.tempo_config = pathlib.Path(f"{setup_information.working_directory}/.tempo.json") + setup_information.git_repo_dir = Path(f"{setup_information.working_directory}/.git") + setup_information.tempo_config = Path(f"{setup_information.working_directory}/.tempo.json") if setup_information.tempo_config.exists() and setup_information.tempo_config.is_file(): config_already_exists_error = ( f'There is already a .tempo.json config in the following directory: "{setup_information.working_directory}"' @@ -64,12 +91,12 @@ def project_init(directory: pathlib.Path): "version management": "should_use_versioning", "should auto close game": "should_auto_close_game", "should auto close fmodel and umodel": "should_close_fmodel_and_umodel", - "should setup prek": "should_use_prek" + "should setup prek": "should_use_prek", } chosen_options = questionary.checkbox( message='Choose your features', - choices=list(feature_choices.keys()) + choices=list(feature_choices.keys()), ).ask() for option in chosen_options: @@ -77,21 +104,19 @@ def project_init(directory: pathlib.Path): git_repo_dir_already_existed = False - if os.path.isdir(setup_information.git_repo_dir): + if setup_information.git_repo_dir.is_dir(): git_repo_dir_already_existed = True subprocess.run("git init") - pyproject_toml = os.path.normpath(f"{setup_information.working_directory}/pyproject.toml") - if not os.path.isfile(pyproject_toml): - # subprocess.run("uv init --package", cwd=setup_information.working_directory) - - # Names must start and end with a letter or digit and may only contain -, _, ., and alphanumeric characters. - # below might need validation for valid project names - project_name = questionary.text(message='What would you like your project name to be?').ask() + pyproject_toml = Path(f"{setup_information.working_directory}/pyproject.toml") + if not pyproject_toml.is_file(): + project_name = questionary.text(message='What would you like your project name to be?', validate=PackageNameValidator()).ask() description = questionary.text(message='What would you like your project description to be?').ask() - subprocess.run(f"uv init --bare --no-readme --no-pin-python --no-workspace --name {project_name} --description {description}", cwd=setup_information.working_directory) + subprocess.run(f'uv init --bare --no-readme --no-pin-python --no-workspace --name "{project_name}" --description "{description}"', cwd=setup_information.working_directory) subprocess.run("uv add git+https://www.github.com/Tempo-Organization/tempo-cli", cwd=setup_information.working_directory) + +# have it list auto detected installs as option, overall auto detect option (will need game or something else to check), and an option to manually specify and an option to skip unreal_engine_dir = questionary.path( message='What is the path to your unreal engine install directory (Most mods will need this but not all)? Example: "C:/Program Files/Epic Games/UE_4.22" (press enter to skip)', only_directories=True, @@ -129,7 +154,7 @@ def project_init(directory: pathlib.Path): game_executable = "" if game_launch_choice == "exe": game_executable = questionary.path( - message='What is the path to your main game executable? Example: "C:/Program Files (x86)/Steam/steamapps/common/Zedfest/KevinSpel/Binaries/Win64/Zedfest.exe" (press enter to skip)' + message='What is the path to your main game executable? Example: "C:/Program Files (x86)/Steam/steamapps/common/Zedfest/KevinSpel/Binaries/Win64/Zedfest.exe" (press enter to skip)', ).ask() setup_information.tempo_config_contents["game_info"]["game_exe_path"] = game_executable setup_information.tempo_config_contents["game_info"]["launch_type"] = game_launch_choice @@ -148,15 +173,15 @@ def project_init(directory: pathlib.Path): if git_repo_dir_already_existed: setup_information.tempo_config_contents["git_info"]["repo_branch"] = git.get_branch_from_git_repo(str(setup_information.git_repo_dir)) else: - gitignore = os.path.normpath(f"{setup_information.working_directory}/.gitignore") - if os.path.isfile(gitignore): - os.remove(gitignore) - tc_file_io.download_files_from_github_repo( - repo_url="https://github.com/Tempo-Organization/tempo-template", - repo_branch="main", - file_paths=[".gitignore"], - output_directory=str(setup_information.working_directory), - ) + tempo_gitignores_tool_info = tempo_gitignores.TempoGitignoresToolInfo(cache=manager.tools_cache) + tempo_gitignores_tool_info.ensure_tool_installed() + gitignores_dir = tempo_gitignores_tool_info.get_tool_directory() + src_gitignore = Path(f'{gitignores_dir}/tempo.gitignore') + dst_gitignore = Path(f"{setup_information.working_directory}/.gitignore") + if dst_gitignore.is_file(): + dst_gitignore.unlink() + shutil.copy(src_gitignore, dst_gitignore) + setup_information.tempo_config_contents["git_info"]["repo_branch"] = "master" setup_information.tempo_config_contents["git_info"]["repo_path"] = setup_information.working_directory @@ -172,33 +197,31 @@ def project_init(directory: pathlib.Path): uproject_path = questionary.path( message='What is the path to your uproject, if you have one already? Example: "C:/Users/Mythi/Documents/GitHub/ZedfestModdingKit/KevinSpel.uproject" (press enter to skip)', ).ask() - if not uproject_path == "" and not os.path.dirname(uproject_path) == setup_information.working_directory: + if not uproject_path == "" and not Path(uproject_path).parent == setup_information.working_directory: logger.log_message( - "Warning: It is recommended to place your uproject in the same directory as your tempo project files." + "Warning: It is recommended to place your uproject in the same directory as your tempo project files.", ) if uproject_path == "" or not uproject_path: if not game_executable or game_executable == "": uproject_name = questionary.text( - message="What is the name of your uproject file? (it should be the same as the game project name usually.)" + message="What is the name of your uproject file? (it should be the same as the game project name usually.)", ).ask() else: - uproject_name = uproject_name = os.path.basename( - os.path.dirname(os.path.dirname(os.path.dirname(game_executable))) - ) + uproject_name = Path(game_executable).parent.parent.parent.name generate_uproject( - project_file=os.path.normpath(f"{setup_information.working_directory}/{uproject_name}.uproject"), + project_file=Path(f"{setup_information.working_directory}/{uproject_name}.uproject"), file_version=3, engine_major_association=unreal_engine_major_version, engine_minor_association=unreal_engine_minor_version, ignore_safety_checks=True, ) - setup_information.tempo_config_contents['engine_info']['unreal_project_file'] = os.path.normpath(f"{setup_information.working_directory}/{uproject_name}.uproject") + setup_information.tempo_config_contents.get('engine_info', {})['unreal_project_file'] = Path(f"{setup_information.working_directory}/{uproject_name}.uproject") else: if not uproject_path == "" and uproject_path: - setup_information.tempo_config_contents['engine_info']['unreal_project_file'] = uproject_path + setup_information.tempo_config_contents.get('engine_info', {})['unreal_project_file'] = uproject_path window_override_title = questionary.text( - message='What is title of the game window, when the game is launched? Example: "Zedfest" (press enter to skip)' + message='What is title of the game window, when the game is launched? Example: "Zedfest" (press enter to skip)', ).ask() if not window_override_title == "" and window_override_title: setup_information.tempo_config_contents["game_info"]["window_title_override"] = window_override_title @@ -225,8 +248,8 @@ def project_init(directory: pathlib.Path): if setup_information.should_download_easy_scripts: easy_scripts_setup(setup_information) - def convert_paths(obj): - if isinstance(obj, pathlib.Path): + def convert_paths(obj): # noqa + if isinstance(obj, Path): return str(obj) elif isinstance(obj, dict): return {k: convert_paths(v) for k, v in obj.items()} @@ -237,7 +260,7 @@ def convert_paths(obj): cleaned_data = convert_paths(setup_information.tempo_config_contents) - with open(setup_information.tempo_config, "w") as config_file: + with Path.open(setup_information.tempo_config, "w") as config_file: json.dump(cleaned_data, config_file, indent=4) logger.log_message(f'.tempo.json created at "{setup_information.tempo_config}".') @@ -250,12 +273,12 @@ def convert_paths(obj): ) @click.option( "--directory", - default=os.getcwd(), - type=click.Path(exists=True, resolve_path=True, path_type=pathlib.Path, file_okay=False, dir_okay=True), + default=Path.cwd(), + type=click.Path(exists=True, resolve_path=True, path_type=Path, file_okay=False, dir_okay=True), help="The tempo project directory, defaults to current working directory.", ) # add game preset options later? -def init(directory): +def init(directory: Path) -> None: if not checks.check_git_is_installed(): no_git_error = 'You need git installed to use this functionality.' raise RuntimeError(no_git_error) @@ -267,21 +290,34 @@ def init(directory): project_init(directory) -def pre_commit_setup(setup_information: SetupInformation): - tc_file_io.download_files_from_github_repo( - repo_url="https://github.com/Tempo-Organization/tempo-template", - repo_branch="main", - file_paths=[".pre-commit-config.yaml"], - output_directory=str(setup_information.working_directory), - ) - subprocess.run("uv add prek") +def pre_commit_setup(setup_information: SetupInformation) -> None: + options = [ + 'trailing-whitespace', + 'end-of-file-fixer', + 'check-added-large-files', + 'check-docstring-first', + 'check-illegal-windows-names', + 'check-shebang-scripts-are-executable', + 'check-symlinks', + 'destroyed-symlinks', + 'check-toml', + 'check-xml', + 'check-yaml', + 'check-json', + 'pretty-format-json', + ] + subprocess.run("uv add prek sample-config --format toml") + subprocess.run("uv run prek") + # add the multiselect here for the above options + # make hooks list empty + # then add each one the multiselected wanted in subprocess.run("uv run prek install") -def versioning_setup(setup_information: SetupInformation): +def versioning_setup(setup_information: SetupInformation) -> None: subprocess.run("uv add commitizen") - toml_path = os.path.normpath(f"{setup_information.working_directory}/pyproject.toml") - with open(toml_path, "r", encoding="utf-8") as f: + toml_path = Path(f"{setup_information.working_directory}/pyproject.toml") + with Path.open(toml_path, "r", encoding="utf-8") as f: content = f.read() toml_doc = tomlkit.parse(content) @@ -289,7 +325,7 @@ def versioning_setup(setup_information: SetupInformation): version_scheme_option = questionary.select( message="Which versioning scheme would you like to use?", choices=version_scheme_options, - default=cz_data_structures.CommitizenVersionSchemeOption.SEMVER2.value + default=cz_data_structures.CommitizenVersionSchemeOption.SEMVER2.value, ).ask() commitizen_table = tomlkit.table() commitizen_table["name"] = "cz_conventional_commits" @@ -302,9 +338,9 @@ def versioning_setup(setup_information: SetupInformation): if "tool" not in toml_doc: toml_doc["tool"] = tomlkit.table() - toml_doc["tool"]["commitizen"] = commitizen_table # type: ignore + toml_doc["tool"]["commitizen"] = commitizen_table - with open(toml_path, "w", encoding="utf-8") as f: + with Path.open(toml_path, "w", encoding="utf-8") as f: f.write(tomlkit.dumps(toml_doc)) if setup_information.should_use_pre_commit: @@ -312,43 +348,38 @@ def versioning_setup(setup_information: SetupInformation): subprocess.run("uv run prek install --hook-type pre-push") -def process_management_setup(): +def process_management_setup() -> None: return -def easy_scripts_setup(setup_information: SetupInformation): +def easy_scripts_setup(setup_information: SetupInformation) -> None: if not setup_information.tempo_config: raise FileNotFoundError(setup_information.tempo_config) - EASY_SCRIPTS_VERSION = "0.4.0" - output_directory_for_scripts = os.path.join(setup_information.working_directory, "Modding", "scripts") - - easy_scripts_download_link = ( - f"https://github.com/Tempo-Organization/tempo-template/" - f"releases/download/{EASY_SCRIPTS_VERSION}/easy_scripts.zip" - ) - - tc_file_io.download_and_extract_zip( - url=easy_scripts_download_link, - output_dir=output_directory_for_scripts - ) + tempo_shell_scripts_tool_info = tempo_shell_scripts.TempoShellScriptsToolInfo(cache=manager.tools_cache) + tempo_shell_scripts_tool_info.ensure_tool_installed() + script_files_dir = tempo_shell_scripts_tool_info.get_tool_directory() + output_directory_for_scripts = Path(setup_information.working_directory / "Modding" / "scripts") + shutil.copytree(script_files_dir, output_directory_for_scripts, dirs_exist_ok=True) -def documentation_setup(setup_information: SetupInformation): +def documentation_setup(setup_information: SetupInformation) -> None: subprocess.run("uv add mkdocs-material") + tempo_mods_docs_template_tool_info = tempo_mod_docs_template.TempoModDocsTemplateToolInfo(cache=manager.tools_cache) + tempo_mods_docs_template_tool_info.ensure_tool_installed() files = [ - "mkdocs.yml", - ".github/workflows/github_pages.yml", - "docs/index.md", - "docs/stylesheets/extra.css", + Path("mkdocs.yml"), + Path(".github/workflows/github_pages.yml"), + Path("docs/index.md"), + Path("docs/stylesheets/extra.css"), ] - tc_file_io.download_files_from_github_repo( - repo_url="https://github.com/Tempo-Organization/tempo-template", - repo_branch="main", - file_paths=files, - output_directory=str(setup_information.working_directory), - ) - mkdocs_yml_path = os.path.normpath(f'{setup_information.working_directory}/mkdocs.yml') - index_md_path = os.path.normpath(f'{setup_information.working_directory}/docs/index.md') + for file in files: + src_file_path = Path(f'{tempo_mods_docs_template_tool_info.get_tool_directory()}/{file}') + dst_file_path = Path(f'{setup_information.working_directory}/{file}') + dst_file_path.parent.mkdir(parents=True, exist_ok=True) + shutil.copy(src_file_path, dst_file_path) + + mkdocs_yml_path = Path(f'{setup_information.working_directory}/mkdocs.yml') + index_md_path = Path(f'{setup_information.working_directory}/docs/index.md') mod_name = questionary.text(message="What is the main name for your docs? Usually your main Mod Name.").ask() github_account_name = questionary.text(message="What is your github account name?").ask() discord_server_link = questionary.text(message="If you have one, what is your discord server link? If none leave blank and hit enter.").ask() diff --git a/src/tempo_cli/commands/json.py b/src/tempo_cli/commands/json.py index 000020da..a8cef5bf 100644 --- a/src/tempo_cli/commands/json.py +++ b/src/tempo_cli/commands/json.py @@ -1,31 +1,31 @@ import json as json_module -import pathlib +from pathlib import Path import rich_click as click from tempo_core import logger @click.group() -def json(): +def json() -> None: """Json related commands""" command_help_add_json = "Add an entry to a JSON file." @json.command( - name="add", help=command_help_add_json, short_help=command_help_add_json + name="add", help=command_help_add_json, short_help=command_help_add_json, ) @click.option( "--json_path", help="Path to the JSON file.", - type=click.Path(exists=True, resolve_path=True, path_type=pathlib.Path), + type=click.Path(exists=True, resolve_path=True, path_type=Path), required=True, ) @click.option("--key", help="Key to add to the JSON file.", type=str, required=True) @click.option( - "--value", help="Value to associate with the key.", type=str, required=True + "--value", help="Value to associate with the key.", type=str, required=True, ) -def add(json_path, key, value): +def add(json_path: Path, key: str, value: str) -> None: with json_path.open("r+") as f: data = json_module.load(f) data[key] = value @@ -45,13 +45,13 @@ def add(json_path, key, value): @click.option( "--json_path", help="Path to the JSON file.", - type=click.Path(exists=True, resolve_path=True, path_type=pathlib.Path), + type=click.Path(exists=True, resolve_path=True, path_type=Path), required=True, ) @click.option( - "--key", help="Key to remove from the JSON file.", type=str, required=True + "--key", help="Key to remove from the JSON file.", type=str, required=True, ) -def remove(json_path, key): +def remove(json_path: Path, key: str) -> None: with json_path.open("r+") as f: data = json_module.load(f) if key in data: diff --git a/src/tempo_cli/commands/list.py b/src/tempo_cli/commands/list.py new file mode 100644 index 00000000..e9a6e67e --- /dev/null +++ b/src/tempo_cli/commands/list.py @@ -0,0 +1,123 @@ +import json +from pathlib import Path + +import rich_click as click +from tempo_core import logger, registry, settings, manager + + +@click.group() +def list() -> None: # noqa + """List related commands""" + + +command_help_list_unreal_installs = "List all detected unreal engine installs." + + +@list.command( + name="unreal_installs", + help=command_help_list_unreal_installs, + short_help=command_help_list_unreal_installs, +) +@click.option( + "--clean", + type=bool, + is_flag=True, + help="Will remove registry entries that don't actually contain unreal engine installs. May have to run with administrator permissions.", +) +def unreal_installs(clean: bool) -> None: + unreal_installs = registry.get_unreal_installs_from_registry() + if unreal_installs: + for version, path in unreal_installs.items(): + logger.log_message(f"{version}: {path}") + else: + logger.log_message('There were no detected unreal engine installs.') + if clean: + registry.remove_invalid_unreal_engine_registry_entries() + + +command_help_list_mods = "List all detected mod entries for the project." + +@list.command( + name="mods", help=command_help_list_mods, short_help=command_help_list_mods, +) +@click.option( + "--config-file", + type=click.Path( + exists=True, + file_okay=True, + dir_okay=False, + readable=True, + resolve_path=True, + path_type=Path, + ), + required=True, + help="Path to the settings JSON file", +) +def mods(settings_config: Path) -> None: + mods_dict = settings.get_mods_info_dict_from_json() + for key, value in mods_dict.items(): + logger.log_message(key) + logger.log_message(json.dumps(value, indent=4)) + + +command_help_list_tools = "List all detected cached tools." + +@list.command( + name="tools", help=command_help_list_tools, short_help=command_help_list_tools, +) +def tools() -> None: + manager.tools_cache.list_tools() + + +command_help_list_uplugins = "List all detected uplugins used by the uproject." + +@list.command( + name="uplugins", help=command_help_list_uplugins, short_help=command_help_list_uplugins, +) +@click.option( + "--config-file", + type=click.Path( + exists=True, + file_okay=True, + dir_okay=False, + readable=True, + resolve_path=True, + path_type=Path, + ), + required=True, + help="Path to the settings JSON file", +) +def uplugins(settings_config: Path) -> None: + with settings_config.open("r", encoding="utf-8") as f: + settings = json.load(f) + + try: + uproject_path = settings["engine_info"]["unreal_project_file"] + except KeyError: + logger.log_message("Missing 'engine_info.unreal_project_file' in settings.") + return + + uproject_path = (settings_config.parent / uproject_path).resolve() + + if not uproject_path.exists(): + logger.log_message(f"Uproject file not found: {uproject_path}") + return + + with uproject_path.open("r", encoding="utf-8") as f: + uproject = json.load(f) + + plugins = uproject.get("Plugins", []) + + if not plugins: + logger.log_message("No plugins found.") + return + + for plugin in plugins: + name = plugin.get("Name", "Unknown") + logger.log_message(f"Plugin: {name}") + + for key, value in plugin.items(): + if key != "Name": + logger.log_message(f" {key}: {value}") + + logger.log_message('') diff --git a/src/tempo_cli/commands/mod.py b/src/tempo_cli/commands/mod.py index de30caba..5c97bd92 100644 --- a/src/tempo_cli/commands/mod.py +++ b/src/tempo_cli/commands/mod.py @@ -1,11 +1,11 @@ -import pathlib +from pathlib import Path import rich_click as click from tempo_core import main_logic, data_structures, settings @click.group() -def mod(): +def mod() -> None: """Mod related commands""" @@ -13,14 +13,14 @@ def mod(): @mod.command(name="enable_mod", help=command_help, short_help=command_help) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", @@ -30,10 +30,10 @@ def mod(): type=str, required=True, help="Name of a mod to enable, can be specified multiple times", - prompt="What is the name of the mod you want to enable? " + prompt="What is the name of the mod you want to enable? ", ) -def enable_mod(settings_json, mod_name): - main_logic.enable_mods(settings_json=settings_json, mod_names=[mod_name]) +def enable_mod(settings_config: Path, mod_name: str) -> None: + main_logic.enable_mods(config_file=settings_config, mod_names=[mod_name]) command_help = "Enable the given mod names in the provided settings JSON." @@ -48,20 +48,20 @@ def enable_mod(settings_json, mod_name): help="Name of a mod to enable, can be specified multiple times", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", ) -def enable_mods(settings_json, mod_names): - main_logic.enable_mods(settings_json=settings_json, mod_names=mod_names) +def enable_mods(settings_config: Path, mod_names: list[str]) -> None: + main_logic.enable_mods(config_file=settings_config, mod_names=mod_names) command_help = "Disable the given mod names in the provided settings JSON." @@ -76,20 +76,20 @@ def enable_mods(settings_json, mod_names): help="Name of a mod to disable, can be specified multiple times", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", ) -def disable_mods(settings_json, mod_names): - main_logic.disable_mods(settings_json=settings_json, mod_names=mod_names) +def disable_mods(settings_config: Path, mod_names: list[str]) -> None: + main_logic.disable_mods(config_file=settings_config, mod_names=mod_names) command_help = "Disable the given mod names in the provided settings JSON." @@ -97,14 +97,14 @@ def disable_mods(settings_json, mod_names): @mod.command(name="disable_mod", help=command_help, short_help=command_help) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", @@ -114,16 +114,16 @@ def disable_mods(settings_json, mod_names): type=str, required=True, help="Name of a mod to disable, can be specified multiple times", - prompt="What is the name of the mod you want to disable?" + prompt="What is the name of the mod you want to disable?", ) -def disable_mod(settings_json, mod_name): - main_logic.disable_mods(settings_json=settings_json, mod_names=[mod_name]) +def disable_mod(settings_config: Path, mod_name: str) -> None: + main_logic.disable_mods(config_file=settings_config, mod_names=[mod_name]) command_help = "Adds the given mod name in the provided settings JSON." packing_type_choices = data_structures.get_enum_strings_from_enum( - data_structures.PackingType + data_structures.PackingType, ) @mod.command(name="add_mod", help=command_help, short_help=command_help) @@ -135,7 +135,7 @@ def disable_mod(settings_json, mod_name): help="Packing type for the mod.", required=True, default=packing_type_choices[1], - prompt="What will be the packaging method for your mod? If left blank, defaults to " + prompt="What will be the packaging method for your mod? If left blank, defaults to ", ) @click.option( "--mod_name_dir_type", @@ -143,7 +143,7 @@ def disable_mod(settings_json, mod_name): default="Mods", help='Directory type for the mod name (default: "Mods").', required=True, - prompt="What is the directory name for your mod type? e.g. The Mods in Content/Mods/ModName or CustomContent in Content/CustomContent/ModName, or another pattern. If left blank, defaults to " + prompt="What is the directory name for your mod type? e.g. The Mods in Content/Mods/ModName or CustomContent in Content/CustomContent/ModName, or another pattern. If left blank, defaults to ", ) @click.option( "--mod_name_dir_name_override", @@ -151,7 +151,7 @@ def disable_mod(settings_json, mod_name): default="", help="Override the mod name directory with this value (optional).", required=True, - prompt="if you need one, what is your mod name dir name override? Useful for Content/Mods/ModName but having ModName_P.pak for example. If left blank, defaults to None and uses the mod_name" + prompt="if you need one, what is your mod name dir name override? Useful for Content/Mods/ModName but having ModName_P.pak for example. If left blank, defaults to None and uses the mod_name", ) @click.option( "--pak_chunk_num", @@ -159,7 +159,7 @@ def disable_mod(settings_json, mod_name): default=0, help="Pak chunk number (optional).", required=True, - prompt="If you are using the engine packing method, what is your mod's chunk id? If left blank, defaults to " + prompt="If you are using the engine packing method, what is your mod's chunk id? If left blank, defaults to ", ) @click.option( "--compression_type", @@ -167,7 +167,7 @@ def disable_mod(settings_json, mod_name): type=str, help="Compression type for the mod (optional).", required=True, - prompt="What if any is your compression method for packing? If left blank, defaults to None" + prompt="What if any is your compression method for packing? If left blank, defaults to None", ) @click.option( "--is_enabled", @@ -185,7 +185,7 @@ def disable_mod(settings_json, mod_name): dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), ) @click.option( @@ -198,38 +198,38 @@ def disable_mod(settings_json, mod_name): dir_okay=True, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", - prompt="What is the path to your tempo config file?" + prompt="What is the path to your tempo config file?", ) def add_mod( - settings_json, - mod_name, - packing_type, - pak_dir_structure, - mod_name_dir_type, - mod_name_dir_name_override, - pak_chunk_num, - compression_type, - is_enabled, - asset_paths, - tree_paths, -): + config_file: Path, + mod_name: str, + packing_type: str, + pak_dir_structure: str, + mod_name_dir_type: str, + mod_name_dir_name_override: str | None, + pak_chunk_num: int | None, + compression_type: str | None, + is_enabled: bool, + asset_paths: list[Path], + tree_paths: list[Path], +) -> None: if pak_chunk_num == 0: pak_chunk_num = None if mod_name_dir_name_override == "": @@ -237,7 +237,7 @@ def add_mod( if compression_type == "": compression_type = None main_logic.add_mod( - settings_json=settings_json, + config_file=config_file, mod_name=mod_name, packing_type=packing_type, pak_dir_structure=pak_dir_structure, @@ -260,43 +260,43 @@ def add_mod( type=str, required=True, help="Name of a mod to be removed.", - prompt="What is the name of the mod to remove?" + prompt="What is the name of the mod to remove?", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", ) -def remove_mod(settings_json, mod_name): - main_logic.remove_mods(settings_json=settings_json, mod_names=[mod_name]) +def remove_mod(settings_config: Path, mod_name: str) -> None: + main_logic.remove_mods(config_file=settings_config, mod_names=[mod_name]) command_help = "Removes the given mod names in the provided settings JSON." @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", ) -def remove_mods(settings_json, mod_names): - main_logic.remove_mods(settings_json=settings_json, mod_names=mod_names) +def remove_mods(settings_config: Path, mod_names: list[str]) -> None: + main_logic.remove_mods(config_file=settings_config, mod_names=mod_names) @@ -319,19 +319,19 @@ def remove_mods(settings_json, mod_names): help="Whether or not to use symlinks to save time with file operations", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", ) -def generate_mods(settings_json, mod_names, use_symlinks): +def generate_mods(settings_config: Path, mod_names: list[str], use_symlinks: bool) -> None: main_logic.generate_mods(input_mod_names=mod_names, use_symlinks=use_symlinks) @@ -347,19 +347,19 @@ def generate_mods(settings_json, mod_names, use_symlinks): help="Whether or not to use symlinks to save time with file operations", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", ) -def generate_mods_all(settings_json, use_symlinks): +def generate_mods_all(settings_config: Path, use_symlinks: bool) -> None: main_logic.generate_mods_all(use_symlinks=use_symlinks) @@ -383,7 +383,7 @@ def generate_mods_all(settings_json, use_symlinks): dir_okay=True, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), ) @click.option( @@ -395,25 +395,25 @@ def generate_mods_all(settings_json, use_symlinks): dir_okay=True, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", ) def generate_mod_releases( - settings_json, mod_names, base_files_directory, output_directory -): + settings_config: Path, mod_names: list[str], base_files_directory: Path, output_directory: Path, +) -> None: main_logic.generate_mod_releases(mod_names, base_files_directory, output_directory) @@ -421,7 +421,7 @@ def generate_mod_releases( @mod.command( - name="generate_mod_releases_all", help=command_help, short_help=command_help + name="generate_mod_releases_all", help=command_help, short_help=command_help, ) @click.option( "--base_files_directory", @@ -432,7 +432,7 @@ def generate_mod_releases( dir_okay=True, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), ) @click.option( @@ -444,25 +444,25 @@ def generate_mod_releases( dir_okay=True, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", ) -def generate_mod_releases_all(settings_json, base_files_directory, output_directory): +def generate_mod_releases_all(settings_config: Path, base_files_directory: Path, output_directory: Path) -> None: if not base_files_directory or base_files_directory == '': - base_files_directory = settings.get_default_release_base_files_dir() + base_files_directory = Path(settings.get_default_release_base_files_dir()) if not output_directory or output_directory == '': - output_directory = settings.get_default_release_dir() + output_directory = Path(settings.get_default_release_dir()) main_logic.generate_mod_releases_all(base_files_directory, output_directory) diff --git a/src/tempo_cli/commands/run.py b/src/tempo_cli/commands/run.py index e61af6b9..bd04c897 100644 --- a/src/tempo_cli/commands/run.py +++ b/src/tempo_cli/commands/run.py @@ -1,14 +1,13 @@ import os -import pathlib +from pathlib import Path import rich_click as click from ue4ss_installer_core import ue4ss -from tempo_core import main_logic, file_io, data_structures, settings +from tempo_core import main_logic, file_io, data_structures, settings, manager from tempo_core.programs import kismet_analyzer as tempo_core_kismet_analyzer -from tempo_cache import cache -from tempo_cache_tools import kismet_analyzer as kismet_analyzer_tool +from tempo_binary_tools import kismet_analyzer as kismet_analyzer_tool # make this only happen if online is working, if online not working, throw error when calling related commands @@ -16,7 +15,7 @@ @click.group() -def run(): +def run() -> None: """Run related commands""" @@ -24,19 +23,19 @@ def run(): @run.command(name="engine", help=command_help, short_help=command_help) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) -def engine(settings_json): +def engine(config_file: Path) -> None: main_logic.run_engine() @@ -50,19 +49,19 @@ def engine(settings_json): help="Whether to close engine instances at the start and open at the end of the command process (default: False).", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) -def game(settings_json, toggle_engine): +def game(config_file: Path, toggle_engine: bool) -> None: main_logic.run_game(toggle_engine=toggle_engine) @@ -72,17 +71,17 @@ def game(settings_json, toggle_engine): short_help="Generates a kismet analyzer dump for the provided directory tree.", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) @click.option( "--kismet_analyzer_executable", @@ -92,7 +91,7 @@ def game(settings_json, toggle_engine): dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=False, help="Path to your kismet analyzer executable, if not provided, one will be automatically downloaded.", @@ -105,7 +104,7 @@ def game(settings_json, toggle_engine): dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=False, help="Path to a jmap or usmap file.", @@ -118,20 +117,20 @@ def game(settings_json, toggle_engine): dir_okay=True, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), - default=os.path.normpath(f'{os.getcwd()}/Modding/game_dump'), + default=Path(f'{Path.cwd()}/Modding/game_dump'), help="Path to an unpacked dir tree from an unreal game.", ) @click.option( "--output", - default=os.path.normpath(f'{os.getcwd()}/Modding/kismet_analyzer_dump'), + default=Path(f'{Path.cwd()}/Modding/kismet_analyzer_dump'), type=click.Path( file_okay=False, dir_okay=True, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The file location you want your utoc outputted to.", ) @@ -142,20 +141,20 @@ def game(settings_json, toggle_engine): type=bool, help="Should the generated kismet analyzer be opened after being completed.", ) -def kismet_analyze_directory(mappings, assets, output, open): - os.makedirs(output, exist_ok=True) +def kismet_analyze_directory(mappings: Path, assets: Path, output: Path, open: bool) -> None: # noqa + output.mkdir(parents=True, exist_ok=True) if len(file_io.get_files_in_tree(assets)) < 1: raise RuntimeError('When kismet analyzing a directory, the provided assets path must not be an empty directory tree.') tempo_core_kismet_analyzer.run_gen_cfg_tree_command( - kismet_analyzer_executable=pathlib.Path(kismet_analyzer_tool.KismetAnalyzerToolInfo().get_executable_path()), + kismet_analyzer_executable=Path(kismet_analyzer_tool.KismetAnalyzerToolInfo(cache=manager.tools_cache).get_executable_path()), mappings_file=mappings, asset_tree=assets, - output_tree=output + output_tree=output, ) if open: import webbrowser # check the below path is actually correct later on - webbrowser.open(os.path.normpath(f'{output}/index.html')) + webbrowser.open(str(Path(output / 'index.html'))) command_help = "Run tests for specific mods" @@ -184,19 +183,19 @@ def kismet_analyze_directory(mappings, assets, output, open): help="Whether or not to use symlinks to save time with file operations", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) -def test_mods(settings_json, mod_names, toggle_engine, use_symlinks): +def test_mods(config_file: Path, mod_names: list[str], toggle_engine: bool, use_symlinks: bool) -> None: main_logic.test_mods( input_mod_names=mod_names, toggle_engine=toggle_engine, @@ -223,19 +222,19 @@ def test_mods(settings_json, mod_names, toggle_engine, use_symlinks): help="Whether or not to use symlinks to save time with file operations", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) -def test_mods_all(settings_json, toggle_engine, use_symlinks): +def test_mods_all(config_file: Path, toggle_engine: bool, use_symlinks: bool) -> None: main_logic.test_mods_all(toggle_engine=toggle_engine, use_symlinks=use_symlinks) @@ -260,12 +259,12 @@ def test_mods_all(settings_json, toggle_engine, use_symlinks): @click.option( "--base_files_directory", help="Path to dir tree whose content to pack alongside the mod for release", - type=click.Path(exists=False, resolve_path=True, path_type=pathlib.Path), + type=click.Path(exists=False, resolve_path=True, path_type=Path), ) @click.option( "--output_directory", help="Path to the output directory", - type=click.Path(exists=False, resolve_path=True, path_type=pathlib.Path), + type=click.Path(exists=False, resolve_path=True, path_type=Path), ) @click.option( "--use_symlinks", @@ -275,30 +274,30 @@ def test_mods_all(settings_json, toggle_engine, use_symlinks): help="Whether or not to use symlinks to save time with file operations", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) def full_run( - settings_json, - mod_names, - toggle_engine, - base_files_directory, - output_directory, - use_symlinks, -): + config_file: Path, + mod_names: list[str], + toggle_engine: bool, + base_files_directory: Path, + output_directory: Path, + use_symlinks: bool, +) -> None: if not base_files_directory or base_files_directory == '': - base_files_directory = settings.get_default_release_base_files_dir() + base_files_directory = Path(settings.get_default_release_base_files_dir()) if not output_directory or output_directory == '': - output_directory = settings.get_default_release_dir() + output_directory = Path(settings.get_default_release_dir()) main_logic.full_run( input_mod_names=mod_names, toggle_engine=toggle_engine, @@ -322,12 +321,12 @@ def full_run( @click.option( "--base_files_directory", help="Path to dir tree whose content to pack alongside the mod for release", - type=click.Path(exists=False, resolve_path=True, path_type=pathlib.Path), + type=click.Path(exists=False, resolve_path=True, path_type=Path), ) @click.option( "--output_directory", help="Path to the output directory", - type=click.Path(exists=False, resolve_path=True, path_type=pathlib.Path), + type=click.Path(exists=False, resolve_path=True, path_type=Path), ) @click.option( "--use_symlinks", @@ -337,25 +336,25 @@ def full_run( help="Whether or not to use symlinks to save time with file operations", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) def full_run_all( - settings_json, toggle_engine, base_files_directory, output_directory, use_symlinks -): + config_file: Path, toggle_engine: bool, base_files_directory: Path, output_directory: Path, use_symlinks: bool, +) -> None: if not base_files_directory or base_files_directory == '': - base_files_directory = settings.get_default_release_base_files_dir() + base_files_directory = Path(settings.get_default_release_base_files_dir()) if not output_directory or output_directory == '': - output_directory = settings.get_default_release_dir() + output_directory = Path(settings.get_default_release_dir()) main_logic.full_run_all( toggle_engine=toggle_engine, base_files_directory=base_files_directory, @@ -374,17 +373,17 @@ def full_run_all( host_type_choices = data_structures.get_enum_strings_from_enum( - data_structures.UnrealHostTypes + data_structures.UnrealHostTypes, ) loading_phase_choices = data_structures.get_enum_strings_from_enum( - data_structures.LoadingPhases + data_structures.LoadingPhases, ) command_help = "Adds the specified module entry to the descriptor file, overwriting if it already exists." @run.command( - name="add_module_to_descriptor", help=command_help, short_help=command_help + name="add_module_to_descriptor", help=command_help, short_help=command_help, ) @click.option( "--host_type", @@ -408,18 +407,18 @@ def full_run_all( dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), ) @click.argument("module_name", type=str) -def add_module_to_descriptor(descriptor_file, module_name, host_type, loading_phase): +def add_module_to_descriptor(descriptor_file: Path, module_name: str, host_type: str, loading_phase: str) -> None: """ Arguments: descriptor_file (str): Path to the descriptor file to add the module to. module_name (str): Name of the module to add. """ main_logic.add_module_to_descriptor( - descriptor_file, module_name, host_type, loading_phase + descriptor_file, module_name, host_type, loading_phase, ) @@ -427,7 +426,7 @@ def add_module_to_descriptor(descriptor_file, module_name, host_type, loading_ph @run.command( - name="add_plugin_to_descriptor", help=command_help, short_help=command_help + name="add_plugin_to_descriptor", help=command_help, short_help=command_help, ) @click.option( "--is_enabled", @@ -443,18 +442,18 @@ def add_module_to_descriptor(descriptor_file, module_name, host_type, loading_ph dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), ) @click.argument("plugin_name", type=str) -def add_plugin_to_descriptor(descriptor_file, plugin_name, is_enabled): +def add_plugin_to_descriptor(descriptor_file: Path, plugin_name: str, is_enabled: bool) -> None: """ Arguments: descriptor_file (str): Path to the descriptor file to add the plugin to. plugin_name (str): Name of the plugin to add. """ main_logic.add_plugin_to_descriptor( - descriptor_file, plugin_name, is_enabled=is_enabled + descriptor_file, plugin_name, is_enabled=is_enabled, ) @@ -464,7 +463,7 @@ def add_plugin_to_descriptor(descriptor_file, plugin_name, is_enabled): @run.command( - name="remove_modules_from_descriptor", help=command_help, short_help=command_help + name="remove_modules_from_descriptor", help=command_help, short_help=command_help, ) @click.option( "--module_names", @@ -481,10 +480,10 @@ def add_plugin_to_descriptor(descriptor_file, plugin_name, is_enabled): dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), ) -def remove_modules_from_descriptor(descriptor_file, module_names): +def remove_modules_from_descriptor(descriptor_file: Path, module_names: list[str]) -> None: """ Arguments: descriptor_file (str): Path to the descriptor file to remove the modules from. @@ -498,7 +497,7 @@ def remove_modules_from_descriptor(descriptor_file, module_names): @run.command( - name="remove_plugins_from_descriptor", help=command_help, short_help=command_help + name="remove_plugins_from_descriptor", help=command_help, short_help=command_help, ) @click.option( "--plugin_names", @@ -515,10 +514,10 @@ def remove_modules_from_descriptor(descriptor_file, module_names): dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), ) -def remove_plugins_from_descriptor(descriptor_file, plugin_names): +def remove_plugins_from_descriptor(descriptor_file: Path, plugin_names: list[str]) -> None: """ Arguments: descriptor_file (str): Path to the descriptor file to remove the plugins from. @@ -527,7 +526,7 @@ def remove_plugins_from_descriptor(descriptor_file, plugin_names): @run.command( - name="install_ue4ss", help=command_help, short_help=command_help + name="install_ue4ss", help=command_help, short_help=command_help, ) @click.option( "--release_tag", @@ -542,28 +541,28 @@ def remove_plugins_from_descriptor(descriptor_file, plugin_names): dir_okay=True, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), help="The directory containing the main executable for your game. Defaults to the dir that contains the game exe specified within the tempo config.", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=False, - help="Path to the settings JSON file", + help="Path to the tempo config file", ) -def install_ue4ss(release_tag, game_exe_directory, settings_json): - cache_dir = os.path.normpath(f'{cache.get_cache_dir()}/lazy_cache/ue4ss/{release_tag}') - os.makedirs(cache_dir, exist_ok=True) +def install_ue4ss(release_tag: str, game_exe_directory: Path, config_file: Path) -> None: + cache_dir = Path(f'{manager.tools_cache.get_download_dir()}/lazy_cache/ue4ss/{release_tag}') + cache_dir.mkdir(parents=True, exist_ok=True) ue4ss.install_ue4ss_to_dir( - cache_dir, - os.path.dirname(str(settings.get_game_exe_path())), - release_tag + str(cache_dir), + str(game_exe_directory), + release_tag, ) diff --git a/src/tempo_cli/commands/toml.py b/src/tempo_cli/commands/toml.py index b5f70c2b..9defecfc 100644 --- a/src/tempo_cli/commands/toml.py +++ b/src/tempo_cli/commands/toml.py @@ -1,4 +1,4 @@ -import pathlib +from pathlib import Path import tomlkit import rich_click as click @@ -6,26 +6,26 @@ @click.group() -def toml(): +def toml() -> None: """Toml related commands""" command_help_add_toml = "Add an entry to a TOML file." @toml.command( - name="add", help=command_help_add_toml, short_help=command_help_add_toml + name="add", help=command_help_add_toml, short_help=command_help_add_toml, ) @click.option( "--toml_path", help="Path to the TOML file.", - type=click.Path(exists=True, resolve_path=True, path_type=pathlib.Path), + type=click.Path(exists=True, resolve_path=True, path_type=Path), required=True, ) @click.option("--key", help="Key to add to the TOML file.", type=str, required=True) @click.option( - "--value", help="Value to associate with the key.", type=str, required=True + "--value", help="Value to associate with the key.", type=str, required=True, ) -def add(toml_path, key, value): +def add(toml_path: Path, key: str, value: str) -> None: with toml_path.open("r+") as f: data = tomlkit.load(f) data[key] = value @@ -46,13 +46,13 @@ def add(toml_path, key, value): @click.option( "--toml_path", help="Path to the TOML file.", - type=click.Path(exists=True, resolve_path=True, path_type=pathlib.Path), + type=click.Path(exists=True, resolve_path=True, path_type=Path), required=True, ) @click.option( - "--key", help="Key to remove from the TOML file.", type=str, required=True + "--key", help="Key to remove from the TOML file.", type=str, required=True, ) -def remove(toml_path, key): +def remove(toml_path: Path, key: str) -> None: with toml_path.open("r+") as f: data = tomlkit.load(f) if key in data: diff --git a/src/tempo_cli/commands/tool.py b/src/tempo_cli/commands/tool.py index 49adff52..72fc7f75 100644 --- a/src/tempo_cli/commands/tool.py +++ b/src/tempo_cli/commands/tool.py @@ -1,14 +1,26 @@ -import os -import pathlib +from typing import Callable +from importlib.metadata import entry_points import rich_click as click from tempo_core import main_logic +from tempo_core.manager import tools_cache +from tempo_binary_tool_manager import manager + + +def load_external_tools() -> list[manager.ToolInfo]: + eps = entry_points(group="tempo.tools") + tools = [] + for ep in eps: + tool_cls = ep.load() + tool = tool_cls(cache=tools_cache) + tools.append(tool) + return tools # ---- TOOL GROUP ---- @click.group() -def tool(): +def tool() -> None: """Tool related commands""" # attach tool group to main CLI @@ -16,7 +28,7 @@ def tool(): # ---- INSTALL GROUP ---- @click.group() -def install(): +def install() -> None: """Install programs""" @@ -33,16 +45,25 @@ def install(): # ---- INSTALL COMMANDS ---- + +inited_tools = [] +# for entry in manager.ToolInfo.registry: +# if issubclass(entry, manager.ToolInfo): +# entry = entry(cache=tools_cache) +# inited_tools.append(entry) + +inited_tools.extend(load_external_tools()) + tools = [ - ("fmodel", "Install FModel", main_logic.install_fmodel), - ("umodel", "Install Umodel", main_logic.install_umodel), - ("stove", "Install Stove", main_logic.install_stove), - ("spaghetti", "Install Spaghetti", main_logic.install_spaghetti), - ("uasset_gui", "Install UAssetGUI", main_logic.install_uasset_gui), - ("kismet_analyzer", "Install Kismet Analyzer", main_logic.install_kismet_analyzer), + ( + entry.tool_name, + f"Install {str(entry.tool_name)[0].upper()}{str(entry.tool_name)[1:].lower()}", + entry.ensure_tool_installed, + ) + for entry in inited_tools ] -for name, help_text, func in tools: +def make_command(name: str, help_text: str, func: Callable) -> Callable: @install.command(name=name, help=help_text, short_help=help_text) @click.option( "--run_after_install", @@ -51,5 +72,14 @@ def install(): type=bool, help="Should the installed program be run after installation.", ) - def _command(run_after_install, func=func): - func(run_after_install=run_after_install) + def _command(run_after_install: bool) -> None: + func() + + + return _command + + +for name, help_text, func in tools: + if not name: + raise RuntimeError('invalid name or no name') + make_command(name, help_text, func) diff --git a/src/tempo_cli/commands/uplugin.py b/src/tempo_cli/commands/uplugin.py index 83fc02dc..4369195a 100644 --- a/src/tempo_cli/commands/uplugin.py +++ b/src/tempo_cli/commands/uplugin.py @@ -1,12 +1,12 @@ import os -import pathlib +from pathlib import Path import rich_click as click from tempo_core import main_logic, file_io, settings, app_runner, data_structures @click.group() -def uplugin(): +def uplugin() -> None: """Uplugin related commands""" @@ -20,10 +20,10 @@ def uplugin(): help="Whether the plugin can contain content.", ) @click.option( - "--is_installed", default=True, type=bool, help="Whether the plugin is installed." + "--is_installed", default=True, type=bool, help="Whether the plugin is installed.", ) @click.option( - "--is_hidden", default=False, type=bool, help="Whether the plugin is hidden." + "--is_hidden", default=False, type=bool, help="Whether the plugin is hidden.", ) @click.option( "--no_code", @@ -32,17 +32,17 @@ def uplugin(): help="Whether the plugin should contain code.", ) @click.option( - "--category", default="Modding", type=str, help="Category for the plugin." + "--category", default="Modding", type=str, help="Category for the plugin.", ) @click.option( - "--created_by", default="", type=str, help="Name of the creator of the plugin." + "--created_by", default="", type=str, help="Name of the creator of the plugin.", ) @click.option( - "--created_by_url", default="", type=str, help="URL of the creator of the plugin." + "--created_by_url", default="", type=str, help="URL of the creator of the plugin.", ) @click.option("--description", default="", type=str, help="Description of the plugin.") @click.option( - "--docs_url", default="", type=str, help="Documentation URL for the plugin." + "--docs_url", default="", type=str, help="Documentation URL for the plugin.", ) @click.option( "--editor_custom_virtual_path", @@ -71,33 +71,33 @@ def uplugin(): @click.option("--support_url", default="", type=str, help="Support URL for the plugin.") @click.option("--version", default=1.0, type=float, help="Version of the plugin.") @click.option( - "--version_name", default="", type=str, help="Version name of the plugin." + "--version_name", default="", type=str, help="Version name of the plugin.", ) @click.argument( "plugins_directory", - type=click.Path(exists=False, resolve_path=True, path_type=pathlib.Path), + type=click.Path(exists=False, resolve_path=True, path_type=Path), ) @click.argument("plugin_name", type=str) def generate( - plugins_directory, - plugin_name, - can_contain_content, - is_installed, - is_hidden, - no_code, - category, - created_by, - created_by_url, - description, - docs_url, - editor_custom_virtual_path, - enabled_by_default, - engine_major_version, - engine_minor_version, - support_url, - version, - version_name, -): + plugins_directory: Path, + plugin_name: str, + can_contain_content: bool, + is_installed: bool, + is_hidden: bool, + no_code: bool, + category: str, + created_by: str, + created_by_url: str, + description: str, + docs_url: str, + editor_custom_virtual_path: str, + enabled_by_default: bool, + engine_major_version: int, + engine_minor_version: int, + support_url: str, + version: float, + version_name: str, +) -> None: """ Arguments: plugins_directory (str): Path to the plugins directory, mainly for use with Uproject plugins folder, and engine plugins folder. @@ -137,12 +137,12 @@ def generate( dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="uplugin_paths: A path to a uplugin to delete, can be specified multiple times.", ) -def remove(uplugin_paths): +def remove(uplugin_paths: list[Path]) -> None: main_logic.remove_uplugins(uplugin_paths) @@ -150,14 +150,14 @@ def remove(uplugin_paths): @uplugin.command(name="build", help=command_help, short_help=command_help) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=False, help="Path to the settings JSON file", @@ -171,7 +171,7 @@ def remove(uplugin_paths): dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=False, help="A path to a uplugin to build. Can be specified multiple times.", @@ -192,7 +192,7 @@ def remove(uplugin_paths): dir_okay=True, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), ) @click.option( @@ -200,7 +200,7 @@ def remove(uplugin_paths): multiple=True, type=str, help="A target platform to target, can be specified multiple times (e.g. Win64).", - default=['Win64'] + default=['Win64'], ) @click.option( "--no_host_platform", @@ -231,45 +231,49 @@ def remove(uplugin_paths): help="Zips the compiled uplugin(s) into the output directory", ) def build( - settings_json, - uplugin_names, - uplugin_paths, - output_directory, - target_platforms, - no_host_platform, - strict_includes, - unversioned, - zip -): + settings_config: Path, + uplugin_names: list[str], + uplugin_paths: list[Path], + output_directory: Path, + target_platforms: list[str], + no_host_platform: bool, + strict_includes: bool, + unversioned: bool, + zip: bool, # noqa +) -> None: + uproject_file = settings.get_uproject_file() + if not uproject_file: + raise FileNotFoundError('was unable to locate the uproject file') + unreal_engine_dir = settings.get_unreal_engine_dir() final_uplugin_paths = [] for uplugin_path in uplugin_paths: - if os.path.isfile(uplugin_path): + if uplugin_path.is_file(): final_uplugin_paths.append(uplugin_path) for uplugin_name in uplugin_names: - potential_path = os.path.normpath( - f"{os.path.dirname(str(settings.get_uproject_file()))}/Plugins/{uplugin_name}/{uplugin_name}.uplugin" + potential_path = Path( + f"{uproject_file.parent}/Plugins/{uplugin_name}/{uplugin_name}.uplugin", ) - if os.path.isfile(potential_path): + if potential_path.is_file(): final_uplugin_paths.append(potential_path) continue - engine_plugins_dir = os.path.normpath( - f"{settings.get_unreal_engine_dir()}/Engine/Plugins" + engine_plugins_dir = Path( + f"{unreal_engine_dir}/Engine/Plugins", ) uplugin_files = file_io.filter_by_extension( file_io.get_files_in_tree(engine_plugins_dir), - "uplugin" + "uplugin", ) matching_plugin = next( ( path for path in uplugin_files - if os.path.basename(path) == f"{uplugin_name}.uplugin" + if path.name == f"{uplugin_name}.uplugin" ), - None + None, ) if matching_plugin: @@ -280,17 +284,17 @@ def build( for uplugin_path in list(set(final_uplugin_paths)): target_platform_string = '+'.join(set(target_platforms)) - automation_tool = os.path.normpath(f'{settings.get_unreal_engine_dir()}/Engine/Build/BatchFiles/RunUAT.{file_io.get_platform_wrapper_extension()}') + automation_tool = Path(f'{unreal_engine_dir}/Engine/Build/BatchFiles/RunUAT.{file_io.get_platform_wrapper_extension()}') if output_directory: package_path = output_directory else: - package_path = os.path.normpath(f'{os.path.dirname(uplugin_path)}/Build') # output to the Build directory inside the directory where the uplugin file is located + package_path = Path(uplugin_path.parent / 'Build') # output to the Build directory inside the directory where the uplugin file is located args = [ 'BuildPlugin', f'-Plugin="{uplugin_path}"', f'-Package="{package_path}"', '-Rocket', - f'TargetPlatform={target_platform_string}' + f'TargetPlatform={target_platform_string}', ] if no_host_platform: args.append('-NoHostPlatform') @@ -302,13 +306,13 @@ def build( app_runner.run_app( exe_path=automation_tool, exec_mode=exec_mode, - args=args + args=args, ) if zip: if output_directory: - uplugin = file_io.filter_by_extension(file_io.get_files_in_tree(str(package_path)), 'uplugin')[0] - plugin_name = os.path.splitext(os.path.basename(uplugin))[0] - zip_name = os.path.normpath(f"{plugin_name}.zip") + uplugin = file_io.filter_by_extension(file_io.get_files_in_tree(package_path), 'uplugin')[0] + plugin_name = Path(uplugin).stem + zip_name = f"{plugin_name}.zip" file_io.zip_directory_tree(package_path, package_path, zip_name) else: - file_io.zip_directory_tree(package_path, package_path, os.path.normpath(f'{os.path.basename(os.path.dirname(package_path))}.zip')) + file_io.zip_directory_tree(package_path, package_path, package_path.parent.name.with_suffix('.zip')) diff --git a/src/tempo_cli/commands/uproject.py b/src/tempo_cli/commands/uproject.py index 8866c4b0..7dbb2363 100644 --- a/src/tempo_cli/commands/uproject.py +++ b/src/tempo_cli/commands/uproject.py @@ -1,11 +1,12 @@ -import pathlib +from pathlib import Path +from pathlib import Path import rich_click as click from tempo_core import main_logic @click.group() -def uproject(): +def uproject() -> None: """Uproject related commands""" @@ -34,7 +35,7 @@ def uproject(): help="Minor Unreal Engine version for the project. Example: the 27 in 4.27.", ) @click.option( - "--category", default="Modding", type=str, help="Category for the uproject." + "--category", default="Modding", type=str, help="Category for the uproject.", ) @click.option( "--description", @@ -50,17 +51,17 @@ def uproject(): ) @click.argument( "project_file", - type=click.Path(exists=False, resolve_path=True, path_type=pathlib.Path), + type=click.Path(exists=False, resolve_path=True, path_type=Path), ) def generate( - project_file, - file_version, - engine_major_association, - engine_minor_association, - category, - description, - ignore_safety_checks, -): + project_file: Path, + file_version: int, + engine_major_association: int, + engine_minor_association: int, + category: str, + description: str, + ignore_safety_checks: bool, +) -> None: """ Arguments: project_file (str): Path to generate the project file at. @@ -87,19 +88,19 @@ def generate( short_help=command_help, ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", ) -def resave_packages_and_fix_up_redirectors(settings_json): +def resave_packages_and_fix_up_redirectors(settings_config: Path) -> None: main_logic.resave_packages_and_fix_up_redirectors() command_help = "Builds the uproject specified within the settings JSON" @@ -112,19 +113,19 @@ def resave_packages_and_fix_up_redirectors(settings_json): help="Will close engine instances at the start and open at the end of the command process", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", ) -def build(settings_json, toggle_engine): +def build(settings_config: Path, toggle_engine: bool) -> None: main_logic.build(toggle_engine=toggle_engine) @@ -140,19 +141,19 @@ def build(settings_json, toggle_engine): help="Will close engine instances at the start and open at the end of the command process", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", ) -def cook(settings_json, toggle_engine): +def cook(settings_config: Path, toggle_engine: bool) -> None: main_logic.cook(toggle_engine=toggle_engine) @@ -175,17 +176,17 @@ def cook(settings_json, toggle_engine): help="Whether or not to use symlinks to save time with file operations", ) @click.option( - "--settings_json", + "--config-file", type=click.Path( exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True, - path_type=pathlib.Path, + path_type=Path, ), required=True, help="Path to the settings JSON file", ) -def package(settings_json, toggle_engine, use_symlinks): +def package(settings_config: Path, toggle_engine: bool, use_symlinks: bool) -> None: main_logic.package(toggle_engine=toggle_engine, use_symlinks=use_symlinks) diff --git a/src/tempo_cli/commitizen/data_structures.py b/src/tempo_cli/commitizen/data_structures.py index 9cc972e7..037adea2 100644 --- a/src/tempo_cli/commitizen/data_structures.py +++ b/src/tempo_cli/commitizen/data_structures.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import Type, Any +from typing import Type, TypeVar class CommitizenVersionSchemeOption(Enum): @@ -11,18 +11,13 @@ class CommitizenVersionSchemeOption(Enum): PEP440 = 'pep440' -def get_enum_from_val(enum_cls: Type[Enum], value: Any) -> Enum: - for entry in enum_cls: - if entry.value == value: - return entry - raise ValueError(f"{value} is not a valid value for {enum_cls.__name__}") - +E = TypeVar("E", bound=Enum) -def get_enum_from_val_loose(enum_cls: Type[Enum], value: Any) -> Enum | None: +def get_enum_from_val[E: Enum](enum_cls: type[E], value: object) -> E: for entry in enum_cls: if entry.value == value: return entry - return None + raise ValueError(f"{value} is not a valid value for {enum_cls.__name__}") def get_enum_strings_from_enum(enum_cls: Type[Enum]) -> list[str]: diff --git a/src/tempo_cli/data_structures.py b/src/tempo_cli/data_structures.py index 3acc2bfc..1bc6037c 100644 --- a/src/tempo_cli/data_structures.py +++ b/src/tempo_cli/data_structures.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import Type, Any +from typing import Type, TypeVar class RichColorSystem(Enum): @@ -14,18 +14,13 @@ class RichColorSystem(Enum): NONE = 'none' -def get_enum_from_val(enum_cls: Type[Enum], value: Any) -> Enum: - for entry in enum_cls: - if entry.value == value: - return entry - raise ValueError(f"{value} is not a valid value for {enum_cls.__name__}") - +E = TypeVar("E", bound=Enum) -def get_enum_from_val_loose(enum_cls: Type[Enum], value: Any) -> Enum | None: +def get_enum_from_val[E: Enum](enum_cls: type[E], value: object) -> E: for entry in enum_cls: if entry.value == value: return entry - return None + raise ValueError(f"{value} is not a valid value for {enum_cls.__name__}") def get_enum_strings_from_enum(enum_cls: Type[Enum]) -> list[str]: diff --git a/src/tempo_cli/file_io.py b/src/tempo_cli/file_io.py index 625ea8e8..4c8d8595 100644 --- a/src/tempo_cli/file_io.py +++ b/src/tempo_cli/file_io.py @@ -1,38 +1,42 @@ import os import zipfile import requests +from pathlib import Path from tempo_core import logger -def download_and_extract_zip(url: str, output_dir: str): - os.makedirs(output_dir, exist_ok=True) +default_output_directory = Path.cwd() - zip_path = os.path.join(output_dir, "easy_scripts.zip") + +def download_and_extract_zip(url: str, output_dir: Path) -> None: + output_dir.mkdir(parents=True, exist_ok=True) + + zip_path = Path(output_dir / "easy_scripts.zip") with requests.get(url, stream=True) as r: r.raise_for_status() - with open(zip_path, "wb") as f: + with Path.open(zip_path, "wb") as f: for chunk in r.iter_content(chunk_size=8192): f.write(chunk) with zipfile.ZipFile(zip_path, "r") as zip_ref: zip_ref.extractall(output_dir) - os.remove(zip_path) + zip_path.unlink() -def replace_text_in_file(file_path, old_text, new_text): +def replace_text_in_file(file_path: Path, old_text: str, new_text: str) -> None: """ Reads a file, replaces all occurrences of old_text with new_text, and writes the changes back to the file. """ - with open(file_path, 'r') as file: + with Path.open(file_path, 'r') as file: file_data = file.read() file_data = file_data.replace(old_text, new_text) - with open(file_path, 'w') as file: + with Path.open(file_path, 'w') as file: file.write(file_data) logger.log_message(f"Text replacement completed successfully in {file_path}") @@ -41,20 +45,23 @@ def replace_text_in_file(file_path, old_text, new_text): def download_files_from_github_repo( repo_url: str, repo_branch: str = "master", - file_paths: list[str] = [], - output_directory: str = os.getcwd(), -): + file_paths: list[Path] | None = None, + output_directory: Path = default_output_directory, +) -> None: + if file_paths is None: + file_paths = [] + try: parts = repo_url.strip("/").split("/") user, repo = parts[-2], parts[-1] - except IndexError: - raise ValueError("Invalid GitHub repository URL") + except IndexError as err: + raise ValueError("Invalid GitHub repository URL") from err for file_path in file_paths: raw_url = ( f"https://raw.githubusercontent.com/{user}/{repo}/{repo_branch}/{file_path}" ) - local_file_path = os.path.join(output_directory, file_path) + local_file_path = Path(output_directory / file_path) try: response = requests.get(raw_url) @@ -63,8 +70,8 @@ def download_files_from_github_repo( logger.log_message(f"Failed to download {file_path}: {e}") continue - os.makedirs(os.path.dirname(local_file_path), exist_ok=True) - with open(local_file_path, "wb") as f: + local_file_path.parent.mkdir(parents=True, exist_ok=True) + with Path.open(local_file_path, "wb") as f: f.write(response.content) logger.log_message(f"Downloaded: {file_path} to {local_file_path}") diff --git a/src/tempo_cli/main.py b/src/tempo_cli/main.py index 1dcb364f..1498f6e9 100644 --- a/src/tempo_cli/main.py +++ b/src/tempo_cli/main.py @@ -8,16 +8,16 @@ from tempo_core.console import console -def _rich_build_prompt(prompt_text: str, fg=log_info.LOG_INFO['default_color'], bg=log_info.LOG_INFO['background_color']): +def _rich_build_prompt(prompt_text: str, fg=log_info.LOG_INFO['default_color'], bg=log_info.LOG_INFO['background_color']) -> str: # noqa style = Style(color=f"rgb{fg}", bgcolor=f"rgb{bg}") rich_text = Text(prompt_text, style=style) console.print(rich_text, end="") return "" -def styled_build_prompt(text, prompt_suffix, show_default, default, show_choices, type_): +def styled_build_prompt(text, prompt_suffix, show_default, default, show_choices, type_) -> str: # noqa prompt = _original_build_prompt( - text, prompt_suffix, show_default, default, show_choices, type_ + text, prompt_suffix, show_default, default, show_choices, type_, ) _rich_build_prompt(prompt) return "" @@ -26,5 +26,5 @@ def styled_build_prompt(text, prompt_suffix, show_default, default, show_choices click.termui._build_prompt = styled_build_prompt # ty: ignore -def main(): +def main() -> None: cli.cli(windows_expand_args=False) diff --git a/src/tempo_cli/unreal.py b/src/tempo_cli/unreal.py index 6b771f3d..37ad1e88 100644 --- a/src/tempo_cli/unreal.py +++ b/src/tempo_cli/unreal.py @@ -1,12 +1,13 @@ import json +from pathlib import Path from tempo_core import file_io -def get_unreal_engine_version(engine_path: str) -> str: - version_file_path = f"{engine_path}/Engine/Build/Build.version" +def get_unreal_engine_version(engine_path: Path) -> str: + version_file_path = Path(f"{engine_path}/Engine/Build/Build.version") file_io.check_path_exists(version_file_path) - with open(version_file_path) as f: + with Path.open(version_file_path) as f: version_info = json.load(f) unreal_engine_major_version = version_info.get("MajorVersion", 0) unreal_engine_minor_version = version_info.get("MinorVersion", 0) diff --git a/src/tempo_cli/validators.py b/src/tempo_cli/validators.py index 52508e6f..02321352 100644 --- a/src/tempo_cli/validators.py +++ b/src/tempo_cli/validators.py @@ -1,46 +1,47 @@ import os +from pathlib import Path -def file_exists_validator(path: str): +def file_exists_validator(path: str) -> str | bool: if not path: return "Path cannot be empty." # Strip quotes and whitespace path = path.strip().strip('"').strip("'") - norm_path = os.path.normpath(path) + norm_path = Path(path) - if not os.path.isfile(norm_path): + if not norm_path.is_file(): return f"File does not exist: {norm_path}" return True -def dir_exists_validator(path: str): +def dir_exists_validator(path: str) -> str | bool: if not path: return "Path cannot be empty." # Strip quotes and whitespace path = path.strip().strip('"').strip("'") - norm_path = os.path.normpath(path) + norm_path = Path(path) - if not os.path.isdir(norm_path): + if not norm_path.is_dir(): return f"Directory does not exist: {norm_path}" return True -def exe_exists_validator(path: str): +def exe_exists_validator(path: str) -> str | bool: if not path: return "Path cannot be empty." # Strip quotes and whitespace path = path.strip().strip('"').strip("'") - norm_path = os.path.normpath(path) + norm_path = Path(path) - if not os.path.isfile(norm_path): + if not norm_path.is_file(): return f"File does not exist: {norm_path}" - if not norm_path.lower().endswith(".exe"): + if not str(norm_path).lower().endswith(".exe"): return f"File is not an executable (.exe): {norm_path}" return True diff --git a/uv.lock b/uv.lock index d2b2eeaa..dd28252a 100644 --- a/uv.lock +++ b/uv.lock @@ -31,25 +31,24 @@ wheels = [ [[package]] name = "backrefs" -version = "6.2" +version = "7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/a6/e325ec73b638d3ede4421b5445d4a0b8b219481826cc079d510100af356c/backrefs-6.2.tar.gz", hash = "sha256:f44ff4d48808b243b6c0cdc6231e22195c32f77046018141556c66f8bab72a49", size = 7012303, upload-time = "2026-02-16T19:10:15.828Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/a7dd63622beef68cc0d3c3c36d472e143dd95443d5ebf14cd1a5b4dfbf11/backrefs-7.0.tar.gz", hash = "sha256:4989bb9e1e99eb23647c7160ed51fb21d0b41b5d200f2d3017da41e023097e82", size = 7012453, upload-time = "2026-04-28T16:28:04.215Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/39/3765df263e08a4df37f4f43cb5aa3c6c17a4bdd42ecfe841e04c26037171/backrefs-6.2-py310-none-any.whl", hash = "sha256:0fdc7b012420b6b144410342caeb8adc54c6866cf12064abc9bb211302e496f8", size = 381075, upload-time = "2026-02-16T19:10:04.322Z" }, - { url = "https://files.pythonhosted.org/packages/0f/f0/35240571e1b67ffb19dafb29ab34150b6f59f93f717b041082cdb1bfceb1/backrefs-6.2-py311-none-any.whl", hash = "sha256:08aa7fae530c6b2361d7bdcbda1a7c454e330cc9dbcd03f5c23205e430e5c3be", size = 392874, upload-time = "2026-02-16T19:10:06.314Z" }, - { url = "https://files.pythonhosted.org/packages/e3/63/77e8c9745b4d227cce9f5e0a6f68041278c5f9b18588b35905f5f19c1beb/backrefs-6.2-py312-none-any.whl", hash = "sha256:c3f4b9cb2af8cda0d87ab4f57800b57b95428488477be164dd2b47be54db0c90", size = 398787, upload-time = "2026-02-16T19:10:08.274Z" }, - { url = "https://files.pythonhosted.org/packages/c5/71/c754b1737ad99102e03fa3235acb6cb6d3ac9d6f596cbc3e5f236705abd8/backrefs-6.2-py313-none-any.whl", hash = "sha256:12df81596ab511f783b7d87c043ce26bc5b0288cf3bb03610fe76b8189282b2b", size = 400747, upload-time = "2026-02-16T19:10:09.791Z" }, - { url = "https://files.pythonhosted.org/packages/af/75/be12ba31a6eb20dccef2320cd8ccb3f7d9013b68ba4c70156259fee9e409/backrefs-6.2-py314-none-any.whl", hash = "sha256:e5f805ae09819caa1aa0623b4a83790e7028604aa2b8c73ba602c4454e665de7", size = 412602, upload-time = "2026-02-16T19:10:12.317Z" }, - { url = "https://files.pythonhosted.org/packages/21/f8/d02f650c47d05034dcd6f9c8cf94f39598b7a89c00ecda0ecb2911bc27e9/backrefs-6.2-py39-none-any.whl", hash = "sha256:664e33cd88c6840b7625b826ecf2555f32d491800900f5a541f772c485f7cda7", size = 381077, upload-time = "2026-02-16T19:10:13.74Z" }, + { url = "https://files.pythonhosted.org/packages/d4/39/39a31d7eae729ea14ed10c3ccef79371197177b9355a86cb3525709e8502/backrefs-7.0-py310-none-any.whl", hash = "sha256:b57cd227ea556b0aed3dc9b8da4628db4eabc0402c6d7fcfc69283a93955f7e9", size = 380824, upload-time = "2026-04-28T16:27:55.647Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b5/9302644225ba7dfa934a2ff2b9c7bb85701313a90dddb3dfaf693fa5bae2/backrefs-7.0-py311-none-any.whl", hash = "sha256:a0fa7360c63509e9e077e174ef4e6d3c21c8db94189b9d957289ae6d794b9475", size = 392626, upload-time = "2026-04-28T16:27:57.42Z" }, + { url = "https://files.pythonhosted.org/packages/36/da/87912ddec6e06feffbaa3d7aa18fc6352bee2e8f1fee185d7d1690f8f4e8/backrefs-7.0-py312-none-any.whl", hash = "sha256:ca42ce6a49ace3d75684dfa9937f3373902a63284ecb385ce36d15e5dcb41c12", size = 398537, upload-time = "2026-04-28T16:27:58.913Z" }, + { url = "https://files.pythonhosted.org/packages/00/bb/90ba423612b6aa0adccc6b1874bcd4a9b44b660c0c16f346611e00f64ac3/backrefs-7.0-py313-none-any.whl", hash = "sha256:f2c52955d631b9e1ac4cd56209f0a3a946d592b98e7790e77699339ae01c102a", size = 400491, upload-time = "2026-04-28T16:28:00.928Z" }, + { url = "https://files.pythonhosted.org/packages/3e/5c/fb93d3092640a24dfb7bd7727a24016d7c01774ca013e60efd3f683c8002/backrefs-7.0-py314-none-any.whl", hash = "sha256:a6448b28180e3ca01134c9cf09dcebafad8531072e09903c5451748a05f24bc9", size = 412349, upload-time = "2026-04-28T16:28:02.412Z" }, ] [[package]] name = "certifi" -version = "2026.2.25" +version = "2026.6.17" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, + { url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" }, ] [[package]] @@ -127,14 +126,14 @@ wheels = [ [[package]] name = "click" -version = "8.3.2" +version = "8.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/57/75/31212c6bf2503fdf920d87fee5d7a86a2e3bcf444984126f13d8e4016804/click-8.3.2.tar.gz", hash = "sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5", size = 302856, upload-time = "2026-04-03T19:14:45.118Z" } +sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/20/71885d8b97d4f3dde17b1fdb92dbd4908b00541c5a3379787137285f602e/click-8.3.2-py3-none-any.whl", hash = "sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d", size = 108379, upload-time = "2026-04-03T19:14:43.505Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, ] [[package]] @@ -148,7 +147,7 @@ wheels = [ [[package]] name = "commitizen" -version = "4.13.10" +version = "4.16.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argcomplete" }, @@ -164,22 +163,22 @@ dependencies = [ { name = "termcolor" }, { name = "tomlkit" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/37/95/da2c71ed6a1c06836cdd4eb60a8b9e1bf05f4ce7029ab508081745171be9/commitizen-4.13.10.tar.gz", hash = "sha256:402b5bcd466be69ba79a3f380be6ba5b55ac658c7d2a93e82fc99668a6eb2673", size = 64106, upload-time = "2026-04-11T06:49:12.907Z" } +sdist = { url = "https://files.pythonhosted.org/packages/50/8a/4fccfa29c95536ac6dc98dc09a676cc2bce60d72f68b8e280278c6674669/commitizen-4.16.4.tar.gz", hash = "sha256:bb2fda50da381979e308d4a443d349598de2487d9e40d2e4f55a1f115ca38a8c", size = 66771, upload-time = "2026-06-22T06:32:50.06Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/3a/ad70b3c7dc3da1255668a9396429b1d820c15b74a501668158e4574c1edd/commitizen-4.13.10-py3-none-any.whl", hash = "sha256:95a281317990ac613501fdfe65745cec1fa4042bc5d003a72d332a74926e3039", size = 85746, upload-time = "2026-04-11T06:49:11.167Z" }, + { url = "https://files.pythonhosted.org/packages/2f/8f/496168ab853b325f62075e78ceb611d975ffc58116e4fabcca17725ac53c/commitizen-4.16.4-py3-none-any.whl", hash = "sha256:054e957491e3c897226a631e5bbeca5f5db772ce23412a5d588a4d7b9bb0bbc2", size = 88933, upload-time = "2026-06-22T06:32:48.615Z" }, ] [[package]] name = "cython" -version = "3.2.4" +version = "3.2.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/91/85/7574c9cd44b69a27210444b6650f6477f56c75fee1b70d7672d3e4166167/cython-3.2.4.tar.gz", hash = "sha256:84226ecd313b233da27dc2eb3601b4f222b8209c3a7216d8733b031da1dc64e6", size = 3280291, upload-time = "2026-01-04T14:14:14.473Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/48/89a4aa420f63ff35bd0081433e483a991234558fefb1ac4e4f1b62b059b9/cython-3.2.6.tar.gz", hash = "sha256:6509472a245ccdf5fd11637a4744a1edfd38debb1a48332a8f3fe4b07db725bd", size = 3286970, upload-time = "2026-06-24T17:17:50.521Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/4d/1eb0c7c196a136b1926f4d7f0492a96c6fabd604d77e6cd43b56a3a16d83/cython-3.2.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:64d7f71be3dd6d6d4a4c575bb3a4674ea06d1e1e5e4cd1b9882a2bc40ed3c4c9", size = 2970064, upload-time = "2026-01-04T14:15:08.567Z" }, - { url = "https://files.pythonhosted.org/packages/18/b5/1cfca43b7d20a0fdb1eac67313d6bb6b18d18897f82dd0f17436bdd2ba7f/cython-3.2.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:28e8075087a59756f2d059273184b8b639fe0f16cf17470bd91c39921bc154e0", size = 2960506, upload-time = "2026-01-04T14:15:16.733Z" }, - { url = "https://files.pythonhosted.org/packages/ee/d7/3bda3efce0c5c6ce79cc21285dbe6f60369c20364e112f5a506ee8a1b067/cython-3.2.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d4b4fd5332ab093131fa6172e8362f16adef3eac3179fd24bbdc392531cb82fa", size = 2971496, upload-time = "2026-01-04T14:15:25.038Z" }, - { url = "https://files.pythonhosted.org/packages/0a/8b/fd393f0923c82be4ec0db712fffb2ff0a7a131707b842c99bf24b549274d/cython-3.2.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:36bf3f5eb56d5281aafabecbaa6ed288bc11db87547bba4e1e52943ae6961ccf", size = 2875622, upload-time = "2026-01-04T14:15:39.749Z" }, - { url = "https://files.pythonhosted.org/packages/ff/fa/d3c15189f7c52aaefbaea76fb012119b04b9013f4bf446cb4eb4c26c4e6b/cython-3.2.4-py3-none-any.whl", hash = "sha256:732fc93bc33ae4b14f6afaca663b916c2fdd5dcbfad7114e17fb2434eeaea45c", size = 1257078, upload-time = "2026-01-04T14:14:12.373Z" }, + { url = "https://files.pythonhosted.org/packages/af/97/e7ea761888e78997683992f7592418fa29a5e375481ffdb9382822fd7606/cython-3.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18e6867912d30ed72193d13ddfa1ee2dee993ebe5cf7ec68d67df72999621ae2", size = 3000843, upload-time = "2026-06-24T17:18:30.934Z" }, + { url = "https://files.pythonhosted.org/packages/37/34/0495ff430b953bb42d8b86a567874691e98ee01ab70f28ba418585e2fa90/cython-3.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1620fa6bc06e08abaf186ca48b537866fec8e36d063bda40baa97b4c69464e30", size = 2977750, upload-time = "2026-06-24T17:18:38.907Z" }, + { url = "https://files.pythonhosted.org/packages/f8/32/6d867607ea2b2cddf90d31ddc97974ffd7fae51407a956a090a98b463ff0/cython-3.2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7a06b86bc7f319f73586d59b9fc7e03b737f4e64340520acb1aa72bacd2ddb4b", size = 2990484, upload-time = "2026-06-24T17:18:47.067Z" }, + { url = "https://files.pythonhosted.org/packages/30/7b/2d744baffbffd51727e4a7f2d6094630e4ca1e88a1399161e526de82b1f1/cython-3.2.6-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9a5e31253e54a93b05aaab46f5b3ee0002112cf040b09476921e75115fd40321", size = 2886759, upload-time = "2026-06-24T17:19:05.13Z" }, + { url = "https://files.pythonhosted.org/packages/57/ed/883d0784250c6a2e21e01bce01752faf1966c634a193e5afb25a67c02fff/cython-3.2.6-py3-none-any.whl", hash = "sha256:6b1e044b465b66eb242d5a415072a54c4b0325557e5e91fc134f003fdcc20150", size = 1257444, upload-time = "2026-06-24T17:17:40.19Z" }, ] [[package]] @@ -217,44 +216,40 @@ wheels = [ [[package]] name = "griffelib" -version = "2.0.2" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/82/74f4a3310cdabfbb10da554c3a672847f1ed33c6f61dd472681ce7f1fe67/griffelib-2.0.2.tar.gz", hash = "sha256:3cf20b3bc470e83763ffbf236e0076b1211bac1bc67de13daf494640f2de707e", size = 166461, upload-time = "2026-03-27T11:34:51.091Z" } +sdist = { url = "https://files.pythonhosted.org/packages/33/e4/8d187ea29c2e30b3a09505c567513077d6117861bde1fbd997a167f262ec/griffelib-2.1.0.tar.gz", hash = "sha256:762a186d2c6fd6794d4ea20d428d597ffb857cb56b66421651cbba15bdd5e813", size = 216234, upload-time = "2026-06-19T12:05:42.278Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/8c/c9138d881c79aa0ea9ed83cbd58d5ca75624378b38cee225dcf5c42cc91f/griffelib-2.0.2-py3-none-any.whl", hash = "sha256:925c857658fb1ba40c0772c37acbc2ab650bd794d9c1b9726922e36ea4117ea1", size = 142357, upload-time = "2026-03-27T11:34:46.275Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d3/5268aeabf2ad82658c4e2ff3a060648d0f02f3926cb53247c0e4d0dab49e/griffelib-2.1.0-py3-none-any.whl", hash = "sha256:cc7b3d2d2865ad0b909fcc38086e3f554b5ea7acbaa7bbb7ecaa3f5dfb7d9f00", size = 142560, upload-time = "2026-06-19T12:05:38.742Z" }, ] [[package]] name = "idna" -version = "3.11" +version = "3.18" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, ] [[package]] -name = "jinja2" -version = "3.1.6" +name = "importtime" +version = "1.0.3.2" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, + { url = "https://files.pythonhosted.org/packages/e3/64/b0758dcb2d6a7139e89ad7fa4026b4000ee5bf12d803d8cf3bbddb4543cb/importTime-1.0.3.2-py2.py3-none-any.whl", hash = "sha256:8c0518e8a09d735a88019839e181246309b99a88ca0d654c40ca7634adc9d0cd", size = 6640, upload-time = "2023-07-28T00:25:40.049Z" }, ] [[package]] -name = "linkify-it-py" -version = "2.1.0" +name = "jinja2" +version = "3.1.6" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "uc-micro-py" }, + { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/c9/06ea13676ef354f0af6169587ae292d3e2406e212876a413bf9eece4eb23/linkify_it_py-2.1.0.tar.gz", hash = "sha256:43360231720999c10e9328dc3691160e27a718e280673d444c38d7d3aaa3b98b", size = 29158, upload-time = "2026-03-01T07:48:47.683Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/de/88b3be5c31b22333b3ca2f6ff1de4e863d8fe45aaea7485f591970ec1d3e/linkify_it_py-2.1.0-py3-none-any.whl", hash = "sha256:0d252c1594ecba2ecedc444053db5d3a9b7ec1b0dd929c8f1d74dce89f86c05e", size = 19878, upload-time = "2026-03-01T07:48:46.098Z" }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] [[package]] @@ -280,19 +275,14 @@ wheels = [ [[package]] name = "markdown-it-py" -version = "4.0.0" +version = "4.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, -] - -[package.optional-dependencies] -linkify = [ - { name = "linkify-it-py" }, + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, ] [[package]] @@ -358,18 +348,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, ] -[[package]] -name = "mdit-py-plugins" -version = "0.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b2/fd/a756d36c0bfba5f6e39a1cdbdbfdd448dc02692467d83816dff4592a1ebc/mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6", size = 44655, upload-time = "2025-08-11T07:25:49.083Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl", hash = "sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f", size = 57205, upload-time = "2025-08-11T07:25:47.597Z" }, -] - [[package]] name = "mdurl" version = "0.1.2" @@ -490,25 +468,25 @@ wheels = [ [[package]] name = "mkdocstrings-python" -version = "2.0.3" +version = "2.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "griffelib" }, { name = "mkdocs-autorefs" }, { name = "mkdocstrings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/29/33/c225eaf898634bdda489a6766fc35d1683c640bffe0e0acd10646b13536d/mkdocstrings_python-2.0.3.tar.gz", hash = "sha256:c518632751cc869439b31c9d3177678ad2bfa5c21b79b863956ad68fc92c13b8", size = 199083, upload-time = "2026-02-20T10:38:36.368Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/b6/e858701499d57eee8b3fd8e78168083956c6683ddbe727b46758b19e1119/mkdocstrings_python-2.0.5.tar.gz", hash = "sha256:3a4d92556ad39637e88af94a5374213af9a8e3040c3824ceaed04b486c017594", size = 199578, upload-time = "2026-06-19T10:41:08.868Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl", hash = "sha256:0b83513478bdfd803ff05aa43e9b1fca9dd22bcd9471f09ca6257f009bc5ee12", size = 104779, upload-time = "2026-02-20T10:38:34.517Z" }, + { url = "https://files.pythonhosted.org/packages/d1/fc/10ab7e80650a9c9e8f4f1105f8c8e73567f88ed0c06ada589ab81d38687c/mkdocstrings_python-2.0.5-py3-none-any.whl", hash = "sha256:30c837bbff016549f659fcba6539ac351303f0fd7e713c89a040611072236e9d", size = 104951, upload-time = "2026-06-19T10:41:07.378Z" }, ] [[package]] name = "packaging" -version = "26.1" +version = "26.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" }, + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, ] [[package]] @@ -522,11 +500,11 @@ wheels = [ [[package]] name = "pathspec" -version = "1.0.4" +version = "1.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, ] [[package]] @@ -540,35 +518,35 @@ wheels = [ [[package]] name = "platformdirs" -version = "4.9.6" +version = "4.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", size = 31224, upload-time = "2026-05-28T03:32:53.587Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, + { url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743, upload-time = "2026-05-28T03:32:52.175Z" }, ] [[package]] name = "prek" -version = "0.3.9" +version = "0.4.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/ff/5b7a2a9c4fa3dd2ffc8b13a9ec22aa550deda5b39ab273f8e02863b12642/prek-0.3.9.tar.gz", hash = "sha256:f82b92d81f42f1f90a47f5fbbf492373e25ef1f790080215b2722dd6da66510e", size = 423801, upload-time = "2026-04-13T12:30:38.191Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/65/23866f43521d31173879aa74bb3a2df50ab7f3f74cdb4eaa31b8f446c7ca/prek-0.4.5.tar.gz", hash = "sha256:2be7bcf839de19a0144ed5a5aadf73bc5899cf6823bb1c58cf1d45ae389c201a", size = 482566, upload-time = "2026-06-15T11:36:48.299Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/08/c11a6b7834b461223763b6b1552f32c9199393685d52d555de621e900ee7/prek-0.3.9-py3-none-linux_armv6l.whl", hash = "sha256:3ed793d51bfaa27bddb64d525d7acb77a7c8644f549412d82252e3eb0b88aad8", size = 5337784, upload-time = "2026-04-13T12:30:46.044Z" }, - { url = "https://files.pythonhosted.org/packages/15/d9/974b02832a645c6411069c713e3191ce807f9962006da108e4727efd2fa1/prek-0.3.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:399c58400c0bd0b82a93a3c09dc1bfd88d8d0cfb242d414d2ed247187b06ead1", size = 5713864, upload-time = "2026-04-13T12:30:27.007Z" }, - { url = "https://files.pythonhosted.org/packages/40/e1/4ed14bef15eb30039a75177b0807ac007095a5a110284706ccf900a8d512/prek-0.3.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e2ea1ffb124e92f081b8e2ca5b5a623a733efb3be0c5b1f4b7ffe2ee17d1f20c", size = 5290437, upload-time = "2026-04-13T12:30:30.658Z" }, - { url = "https://files.pythonhosted.org/packages/67/80/d5c3015e9da161dede566bfeef41f098f92470613157daa4f7377ab08d58/prek-0.3.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:aaf639f95b7301639298311d8d44aad0d0b4864e9736083ad3c71ce9765d37ab", size = 5536208, upload-time = "2026-04-13T12:30:47.964Z" }, - { url = "https://files.pythonhosted.org/packages/c8/54/8cdc5eb1018437d7828740defd322e7a96459c02fc8961160c4120325313/prek-0.3.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff104863b187fa443ea8451ca55d51e2c6e94f99f00d88784b5c3c4c623f1ebe", size = 5251785, upload-time = "2026-04-13T12:30:39.78Z" }, - { url = "https://files.pythonhosted.org/packages/bd/e2/a5fc35a0fd3167224a000ca1b6235ecbdea0ac77e24af5979a75b0e6b5a4/prek-0.3.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:039ecaf87c63a3e67cca645ebd5bc5eb6aafa6c9d929e9a27b2921e7849d7ef9", size = 5668548, upload-time = "2026-04-13T12:30:24.914Z" }, - { url = "https://files.pythonhosted.org/packages/09/e8/a189ee79f401c259f66f8af587f899d4d5bfb04e0ca371bfd01e49871007/prek-0.3.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3bde2a3d045705095983c7f78ba04f72a7565fe1c2b4e85f5628502a254754ff", size = 6660927, upload-time = "2026-04-13T12:30:44.495Z" }, - { url = "https://files.pythonhosted.org/packages/a4/5a/54117316e98ff62a14911ad1488a3a0945530242a2ce3e92f7a40b6ccc02/prek-0.3.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28a0960a21543563e2c8e19aaad176cc8423a87aac3c914d0f313030d7a9244a", size = 5932244, upload-time = "2026-04-13T12:30:49.532Z" }, - { url = "https://files.pythonhosted.org/packages/a7/f9/e88d4361f59be7adeeb3a8a3819d69d286d86fe6f7606840af6734362675/prek-0.3.9-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:0dfb5d5171d7523271909246ee306b4dc3d5b63752e7dd7c7e8a8908fc9490d1", size = 5542139, upload-time = "2026-04-13T12:30:41.266Z" }, - { url = "https://files.pythonhosted.org/packages/11/1f/204837115087bb8d063bda754a7fe975428c5d5b6548c30dd749f8ab85d4/prek-0.3.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:82b791bd36c1430c84d3ae7220a85152babc7eaf00f70adcb961bd594e756ba3", size = 5392519, upload-time = "2026-04-13T12:30:32.603Z" }, - { url = "https://files.pythonhosted.org/packages/bd/00/de57b5795e670b6d38e7eda6d9ac6fd6d757ca22f725e5054b042104cd53/prek-0.3.9-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:6eac6d2f736b041118f053a1487abed468a70dd85a8688eaf87bb42d3dcecf20", size = 5222780, upload-time = "2026-04-13T12:30:36.576Z" }, - { url = "https://files.pythonhosted.org/packages/f5/14/0bc055c305d92980b151f2ec00c14d28fe94c6d51180ca07fded28771cbf/prek-0.3.9-py3-none-musllinux_1_1_i686.whl", hash = "sha256:5517e46e761367a3759b3168eabc120840ffbca9dfbc53187167298a98f87dc4", size = 5524310, upload-time = "2026-04-13T12:30:34.469Z" }, - { url = "https://files.pythonhosted.org/packages/b9/d1/eebc2b69be0de36cd84adbe0a0710f4deb468a90e30525be027d6db02d54/prek-0.3.9-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:92024778cf78683ca32687bb249ab6a7d5c33887b5ee1d1a9f6d0c14228f4cf3", size = 6043751, upload-time = "2026-04-13T12:30:29.101Z" }, - { url = "https://files.pythonhosted.org/packages/46/cb/be98c04e702cbc0b0328cd745ff4634ace69ad5a84461bde36f88a7be873/prek-0.3.9-py3-none-win32.whl", hash = "sha256:7f89c55e5f480f5d073769e319924ad69d4bf9f98c5cb46a83082e26e634c958", size = 5045940, upload-time = "2026-04-13T12:30:42.882Z" }, - { url = "https://files.pythonhosted.org/packages/a6/b6/b51771d69f6282e34edeb73f23d956da34f2cabbb5ba16ba175cc0a056f9/prek-0.3.9-py3-none-win_amd64.whl", hash = "sha256:7722f3372eaa83b147e70a43cb7b9fe2128c13d0c78d8a1cdbf2a8ec2ee071eb", size = 5435204, upload-time = "2026-04-13T12:30:51.482Z" }, - { url = "https://files.pythonhosted.org/packages/30/8a/f8a87c15b095460eccd67c8d89a086b7a37aac8d363f89544b8ce6ec653d/prek-0.3.9-py3-none-win_arm64.whl", hash = "sha256:0bced6278d6cc8a4b46048979e36bc9da034611dc8facd77ab123177b833a929", size = 5279552, upload-time = "2026-04-13T12:30:53.011Z" }, + { url = "https://files.pythonhosted.org/packages/f4/cb/a9eedf9a35ca6ec72f12af2b4392d7f757bb24863b7b7af4523f939cf3fa/prek-0.4.5-py3-none-linux_armv6l.whl", hash = "sha256:f7517774c72b001573520dc7111156779fd3e5b4452c11f09ff53c71a067e835", size = 5618105, upload-time = "2026-06-15T11:36:21.998Z" }, + { url = "https://files.pythonhosted.org/packages/30/a7/c96c06f17db7da0a57be2be4c229aa00b525bca8001c9c765663b339cbb7/prek-0.4.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aca9fa995536036a0171bcf7a4db96dc0a14f480054eda1d7d1c2e7739650993", size = 5972998, upload-time = "2026-06-15T11:36:41.12Z" }, + { url = "https://files.pythonhosted.org/packages/28/f1/721695355cdaa44be6f091e3a77fb9c72ed60289520f78b2f8c9a7197bdd/prek-0.4.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:66877ff21ae9d548f0f7e56fab8e65f1500a74a810e7749188c3f35a4a1b911b", size = 5525098, upload-time = "2026-06-15T11:36:30.127Z" }, + { url = "https://files.pythonhosted.org/packages/9b/1b/a334e1bb5361b49adf52b5ac7b6532018940f9f0f253437e8f43c3c1f7f3/prek-0.4.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:50697089a86a78d16f087c1912a2f3bc2bea82319a220fac52cc8e3ec9fc0426", size = 5793732, upload-time = "2026-06-15T11:36:35.745Z" }, + { url = "https://files.pythonhosted.org/packages/28/8c/aff94d276e91207a87cedff7cfefdd4aca20444137cca77bf53fffebe77a/prek-0.4.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:590427a42a3c1e5064487a0dc91167ae0c8a52168e77f574758ef9b138fcfd61", size = 5521719, upload-time = "2026-06-15T11:36:39.383Z" }, + { url = "https://files.pythonhosted.org/packages/4f/73/cfb0c5c909442050a8357e26233f7e511ba8e0d2f4b0bdc460065d62beb6/prek-0.4.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fd98b986767dafdb6b4305b563ee5a3a8f13bd3c78b98d708626815ea9f147f", size = 5922623, upload-time = "2026-06-15T11:36:18.063Z" }, + { url = "https://files.pythonhosted.org/packages/0a/ad/ff9d26551ba80d190bd08c6341176a5d56d4e6de9c2ebf077793d4adbb78/prek-0.4.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fccd11613ae92619d1ecda0ab3359ceebeb38898909ec84a8d383733d12158cc", size = 6722071, upload-time = "2026-06-15T11:36:43.086Z" }, + { url = "https://files.pythonhosted.org/packages/d3/43/11d1dfd66c919953fe89ae2fdedd4f413ee923883043816d35982177bb75/prek-0.4.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14109d37b33e5529db41a3539d4f8f72d295f6eeddede3964994d898b8cec05c", size = 6176454, upload-time = "2026-06-15T11:36:33.803Z" }, + { url = "https://files.pythonhosted.org/packages/d7/d4/9749f25c2e0ee5225f812457b888acef301e0ccce64bebcda2ac1d04abee/prek-0.4.5-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:40d262418105b2ede9836593a1927fc927cc8093c432e998640964102196996e", size = 5791133, upload-time = "2026-06-15T11:36:23.891Z" }, + { url = "https://files.pythonhosted.org/packages/c7/72/5e0344bab1eacf813a5b1b082cb4c6253930096166dad51c1cccee0a4f83/prek-0.4.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a586d14c3b852fdee1c3dcd0b9cb0915db9f9d054334b854fd9470bf68edf129", size = 5658098, upload-time = "2026-06-15T11:36:44.862Z" }, + { url = "https://files.pythonhosted.org/packages/be/a5/1f406e0362dd0f18ba09a562d50d7c04a70ac05d350b1ab6fba36ca3e9f0/prek-0.4.5-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:a8ed0d28f3e7790e4402a9324c386509066df6e67cc587f7406f9a245b97b7e8", size = 5498634, upload-time = "2026-06-15T11:36:31.828Z" }, + { url = "https://files.pythonhosted.org/packages/c7/df/b0cbf0fa527330188390b7b6c8d279cd5e509923262d0a6c5cc44bbdf103/prek-0.4.5-py3-none-musllinux_1_1_i686.whl", hash = "sha256:86f76bd3d2ecf6fd9034d75c62ff4c786eb11d0dd0a1f79bbb4343b023e12769", size = 5784840, upload-time = "2026-06-15T11:36:37.481Z" }, + { url = "https://files.pythonhosted.org/packages/9d/d7/977ee3c622c906677dd94187a00392ce2dd76035486b3a3b1b5a5267dd34/prek-0.4.5-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:e491a1a4641d91d8b03dcce5588397e76d2a5b432c9b0a6c70475972b4512ab4", size = 6300384, upload-time = "2026-06-15T11:36:27.602Z" }, + { url = "https://files.pythonhosted.org/packages/79/fa/43b1d761381dc1c7eeb8f2235c66e902970d4b2bff2dec0f02836c085769/prek-0.4.5-py3-none-win32.whl", hash = "sha256:7546989b2403c96137bd79d19ebfe21facb87266cefe819db2458c3b9b23f350", size = 5287935, upload-time = "2026-06-15T11:36:20.293Z" }, + { url = "https://files.pythonhosted.org/packages/f5/fe/59b5eb3124f5a4cc255a93857b9ab42402635b273f157e91de23bfa40e8f/prek-0.4.5-py3-none-win_amd64.whl", hash = "sha256:8b2ac9227504371d97338215b344184cb0b31ca94113515a3a90c509c6c5a707", size = 5682560, upload-time = "2026-06-15T11:36:25.865Z" }, + { url = "https://files.pythonhosted.org/packages/97/0e/589ff0eab9034909b1ec8654ee03483797305fb743b3554ce6140d82da9d/prek-0.4.5-py3-none-win_arm64.whl", hash = "sha256:646a86a1a082dbd99fed96314b1064f5644bb34c1f4037a63547a18e2160fb86", size = 5509019, upload-time = "2026-06-15T11:36:46.595Z" }, ] [[package]] @@ -622,7 +600,7 @@ wheels = [ [[package]] name = "pyinstaller" -version = "6.19.0" +version = "6.21.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "altgraph" }, @@ -633,74 +611,77 @@ dependencies = [ { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, { name = "setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c8/63/fd62472b6371d89dc138d40c36d87a50dc2de18a035803bbdc376b4ffac4/pyinstaller-6.19.0.tar.gz", hash = "sha256:ec73aeb8bd9b7f2f1240d328a4542e90b3c6e6fbc106014778431c616592a865", size = 4036072, upload-time = "2026-02-14T18:06:28.718Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/4d/ec706c3fcf39e26888c35b39615ff4d5865d184069666c47492cff1fbe50/pyinstaller-6.21.0.tar.gz", hash = "sha256:bb9fab705983e393a2d1cac77d6972513057ad800215fd861dc15ff5272e98fd", size = 4061519, upload-time = "2026-06-13T14:15:06.25Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/eb/23374721fecfa72677e79800921cb6aceefa6ba48574dc404f3f6c6c3be7/pyinstaller-6.19.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:4190e76b74f0c4b5c5f11ac360928cd2e36ec8e3194d437bf6b8648c7bc0c134", size = 1040563, upload-time = "2026-02-14T18:05:22.436Z" }, - { url = "https://files.pythonhosted.org/packages/cd/7e/dfd724b0b533f5aaec0ee5df406fe2319987ed6964480a706f85478b12ea/pyinstaller-6.19.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8bd68abd812d8a6ba33b9f1810e91fee0f325969733721b78151f0065319ca11", size = 735477, upload-time = "2026-02-14T18:05:27.143Z" }, - { url = "https://files.pythonhosted.org/packages/88/c9/ee3a4101c31f26344e66896c73c1fd6ed8282bf871473365b7f8674af406/pyinstaller-6.19.0-py3-none-manylinux2014_i686.whl", hash = "sha256:1ec54ef967996ca61dacba676227e2b23219878ccce5ee9d6f3aada7b8ed8abf", size = 747143, upload-time = "2026-02-14T18:05:31.488Z" }, - { url = "https://files.pythonhosted.org/packages/da/0a/fc77e9f861be8cf300ac37155f59cc92aff99b29f2ddd78546f563a5b5a6/pyinstaller-6.19.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:4ab2bb52e58448e14ddf9450601bdedd66800465043501c1d8f1cab87b60b122", size = 744849, upload-time = "2026-02-14T18:05:35.492Z" }, - { url = "https://files.pythonhosted.org/packages/6d/e3/6872e020ee758afe0b821663858492c10745608b07150e5e2c824a5b3e1c/pyinstaller-6.19.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:da6d5c6391ccefe73554b9fa29b86001c8e378e0f20c2a4004f836ba537eff63", size = 741590, upload-time = "2026-02-14T18:05:39.59Z" }, - { url = "https://files.pythonhosted.org/packages/53/60/b8db5f1a4b0fb228175f2ea0aa33f949adcc097fbe981cc524f9faf85777/pyinstaller-6.19.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a0fc5f6b3c55aa54353f0c74ffa59b1115433c1850c6f655d62b461a2ed6cbbe", size = 741448, upload-time = "2026-02-14T18:05:45.636Z" }, - { url = "https://files.pythonhosted.org/packages/6f/4d/63b0600f2694e9141b83129fbc1c488ec84d5a0770b1448ec154dcd0fee9/pyinstaller-6.19.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:e649ba6bd1b0b89b210ad92adb5fbdc8a42dd2c5ca4f72ef3a0bfec83a424b83", size = 740613, upload-time = "2026-02-14T18:05:49.726Z" }, - { url = "https://files.pythonhosted.org/packages/01/d4/e812ad36178093a0e9fd4b8127577748dd85b0cb71de912229dca21fd741/pyinstaller-6.19.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:481a909c8e60c8692fc60fcb1344d984b44b943f8bc9682f2fcdae305ad297e6", size = 740350, upload-time = "2026-02-14T18:05:54.093Z" }, - { url = "https://files.pythonhosted.org/packages/52/03/b2c2ee41fb8e10fd2a45d21f5ec2ef25852cfb978dbf762972eed59e3d63/pyinstaller-6.19.0-py3-none-win32.whl", hash = "sha256:3c5c251054fe4cfaa04c34a363dcfbf811545438cb7198304cd444756bc2edd2", size = 1324317, upload-time = "2026-02-14T18:06:00.085Z" }, - { url = "https://files.pythonhosted.org/packages/9c/d3/6d5e62b8270e2b53a6065e281b3a7785079b00e9019c8019952828dd1669/pyinstaller-6.19.0-py3-none-win_amd64.whl", hash = "sha256:b5bb6536c6560330d364d91522250f254b107cf69129d9cbcd0e6727c570be33", size = 1384894, upload-time = "2026-02-14T18:06:06.425Z" }, - { url = "https://files.pythonhosted.org/packages/81/65/458cd523308a101a22fd2742893405030cc24994cc74b1b767cecf137160/pyinstaller-6.19.0-py3-none-win_arm64.whl", hash = "sha256:c2d5a539b0bfe6159d5522c8c70e1c0e487f22c2badae0f97d45246223b798ea", size = 1325374, upload-time = "2026-02-14T18:06:12.804Z" }, + { url = "https://files.pythonhosted.org/packages/0c/4a/53cf98bf66daed012dc9cd78c8203f19a675d696f2fc12afcf8c5049a0e0/pyinstaller-6.21.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:327d132389f37912609e01be62810cf96b5aa95b613903e4b8692e0d12fb0eda", size = 1052350, upload-time = "2026-06-13T14:13:55.88Z" }, + { url = "https://files.pythonhosted.org/packages/30/83/b591295c352ef464c50b4c6ffff1c4f771d875c9e833f578d1b9f564f6b3/pyinstaller-6.21.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7071d4b094d5b40deeef5fa3d3b98a1b846087f7562b49209663d5f9281fe251", size = 748477, upload-time = "2026-06-13T14:14:00.327Z" }, + { url = "https://files.pythonhosted.org/packages/3d/8f/88fff4e403873b1e22286911350e75ff00db014aa08e57045da9d4328993/pyinstaller-6.21.0-py3-none-manylinux2014_i686.whl", hash = "sha256:6b6374d652107dd4a2eeece903ff82bb4045bb5e1006c5a158a6dcdbefe84bf2", size = 760877, upload-time = "2026-06-13T14:14:04.836Z" }, + { url = "https://files.pythonhosted.org/packages/8a/13/f0e48fbdfd1d05d948157121cea8b1b823dcb89efe6934b71fdd8bdb3f0f/pyinstaller-6.21.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:4e3108b3f02384560da70e39b8bf22b0ad597d02bd68a40d76ea91c1cfa00cad", size = 759194, upload-time = "2026-06-13T14:14:10.61Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d5/ea7878cf9924ed30d946d8288777424e6d069d94f5bde56b4d0890069664/pyinstaller-6.21.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:697532279f535ad572bda613db4f821540e235c7854ca6da4d3bf0373f4415ee", size = 754979, upload-time = "2026-06-13T14:14:15.226Z" }, + { url = "https://files.pythonhosted.org/packages/9f/09/51b8905714b733bac66dbc041a7821372d70d888d273ae474c4037d4202d/pyinstaller-6.21.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:605169523a6b5ace39f13dfbff21add9f2bc43df99c7daf9394fefb2c45e8b6f", size = 754812, upload-time = "2026-06-13T14:14:20.264Z" }, + { url = "https://files.pythonhosted.org/packages/4b/43/d77779439d8c6c2e27a77bcfbd1d5cc0f568ebb611bb472b11af81b5f177/pyinstaller-6.21.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5fa56746c1e76f93634d018502301378a2d0c382553d37d8c3c34ff436c12dd1", size = 753887, upload-time = "2026-06-13T14:14:25.268Z" }, + { url = "https://files.pythonhosted.org/packages/51/8f/c22df1f6837784ac349057ba693f08e7b1ca7a0e06f9c33c63bc6280007b/pyinstaller-6.21.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:42395ec76df8e8120c36b13339d9db8cab83e316a12839ee303cc00fc941bb74", size = 753779, upload-time = "2026-06-13T14:14:29.445Z" }, + { url = "https://files.pythonhosted.org/packages/c9/76/1ce8a27ce62ba8cf3a87c9ce6d575610f4e55d7cb0123e7512fc3f4b921a/pyinstaller-6.21.0-py3-none-win32.whl", hash = "sha256:c6b28d30d8fd99ce162ff3aab5013ed44dbfb747566b1f01b9bed7964d7c14e9", size = 1336462, upload-time = "2026-06-13T14:14:35.785Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fa/ca1d7e5257dd8566a9dfc0dfb02f8a8075eeb53d4b2d3c579f1276759042/pyinstaller-6.21.0-py3-none-win_amd64.whl", hash = "sha256:7fae06c494ce0ebfe6bd3055c0e409def884f63af2e3705d06bd431ad9237fc7", size = 1397487, upload-time = "2026-06-13T14:14:42.328Z" }, + { url = "https://files.pythonhosted.org/packages/dc/75/21b51523ce8d96629b71311775a0a65f5f5a872124ab0de33e5c848f8bff/pyinstaller-6.21.0-py3-none-win_arm64.whl", hash = "sha256:f13c95c9c03fb567217135919f93815c305813126780b0ed6e0123cb8acaf025", size = 1346094, upload-time = "2026-06-13T14:14:48.914Z" }, ] [[package]] name = "pyinstaller-hooks-contrib" -version = "2026.4" +version = "2026.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, - { name = "setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c7/fe/9278c29394bf69169febc21f96b4252c3ee7c8ec22c2fc545004bed47e71/pyinstaller_hooks_contrib-2026.4.tar.gz", hash = "sha256:766c281acb1ecc32e21c8c667056d7ebf5da0aabd5e30c219f9c2a283620eeaa", size = 173050, upload-time = "2026-03-31T14:10:51.188Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/5b/c9fe0db5e83ee1c39b2258fa21d23b15e1a60786b6c5990ee5074ead8bb6/pyinstaller_hooks_contrib-2026.6.tar.gz", hash = "sha256:bef5002c32f4f50bd55b005da12cff64eca8783e7eaf86a06a62410164bab725", size = 173354, upload-time = "2026-06-08T22:37:16.152Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/f4/035fb8c06deff827f540a9a4ed9122c54e5376fca3e42eddf0c263730775/pyinstaller_hooks_contrib-2026.4-py3-none-any.whl", hash = "sha256:1de1a5e49a878122010b88c7e295502bc69776c157c4a4dc78741a4e6178b00f", size = 455496, upload-time = "2026-03-31T14:10:49.867Z" }, + { url = "https://files.pythonhosted.org/packages/e7/31/f2d7343d8ed5f7c4678377886f6ce533e6eaaa131b252ce950114c2a7efa/pyinstaller_hooks_contrib-2026.6-py3-none-any.whl", hash = "sha256:fd13b8ac126b35361175edacd41a0d97080b75dd5f4b594ecefefff969509dd3", size = 457159, upload-time = "2026-06-08T22:37:14.722Z" }, ] [[package]] name = "pymdown-extensions" -version = "10.21.2" +version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/08/f1c908c581fd11913da4711ea7ba32c0eee40b0190000996bb863b0c9349/pymdown_extensions-10.21.2.tar.gz", hash = "sha256:c3f55a5b8a1d0edf6699e35dcbea71d978d34ff3fa79f3d807b8a5b3fa90fbdc", size = 853922, upload-time = "2026-03-29T15:01:55.233Z" } +sdist = { url = "https://files.pythonhosted.org/packages/47/67/f1e79672a5f91985577c7984c9709ca110e4fd37fe7fd167b60422e6ccc2/pymdown_extensions-11.0.tar.gz", hash = "sha256:8269cef0247f9e2d0a62fcea10860aba05c1cbab5470fd4b63230b96434dc589", size = 857049, upload-time = "2026-06-23T02:27:45.146Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/27/a2fc51a4a122dfd1015e921ae9d22fee3d20b0b8080d9a704578bf9deece/pymdown_extensions-10.21.2-py3-none-any.whl", hash = "sha256:5c0fd2a2bea14eb39af8ff284f1066d898ab2187d81b889b75d46d4348c01638", size = 268901, upload-time = "2026-03-29T15:01:53.244Z" }, + { url = "https://files.pythonhosted.org/packages/af/b6/1ae53367e28b9cffa3be7574e13fbe4589694272fd47710fbdbafd3d63c6/pymdown_extensions-11.0-py3-none-any.whl", hash = "sha256:fbc4acb641814fa9d17521bbd21a5240ef739a662f11c06330c4b78c93e954d6", size = 269415, upload-time = "2026-06-23T02:27:43.826Z" }, ] [[package]] name = "pyobjc-core" -version = "12.1" +version = "12.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/b6/d5612eb40be4fd5ef88c259339e6313f46ba67577a95d86c3470b951fce0/pyobjc_core-12.1.tar.gz", hash = "sha256:2bb3903f5387f72422145e1466b3ac3f7f0ef2e9960afa9bcd8961c5cbf8bd21", size = 1000532, upload-time = "2025-11-14T10:08:28.292Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b4/b1/729f7458a63758bd21716648a8abcd9a0c8f2d2e9897763c8a1a1c7fd31b/pyobjc_core-12.2.1.tar.gz", hash = "sha256:7a7b9b018402342cf32bf1956366896350fbe5c0478cb3ef59778f77abed7f07", size = 1063383, upload-time = "2026-06-19T16:19:39.357Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/5a/6b15e499de73050f4a2c88fff664ae154307d25dc04da8fb38998a428358/pyobjc_core-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:818bcc6723561f207e5b5453efe9703f34bc8781d11ce9b8be286bb415eb4962", size = 678335, upload-time = "2025-11-14T09:32:20.107Z" }, - { url = "https://files.pythonhosted.org/packages/f4/d2/29e5e536adc07bc3d33dd09f3f7cf844bf7b4981820dc2a91dd810f3c782/pyobjc_core-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:01c0cf500596f03e21c23aef9b5f326b9fb1f8f118cf0d8b66749b6cf4cbb37a", size = 677370, upload-time = "2025-11-14T09:33:05.273Z" }, - { url = "https://files.pythonhosted.org/packages/1b/f0/4b4ed8924cd04e425f2a07269943018d43949afad1c348c3ed4d9d032787/pyobjc_core-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:177aaca84bb369a483e4961186704f64b2697708046745f8167e818d968c88fc", size = 719586, upload-time = "2025-11-14T09:33:53.302Z" }, - { url = "https://files.pythonhosted.org/packages/25/98/9f4ed07162de69603144ff480be35cd021808faa7f730d082b92f7ebf2b5/pyobjc_core-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:844515f5d86395b979d02152576e7dee9cc679acc0b32dc626ef5bda315eaa43", size = 670164, upload-time = "2025-11-14T09:34:37.458Z" }, - { url = "https://files.pythonhosted.org/packages/62/50/dc076965c96c7f0de25c0a32b7f8aa98133ed244deaeeacfc758783f1f30/pyobjc_core-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:453b191df1a4b80e756445b935491b974714456ae2cbae816840bd96f86db882", size = 712204, upload-time = "2025-11-14T09:35:24.148Z" }, + { url = "https://files.pythonhosted.org/packages/8c/88/300ad283bed0c971c52dcac6f70113e138169d4ce6d856ddd03d16081e51/pyobjc_core-12.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a64232bb27ed101d4adc7d42b0e64a6d3331aac7bee7861c037a6777a163f10b", size = 6433347, upload-time = "2026-06-19T16:04:49.341Z" }, + { url = "https://files.pythonhosted.org/packages/3e/1e/b9b0ddffae66996b8779f1f7958adc9f21c13a0448cd3be8d7fe589b5b0f/pyobjc_core-12.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:af101222762665a4125157906cb4b23f5d5a63d3851d5e0504f72a1eaaa2cfd2", size = 6436004, upload-time = "2026-06-19T16:04:53.257Z" }, + { url = "https://files.pythonhosted.org/packages/8f/26/bd309ede07784c6e5fac4b440c90a5f72a66da7859ed303a9392fe8a5f3f/pyobjc_core-12.2.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:efe465e3ecc6fc73f7c7622620345d134a8d34564ab1c29d8247e45f4ed55071", size = 6687044, upload-time = "2026-06-19T16:04:57.42Z" }, + { url = "https://files.pythonhosted.org/packages/bd/8a/cfa4f56939d554dbb342ec6e5226a441e2f552bc2002a0ddf7705bb11bef/pyobjc_core-12.2.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:2b8fc0531c27277325e113ac00b8a72a82e6145f0a88175b9425d8de814ff69a", size = 6429289, upload-time = "2026-06-19T16:05:02.191Z" }, + { url = "https://files.pythonhosted.org/packages/42/74/446c89bc18103aaa4a00d1fb85ff8acace9a0dc3f362d9678ebf7571e275/pyobjc_core-12.2.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9bef500f979e22d54f9da3aaebf6a48f873234b324858bd69256055a318955c7", size = 6690181, upload-time = "2026-06-19T16:05:06.201Z" }, + { url = "https://files.pythonhosted.org/packages/99/c7/0121ee4c616af07ad2de8cd1a286f6978dc9a227eb58b7c2e875cb68a1df/pyobjc_core-12.2.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:047c226eeb58a2993ace5e8904e71cc9426ee20d064c617f8fbf32717d37093e", size = 6487078, upload-time = "2026-06-19T16:05:10.093Z" }, + { url = "https://files.pythonhosted.org/packages/b5/a8/cb9fcc150f97d0bf22a2028f88b24cc35949beb1bcc7b8bc5c17d4401677/pyobjc_core-12.2.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:1188613805336270279570467e4455b74cb6c0f60913ac74c917ee1c37cfaecb", size = 6733064, upload-time = "2026-06-19T16:05:14.313Z" }, ] [[package]] name = "pyobjc-framework-cocoa" -version = "12.1" +version = "12.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyobjc-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/a3/16ca9a15e77c061a9250afbae2eae26f2e1579eb8ca9462ae2d2c71e1169/pyobjc_framework_cocoa-12.1.tar.gz", hash = "sha256:5556c87db95711b985d5efdaaf01c917ddd41d148b1e52a0c66b1a2e2c5c1640", size = 2772191, upload-time = "2025-11-14T10:13:02.069Z" } +sdist = { url = "https://files.pythonhosted.org/packages/51/34/fbe38a204643aa4e1b91391cdce07a34da565a69171ebcad08de7438a556/pyobjc_framework_cocoa-12.2.1.tar.gz", hash = "sha256:b94b37fe5730e5ae1fb0052912cd174e6ec329b0bfba4a012ae5db1014b5864b", size = 3125751, upload-time = "2026-06-19T16:20:05.159Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/bf/ee4f27ec3920d5c6fc63c63e797c5b2cc4e20fe439217085d01ea5b63856/pyobjc_framework_cocoa-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:547c182837214b7ec4796dac5aee3aa25abc665757b75d7f44f83c994bcb0858", size = 384590, upload-time = "2025-11-14T09:41:17.336Z" }, - { url = "https://files.pythonhosted.org/packages/ad/31/0c2e734165abb46215797bd830c4bdcb780b699854b15f2b6240515edcc6/pyobjc_framework_cocoa-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5a3dcd491cacc2f5a197142b3c556d8aafa3963011110102a093349017705118", size = 384689, upload-time = "2025-11-14T09:41:41.478Z" }, - { url = "https://files.pythonhosted.org/packages/23/3b/b9f61be7b9f9b4e0a6db18b3c35c4c4d589f2d04e963e2174d38c6555a92/pyobjc_framework_cocoa-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:914b74328c22d8ca261d78c23ef2befc29776e0b85555973927b338c5734ca44", size = 388843, upload-time = "2025-11-14T09:42:05.719Z" }, - { url = "https://files.pythonhosted.org/packages/59/bb/f777cc9e775fc7dae77b569254570fe46eb842516b3e4fe383ab49eab598/pyobjc_framework_cocoa-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:03342a60fc0015bcdf9b93ac0b4f457d3938e9ef761b28df9564c91a14f0129a", size = 384932, upload-time = "2025-11-14T09:42:29.771Z" }, - { url = "https://files.pythonhosted.org/packages/58/27/b457b7b37089cad692c8aada90119162dfb4c4a16f513b79a8b2b022b33b/pyobjc_framework_cocoa-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6ba1dc1bfa4da42d04e93d2363491275fb2e2be5c20790e561c8a9e09b8cf2cc", size = 388970, upload-time = "2025-11-14T09:42:53.964Z" }, + { url = "https://files.pythonhosted.org/packages/f7/cf/1b3b32b2f28f66cc053c3438ef4e6df36a1591945bf05e7399da18d74553/pyobjc_framework_cocoa-12.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:28b9b8bab1c36efb94744786918752d0c1842f5fbb67e7d5ca97b5f736512080", size = 388113, upload-time = "2026-06-19T16:07:38.9Z" }, + { url = "https://files.pythonhosted.org/packages/cc/46/68e8e4d926a2f70fed0437047bc3f9fe08af8fe620d94d80656ebc3cfa9b/pyobjc_framework_cocoa-12.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3b74a78fa7803e547b32e5e8ec1b49987b52fe318383e793bc6cd49b80efbd9f", size = 388183, upload-time = "2026-06-19T16:07:40.483Z" }, + { url = "https://files.pythonhosted.org/packages/2e/f3/dfc9af4c9eb2e5389c860ad5ef252be9fe456db09f39d537555dc5057aa1/pyobjc_framework_cocoa-12.2.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:dc2eaca2f13c7bcd8e41e51a372e47825dea9dd3126108760eed7ba883d2945c", size = 392275, upload-time = "2026-06-19T16:07:42.078Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c8/b90baa8f3592eded79b4be98fb59d2b8dc16b62361e34292bd95806ebd9f/pyobjc_framework_cocoa-12.2.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:b386c324d64ae565c1f6b7dfb77be68f640a1c7c23caa6966ab661131f519561", size = 388357, upload-time = "2026-06-19T16:07:43.364Z" }, + { url = "https://files.pythonhosted.org/packages/98/d8/64a94651b9294702d55e748d94de30e25bc59d0784526be7643f4467eccd/pyobjc_framework_cocoa-12.2.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a6c584e2af0813cb2f6103b184e632665a26f58c1bd5b08ffd6e95a19c617f7b", size = 392404, upload-time = "2026-06-19T16:07:44.955Z" }, + { url = "https://files.pythonhosted.org/packages/5c/cc/26e8a7bf1f5e8caa38b7f80d486296f9fd3c97e71ad7e5444ef22e802758/pyobjc_framework_cocoa-12.2.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:b6023657b8d6cc049a21bd6b4752425f2f53c42f9f0b02d64c7608cc484bf103", size = 388589, upload-time = "2026-06-19T16:07:46.276Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f3/eedf743a303ea742b8e082afe3613fb4d6618bc1a48cf2568b004ce906f7/pyobjc_framework_cocoa-12.2.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:c685ccd8e266a07cf912a2c5a13b1f2eff2a868a1aff163b4801b4687bd425e1", size = 392691, upload-time = "2026-06-19T16:07:47.477Z" }, ] [[package]] @@ -796,7 +777,7 @@ wheels = [ [[package]] name = "requests" -version = "2.33.1" +version = "2.34.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -804,9 +785,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, ] [[package]] @@ -824,41 +805,41 @@ wheels = [ [[package]] name = "rich-click" -version = "1.9.7" +version = "1.9.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "rich" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/27/091e140ea834272188e63f8dd6faac1f5c687582b687197b3e0ec3c78ebf/rich_click-1.9.7.tar.gz", hash = "sha256:022997c1e30731995bdbc8ec2f82819340d42543237f033a003c7b1f843fc5dc", size = 74838, upload-time = "2026-01-31T04:29:27.707Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/ea/21e4867ea0ef881ffd4c0550fc21a061435e50d6324bcd034396633cbc18/rich_click-1.9.8.tar.gz", hash = "sha256:4008f921da88b5d91646c134ec881c1500e5a6b3f093e90e8f29400e09608371", size = 75363, upload-time = "2026-05-28T19:54:59.144Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/e5/d708d262b600a352abe01c2ae360d8ff75b0af819b78e9af293191d928e6/rich_click-1.9.7-py3-none-any.whl", hash = "sha256:2f99120fca78f536e07b114d3b60333bc4bb2a0969053b1250869bcdc1b5351b", size = 71491, upload-time = "2026-01-31T04:29:26.777Z" }, + { url = "https://files.pythonhosted.org/packages/6d/97/a87901aef6b7e7e4a34c6dd6cc17dca8594a592ef9d9dd765fca2b7facf7/rich_click-1.9.8-py3-none-any.whl", hash = "sha256:12873865396e6927835d4eabb1cc3996edcd65b7ac9b2391a29eca4f335a2f93", size = 72189, upload-time = "2026-05-28T19:54:57.867Z" }, ] [[package]] name = "ruff" -version = "0.15.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/8d/192f3d7103816158dfd5ea50d098ef2aec19194e6cbccd4b3485bdb2eb2d/ruff-0.15.11.tar.gz", hash = "sha256:f092b21708bf0e7437ce9ada249dfe688ff9a0954fc94abab05dcea7dcd29c33", size = 4637264, upload-time = "2026-04-16T18:46:26.58Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/1e/6aca3427f751295ab011828e15e9bf452200ac74484f1db4be0197b8170b/ruff-0.15.11-py3-none-linux_armv6l.whl", hash = "sha256:e927cfff503135c558eb581a0c9792264aae9507904eb27809cdcff2f2c847b7", size = 10607943, upload-time = "2026-04-16T18:46:05.967Z" }, - { url = "https://files.pythonhosted.org/packages/e7/26/1341c262e74f36d4e84f3d6f4df0ac68cd53331a66bfc5080daa17c84c0b/ruff-0.15.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7a1b5b2938d8f890b76084d4fa843604d787a912541eae85fd7e233398bbb73e", size = 10988592, upload-time = "2026-04-16T18:46:00.742Z" }, - { url = "https://files.pythonhosted.org/packages/03/71/850b1d6ffa9564fbb6740429bad53df1094082fe515c8c1e74b6d8d05f18/ruff-0.15.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d4176f3d194afbdaee6e41b9ccb1a2c287dba8700047df474abfbe773825d1cb", size = 10338501, upload-time = "2026-04-16T18:46:03.723Z" }, - { url = "https://files.pythonhosted.org/packages/f2/11/cc1284d3e298c45a817a6aadb6c3e1d70b45c9b36d8d9cce3387b495a03a/ruff-0.15.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b17c886fb88203ced3afe7f14e8d5ae96e9d2f4ccc0ee66aa19f2c2675a27e4", size = 10670693, upload-time = "2026-04-16T18:46:41.941Z" }, - { url = "https://files.pythonhosted.org/packages/ce/9e/f8288b034ab72b371513c13f9a41d9ba3effac54e24bfb467b007daee2ca/ruff-0.15.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:49fafa220220afe7758a487b048de4c8f9f767f37dfefad46b9dd06759d003eb", size = 10416177, upload-time = "2026-04-16T18:46:21.717Z" }, - { url = "https://files.pythonhosted.org/packages/85/71/504d79abfd3d92532ba6bbe3d1c19fada03e494332a59e37c7c2dabae427/ruff-0.15.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2ab8427e74a00d93b8bda1307b1e60970d40f304af38bccb218e056c220120d", size = 11221886, upload-time = "2026-04-16T18:46:15.086Z" }, - { url = "https://files.pythonhosted.org/packages/43/5a/947e6ab7a5ad603d65b474be15a4cbc6d29832db5d762cd142e4e3a74164/ruff-0.15.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:195072c0c8e1fc8f940652073df082e37a5d9cb43b4ab1e4d0566ab8977a13b7", size = 12075183, upload-time = "2026-04-16T18:46:07.944Z" }, - { url = "https://files.pythonhosted.org/packages/9f/a1/0b7bb6268775fdd3a0818aee8efd8f5b4e231d24dd4d528ced2534023182/ruff-0.15.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a3a0996d486af3920dec930a2e7daed4847dfc12649b537a9335585ada163e9e", size = 11516575, upload-time = "2026-04-16T18:46:31.687Z" }, - { url = "https://files.pythonhosted.org/packages/30/c3/bb5168fc4d233cc06e95f482770d0f3c87945a0cd9f614b90ea8dc2f2833/ruff-0.15.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bef2cb556d509259f1fe440bb9cd33c756222cf0a7afe90d15edf0866702431", size = 11306537, upload-time = "2026-04-16T18:46:36.988Z" }, - { url = "https://files.pythonhosted.org/packages/e4/92/4cfae6441f3967317946f3b788136eecf093729b94d6561f963ed810c82e/ruff-0.15.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:030d921a836d7d4a12cf6e8d984a88b66094ccb0e0f17ddd55067c331191bf19", size = 11296813, upload-time = "2026-04-16T18:46:24.182Z" }, - { url = "https://files.pythonhosted.org/packages/43/26/972784c5dde8313acde8ac71ba8ac65475b85db4a2352a76c9934361f9bc/ruff-0.15.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0e783b599b4577788dbbb66b9addcef87e9a8832f4ce0c19e34bf55543a2f890", size = 10633136, upload-time = "2026-04-16T18:46:39.802Z" }, - { url = "https://files.pythonhosted.org/packages/5b/53/3985a4f185020c2f367f2e08a103032e12564829742a1b417980ce1514a0/ruff-0.15.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ae90592246625ba4a34349d68ec28d4400d75182b71baa196ddb9f82db025ef5", size = 10424701, upload-time = "2026-04-16T18:46:10.381Z" }, - { url = "https://files.pythonhosted.org/packages/d3/57/bf0dfb32241b56c83bb663a826133da4bf17f682ba8c096973065f6e6a68/ruff-0.15.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1f111d62e3c983ed20e0ca2e800f8d77433a5b1161947df99a5c2a3fb60514f0", size = 10873887, upload-time = "2026-04-16T18:46:29.157Z" }, - { url = "https://files.pythonhosted.org/packages/02/05/e48076b2a57dc33ee8c7a957296f97c744ca891a8ffb4ffb1aaa3b3f517d/ruff-0.15.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:06f483d6646f59eaffba9ae30956370d3a886625f511a3108994000480621d1c", size = 11404316, upload-time = "2026-04-16T18:46:19.462Z" }, - { url = "https://files.pythonhosted.org/packages/88/27/0195d15fe7a897cbcba0904792c4b7c9fdd958456c3a17d2ea6093716a9a/ruff-0.15.11-py3-none-win32.whl", hash = "sha256:476a2aa56b7da0b73a3ee80b6b2f0e19cce544245479adde7baa65466664d5f3", size = 10655535, upload-time = "2026-04-16T18:46:12.47Z" }, - { url = "https://files.pythonhosted.org/packages/3a/5e/c927b325bd4c1d3620211a4b96f47864633199feed60fa936025ab27e090/ruff-0.15.11-py3-none-win_amd64.whl", hash = "sha256:8b6756d88d7e234fb0c98c91511aae3cd519d5e3ed271cae31b20f39cb2a12a3", size = 11779692, upload-time = "2026-04-16T18:46:17.268Z" }, - { url = "https://files.pythonhosted.org/packages/63/b6/aeadee5443e49baa2facd51131159fd6301cc4ccfc1541e4df7b021c37dd/ruff-0.15.11-py3-none-win_arm64.whl", hash = "sha256:063fed18cc1bbe0ee7393957284a6fe8b588c6a406a285af3ee3f46da2391ee4", size = 11032614, upload-time = "2026-04-16T18:46:34.487Z" }, +version = "0.15.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/dc/35b341fc554ba02f217fc10da57d1a75168cfbcf75b0ef2202176d4c4f2d/ruff-0.15.20.tar.gz", hash = "sha256:1416eb04349192646b54de98f146c4f59afe37d0decfc02c3cbbf396f3a28566", size = 4755489, upload-time = "2026-06-25T17:20:37.578Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/d9/2d5014f0253ba541d2061d9fa7193f48e941c8b21bb88a7ff9bbe0bd0596/ruff-0.15.20-py3-none-linux_armv6l.whl", hash = "sha256:00e188c53e499c3c1637f73c91dcf2fb56d576cab76ce1be50a27c4e80e37078", size = 10839665, upload-time = "2026-06-25T17:19:44.702Z" }, + { url = "https://files.pythonhosted.org/packages/c6/d3/ac1798ba64f670698867fcfc591d50e7e421bef137db564858f619a30fcf/ruff-0.15.20-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9ebd1fd9b9c95fc0bd7b2761aebec1f030013d2e193a2901b224af68fe47251b", size = 11208649, upload-time = "2026-06-25T17:19:48.787Z" }, + { url = "https://files.pythonhosted.org/packages/47/47/d3ac899991202095dfcf3d5176be4272642be3cf981a2f1a30f72a2afb95/ruff-0.15.20-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5b16cdd67ca108185cd36dce98c576350c03b1660a751de725fb049193a0632", size = 10622638, upload-time = "2026-06-25T17:19:51.354Z" }, + { url = "https://files.pythonhosted.org/packages/33/13/4e043fe30aa94d4ff5213a9881fc296d12960f5971b234a5263fdc225312/ruff-0.15.20-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3413bb3c3d2ca6a8208f1f4809cd2dca3c6de6d0b491c0e70847672bde6e6efd", size = 10984227, upload-time = "2026-06-25T17:19:54.044Z" }, + { url = "https://files.pythonhosted.org/packages/76/e6/92e7bf40388bc5800073b96564f56264f7e48bfd1a498f5ced6ae6d5a769/ruff-0.15.20-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd7ec42b3bb3da066488db093308a69c4ac5ee6d2af333a86ba6e2eb2e7dd44b", size = 10622882, upload-time = "2026-06-25T17:19:57.037Z" }, + { url = "https://files.pythonhosted.org/packages/13/7a/43460be3f24495a3aa46d4b16873e2c4941b3b5f0b00cf88c03b7b94b339/ruff-0.15.20-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1a36ad0eb77fba9aabfb69ede54de6f376d04ac18ebea022847046d340a8267", size = 11474808, upload-time = "2026-06-25T17:20:00.357Z" }, + { url = "https://files.pythonhosted.org/packages/27/a0/f37077884873221c6b33b4ab49eb18f9f88e54a16a25a5bca59bef46dd66/ruff-0.15.20-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b6df3b1e4610432f0386dba04d853b5f08cbbc903410c6fcc02f620f05aff53c", size = 12293094, upload-time = "2026-06-25T17:20:03.446Z" }, + { url = "https://files.pythonhosted.org/packages/a6/74/165545b60256a9704c21ac0ec4a0d07933b320812f9584836c9f4aca4292/ruff-0.15.20-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e89f198a1ea6ef0d727c1cf16088bc91a6cb0ab947dedc966715691647186eae", size = 11526176, upload-time = "2026-06-25T17:20:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/86/b1/a976a136d40ade83ce743578399865f57001003a409acadc0ecbb3051082/ruff-0.15.20-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309809086c2acb67624950a3c8133e80f32d0d3e27106c0cd60ff26657c9f24b", size = 11520767, upload-time = "2026-06-25T17:20:09.191Z" }, + { url = "https://files.pythonhosted.org/packages/19/0f/f032696cb01c9b54c0263fa393474d7758f1cdc021a01b04e3cbc2500999/ruff-0.15.20-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2d2374caa2f2c2f9e2b7da0a50802cfb8b79f55a9b5e49379f564544fbf56487", size = 11500132, upload-time = "2026-06-25T17:20:13.602Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f4/51b1a14bc69e8c224b15dab9cce8e99b425e0455d462caa2b3c9be2b6a8e/ruff-0.15.20-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a1ed17b65293e0c2f22fc387bc13198a5de94bf4429589b0ff6946b0feaf21a3", size = 10943828, upload-time = "2026-06-25T17:20:16.635Z" }, + { url = "https://files.pythonhosted.org/packages/71/4b/fe267640783cd02bf6c5cc290b1df1051be2ec294c678b5c15fe19e52343/ruff-0.15.20-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f701305e66b38ea6c91882490eb73459796808e4c6362a1b765255e0cdcd4053", size = 10645418, upload-time = "2026-06-25T17:20:19.4Z" }, + { url = "https://files.pythonhosted.org/packages/b0/c0/a65aa4ec2f5e87a1df32dc3ec1fede434fe3dfd5cbcf3b503cafc676ab54/ruff-0.15.20-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b9c0c367ad8e5d0d5b5b8537864c469a0a0e55417aadfbeca41fa61333be9f4", size = 11211770, upload-time = "2026-06-25T17:20:22.033Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a4/0caa331d954ae2723d729d351c989cb4ca8b6077d5c6c2cb6de75e98c041/ruff-0.15.20-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:01cc00dd58f0df339d0e902219dd53990ea99996a0344e5d9cc8d45d5307e460", size = 11618698, upload-time = "2026-06-25T17:20:25.259Z" }, + { url = "https://files.pythonhosted.org/packages/10/9b/5f14927848d2fd4aa891fd88d883788c5a7baba561c7874732364045708c/ruff-0.15.20-py3-none-win32.whl", hash = "sha256:ed65ef510e43a137207e0f01cfcf998aeddb1aeeda5c9d35023e910284d7cf21", size = 10857322, upload-time = "2026-06-25T17:20:28.612Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f0/fe47c501f9dea92a26d788ff98bb5d92ed4cb4c88792c5c88af6b697dc8e/ruff-0.15.20-py3-none-win_amd64.whl", hash = "sha256:a525c81c70fb0380344dd1d8745d8cc1c890b7fc94a58d5a07bd8eb9557b8415", size = 11993274, upload-time = "2026-06-25T17:20:31.871Z" }, + { url = "https://files.pythonhosted.org/packages/d7/2b/9555445e1201d92b3195f45cdb153a0b68f24e0a4273f6e3d5ab46e212bb/ruff-0.15.20-py3-none-win_arm64.whl", hash = "sha256:2f5b2a6d614e8700388806a14996c40fab2c47b819ef57d790a34878858ed9ca", size = 11343498, upload-time = "2026-06-25T17:20:35.03Z" }, ] [[package]] @@ -893,28 +874,27 @@ wheels = [ ] [[package]] -name = "tempo-cache" +name = "tempo-binary-tool-manager" version = "0.1.0" -source = { git = "https://www.github.com/Tempo-Organization/tempo-cache#ea2455ef94917ad003eea5bec1e062d45b729b8e" } +source = { git = "https://www.github.com/Tempo-Organization/tempo-binary-tool-manager#1938cb8f9e4a571c2645db6b86d39d5fa1c3f38b" } dependencies = [ { name = "platformdirs" }, { name = "requests" }, - { name = "tempo-settings" }, { name = "tomlkit" }, ] [[package]] -name = "tempo-cache-tools" +name = "tempo-binary-tools" version = "0.1.0" -source = { git = "https://www.github.com/Tempo-Organization/tempo-cache-tools#12dbceb01809f49082d98d6fad6a5d038af02761" } +source = { git = "https://www.github.com/Tempo-Organization/tempo-binary-tools#d6efcc9cb4ba413b16edabe1599f101d00f8d2ce" } dependencies = [ { name = "packaging" }, - { name = "tempo-cache" }, + { name = "tempo-binary-tool-manager" }, ] [[package]] name = "tempo-cli" -version = "5.1.1.dev23959330723" +version = "5.2.0.dev26602054573" source = { editable = "." } dependencies = [ { name = "commitizen" }, @@ -922,15 +902,18 @@ dependencies = [ { name = "prek" }, { name = "questionary" }, { name = "rich-click" }, + { name = "tempo-binary-tool-manager" }, + { name = "tempo-binary-tools" }, { name = "tempo-core" }, + { name = "tempo-settings" }, { name = "tomlkit" }, - { name = "trogon" }, { name = "ue4ss-installer-core" }, ] [package.dev-dependencies] dev = [ { name = "commitizen" }, + { name = "importtime" }, { name = "mkdocs-material" }, { name = "mkdocstrings-python" }, { name = "prek" }, @@ -946,9 +929,11 @@ requires-dist = [ { name = "prek", specifier = ">=0.3.9" }, { name = "questionary", specifier = ">=2.1.0" }, { name = "rich-click", specifier = ">=1.9.4" }, + { name = "tempo-binary-tool-manager", git = "https://www.github.com/Tempo-Organization/tempo-binary-tool-manager" }, + { name = "tempo-binary-tools", git = "https://www.github.com/Tempo-Organization/tempo-binary-tools" }, { name = "tempo-core", git = "https://www.github.com/Tempo-Organization/tempo-core" }, + { name = "tempo-settings", git = "https://www.github.com/Tempo-Organization/tempo-settings" }, { name = "tomlkit", specifier = ">=0.13.3" }, - { name = "trogon", specifier = ">=0.6.0" }, { name = "ue4ss-installer-core", git = "https://www.github.com/Tempo-Organization/ue4ss-installer-core" }, ] @@ -956,6 +941,7 @@ requires-dist = [ aarch64-unknown-linux-gnu = [] dev = [ { name = "commitizen", specifier = ">=4.8.3" }, + { name = "importtime", specifier = ">=1.0.3.2" }, { name = "mkdocs-material", specifier = ">=9.6.15" }, { name = "mkdocstrings-python", specifier = ">=1.16.12" }, { name = "prek", specifier = ">=0.3.9" }, @@ -970,15 +956,15 @@ x86-64-unknown-linux-gnu = [] [[package]] name = "tempo-core" version = "0.1.0" -source = { git = "https://www.github.com/Tempo-Organization/tempo-core#fce6649349baa1567df126a971c8b4a3c2d61bdb" } +source = { git = "https://www.github.com/Tempo-Organization/tempo-core#c8613404ae40f98447782087809727c28b886d43" } dependencies = [ { name = "platformdirs" }, { name = "psutil" }, { name = "requests" }, { name = "rich" }, { name = "screeninfo" }, - { name = "tempo-cache" }, - { name = "tempo-cache-tools" }, + { name = "tempo-binary-tool-manager" }, + { name = "tempo-binary-tools" }, { name = "tempo-settings" }, { name = "ue4ss-installer-core" }, ] @@ -986,7 +972,7 @@ dependencies = [ [[package]] name = "tempo-settings" version = "0.1.0" -source = { git = "https://www.github.com/Tempo-Organization/tempo-settings#0cdabc4e19b3c287c041972eb47d4704becb99dc" } +source = { git = "https://www.github.com/Tempo-Organization/tempo-settings#a8bea9dae53abf30cd48ae1f9d34280aac8ed583" } [[package]] name = "termcolor" @@ -997,85 +983,38 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/d1/8bb87d21e9aeb323cc03034f5eaf2c8f69841e40e4853c2627edf8111ed3/termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5", size = 7734, upload-time = "2025-12-29T12:55:20.718Z" }, ] -[[package]] -name = "textual" -version = "8.2.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py", extra = ["linkify"] }, - { name = "mdit-py-plugins" }, - { name = "platformdirs" }, - { name = "pygments" }, - { name = "rich" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cf/2f/d44f0f12b3ddb1f0b88f7775652e99c6b5a43fd733badf4ce064bdbfef4a/textual-8.2.3.tar.gz", hash = "sha256:beea7b86b03b03558a2224f0cc35252e60ef8b0c4353b117b2f40972902d976a", size = 1848738, upload-time = "2026-04-05T09:12:45.338Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/28/a81d6ce9f4804818bd1231a9a6e4d56ea84ebbe8385c49591444f0234fa2/textual-8.2.3-py3-none-any.whl", hash = "sha256:5008ac581bebf1f6fa0520404261844a231e5715fdbddd10ca73916a3af48ca2", size = 724231, upload-time = "2026-04-05T09:12:48.747Z" }, -] - [[package]] name = "tomlkit" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/af/14b24e41977adb296d6bd1fb59402cf7d60ce364f90c890bd2ec65c43b5a/tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064", size = 187167, upload-time = "2026-01-13T01:14:53.304Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", size = 39310, upload-time = "2026-01-13T01:14:51.965Z" }, -] - -[[package]] -name = "trogon" -version = "0.6.0" +version = "0.15.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "textual" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cf/ae/7367acac2194a215b092ba3fccde3b558272702110b8bb9bea164ab4ac42/trogon-0.6.0.tar.gz", hash = "sha256:fd1abfeb7b15d79d6e6cfc9e724aad2a2728812e4713a744d975f133e7ec73a4", size = 22902, upload-time = "2024-10-02T13:38:12.511Z" } +sdist = { url = "https://files.pythonhosted.org/packages/51/db/03eaf4331631ef6b27d6e3c9b68c54dc6f0d63d87201fed600cc409307fd/tomlkit-0.15.0.tar.gz", hash = "sha256:7d1a9ecba3086638211b13814ea79c90dd54dd11993564376f3aa92271f5c7a3", size = 161875, upload-time = "2026-05-10T07:38:22.245Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/30/33035d5796a3b8b9624997fec7545e3febd2268c7b48df38a715a95cb5e4/trogon-0.6.0-py3-none-any.whl", hash = "sha256:fb5b6c25acd7a0eaba8d2cd32a57f1d80c26413cea737dad7a4eebcda56060e0", size = 26077, upload-time = "2024-10-02T13:38:11.09Z" }, + { url = "https://files.pythonhosted.org/packages/6a/43/8bd850ee71a191bf072e31302c73a66be413fecdd98fdcd111ecbcce13ca/tomlkit-0.15.0-py3-none-any.whl", hash = "sha256:4dbc8f0fc024412b57ced8757ac7461305126a648ff8c2c807fcb8e133a78738", size = 41328, upload-time = "2026-05-10T07:38:23.517Z" }, ] [[package]] name = "ty" -version = "0.0.31" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/31/cc/5ea5d3a72216c8c2bf77d83066dd4f3553532d0aacc03d4a8397dd9845e1/ty-0.0.31.tar.gz", hash = "sha256:4a4094292d9671caf3b510c7edf36991acd9c962bb5d97205374ffed9f541c45", size = 5516619, upload-time = "2026-04-15T15:47:59.87Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/10/ea805cbbd75d5d50792551a2b383de8521eeab0c44f38c73e12819ced65e/ty-0.0.31-py3-none-linux_armv6l.whl", hash = "sha256:761651dc17ad7bc0abfc1b04b3f0e84df263ed435d34f29760b3da739ab02d35", size = 10834749, upload-time = "2026-04-15T15:48:14.877Z" }, - { url = "https://files.pythonhosted.org/packages/d9/4c/fabf951850401d24d36b21bced088a366c6827e1c37dab4523afff84c4b2/ty-0.0.31-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c529922395a07231c27488f0290651e05d27d149f7e0aa807678f1f7e9c58a5e", size = 10626012, upload-time = "2026-04-15T15:48:22.554Z" }, - { url = "https://files.pythonhosted.org/packages/04/b0/4a5aff88d2544f19514a59c8f693d63144aa7307fe2ee5df608333ab5460/ty-0.0.31-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5f345df2b87d747859e72c2cbc9be607ea1bbc8bc93dd32fa3d03ea091cb4fee", size = 10075790, upload-time = "2026-04-15T15:47:46.959Z" }, - { url = "https://files.pythonhosted.org/packages/d5/73/9d4dcad12cd4e85274014f2c0510ef93f590b2a1e5148de3a9f276098dad/ty-0.0.31-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4b207eddcfbafd376132689d3435b14efcb531289cb59cd961c6a611133bd54", size = 10590286, upload-time = "2026-04-15T15:48:06.222Z" }, - { url = "https://files.pythonhosted.org/packages/47/45/fe40adde18692359ded174ae7ddbfac056e876eb0f43b65be74fde7f6072/ty-0.0.31-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:663778b220f357067488ce68bfc52335ccbd161549776f70dcbde6bbde82f77a", size = 10623824, upload-time = "2026-04-15T15:48:12.965Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e8/0ffa2e09b548e6daa9ebc368d68b767dc2405ca4cbeadb7ede0e2cb21059/ty-0.0.31-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3506cfe87dfade0fb2960dd4fffd4fd8089003587b3445c0a1a295c9d83764fb", size = 11156864, upload-time = "2026-04-15T15:48:08.473Z" }, - { url = "https://files.pythonhosted.org/packages/08/e9/fd44c2075115d569593ee9473d7e2a38b750fd7e783421c95eb528c15df5/ty-0.0.31-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8b3f3d8492f08e81916026354c1d1599e9ddfa1241804141a74d5662fc710085", size = 11696401, upload-time = "2026-04-15T15:48:17.355Z" }, - { url = "https://files.pythonhosted.org/packages/4e/50/35aad8eadf964d23e2a4faa5b38a206aa85c78833c8ce335dddd2c34ba63/ty-0.0.31-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a97de32ee6a619393a4c495e056a1c547de7877510f3152e61345c71d774d2d0", size = 11374903, upload-time = "2026-04-15T15:47:55.893Z" }, - { url = "https://files.pythonhosted.org/packages/c8/37/01eccd25d23f5aaa7f7ff1a87b5b215469f6b202cf689a1812b71c1e7f6b/ty-0.0.31-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c906354ce441e342646582bc9b8f48a676f79f3d061e25de15ff870e015ca14e", size = 11206624, upload-time = "2026-04-15T15:47:51.778Z" }, - { url = "https://files.pythonhosted.org/packages/f4/70/baad2914cb097453f127a221f8addb2b41926098059cd773c75e6a662fc4/ty-0.0.31-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:275bb7c82afcbf89fe2dbef1b2692f2bc98451f1ee2c8eb809ddd91317822388", size = 10575089, upload-time = "2026-04-15T15:47:49.448Z" }, - { url = "https://files.pythonhosted.org/packages/83/12/bae3a7bba2e785eb72ce00f9da70eedcb8c5e8299efecbd16e6e436abd82/ty-0.0.31-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:405da247027c6efd1e264886b6ac4a86ab3a4f09200b02e33630efe85f119e53", size = 10642315, upload-time = "2026-04-15T15:48:19.661Z" }, - { url = "https://files.pythonhosted.org/packages/93/9e/cad04d5d839bc60355cea98c7e09d724ea65f47184def0fae8b90dc54591/ty-0.0.31-py3-none-musllinux_1_2_i686.whl", hash = "sha256:54d9835608eed196853d6643f645c50ce83bcc7fe546cdb3e210c1bcf7c58c09", size = 10834473, upload-time = "2026-04-15T15:48:02.091Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ba/84112d280182d37690d3d2b4018b2667e42bc281585e607015635310016a/ty-0.0.31-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5ee11be9b07e8c0c6b455ff075a0abe4f194de9476f57624db98eec9df618355", size = 11315785, upload-time = "2026-04-15T15:48:10.754Z" }, - { url = "https://files.pythonhosted.org/packages/50/9f/ac42dc223d7e0950e97a1854567a8b3e7fe09ad7375adbf91bfb43290482/ty-0.0.31-py3-none-win32.whl", hash = "sha256:7286587aacf3eef0956062d6492b893b02f82b0f22c5e230008e13ff0d216a8b", size = 10187657, upload-time = "2026-04-15T15:48:04.264Z" }, - { url = "https://files.pythonhosted.org/packages/75/3e/57ba7ea7ecb2f4751644ba91756e2be70e33ef5952c0c41a256a0e4c2437/ty-0.0.31-py3-none-win_amd64.whl", hash = "sha256:81134e25d2a2562ab372f24de8f9bd05034d27d30377a5d7540f259791c6234c", size = 11205258, upload-time = "2026-04-15T15:47:53.759Z" }, - { url = "https://files.pythonhosted.org/packages/88/39/bca669095ccf0a400af941fdf741578d4c2d6719f1b7f10e6dbec10aa862/ty-0.0.31-py3-none-win_arm64.whl", hash = "sha256:e9cb15fad26545c6a608f40f227af3a5513cb376998ca6feddd47ca7d93ffafa", size = 10590392, upload-time = "2026-04-15T15:47:57.968Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] - -[[package]] -name = "uc-micro-py" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/78/67/9a363818028526e2d4579334460df777115bdec1bb77c08f9db88f6389f2/uc_micro_py-2.0.0.tar.gz", hash = "sha256:c53691e495c8db60e16ffc4861a35469b0ba0821fe409a8a7a0a71864d33a811", size = 6611, upload-time = "2026-03-01T06:31:27.526Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/61/73/d21edf5b204d1467e06500080a50f79d49ef2b997c79123a536d4a17d97c/uc_micro_py-2.0.0-py3-none-any.whl", hash = "sha256:3603a3859af53e5a39bc7677713c78ea6589ff188d70f4fee165db88e22b242c", size = 6383, upload-time = "2026-03-01T06:31:26.257Z" }, +version = "0.0.55" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/08/48/f687c8d268e3581f2f104d1f2ac5944d5b5e841b3695c613b3f263e5bbf7/ty-0.0.55.tar.gz", hash = "sha256:88ca87073825a79a8327c550efcc86cec94344890244c5946f84c9e44a969f31", size = 6040230, upload-time = "2026-06-27T00:27:29.385Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/a3/1a90ba7e5a61c6d09adb92346ddba97668095fc257b577af433e5ac4f404/ty-0.0.55-py3-none-linux_armv6l.whl", hash = "sha256:31e83eef512d066542fe990fe1a3b814423abd1616376c54e48af7045b3e1749", size = 11677249, upload-time = "2026-06-27T00:26:52.18Z" }, + { url = "https://files.pythonhosted.org/packages/82/3a/669f9aa478c38243e213a2684db1502086026cfadc15bb1b29b7cbde030d/ty-0.0.55-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab4bca857950608fea73e269e2da369d43e6467131de85160d68e2fa466fa248", size = 11444180, upload-time = "2026-06-27T00:26:54.576Z" }, + { url = "https://files.pythonhosted.org/packages/15/a4/6a4b2507a53ce6530c66c5b4fe0d58551eb1748ffa9e0696c32fdd55bbd4/ty-0.0.55-py3-none-macosx_11_0_arm64.whl", hash = "sha256:55032bfd31bf2c5355ee81bdc6407b144a1cc7ee41e5681dd1368e4cef2ba327", size = 10963134, upload-time = "2026-06-27T00:26:57.348Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ae/a3b1a0f1cc83b7d258662cb98aa80a720c2e671d0e8fa0d17a4d5d057a7a/ty-0.0.55-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1e049f69ce65b3c269af67624607f435e1c32319786c1e453ef9611502f295", size = 11493517, upload-time = "2026-06-27T00:26:59.26Z" }, + { url = "https://files.pythonhosted.org/packages/0d/9f/311ce39065a979ef40a9b847f685c8e02464e53adf1671e081eea90640ca/ty-0.0.55-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:631409975c681d5a280fc5a99b7b32e9e801f33be7567c6b42ec331362f59d7d", size = 11460590, upload-time = "2026-06-27T00:27:01.425Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8f/3bf29aa77bd78aae48275153135a2052fa7d3ccdf1ecabeb99c8773abd66/ty-0.0.55-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e08cb0436e68b9351555ae8f2697138c9009b4d5b4ae4272232988b2a431a98f", size = 12098430, upload-time = "2026-06-27T00:27:03.596Z" }, + { url = "https://files.pythonhosted.org/packages/bc/6e/e88411a88240b94640bba06fb6d0d92b247fbeef47ee2bc71f39e58c2558/ty-0.0.55-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16c215ad9f823829409b94ee188cfaa4563f6e1384f6ce3fecb1db75f6c7cf7c", size = 12673086, upload-time = "2026-06-27T00:27:05.589Z" }, + { url = "https://files.pythonhosted.org/packages/6c/7e/8f1762fb7f9245a68ba5ae338d73c59403ce57554e5d311b8bb55027b0ec/ty-0.0.55-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b510eb8f4032baf11b7aee2f1d53babc3b4ca03939b9cdcf6a9d15761d575188", size = 12242559, upload-time = "2026-06-27T00:27:07.714Z" }, + { url = "https://files.pythonhosted.org/packages/72/1f/143657daf2670d977dac83435f1fe03d4843efb798d8e1e75950e541aadd/ty-0.0.55-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ddc05e7959709c3b9b83aa627128a80446865e3c1a4882638dcff6d776dc34a", size = 12021409, upload-time = "2026-06-27T00:27:09.881Z" }, + { url = "https://files.pythonhosted.org/packages/6d/30/69487c439dd1fad3a4a3d96f0a472193de297eaba6fc4b8ea687ce434ac2/ty-0.0.55-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:636e8e5078787b8c6916c94e1406719f10189a4ca6b37b813a5922ce5857a8c7", size = 12303807, upload-time = "2026-06-27T00:27:11.986Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ca/cd88b6493dafc7db077f5e17c0438eb3af6e2d6d08f616dbb52a8ddfd567/ty-0.0.55-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ef7d6deaacb73fec603666b5471f1dc5a5699aa84e11a6d4d644dd07ca72121e", size = 11441263, upload-time = "2026-06-27T00:27:14.087Z" }, + { url = "https://files.pythonhosted.org/packages/aa/fe/66b6915671653ab739f71e4f1b0528e69da64429b7ebf3840c625b6e43f2/ty-0.0.55-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9aeea0fe5875d3cf37faf0e44d0fdf9669335467749741b8fc0103916fb5cd32", size = 11484584, upload-time = "2026-06-27T00:27:16.311Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4f/7a9c0bbac8b899e9f6c0ec110c6612f52e4db35f6bb17ddc0ef60384fa3e/ty-0.0.55-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0b699c01310dbd2705a07c97c5f4aaeedef61bd9adeea2e7c46aed32401d3576", size = 11759309, upload-time = "2026-06-27T00:27:18.471Z" }, + { url = "https://files.pythonhosted.org/packages/ca/de/b6f8b1b69aa631b5716ef3f985c3b56de0e46c2499cc00d30c402b41f714/ty-0.0.55-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:32cbeba543e46de2a983ec6d525d8b56514f7422bd1e1b57c44ccf7bfa72c38a", size = 12128755, upload-time = "2026-06-27T00:27:20.55Z" }, + { url = "https://files.pythonhosted.org/packages/7d/90/a912531e51ee7e076b42972479290fa687c0f5e747b7e773f3033164acaa/ty-0.0.55-py3-none-win32.whl", hash = "sha256:52b968e24eb4f7a5c3bd251db1f99f60dd385890356d38fc619d84f1b423446a", size = 11117501, upload-time = "2026-06-27T00:27:22.714Z" }, + { url = "https://files.pythonhosted.org/packages/4c/7a/99d59843bf8908a7f9f4d13fda107dbad07b7faa28ecd7860eacf363fb1c/ty-0.0.55-py3-none-win_amd64.whl", hash = "sha256:bf39cbfdc0add44d94bd3fff1f53c351418d134b6a66b87efdb7876d7b7a2224", size = 12150106, upload-time = "2026-06-27T00:27:24.881Z" }, + { url = "https://files.pythonhosted.org/packages/b3/44/20987505cedf2a865b08482f0eabc181fd9599b062964057ec8a128a4296/ty-0.0.55-py3-none-win_arm64.whl", hash = "sha256:f7f3700a9a060e8f1af11e4fb63fafcaf272b041781f4ccdfda2b3b5c6c1e439", size = 11560157, upload-time = "2026-06-27T00:27:27.332Z" }, ] [[package]] @@ -1088,11 +1027,11 @@ dependencies = [ [[package]] name = "urllib3" -version = "2.6.3" +version = "2.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, ] [[package]] @@ -1121,73 +1060,73 @@ wheels = [ [[package]] name = "wcwidth" -version = "0.6.0" +version = "0.8.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" } +sdist = { url = "https://files.pythonhosted.org/packages/49/b4/51fe890511f0f242d07cb1ebe6a5b6db417262b9d2568b460347c57d95cc/wcwidth-0.8.1.tar.gz", hash = "sha256:faf5b4a5366a72dc49cad48cdf21f52bdf63bdda995178e483ba247ff79089b9", size = 1466072, upload-time = "2026-06-08T05:57:23.146Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" }, + { url = "https://files.pythonhosted.org/packages/bd/6e/95b0e537de1f4d4301f76f944642c6da50d1511cc7b3d64dc418a66c7509/wcwidth-0.8.1-py3-none-any.whl", hash = "sha256:f453740b1e4a4f3291faa37944c555d71056c4da08d59809b307ef4feba695c8", size = 323092, upload-time = "2026-06-08T05:57:21.413Z" }, ] [[package]] name = "wrapt" -version = "2.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/64/925f213fdcbb9baeb1530449ac71a4d57fc361c053d06bf78d0c5c7cd80c/wrapt-2.1.2.tar.gz", hash = "sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e", size = 81678, upload-time = "2026-03-06T02:53:25.134Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/b6/1db817582c49c7fcbb7df6809d0f515af29d7c2fbf57eb44c36e98fb1492/wrapt-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9", size = 61255, upload-time = "2026-03-06T02:52:45.663Z" }, - { url = "https://files.pythonhosted.org/packages/a2/16/9b02a6b99c09227c93cd4b73acc3678114154ec38da53043c0ddc1fba0dc/wrapt-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748", size = 61848, upload-time = "2026-03-06T02:53:48.728Z" }, - { url = "https://files.pythonhosted.org/packages/af/aa/ead46a88f9ec3a432a4832dfedb84092fc35af2d0ba40cd04aea3889f247/wrapt-2.1.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e", size = 121433, upload-time = "2026-03-06T02:54:40.328Z" }, - { url = "https://files.pythonhosted.org/packages/3a/9f/742c7c7cdf58b59085a1ee4b6c37b013f66ac33673a7ef4aaed5e992bc33/wrapt-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8", size = 123013, upload-time = "2026-03-06T02:53:26.58Z" }, - { url = "https://files.pythonhosted.org/packages/e8/44/2c3dd45d53236b7ed7c646fcf212251dc19e48e599debd3926b52310fafb/wrapt-2.1.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c", size = 117326, upload-time = "2026-03-06T02:53:11.547Z" }, - { url = "https://files.pythonhosted.org/packages/74/e2/b17d66abc26bd96f89dec0ecd0ef03da4a1286e6ff793839ec431b9fae57/wrapt-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c", size = 121444, upload-time = "2026-03-06T02:54:09.5Z" }, - { url = "https://files.pythonhosted.org/packages/3c/62/e2977843fdf9f03daf1586a0ff49060b1b2fc7ff85a7ea82b6217c1ae36e/wrapt-2.1.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1", size = 116237, upload-time = "2026-03-06T02:54:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/88/dd/27fc67914e68d740bce512f11734aec08696e6b17641fef8867c00c949fc/wrapt-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2", size = 120563, upload-time = "2026-03-06T02:53:20.412Z" }, - { url = "https://files.pythonhosted.org/packages/ec/9f/b750b3692ed2ef4705cb305bd68858e73010492b80e43d2a4faa5573cbe7/wrapt-2.1.2-cp312-cp312-win32.whl", hash = "sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0", size = 58198, upload-time = "2026-03-06T02:53:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/8e/b2/feecfe29f28483d888d76a48f03c4c4d8afea944dbee2b0cd3380f9df032/wrapt-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63", size = 60441, upload-time = "2026-03-06T02:52:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/44/e1/e328f605d6e208547ea9fd120804fcdec68536ac748987a68c47c606eea8/wrapt-2.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf", size = 58836, upload-time = "2026-03-06T02:53:22.053Z" }, - { url = "https://files.pythonhosted.org/packages/4c/7a/d936840735c828b38d26a854e85d5338894cda544cb7a85a9d5b8b9c4df7/wrapt-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b", size = 61259, upload-time = "2026-03-06T02:53:41.922Z" }, - { url = "https://files.pythonhosted.org/packages/5e/88/9a9b9a90ac8ca11c2fdb6a286cb3a1fc7dd774c00ed70929a6434f6bc634/wrapt-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e", size = 61851, upload-time = "2026-03-06T02:52:48.672Z" }, - { url = "https://files.pythonhosted.org/packages/03/a9/5b7d6a16fd6533fed2756900fc8fc923f678179aea62ada6d65c92718c00/wrapt-2.1.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb", size = 121446, upload-time = "2026-03-06T02:54:14.013Z" }, - { url = "https://files.pythonhosted.org/packages/45/bb/34c443690c847835cfe9f892be78c533d4f32366ad2888972c094a897e39/wrapt-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca", size = 123056, upload-time = "2026-03-06T02:54:10.829Z" }, - { url = "https://files.pythonhosted.org/packages/93/b9/ff205f391cb708f67f41ea148545f2b53ff543a7ac293b30d178af4d2271/wrapt-2.1.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267", size = 117359, upload-time = "2026-03-06T02:53:03.623Z" }, - { url = "https://files.pythonhosted.org/packages/1f/3d/1ea04d7747825119c3c9a5e0874a40b33594ada92e5649347c457d982805/wrapt-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f", size = 121479, upload-time = "2026-03-06T02:53:45.844Z" }, - { url = "https://files.pythonhosted.org/packages/78/cc/ee3a011920c7a023b25e8df26f306b2484a531ab84ca5c96260a73de76c0/wrapt-2.1.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8", size = 116271, upload-time = "2026-03-06T02:54:46.356Z" }, - { url = "https://files.pythonhosted.org/packages/98/fd/e5ff7ded41b76d802cf1191288473e850d24ba2e39a6ec540f21ae3b57cb/wrapt-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413", size = 120573, upload-time = "2026-03-06T02:52:50.163Z" }, - { url = "https://files.pythonhosted.org/packages/47/c5/242cae3b5b080cd09bacef0591691ba1879739050cc7c801ff35c8886b66/wrapt-2.1.2-cp313-cp313-win32.whl", hash = "sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6", size = 58205, upload-time = "2026-03-06T02:53:47.494Z" }, - { url = "https://files.pythonhosted.org/packages/12/69/c358c61e7a50f290958809b3c61ebe8b3838ea3e070d7aac9814f95a0528/wrapt-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1", size = 60452, upload-time = "2026-03-06T02:53:30.038Z" }, - { url = "https://files.pythonhosted.org/packages/8e/66/c8a6fcfe321295fd8c0ab1bd685b5a01462a9b3aa2f597254462fc2bc975/wrapt-2.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf", size = 58842, upload-time = "2026-03-06T02:52:52.114Z" }, - { url = "https://files.pythonhosted.org/packages/da/55/9c7052c349106e0b3f17ae8db4b23a691a963c334de7f9dbd60f8f74a831/wrapt-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b", size = 63075, upload-time = "2026-03-06T02:53:19.108Z" }, - { url = "https://files.pythonhosted.org/packages/09/a8/ce7b4006f7218248dd71b7b2b732d0710845a0e49213b18faef64811ffef/wrapt-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18", size = 63719, upload-time = "2026-03-06T02:54:33.452Z" }, - { url = "https://files.pythonhosted.org/packages/e4/e5/2ca472e80b9e2b7a17f106bb8f9df1db11e62101652ce210f66935c6af67/wrapt-2.1.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d", size = 152643, upload-time = "2026-03-06T02:52:42.721Z" }, - { url = "https://files.pythonhosted.org/packages/36/42/30f0f2cefca9d9cbf6835f544d825064570203c3e70aa873d8ae12e23791/wrapt-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015", size = 158805, upload-time = "2026-03-06T02:54:25.441Z" }, - { url = "https://files.pythonhosted.org/packages/bb/67/d08672f801f604889dcf58f1a0b424fe3808860ede9e03affc1876b295af/wrapt-2.1.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92", size = 145990, upload-time = "2026-03-06T02:53:57.456Z" }, - { url = "https://files.pythonhosted.org/packages/68/a7/fd371b02e73babec1de6ade596e8cd9691051058cfdadbfd62a5898f3295/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf", size = 155670, upload-time = "2026-03-06T02:54:55.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/2d/9fe0095dfdb621009f40117dcebf41d7396c2c22dca6eac779f4c007b86c/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67", size = 144357, upload-time = "2026-03-06T02:54:24.092Z" }, - { url = "https://files.pythonhosted.org/packages/0e/b6/ec7b4a254abbe4cde9fa15c5d2cca4518f6b07d0f1b77d4ee9655e30280e/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a", size = 150269, upload-time = "2026-03-06T02:53:31.268Z" }, - { url = "https://files.pythonhosted.org/packages/6e/6b/2fabe8ebf148f4ee3c782aae86a795cc68ffe7d432ef550f234025ce0cfa/wrapt-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd", size = 59894, upload-time = "2026-03-06T02:54:15.391Z" }, - { url = "https://files.pythonhosted.org/packages/ca/fb/9ba66fc2dedc936de5f8073c0217b5d4484e966d87723415cc8262c5d9c2/wrapt-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f", size = 63197, upload-time = "2026-03-06T02:54:41.943Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1c/012d7423c95d0e337117723eb8ecf73c622ce15a97847e84cf3f8f26cd7e/wrapt-2.1.2-cp313-cp313t-win_arm64.whl", hash = "sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679", size = 60363, upload-time = "2026-03-06T02:54:48.093Z" }, - { url = "https://files.pythonhosted.org/packages/39/25/e7ea0b417db02bb796182a5316398a75792cd9a22528783d868755e1f669/wrapt-2.1.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9", size = 61418, upload-time = "2026-03-06T02:53:55.706Z" }, - { url = "https://files.pythonhosted.org/packages/ec/0f/fa539e2f6a770249907757eaeb9a5ff4deb41c026f8466c1c6d799088a9b/wrapt-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9", size = 61914, upload-time = "2026-03-06T02:52:53.37Z" }, - { url = "https://files.pythonhosted.org/packages/53/37/02af1867f5b1441aaeda9c82deed061b7cd1372572ddcd717f6df90b5e93/wrapt-2.1.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e", size = 120417, upload-time = "2026-03-06T02:54:30.74Z" }, - { url = "https://files.pythonhosted.org/packages/c3/b7/0138a6238c8ba7476c77cf786a807f871672b37f37a422970342308276e7/wrapt-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c", size = 122797, upload-time = "2026-03-06T02:54:51.539Z" }, - { url = "https://files.pythonhosted.org/packages/e1/ad/819ae558036d6a15b7ed290d5b14e209ca795dd4da9c58e50c067d5927b0/wrapt-2.1.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a", size = 117350, upload-time = "2026-03-06T02:54:37.651Z" }, - { url = "https://files.pythonhosted.org/packages/8b/2d/afc18dc57a4600a6e594f77a9ae09db54f55ba455440a54886694a84c71b/wrapt-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90", size = 121223, upload-time = "2026-03-06T02:54:35.221Z" }, - { url = "https://files.pythonhosted.org/packages/b9/5b/5ec189b22205697bc56eb3b62aed87a1e0423e9c8285d0781c7a83170d15/wrapt-2.1.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586", size = 116287, upload-time = "2026-03-06T02:54:19.654Z" }, - { url = "https://files.pythonhosted.org/packages/f7/2d/f84939a7c9b5e6cdd8a8d0f6a26cabf36a0f7e468b967720e8b0cd2bdf69/wrapt-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19", size = 119593, upload-time = "2026-03-06T02:54:16.697Z" }, - { url = "https://files.pythonhosted.org/packages/0b/fe/ccd22a1263159c4ac811ab9374c061bcb4a702773f6e06e38de5f81a1bdc/wrapt-2.1.2-cp314-cp314-win32.whl", hash = "sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508", size = 58631, upload-time = "2026-03-06T02:53:06.498Z" }, - { url = "https://files.pythonhosted.org/packages/65/0a/6bd83be7bff2e7efaac7b4ac9748da9d75a34634bbbbc8ad077d527146df/wrapt-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04", size = 60875, upload-time = "2026-03-06T02:53:50.252Z" }, - { url = "https://files.pythonhosted.org/packages/6c/c0/0b3056397fe02ff80e5a5d72d627c11eb885d1ca78e71b1a5c1e8c7d45de/wrapt-2.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575", size = 59164, upload-time = "2026-03-06T02:53:59.128Z" }, - { url = "https://files.pythonhosted.org/packages/71/ed/5d89c798741993b2371396eb9d4634f009ff1ad8a6c78d366fe2883ea7a6/wrapt-2.1.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb", size = 63163, upload-time = "2026-03-06T02:52:54.873Z" }, - { url = "https://files.pythonhosted.org/packages/c6/8c/05d277d182bf36b0a13d6bd393ed1dec3468a25b59d01fba2dd70fe4d6ae/wrapt-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22", size = 63723, upload-time = "2026-03-06T02:52:56.374Z" }, - { url = "https://files.pythonhosted.org/packages/f4/27/6c51ec1eff4413c57e72d6106bb8dec6f0c7cdba6503d78f0fa98767bcc9/wrapt-2.1.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596", size = 152652, upload-time = "2026-03-06T02:53:23.79Z" }, - { url = "https://files.pythonhosted.org/packages/db/4c/d7dd662d6963fc7335bfe29d512b02b71cdfa23eeca7ab3ac74a67505deb/wrapt-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044", size = 158807, upload-time = "2026-03-06T02:53:35.742Z" }, - { url = "https://files.pythonhosted.org/packages/b4/4d/1e5eea1a78d539d346765727422976676615814029522c76b87a95f6bcdd/wrapt-2.1.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b", size = 146061, upload-time = "2026-03-06T02:52:57.574Z" }, - { url = "https://files.pythonhosted.org/packages/89/bc/62cabea7695cd12a288023251eeefdcb8465056ddaab6227cb78a2de005b/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf", size = 155667, upload-time = "2026-03-06T02:53:39.422Z" }, - { url = "https://files.pythonhosted.org/packages/e9/99/6f2888cd68588f24df3a76572c69c2de28287acb9e1972bf0c83ce97dbc1/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2", size = 144392, upload-time = "2026-03-06T02:54:22.41Z" }, - { url = "https://files.pythonhosted.org/packages/40/51/1dfc783a6c57971614c48e361a82ca3b6da9055879952587bc99fe1a7171/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3", size = 150296, upload-time = "2026-03-06T02:54:07.848Z" }, - { url = "https://files.pythonhosted.org/packages/6c/38/cbb8b933a0201076c1f64fc42883b0023002bdc14a4964219154e6ff3350/wrapt-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7", size = 60539, upload-time = "2026-03-06T02:54:00.594Z" }, - { url = "https://files.pythonhosted.org/packages/82/dd/e5176e4b241c9f528402cebb238a36785a628179d7d8b71091154b3e4c9e/wrapt-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5", size = 63969, upload-time = "2026-03-06T02:54:39Z" }, - { url = "https://files.pythonhosted.org/packages/5c/99/79f17046cf67e4a95b9987ea129632ba8bcec0bc81f3fb3d19bdb0bd60cd/wrapt-2.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00", size = 60554, upload-time = "2026-03-06T02:53:14.132Z" }, - { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" }, +version = "2.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/a4/282c8e64300a59fc834518a54bf0afabb4ff9218b5fa76958b450459a844/wrapt-2.2.2.tar.gz", hash = "sha256:0788e321027c999bf221b667bd4a54aaefd1a36283749a860ac3eb77daed0302", size = 129068, upload-time = "2026-06-20T23:49:44.49Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/85/180b40628b23772692a0c76e8030114e1c0ae068470ed531919f0a5f2a4a/wrapt-2.2.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8417fd3c674d3c8023d080292d29301531a12daf8bd938dd419710dd2f464f2b", size = 81484, upload-time = "2026-06-20T23:47:59.924Z" }, + { url = "https://files.pythonhosted.org/packages/94/f2/21c90f2a16689702e2aaff45795b11018dff2c9b1242bac10d225483f676/wrapt-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0e7070c7472582e31af3dfc2622b2381a0df7435110a9388ed8db5ffbce67efb", size = 82151, upload-time = "2026-06-20T23:48:01.303Z" }, + { url = "https://files.pythonhosted.org/packages/5f/b3/7e6e9fcf4fe7e1b69a49fe6cc5a44e8224bab6283c5233c97e132f14908e/wrapt-2.2.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2e096c9d39a59b35b63c9aacfbbbec2088ff51ff1fc31051acc60a07f42f273a", size = 169828, upload-time = "2026-06-20T23:48:02.719Z" }, + { url = "https://files.pythonhosted.org/packages/0b/43/894f132d857ed5a9904d937baf368badcbe5ea9e436e2f1930fe21c9f1f0/wrapt-2.2.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d1a6050405bf334be33bf66296f113563622972a34900ae6fa60fd283a1a900", size = 171544, upload-time = "2026-06-20T23:48:04.266Z" }, + { url = "https://files.pythonhosted.org/packages/29/de/3c833e03725b477e9ea34028224dd21a48781830101e4e036f77e8b6b102/wrapt-2.2.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:10adb01371408c6de504a6658b9886480f1a4919a83752748a387a504a21df79", size = 160663, upload-time = "2026-06-20T23:48:05.708Z" }, + { url = "https://files.pythonhosted.org/packages/33/be/27edce350b24e3054d9d047f65f16d4c4d4c1f3f31c4278a1f8a95c723c8/wrapt-2.2.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3442eee2a5798f9b451f1b2cd7518ce8b7e28a2a364696c414460a0e295c012a", size = 169387, upload-time = "2026-06-20T23:48:07.243Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c4/9fd9679af8bf38e146652c7f47b6b352c3e5795b4ad1c0b7f94e15ac2aa7/wrapt-2.2.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:6c99012a22f735a85eed7c4b86a3e99c30fdd57d9e115b2b45f796264b58d0bf", size = 158849, upload-time = "2026-06-20T23:48:08.91Z" }, + { url = "https://files.pythonhosted.org/packages/bc/c2/aa6c0c2206803068c6859dabe01f8c84c43744da93d4c67b8946d21655ee/wrapt-2.2.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3b686cfc008776a3952d6213cb296ed7f45d782a8453936406faa89eac0835ab", size = 168147, upload-time = "2026-06-20T23:48:10.374Z" }, + { url = "https://files.pythonhosted.org/packages/42/63/3eb25da41049d20ae18fcab2dd8b056e02387c4bfa626cbdfb7c3b872e4f/wrapt-2.2.2-cp312-cp312-win32.whl", hash = "sha256:ef2cce266b5b0b07e19fa82e59673b81142b7a3607c8ed1254113d048ed668da", size = 77734, upload-time = "2026-06-20T23:48:11.769Z" }, + { url = "https://files.pythonhosted.org/packages/da/09/0390e008a305360948fa9ce69507d041ac12cb2ee5d28e34467e2ee79391/wrapt-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:abf8c20a2d72ee69e16328b3c91342c446e723bfe48bfcc4dded3b9722ac027f", size = 80585, upload-time = "2026-06-20T23:48:13.117Z" }, + { url = "https://files.pythonhosted.org/packages/d3/b3/84c445c66969f2d3457276b183a48c91097d59bbef9af6c075366b0f8c36/wrapt-2.2.2-cp312-cp312-win_arm64.whl", hash = "sha256:c6c64c5d02578bc4c4bca4f0aef1504de933c1d5b4ac2710b9131111459506c8", size = 79553, upload-time = "2026-06-20T23:48:14.5Z" }, + { url = "https://files.pythonhosted.org/packages/43/fc/f32f4b22c6511173c11d9e541ab4e7d8467a0f1b3455acaf784115d31ff8/wrapt-2.2.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9e8b648270c613720a202d9a45ebabc33261b22c3a839b115ac5bce8c0bb0d69", size = 81296, upload-time = "2026-06-20T23:48:15.881Z" }, + { url = "https://files.pythonhosted.org/packages/72/06/4d117d5d77a9344776c0248b24dae3d3dd2f58e5f765fa08cf887072e719/wrapt-2.2.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6fb7e94e8fe3e4c3067bb1653a91cce7c5e83acc119fdd41501b1bf74654617", size = 81841, upload-time = "2026-06-20T23:48:17.262Z" }, + { url = "https://files.pythonhosted.org/packages/15/ff/63ad96f98eb58a742b1a20d80f21da88924405910149950b912368150468/wrapt-2.2.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb18fc51e813df0d9c98049e3bf2298a5495a648602040e21fa3c7329371159e", size = 167882, upload-time = "2026-06-20T23:48:18.764Z" }, + { url = "https://files.pythonhosted.org/packages/20/1f/8bb62d8933df7acf3247194e6e9fc68edf9d2fa203252c89c94b319dd472/wrapt-2.2.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94b00b00f806eb3ef2abe9049ed45994a81ee9284884d96e6b8314927c6cea3d", size = 167411, upload-time = "2026-06-20T23:48:20.315Z" }, + { url = "https://files.pythonhosted.org/packages/17/09/8789dcb09ee1de715727db7521aabbb68ffa68dfade3a49468440cfced49/wrapt-2.2.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:62415fd095bc590b842b6d092f2b5d9ccbaeb7e0b28535c03dcea2718b48636b", size = 158607, upload-time = "2026-06-20T23:48:21.728Z" }, + { url = "https://files.pythonhosted.org/packages/9c/20/66e02562d53ee67d841f175e38e3c993c2d78a3e104c576cad61c028b43c/wrapt-2.2.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a41e758d80dc0ab8c210f641ac892009d356cf1f955d97db544c8dd317b4d14c", size = 166367, upload-time = "2026-06-20T23:48:23.177Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a3/832ac4e41222fb263b3042d42c2f08d305db7d0f0c9b1d3a271a9eede8f6/wrapt-2.2.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b84cd4058001c9727b0e9980b7a9e66325b5ca748b1b578e822cade1bc6b304f", size = 157176, upload-time = "2026-06-20T23:48:24.711Z" }, + { url = "https://files.pythonhosted.org/packages/b7/01/1bd5e4d2df9c0178989ac8da9186543465388588ee2ef153e2591accebef/wrapt-2.2.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:26fc73a1b15e0946d2942b9a4426d162b51676338327dc067ccd8d2d76385f94", size = 167025, upload-time = "2026-06-20T23:48:26.118Z" }, + { url = "https://files.pythonhosted.org/packages/1c/69/583ed25291ab53e1ec117135fb1c33425e2f46d2bc8f29c17f7a94cf4274/wrapt-2.2.2-cp313-cp313-win32.whl", hash = "sha256:3c4095803491f6ef72128914c28ec05bbad9758433bb35f6715a3e9c8e46fb2d", size = 77605, upload-time = "2026-06-20T23:48:27.643Z" }, + { url = "https://files.pythonhosted.org/packages/29/68/e69fc6d06e1523c68e0d00f95c9aed1158ce9908ee41603f7f2eae3d5db6/wrapt-2.2.2-cp313-cp313-win_amd64.whl", hash = "sha256:2cb07f414fab25dbe6b5c7398e1491423a5c81a6209533639969a6c928d474a4", size = 80508, upload-time = "2026-06-20T23:48:29.013Z" }, + { url = "https://files.pythonhosted.org/packages/55/21/fe7a393d9e5dc0923bed8f5d857e9dcff210f1fa0888c02cc8f3ffaa55aa/wrapt-2.2.2-cp313-cp313-win_arm64.whl", hash = "sha256:1fc7691f070220215cccb2a20836b9adbaecb8ff22ad47abe63de5f110994fac", size = 79565, upload-time = "2026-06-20T23:48:30.429Z" }, + { url = "https://files.pythonhosted.org/packages/b6/e5/c120d13bf5091164f68c3c1657e84f16f57e71d978421b626393ac5bd7eb/wrapt-2.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ec8f83949028366531383603139403cac7a826e4011955813cdd640017845ce5", size = 83264, upload-time = "2026-06-20T23:48:31.807Z" }, + { url = "https://files.pythonhosted.org/packages/d3/b0/d4a1eb97e0e286625bdf21bc7f702637f9607787ffbbdb5ec14d50c79dbf/wrapt-2.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4b481fb0c40d9fd90a5809911208da700987d373a20a4709dc9e3944af7a6bec", size = 83791, upload-time = "2026-06-20T23:48:33.482Z" }, + { url = "https://files.pythonhosted.org/packages/18/1e/f060df47755e87b57684cee7bfc1362b204df55fac96ffebc0631b697b79/wrapt-2.2.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0065a3b657cec06813b4241d2462ccec287f6863103d7445b725fb3a889736f9", size = 203399, upload-time = "2026-06-20T23:48:34.97Z" }, + { url = "https://files.pythonhosted.org/packages/c4/de/2316a757a1abb6453700b79d83e532146dcef2611348282d4d8889792161/wrapt-2.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:30f7424af5c5c345b7f26490e097f74a2ef45b3d08b664dc33571aee3bd3b56c", size = 210461, upload-time = "2026-06-20T23:48:36.569Z" }, + { url = "https://files.pythonhosted.org/packages/ed/29/d1160785ae18ca2495a6d82a21154103d74f656c9fd457fb35f6b11b965a/wrapt-2.2.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:07fdcb012821859168641acf68afad61ef9783cf37100af85f152550e9677194", size = 195313, upload-time = "2026-06-20T23:48:38.175Z" }, + { url = "https://files.pythonhosted.org/packages/f5/2d/7caa9598ae61a9cf0989cc501739cbeeb7d650ab3193cca1407b9af0c6ab/wrapt-2.2.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f90038ab58fafb584801ca62d72384d7d5225d93c76f7b773c22fae545bd8066", size = 206116, upload-time = "2026-06-20T23:48:39.804Z" }, + { url = "https://files.pythonhosted.org/packages/ac/02/281ea1088b8650d865f311b35cf86fd21df89128e2909714f1161e01c9d0/wrapt-2.2.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:c5d7825491bfa2d08b97e9557768987952c7b9ae687d06c3320b40a37ccb7f20", size = 192668, upload-time = "2026-06-20T23:48:41.346Z" }, + { url = "https://files.pythonhosted.org/packages/be/7d/976e2d5b4b5c5babda40974edd54d0a5585cb60132ed86b46f4b80239b16/wrapt-2.2.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0ad520e6daa9bbf136f14de735474dbec7dcc0891f718e1d274ce8dc92e645af", size = 198891, upload-time = "2026-06-20T23:48:43.056Z" }, + { url = "https://files.pythonhosted.org/packages/59/b7/e47651797c097f75a37e2ce86dcf04048ff576f3a674f7c558df7b5e9622/wrapt-2.2.2-cp313-cp313t-win32.whl", hash = "sha256:25904acb9475f46c24fe0423dbc8fda8cc5fbc282ab3dc6e72e919748c53f4e9", size = 78537, upload-time = "2026-06-20T23:48:44.509Z" }, + { url = "https://files.pythonhosted.org/packages/d1/6f/9fa5d59fb06d890defb5a8f727ce6a14d2932c8760153f96956628559fee/wrapt-2.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:305d4c247d61c4115794a169141823c62f719525ddb90b23aa332741c77d2c28", size = 82005, upload-time = "2026-06-20T23:48:46.391Z" }, + { url = "https://files.pythonhosted.org/packages/15/80/4c7bd9873d1f9f7d138d93556b500469dbe24f42710b877519c2b9eb380d/wrapt-2.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c20279cd1a29800815d7b2d6338b60a6c6e78263f9d6e62e0eda251ba9cae2d0", size = 80762, upload-time = "2026-06-20T23:48:47.964Z" }, + { url = "https://files.pythonhosted.org/packages/24/05/7fd9c3f83b2c74cbfc572a0b88aa37431e04bd8aed70d2c0efd3464206de/wrapt-2.2.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0e64826f920c42d9d9f87e8cc09ffae66c51ede12d59061a5a426deb9aa71745", size = 81341, upload-time = "2026-06-20T23:48:49.39Z" }, + { url = "https://files.pythonhosted.org/packages/4b/68/1bfa43100dd90d4ef74a05897b86275cf57e1313ca14aae2545bc9f872c9/wrapt-2.2.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dcaa5e1451bd8751d7bd1568dfa3321c78092a52a7ecb5d1a0f18a5791e1fd00", size = 81921, upload-time = "2026-06-20T23:48:50.986Z" }, + { url = "https://files.pythonhosted.org/packages/74/eb/df7b7f0b631dbbc750f39be27d8b55f65777d8ac86da80e12be41a644c4b/wrapt-2.2.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0abfd648dac9ac9c5b3aa9b523d27f1789046640b58dcd5652a720ddb325e1fc", size = 167713, upload-time = "2026-06-20T23:48:52.598Z" }, + { url = "https://files.pythonhosted.org/packages/4d/9a/d1bd36f6d088c8e652a9383cabbd49af30b8c576302a7eccddbab6963e3f/wrapt-2.2.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f4bfd8d1eb438153eff8b8cfe87f032ba65731e1ce06138b5090f745a33f6f95", size = 166779, upload-time = "2026-06-20T23:48:54.33Z" }, + { url = "https://files.pythonhosted.org/packages/4c/ae/24ffacd4187fac2740a1972093929e836dea092d42c87d728cd98fee11a6/wrapt-2.2.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c427c9d06d859848a69f0d928fe28b5c33a941b2265d10a0e1f15cd244f1ee33", size = 158407, upload-time = "2026-06-20T23:48:55.944Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ed/974427668249a356051e8d67d47fa54ef6c777f0fcf3bae9d292c047d4b6/wrapt-2.2.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4250b43d1a129d947e083c4dc6baf333c9bb34edd26f912d5b0457841fc858ab", size = 166594, upload-time = "2026-06-20T23:48:57.617Z" }, + { url = "https://files.pythonhosted.org/packages/fb/5f/e1d7c6e4523f78db2fbd7826babd0348da1d5e0834c4f918b9ab5757dfae/wrapt-2.2.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:173e5bb5ca350a6e0abab60b7ec7cdd7992a814cb14b4de670a28f067f105663", size = 157068, upload-time = "2026-06-20T23:48:59.171Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c1/7ebd1027f00700c0b0233b20aceef2b4784294ed64971424c4a78e069e34/wrapt-2.2.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:aa14b01804bce36c6d63d7b6a4f55df390f29f8648cc13a1f40b166f4d54680d", size = 166470, upload-time = "2026-06-20T23:49:00.737Z" }, + { url = "https://files.pythonhosted.org/packages/99/eb/974e471a6a978b8180186b8a9dc5ae3361ce269a967190b709b8ce17abfb/wrapt-2.2.2-cp314-cp314-win32.whl", hash = "sha256:58f9f8d637c9a6e245c6ef5b109b67ec187d2faed23d1405656b51d96e0a5b56", size = 78062, upload-time = "2026-06-20T23:49:02.327Z" }, + { url = "https://files.pythonhosted.org/packages/49/ec/e1281156cdc7a66693838ad7a0865ad641c74abd337a957d668b575aaffb/wrapt-2.2.2-cp314-cp314-win_amd64.whl", hash = "sha256:385cb1866f20479e83299af585375bfa0a4b0c6c9907a981483ea782ea8ae406", size = 80832, upload-time = "2026-06-20T23:49:03.837Z" }, + { url = "https://files.pythonhosted.org/packages/45/7d/1b6b5ddd94005a2dac97a4490c9838f3154977850d633abcb65b30089437/wrapt-2.2.2-cp314-cp314-win_arm64.whl", hash = "sha256:8ffbeaea6771a6eba6e6eeb09767864995726bc8240bb54baf88a9bb1db34d5c", size = 80029, upload-time = "2026-06-20T23:49:05.237Z" }, + { url = "https://files.pythonhosted.org/packages/b0/33/9ebcf8aafe91c601127cbd93708c16aa8f688f34a10bf004046803ecdc4f/wrapt-2.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:09f811d43f6f33ec7515f0be76b159569f4057ab54d3e079c3204dddb90afa2a", size = 83357, upload-time = "2026-06-20T23:49:06.632Z" }, + { url = "https://files.pythonhosted.org/packages/39/38/ec45b635153327b52e52732a0ea980e5f00b7efba65f9e018828f1e69daa/wrapt-2.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a795d3c06e5fbf9ea2f13196180b77aeab1b4685917256ee0d014cc163d90063", size = 83794, upload-time = "2026-06-20T23:49:08.098Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ea/1a89e6d3b7a83c3affe5c09cde77792c947e63e4bc85ad84cd5bb9abb0d8/wrapt-2.2.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:45c2f2768e790c9f8db90f239ef23a2af8e7570f25a35619ef902df4a738447f", size = 203362, upload-time = "2026-06-20T23:49:09.811Z" }, + { url = "https://files.pythonhosted.org/packages/19/d8/3b58763d9863b5a73771c0d97110f9595d248db454009e07e1535ee905a4/wrapt-2.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bbf00ee0cb55ec24e2b0995a71942b85b21a066db8f3f46e1dbfdb9433ffba81", size = 210449, upload-time = "2026-06-20T23:49:11.521Z" }, + { url = "https://files.pythonhosted.org/packages/2d/6f/17fd9e053103d8be148d20d5d7505facc72d5fe1f9127973904ceaed79cf/wrapt-2.2.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2252f77663651b89255895f58cc6ac08fcb206d4371813e5af61bb62d4f7689c", size = 195349, upload-time = "2026-06-20T23:49:13.346Z" }, + { url = "https://files.pythonhosted.org/packages/ef/04/d0d1ccaaa12cb7dccf28a23f0279a608ba498f71e81d949d5ed54bcfd5c1/wrapt-2.2.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2cd7181ab1c31192ff5219269830744b5a62020b3a6d433588c4f1c95b8f8bff", size = 206099, upload-time = "2026-06-20T23:49:15.051Z" }, + { url = "https://files.pythonhosted.org/packages/44/b3/e8aa07b619890a2aa6cde1931b1887abb08820721b564a5f80b7ca3f3aa0/wrapt-2.2.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:6fe35fd51b74867d8b80174c277bd6bbf6a73e443f908129dc531c4b688a20d5", size = 192728, upload-time = "2026-06-20T23:49:16.854Z" }, + { url = "https://files.pythonhosted.org/packages/b7/f0/1819fb50f0d3c9bd758d8a83b56f1b470dee8b5b8eac8702b7c137cea9d4/wrapt-2.2.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:11d95fc2fbad3163596c39d440e6f21ca9fccece74b56e30a37ac2fca786a07c", size = 198842, upload-time = "2026-06-20T23:49:18.504Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/e88313f16a99930b899ef970d91c281544a470749a359decad994483bbda/wrapt-2.2.2-cp314-cp314t-win32.whl", hash = "sha256:d8a15813215f33fa83667bfc978b300e35669ea8bb424e970a1426bcb7bc6cca", size = 79059, upload-time = "2026-06-20T23:49:20.107Z" }, + { url = "https://files.pythonhosted.org/packages/a0/4f/ac12fda57a55068a094ec42851fb0a40e8489d8941863d517452de62e507/wrapt-2.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d09db0f7e8357060d3c38fc22a018aba683a796bf184360fd1a58f6fc180dc77", size = 82462, upload-time = "2026-06-20T23:49:21.631Z" }, + { url = "https://files.pythonhosted.org/packages/48/a7/df732dac86d9b2027c56bd163dbc883e037b16c3469614752e148d219c61/wrapt-2.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:f32fe639c39561ccc187bcae17e9271be0eb45f1c2952510d2f29b33ab577347", size = 81182, upload-time = "2026-06-20T23:49:23.199Z" }, + { url = "https://files.pythonhosted.org/packages/6e/d2/6317eb6d4554855bbf12d61857774af34747bf88a42c19bf306de67e2fa3/wrapt-2.2.2-py3-none-any.whl", hash = "sha256:5bad217350f19ce99ca5b5e71d406765ea86fe541628426772b657375ee1c048", size = 61460, upload-time = "2026-06-20T23:49:42.966Z" }, ]