feat: Working app!
commit
3409e41680
|
@ -0,0 +1,16 @@
|
|||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.yaml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
|
@ -0,0 +1,10 @@
|
|||
# Python-generated files
|
||||
__pycache__/
|
||||
*.py[oc]
|
||||
build/
|
||||
dist/
|
||||
wheels/
|
||||
*.egg-info
|
||||
|
||||
# Virtual environments
|
||||
.venv
|
|
@ -0,0 +1 @@
|
|||
3.11
|
|
@ -0,0 +1,9 @@
|
|||
# `blext`: Effortlessly Manage Blender Extension Projects
|
||||
Documentation TBD.
|
||||
Quite experimental for the moment.
|
||||
|
||||
For now just do
|
||||
|
||||
`uvx blext`
|
||||
|
||||
for the help text.
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
"""Runs when the package is executed.
|
||||
|
||||
Executes the `typer` app defined in `cli.py`.
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
from blext.cli import app
|
||||
|
||||
app()
|
|
@ -0,0 +1,87 @@
|
|||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
|
||||
####################
|
||||
# - Constants
|
||||
####################
|
||||
# Addon Name
|
||||
if os.environ.get('BLEXT_ADDON_NAME') is not None:
|
||||
BLEXT_ADDON_NAME = os.environ['BLEXT_ADDON_NAME']
|
||||
else:
|
||||
msg = "The env var 'BLEXT_ADDON_NAME' must be available, but it is not"
|
||||
raise ValueError(msg)
|
||||
|
||||
# Zip Path
|
||||
if os.environ.get('BLEXT_ZIP_PATH') is not None:
|
||||
BLEXT_ZIP_PATH = Path(os.environ['BLEXT_ZIP_PATH'])
|
||||
else:
|
||||
msg = "The env var 'BLEXT_ZIP_PATH' must be available, but it is not"
|
||||
raise ValueError(msg)
|
||||
|
||||
# Local Path
|
||||
if os.environ.get('BLEXT_ZIP_PATH') is not None:
|
||||
BLEXT_LOCAL_PATH = Path(os.environ['BLEXT_LOCAL_PATH'])
|
||||
else:
|
||||
msg = "The env var 'BLEXT_LOCAL_PATH' must be available, but it is not"
|
||||
raise ValueError(msg)
|
||||
|
||||
BLEXT_DEV_REPO_NAME = 'blext_dev_repo'
|
||||
|
||||
|
||||
####################
|
||||
# - main()
|
||||
####################
|
||||
if __name__ == '__main__':
|
||||
# Suppress Splash Screen
|
||||
## - It just gets in the way.
|
||||
bpy.context.preferences.view.show_splash = False
|
||||
|
||||
# Check for Local Dev Repo
|
||||
## - Create if non-existant.
|
||||
if BLEXT_DEV_REPO_NAME not in {
|
||||
repo.name for repo in bpy.context.preferences.extensions.repos
|
||||
}:
|
||||
bpy.ops.preferences.extension_repo_add(
|
||||
name=BLEXT_DEV_REPO_NAME,
|
||||
remote_url='',
|
||||
use_access_token=False,
|
||||
use_sync_on_startup=False,
|
||||
# use_custom_directory=True,
|
||||
# custom_directory=str(BLEXT_LOCAL_PATH),
|
||||
type='LOCAL',
|
||||
)
|
||||
|
||||
# Get the Repository Index of Local Dev Repo
|
||||
dev_repo_idx = next(
|
||||
repo_idx
|
||||
for repo_idx, repo in enumerate(bpy.context.preferences.extensions.repos)
|
||||
if repo.name == BLEXT_DEV_REPO_NAME
|
||||
)
|
||||
dev_repo_module = next(
|
||||
repo.module
|
||||
for repo_idx, repo in enumerate(bpy.context.preferences.extensions.repos)
|
||||
if repo.name == BLEXT_DEV_REPO_NAME
|
||||
)
|
||||
|
||||
# Uninstall Existing Addon
|
||||
if BLEXT_ADDON_NAME in bpy.context.preferences.addons.keys(): # noqa: SIM118
|
||||
bpy.ops.extensions.package_uninstall(
|
||||
repo_index=dev_repo_idx,
|
||||
pkg_id=BLEXT_ADDON_NAME,
|
||||
)
|
||||
|
||||
# Vacuum sys.modules (remove bl_ext.<addon_repo>.<addon_name>)
|
||||
## - TODO: Start conversation upstream abt. overlapping dependencies.
|
||||
if f'blext.{BLEXT_DEV_REPO_NAME}.{BLEXT_ADDON_NAME}' in sys.modules:
|
||||
del sys.modules[BLEXT_ADDON_NAME]
|
||||
|
||||
# Install New Extension
|
||||
bpy.ops.extensions.package_install_files(
|
||||
#'INVOKE_DEFAULT',
|
||||
repo=dev_repo_module,
|
||||
filepath=str(BLEXT_ZIP_PATH),
|
||||
enable_on_install=True,
|
||||
)
|
|
@ -0,0 +1,275 @@
|
|||
"""A `typer`-based command line interface for the `blext` project manager."""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import typing as typ
|
||||
from pathlib import Path
|
||||
|
||||
import cyclopts
|
||||
import rich
|
||||
|
||||
from . import finders, pack, spec, supported, wheels
|
||||
|
||||
console = rich.console.Console()
|
||||
app = cyclopts.App(
|
||||
name='blext',
|
||||
help='blext simplifies the development and management of Blender extensions.',
|
||||
help_format='markdown',
|
||||
)
|
||||
|
||||
####################
|
||||
# - Constants
|
||||
####################
|
||||
PATH_BL_INIT_PY: Path = (
|
||||
Path(__file__).resolve().parent / 'blender_python' / 'bl_init.py'
|
||||
)
|
||||
|
||||
|
||||
####################
|
||||
# - Command: Build
|
||||
####################
|
||||
@app.command()
|
||||
def build(
|
||||
bl_platform: supported.BLPlatform | None = None,
|
||||
proj_path: Path | None = None,
|
||||
release_profile: supported.ReleaseProfile = supported.ReleaseProfile.Release,
|
||||
_return_blext_spec: typ.Annotated[bool, cyclopts.Parameter(show=False)] = False,
|
||||
) -> spec.BLExtSpec | None:
|
||||
"""[Build] the extension to an installable `.zip` file.
|
||||
|
||||
Parameters:
|
||||
bl_platform: The Blender platform to build the extension for.
|
||||
proj_path: Path to a `pyproject.toml` or a folder containing a `pyproject.toml`, which specifies the Blender extension.
|
||||
release_profile: The release profile to bake into the extension.
|
||||
"""
|
||||
# Parse CLI
|
||||
if bl_platform is None:
|
||||
bl_platform = finders.detect_local_blplatform()
|
||||
path_proj_spec = finders.find_proj_spec(proj_path)
|
||||
|
||||
# Parse Blender Extension Specification
|
||||
blext_spec = spec.BLExtSpec.from_proj_spec(
|
||||
path_proj_spec=path_proj_spec,
|
||||
release_profile=release_profile,
|
||||
).constrain_to_bl_platform(bl_platform)
|
||||
|
||||
# Download Wheels
|
||||
wheels.download_wheels(
|
||||
blext_spec,
|
||||
bl_platform=bl_platform,
|
||||
no_prompt=False,
|
||||
)
|
||||
|
||||
# Pack the Blender Extension
|
||||
pack.pack_bl_extension(blext_spec, overwrite=True)
|
||||
|
||||
# Validate the Blender Extension
|
||||
console.print()
|
||||
console.rule('[bold green]Extension Validation')
|
||||
|
||||
console.print('[italic]$ blender --factory-startup --command extension validate')
|
||||
console.print()
|
||||
console.rule(characters='--', style='gray')
|
||||
|
||||
blender_exe = finders.find_blender_exe()
|
||||
bl_process = subprocess.Popen(
|
||||
[
|
||||
blender_exe,
|
||||
'--factory-startup', ## Temporarily Disable All Addons
|
||||
'--command', ## Validate an Extension
|
||||
'extension',
|
||||
'validate',
|
||||
str(blext_spec.path_zip),
|
||||
],
|
||||
bufsize=0, ## TODO: Check if -1 is OK
|
||||
env=os.environ,
|
||||
stdout=sys.stdout,
|
||||
stderr=sys.stderr,
|
||||
)
|
||||
return_code = bl_process.wait()
|
||||
|
||||
console.rule(characters='--', style='gray')
|
||||
console.print()
|
||||
if return_code == 0:
|
||||
console.print('[✔] Blender Extension Validation Succeeded')
|
||||
else:
|
||||
msg = 'Packed extension did not validate'
|
||||
raise ValueError(msg)
|
||||
|
||||
if _return_blext_spec:
|
||||
return blext_spec
|
||||
return None
|
||||
|
||||
|
||||
####################
|
||||
# - Command: Dev
|
||||
####################
|
||||
@app.command()
|
||||
def dev(
|
||||
proj_path: Path | None = None,
|
||||
) -> None:
|
||||
"""Launch the local [dev] extension w/Blender."""
|
||||
# Build the Extension
|
||||
blext_spec = build(
|
||||
bl_platform=None,
|
||||
proj_path=proj_path,
|
||||
release_profile=supported.ReleaseProfile.Dev,
|
||||
_return_blext_spec=True,
|
||||
)
|
||||
|
||||
# Find Blender
|
||||
blender_exe = finders.find_blender_exe()
|
||||
|
||||
# Launch Blender
|
||||
## - Same as running 'blender --python ./blender_scripts/bl_init.py'
|
||||
console.print()
|
||||
console.rule('[bold green]Running Blender')
|
||||
console.print('[italic]$ blender --factory-startup --python .../bl_init.py')
|
||||
console.print()
|
||||
console.rule(characters='--', style='gray')
|
||||
|
||||
bl_process = subprocess.Popen(
|
||||
[
|
||||
blender_exe,
|
||||
'--python',
|
||||
str(PATH_BL_INIT_PY),
|
||||
'--factory-startup', ## Temporarily Disable All Addons
|
||||
],
|
||||
bufsize=0, ## TODO: Check if -1 is OK
|
||||
env=os.environ
|
||||
| {
|
||||
'BLEXT_ADDON_NAME': blext_spec.id,
|
||||
'BLEXT_ZIP_PATH': str(blext_spec.path_zip),
|
||||
'BLEXT_LOCAL_PATH': str(blext_spec.path_local),
|
||||
},
|
||||
stdout=sys.stdout,
|
||||
stderr=sys.stderr,
|
||||
)
|
||||
bl_process.wait()
|
||||
|
||||
|
||||
####################
|
||||
# - Command: Show
|
||||
####################
|
||||
app_show = cyclopts.App(name='show', help='[Show] information about the extension.')
|
||||
app.command(app_show)
|
||||
|
||||
|
||||
####################
|
||||
# - Command: Show Spec
|
||||
####################
|
||||
@app_show.command(name='spec', group='Information')
|
||||
def show_spec(
|
||||
bl_platform: supported.BLPlatform | None = None,
|
||||
proj_path: Path | None = None,
|
||||
release_profile: supported.ReleaseProfile = supported.ReleaseProfile.Release,
|
||||
) -> None:
|
||||
"""Print the complete extension specification.
|
||||
|
||||
Parameters:
|
||||
bl_platform: The Blender platform to build the extension for.
|
||||
proj_path: Path to a `pyproject.toml` or a folder containing a `pyproject.toml`, which specifies the Blender extension.
|
||||
release_profile: The release profile to bake into the extension.
|
||||
"""
|
||||
# Parse BLExtSpec
|
||||
path_proj_spec = finders.find_proj_spec(proj_path)
|
||||
blext_spec = spec.BLExtSpec.from_proj_spec(
|
||||
path_proj_spec=path_proj_spec,
|
||||
release_profile=release_profile,
|
||||
)
|
||||
|
||||
# Constrain BLExtSpec to BLPlatform
|
||||
if bl_platform is not None:
|
||||
blext_spec = blext_spec.constrain_to_bl_platform(bl_platform)
|
||||
|
||||
# Show BLExtSpec
|
||||
rich.print(blext_spec)
|
||||
|
||||
|
||||
####################
|
||||
# - Command: Show Manifest
|
||||
####################
|
||||
@app_show.command(name='bl_manifest', group='Information')
|
||||
def show_bl_manifest(
|
||||
bl_platform: supported.BLPlatform | None = None,
|
||||
proj_path: Path | None = None,
|
||||
release_profile: supported.ReleaseProfile = supported.ReleaseProfile.Release,
|
||||
) -> None:
|
||||
"""Print the generated Blender extension manifest.
|
||||
|
||||
Parameters:
|
||||
bl_platform: The Blender platform to build the extension for.
|
||||
proj_path: Path to a `pyproject.toml` or a folder containing a `pyproject.toml`, which specifies the Blender extension.
|
||||
release_profile: The release profile to bake into the extension.
|
||||
"""
|
||||
# Parse CLI
|
||||
if bl_platform is None:
|
||||
bl_platform = finders.detect_local_blplatform()
|
||||
path_proj_spec = finders.find_proj_spec(proj_path)
|
||||
|
||||
# Parse Blender Extension Specification
|
||||
blext_spec = spec.BLExtSpec.from_proj_spec(
|
||||
path_proj_spec=path_proj_spec,
|
||||
release_profile=release_profile,
|
||||
)
|
||||
|
||||
# [Maybe] Constrain BLExtSpec to BLPlatform
|
||||
if bl_platform is not None:
|
||||
blext_spec = blext_spec.constrain_to_bl_platform(bl_platform)
|
||||
|
||||
# Show BLExtSpec
|
||||
rich.print(blext_spec.manifest_str)
|
||||
|
||||
|
||||
@app_show.command(name='init_settings', group='Information')
|
||||
def show_init_settings(
|
||||
bl_platform: supported.BLPlatform | None = None,
|
||||
proj_path: Path | None = None,
|
||||
release_profile: supported.ReleaseProfile = supported.ReleaseProfile.Release,
|
||||
) -> None:
|
||||
"""Print the generated initial settings for the release profile.
|
||||
|
||||
Parameters:
|
||||
bl_platform: The Blender platform to build the extension for.
|
||||
proj_path: Path to a `pyproject.toml` or a folder containing a `pyproject.toml`, which specifies the Blender extension.
|
||||
release_profile: The release profile to bake into the extension.
|
||||
"""
|
||||
# Parse CLI
|
||||
if bl_platform is None:
|
||||
bl_platform = finders.detect_local_blplatform()
|
||||
path_proj_spec = finders.find_proj_spec(proj_path)
|
||||
|
||||
# Parse Blender Extension Specification
|
||||
blext_spec = spec.BLExtSpec.from_proj_spec(
|
||||
path_proj_spec=path_proj_spec,
|
||||
release_profile=release_profile,
|
||||
)
|
||||
|
||||
# [Maybe] Constrain BLExtSpec to BLPlatform
|
||||
if bl_platform is not None:
|
||||
blext_spec = blext_spec.constrain_to_bl_platform(bl_platform)
|
||||
|
||||
# Show BLExtSpec
|
||||
rich.print(blext_spec.init_settings_str)
|
||||
|
||||
|
||||
####################
|
||||
# - Command: Show Path to Blender
|
||||
####################
|
||||
@app_show.command(name='path_blender', group='Paths')
|
||||
def show_path_blender() -> None:
|
||||
"""Display the found path to the Blender executable."""
|
||||
blender_exe = finders.find_blender_exe()
|
||||
|
||||
rich.print(blender_exe)
|
||||
|
||||
|
||||
####################
|
||||
# - Command: Help and Version
|
||||
####################
|
||||
app['--help'].group = 'Debug'
|
||||
app['--version'].group = 'Debug'
|
||||
|
||||
app_show['--help'].group = 'Debug'
|
||||
app_show['--version'].group = 'Debug'
|
|
@ -0,0 +1,108 @@
|
|||
"""Tools for finding common information and files using platform-specific methods."""
|
||||
|
||||
import platform
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from .supported import BLPlatform
|
||||
|
||||
|
||||
def detect_local_blplatform() -> BLPlatform:
|
||||
"""Deduce the local Blender platform from `platform.system()` and `platform.machine()`.
|
||||
|
||||
References:
|
||||
Architecture Strings on Linus: <https://stackoverflow.com/questions/45125516/possible-values-for-uname-m>
|
||||
|
||||
Warnings:
|
||||
This method is mostly untested, especially on Windows.
|
||||
|
||||
Returns:
|
||||
A local operating system supported by Blender, conforming to the format expected by Blender's extension manifest.
|
||||
|
||||
Raises:
|
||||
ValueError: If a Blender-supported operating system could not be detected locally.
|
||||
"""
|
||||
platform_system = platform.system().lower()
|
||||
platform_machine = platform.machine().lower()
|
||||
|
||||
match (platform_system, platform_machine):
|
||||
case ('linux', 'x86_64' | 'amd64'):
|
||||
return BLPlatform.linux_x64
|
||||
case ('linux', arch) if arch.startswith(('aarch64', 'arm')):
|
||||
return BLPlatform.linux_arm64
|
||||
case ('darwin', 'x86_64' | 'amd64'):
|
||||
return BLPlatform.macos_x64
|
||||
case ('darwin', arch) if arch.startswith(('aarch64', 'arm')):
|
||||
return BLPlatform.macos_arm64
|
||||
case ('linux', 'x86_64' | 'amd64'):
|
||||
return BLPlatform.windows_x64
|
||||
case ('linux', arch) if arch.startswith(('aarch64', 'arm')):
|
||||
return BLPlatform.windows_arm64
|
||||
|
||||
msg = "Could not detect a local operating system supported by Blender from 'platform.system(), platform.machine() = {platform_system}, {platform_machine}'"
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
def find_proj_spec(proj_path: Path | None) -> Path:
|
||||
"""Locate the project specification using the given hint.
|
||||
|
||||
Parameters:
|
||||
proj_path: Search for a `pyproject.toml` project specification at this path.
|
||||
When `None`, search in the current working directory.
|
||||
"""
|
||||
if proj_path is None:
|
||||
path_proj_spec = Path.cwd() / 'pyproject.toml'
|
||||
if not path_proj_spec.is_file():
|
||||
symptom = (
|
||||
'does not exist' if not path_proj_spec.exists() else 'is not a file'
|
||||
)
|
||||
msg = "Couldn't find 'pyproject.toml' in the current working directory"
|
||||
|
||||
elif proj_path.is_dir() and (proj_path / 'pyproject.toml').is_file():
|
||||
path_proj_spec = proj_path / 'pyproject.toml'
|
||||
|
||||
elif not proj_path.is_file():
|
||||
symptom = 'does not exist' if not proj_path.exists() else 'is not a file'
|
||||
msg = f"Provided project specification {symptom} (tried to load '{proj_path}')"
|
||||
raise ValueError(msg)
|
||||
|
||||
return path_proj_spec.resolve()
|
||||
|
||||
|
||||
def find_blender_exe() -> str:
|
||||
"""Locate the Blender executable, using the current platform as a hint.
|
||||
|
||||
Parameters:
|
||||
os: The currently supported operating system.
|
||||
|
||||
Returns:
|
||||
Absolute path to a valid Blender executable, as a string.
|
||||
"""
|
||||
bl_platform = detect_local_blplatform()
|
||||
match bl_platform:
|
||||
case BLPlatform.linux_x64:
|
||||
blender_exe = shutil.which('blender')
|
||||
if blender_exe is not None:
|
||||
return blender_exe
|
||||
|
||||
msg = "Couldn't find executable command 'blender' on the system PATH. Is it installed?"
|
||||
raise ValueError(msg)
|
||||
|
||||
case BLPlatform.macos_arm64:
|
||||
blender_exe = '/Applications/Blender.app/Contents/MacOS/Blender'
|
||||
if Path(blender_exe).is_file():
|
||||
return blender_exe
|
||||
|
||||
msg = f"Couldn't find Blender executable at standard path. Is it installed? (searched '{blender_exe}')"
|
||||
raise ValueError(msg)
|
||||
|
||||
case BLPlatform.windows_x64:
|
||||
blender_exe = shutil.which('blender.exe')
|
||||
if blender_exe is not None:
|
||||
return blender_exe
|
||||
|
||||
msg = "Couldn't find executable command 'blender.exe' on the system PATH. Is it installed?"
|
||||
raise ValueError(msg)
|
||||
|
||||
msg = f"Could not detect a Blender executable for the current Blender platform '{bl_platform}'."
|
||||
raise ValueError(msg)
|
|
@ -0,0 +1,132 @@
|
|||
"""Contains tools and procedures that deterministically and reliably packages the Blender extension.
|
||||
|
||||
This involves parsing and validating the plugin configuration from `pyproject.toml`, generating the extension manifest, downloading the correct platform-specific binary wheels for distribution, and zipping it all up together.
|
||||
"""
|
||||
|
||||
import shutil
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import rich
|
||||
import rich.progress
|
||||
import rich.prompt
|
||||
|
||||
from blext import spec
|
||||
|
||||
console = rich.console.Console()
|
||||
|
||||
|
||||
####################
|
||||
# - Pack Extension to ZIP
|
||||
####################
|
||||
def prepack_bl_extension(blext_spec: spec.BLExtSpec) -> None:
|
||||
"""Prepare a `.zip` file containing all wheels, but no code.
|
||||
|
||||
Since the wheels tend to be the slowest part of packing an extension, a two-step process allows reusing a base `.zip` file that already contains required wheels.
|
||||
"""
|
||||
# Overwrite
|
||||
if blext_spec.path_zip_prepack.is_file():
|
||||
blext_spec.path_zip_prepack.unlink()
|
||||
|
||||
console.print()
|
||||
console.rule('[bold green]Pre-Packing Extension')
|
||||
with zipfile.ZipFile(
|
||||
blext_spec.path_zip_prepack, 'w', zipfile.ZIP_DEFLATED
|
||||
) as f_zip:
|
||||
# Install Wheels @ /wheels/*
|
||||
## Setup UI
|
||||
total_wheel_size = sum(
|
||||
f.stat().st_size for f in blext_spec.path_wheels.rglob('*') if f.is_file()
|
||||
)
|
||||
progress = rich.progress.Progress(
|
||||
rich.progress.TextColumn(
|
||||
'Writing Wheel: {task.description}...',
|
||||
table_column=rich.progress.Column(ratio=2),
|
||||
),
|
||||
rich.progress.BarColumn(
|
||||
bar_width=None,
|
||||
table_column=rich.progress.Column(ratio=2),
|
||||
),
|
||||
expand=True,
|
||||
)
|
||||
progress_task = progress.add_task('Writing Wheels...', total=total_wheel_size)
|
||||
|
||||
## Write Wheels
|
||||
with rich.progress.Live(progress, console=console, transient=True) as live:
|
||||
for wheel_to_zip in blext_spec.path_wheels.rglob('*.whl'):
|
||||
f_zip.write(wheel_to_zip, Path('wheels') / wheel_to_zip.name)
|
||||
progress.update(
|
||||
progress_task,
|
||||
description=wheel_to_zip.name,
|
||||
advance=wheel_to_zip.stat().st_size,
|
||||
)
|
||||
live.refresh()
|
||||
|
||||
console.print('[✔] Wrote Wheels')
|
||||
|
||||
|
||||
def pack_bl_extension(
|
||||
blext_spec: spec.BLExtSpec,
|
||||
*,
|
||||
force_prepack: bool = False,
|
||||
overwrite: bool = False,
|
||||
) -> None:
|
||||
"""Pack all files needed by a Blender extension, into an installable `.zip`.
|
||||
|
||||
Configuration data is sourced from `paths`, which in turns sources much of its user-facing configuration from `pyproject.toml`.
|
||||
|
||||
Parameters:
|
||||
profile: Selects a predefined set of initial extension settings from a `[tool.bl_ext.profiles]` table in `pyproject.toml`.
|
||||
os: The operating system to pack the extension for.
|
||||
force_prepack: Force pre-packing all wheels into the zip file.
|
||||
overwrite: Replace the zip file if it already exists.
|
||||
"""
|
||||
path_zip = blext_spec.path_zip
|
||||
if force_prepack or not blext_spec.path_zip_prepack.is_file():
|
||||
prepack_bl_extension(blext_spec)
|
||||
|
||||
# Overwrite Existing ZIP
|
||||
if path_zip.is_file():
|
||||
if not overwrite:
|
||||
msg = f"File already exists where extension ZIP would be generated at '{path_zip}'"
|
||||
raise ValueError(msg)
|
||||
path_zip.unlink()
|
||||
|
||||
# Pack Extension
|
||||
console.print()
|
||||
console.rule('[bold green]Packing Extension')
|
||||
console.print(f'[italic]Writing to: {path_zip.parent}')
|
||||
console.print()
|
||||
|
||||
## Copy Prepacked ZIP and Finish Packing
|
||||
with console.status('Copying Prepacked ZIP...', spinner='dots'):
|
||||
shutil.copyfile(blext_spec.path_zip_prepack, path_zip)
|
||||
console.print('[✔] Copied Prepacked Extension ZIP')
|
||||
|
||||
with zipfile.ZipFile(path_zip, 'a', zipfile.ZIP_DEFLATED) as f_zip:
|
||||
# Write Blender Extension Manifest @ /blender_manifest.toml
|
||||
with console.status('Writing Extension Manifest...', spinner='dots'):
|
||||
f_zip.writestr(
|
||||
blext_spec.manifest_filename,
|
||||
blext_spec.manifest_str,
|
||||
)
|
||||
console.print('[✔] Wrote Extension Manifest')
|
||||
|
||||
# Write Init Settings @ /init_settings.toml
|
||||
with console.status('Writing Init Settings...', spinner='dots'):
|
||||
f_zip.writestr(
|
||||
blext_spec.init_settings_filename,
|
||||
blext_spec.init_settings_str,
|
||||
)
|
||||
console.print('[✔] Wrote Init Settings')
|
||||
|
||||
# Install Addon Files @ /*
|
||||
with console.status('Writing Addon Files...', spinner='dots'):
|
||||
for file_to_zip in blext_spec.path_pkg.rglob('*'):
|
||||
f_zip.write(
|
||||
file_to_zip,
|
||||
file_to_zip.relative_to(blext_spec.path_pkg),
|
||||
)
|
||||
console.print('[✔] Wrote Addon Files')
|
||||
|
||||
console.print(f'[✔] Finished Writing: "{path_zip.name}"')
|
|
@ -0,0 +1,501 @@
|
|||
"""Defines the `BLExtSpec` model."""
|
||||
|
||||
import functools
|
||||
import json
|
||||
import tomllib
|
||||
import typing as typ
|
||||
from pathlib import Path
|
||||
|
||||
import pydantic as pyd
|
||||
import tomli_w
|
||||
|
||||
from . import supported
|
||||
|
||||
|
||||
####################
|
||||
# - Path Mangling
|
||||
####################
|
||||
def parse_spec_path(path_proj_root: Path, p_str: str) -> Path:
|
||||
"""Convert a project-root-relative '/'-delimited string path to an absolute, cross-platform path.
|
||||
|
||||
This allows for cross-platform path specification, while retaining normalized use of '/' in configuration.
|
||||
|
||||
Args:
|
||||
path_proj_root: The path to the project root directory.
|
||||
p_str: The '/'-delimited string denoting a path relative to the project root.
|
||||
|
||||
Returns:
|
||||
The full absolute path to use when ex. packaging.
|
||||
"""
|
||||
return path_proj_root / Path(*p_str.split('/'))
|
||||
|
||||
|
||||
####################
|
||||
# - Types
|
||||
####################
|
||||
# class BLExtSpec(pyd.BaseModel, frozen=True): ## TODO: FrozenDict
|
||||
class BLExtSpec(pyd.BaseModel):
|
||||
"""Completely encapsulates information about the packaging of a Blender extension.
|
||||
|
||||
This model allows `pyproject.toml` to be the single source of truth for a Blender extension project.
|
||||
Thus, this model is designed to be parsed entirely from a `pyproject.toml` file, and in turn is capable of generating the Blender extension manifest file and more.
|
||||
|
||||
To the extent possible, appropriate standard `pyproject.toml` fields are scraped for information relevant to a Blender extension.
|
||||
This includes name, version, license, desired dependencies, homepage, and more.
|
||||
Naturally, many fields are _quite_ specific to Blender extensions, such as Blender version constraints, permissions, and extension tags.
|
||||
For such options, the `[tool.blext]` section is introduced.
|
||||
|
||||
Attributes:
|
||||
init_settings_filename: Must be `init_settings.toml`.
|
||||
use_path_local: Whether to use a local path, instead of a global system path.
|
||||
Useful for debugging during development.
|
||||
use_log_file: Whether the extension should default to the use of file logging.
|
||||
log_file_path: The path to the file log (if enabled).
|
||||
log_file_level: The file log level to use (if enabled).
|
||||
use_log_console: Whether the extension should default to the use of console logging.
|
||||
log_console_level: The console log level to use (if enabled).
|
||||
schema_version: Must be 1.0.0.
|
||||
id: Unique identifying name of the extension.
|
||||
name: Pretty, user-facing name of the extension.
|
||||
version: The version of the extension.
|
||||
tagline: Short description of the extension.
|
||||
maintainer: Primary maintainer of the extension (name and email).
|
||||
type: Type of extension.
|
||||
Currently, only `add-on` is supported.
|
||||
blender_version_min: The minimum version of Blender that this extension supports.
|
||||
blender_version_max: The maximum version of Blender that this extension supports.
|
||||
wheels: Relative paths to wheels distributed with this extension.
|
||||
These should be installed by Blender alongside the extension.
|
||||
See <https://docs.blender.org/manual/en/dev/extensions/python_wheels.html> for more information.
|
||||
permissions: Permissions required by the extension.
|
||||
tags: Tags for categorizing the extension.
|
||||
license: License of the extension's source code.
|
||||
copyright: Copyright declaration of the extension.
|
||||
website: Homepage of the extension.
|
||||
|
||||
References:
|
||||
- <https://docs.blender.org/manual/en/4.2/extensions/getting_started.html>
|
||||
- <https://packaging.python.org/en/latest/guides/writing-pyproject-toml>
|
||||
"""
|
||||
|
||||
# Base
|
||||
path_proj_root: Path
|
||||
req_python_version: str
|
||||
|
||||
# Platform Support
|
||||
is_universal_blext: bool = True
|
||||
bl_platform_pypa_tags: dict[str, tuple[str, ...]]
|
||||
|
||||
####################
|
||||
# - Packed Filenames
|
||||
####################
|
||||
init_settings_filename: typ.Literal['init_settings.toml'] = 'init_settings.toml'
|
||||
manifest_filename: typ.Literal['blender_manifest.toml'] = 'blender_manifest.toml'
|
||||
|
||||
####################
|
||||
# - Init Settings
|
||||
####################
|
||||
init_schema_version: typ.Literal['0.1.0'] = pyd.Field(
|
||||
default='0.1.0', serialization_alias='schema_version'
|
||||
)
|
||||
## TODO: Conform to extension version?
|
||||
|
||||
# Path Locality
|
||||
use_path_local: bool
|
||||
|
||||
# File Logging
|
||||
use_log_file: bool
|
||||
log_file_path: Path
|
||||
log_file_level: supported.StrLogLevel
|
||||
|
||||
# Console Logging
|
||||
use_log_console: bool
|
||||
log_console_level: supported.StrLogLevel
|
||||
|
||||
####################
|
||||
# - Extension Manifest
|
||||
####################
|
||||
manifest_schema_version: typ.Literal['1.0.0'] = pyd.Field(
|
||||
default='1.0.0', serialization_alias='schema_version'
|
||||
)
|
||||
|
||||
# Basics
|
||||
id: str
|
||||
name: str
|
||||
version: str
|
||||
tagline: str
|
||||
maintainer: str
|
||||
|
||||
## TODO: Validator on tagline that prohibits ending with punctuation
|
||||
## - In fact, alpha-numeric suffix is required.
|
||||
|
||||
# Blender Compatibility
|
||||
type: typ.Literal['add-on'] = 'add-on'
|
||||
blender_version_min: str
|
||||
blender_version_max: str
|
||||
|
||||
# OS/Arch Compatibility
|
||||
|
||||
# Permissions
|
||||
## - "files" (for access of any filesystem operations)
|
||||
## - "network" (for internet access)
|
||||
## - "clipboard" (to read and/or write the system clipboard)
|
||||
## - "camera" (to capture photos and videos)
|
||||
## - "microphone" (to capture audio)
|
||||
permissions: dict[
|
||||
typ.Literal['files', 'network', 'clipboard', 'camera', 'microphone'], str
|
||||
] = {}
|
||||
|
||||
# Addon Tags
|
||||
tags: tuple[
|
||||
typ.Literal[
|
||||
'3D View',
|
||||
'Add Curve',
|
||||
'Add Mesh',
|
||||
'Animation',
|
||||
'Bake',
|
||||
'Camera',
|
||||
'Compositing',
|
||||
'Development',
|
||||
'Game Engine',
|
||||
'Geometry Nodes',
|
||||
'Grease Pencil',
|
||||
'Import-Export',
|
||||
'Lighting',
|
||||
'Material',
|
||||
'Modeling',
|
||||
'Mesh',
|
||||
'Node',
|
||||
'Object',
|
||||
'Paint',
|
||||
'Pipeline',
|
||||
'Physics',
|
||||
'Render',
|
||||
'Rigging',
|
||||
'Scene',
|
||||
'Sculpt',
|
||||
'Sequencer',
|
||||
'System',
|
||||
'Text Editor',
|
||||
'Tracking',
|
||||
'User Interface',
|
||||
'UV',
|
||||
],
|
||||
...,
|
||||
] = ()
|
||||
license: tuple[str, ...]
|
||||
copyright: tuple[str, ...]
|
||||
website: pyd.HttpUrl | None = None
|
||||
|
||||
####################
|
||||
# - Extension Manifest: Computed Properties
|
||||
####################
|
||||
@pyd.computed_field(alias='platforms') # type: ignore[prop-decorator]
|
||||
@property
|
||||
def bl_platforms(self) -> frozenset[supported.BLPlatform]:
|
||||
"""Operating systems supported by the extension."""
|
||||
return frozenset(self.bl_platform_pypa_tags.keys()) # type: ignore[arg-type]
|
||||
|
||||
@pyd.computed_field # type: ignore[prop-decorator]
|
||||
@property
|
||||
def wheels(self) -> tuple[Path, ...]:
|
||||
"""Path to all shipped wheels, relative to the root of the unpacked extension `.zip` file."""
|
||||
return tuple(
|
||||
[
|
||||
Path(f'./wheels/{wheel_path.name}')
|
||||
for wheel_path in self.path_wheels.iterdir()
|
||||
]
|
||||
)
|
||||
|
||||
####################
|
||||
# - Path Properties
|
||||
####################
|
||||
@functools.cached_property
|
||||
def path_proj_spec(self) -> Path:
|
||||
"""Path to the project `pyproject.toml`."""
|
||||
return self.path_proj_root / 'pyproject.toml'
|
||||
|
||||
@functools.cached_property
|
||||
def path_pkg(self) -> Path:
|
||||
"""Path to the Python package of the extension."""
|
||||
return self.path_proj_root / self.id
|
||||
|
||||
@functools.cached_property
|
||||
def path_dev(self) -> Path:
|
||||
"""Path to the project `dev/` folder, which should not be checked in."""
|
||||
path_dev = self.path_proj_root / 'dev'
|
||||
path_dev.mkdir(exist_ok=True)
|
||||
return path_dev
|
||||
|
||||
@functools.cached_property
|
||||
def path_wheels(self) -> Path:
|
||||
"""Path to the project's downloaded wheel cache."""
|
||||
path_wheels = self.path_dev / 'wheels'
|
||||
path_wheels.mkdir(exist_ok=True)
|
||||
return path_wheels
|
||||
|
||||
@functools.cached_property
|
||||
def path_prepack(self) -> Path:
|
||||
"""Path to the project's prepack folder, where pre-packed `.zip`s are written to."""
|
||||
path_prepack = self.path_dev / 'prepack'
|
||||
path_prepack.mkdir(exist_ok=True)
|
||||
return path_prepack
|
||||
|
||||
@functools.cached_property
|
||||
def path_build(self) -> Path:
|
||||
"""Path to the project's build folder, where extension `.zip`s are written to."""
|
||||
path_build = self.path_dev / 'build'
|
||||
path_build.mkdir(exist_ok=True)
|
||||
return path_build
|
||||
|
||||
@functools.cached_property
|
||||
def path_local(self) -> Path:
|
||||
"""Path to the project's build folder, where extension `.zip`s are written to."""
|
||||
path_build = self.path_dev / 'build'
|
||||
path_build.mkdir(exist_ok=True)
|
||||
return path_build
|
||||
|
||||
@functools.cached_property
|
||||
def filename_zip(self) -> str:
|
||||
"""Deduce the filename of the `.zip` extension file to build."""
|
||||
# Deduce Zip Filename
|
||||
if self.is_universal_blext:
|
||||
return f'{self.id}__{self.version}.zip'
|
||||
|
||||
if len(self.bl_platforms) == 1:
|
||||
only_supported_os = next(iter(self.bl_platforms))
|
||||
return f'{self.id}__{self.version}_{only_supported_os}.zip'
|
||||
|
||||
msg = "Cannot deduce filename of non-universal Blender extension when more than one 'BLPlatform' is supported."
|
||||
raise ValueError(msg)
|
||||
|
||||
@functools.cached_property
|
||||
def path_zip(self) -> Path:
|
||||
"""Path to the Blender extension `.zip` to build."""
|
||||
return self.path_build / self.filename_zip
|
||||
|
||||
@functools.cached_property
|
||||
def path_zip_prepack(self) -> Path:
|
||||
"""Path to the Blender extension `.zip` to build."""
|
||||
return self.path_prepack / self.filename_zip
|
||||
|
||||
####################
|
||||
# - Exporters
|
||||
####################
|
||||
@functools.cached_property
|
||||
def init_settings_str(self) -> str:
|
||||
"""The Blender extension manifest TOML as a string."""
|
||||
return tomli_w.dumps(
|
||||
json.loads(
|
||||
self.model_dump_json(
|
||||
include={
|
||||
'init_schema_version',
|
||||
'use_path_local',
|
||||
'use_log_file',
|
||||
'log_file_path',
|
||||
'log_file_level',
|
||||
'use_log_console',
|
||||
'log_console_level',
|
||||
},
|
||||
by_alias=True,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@functools.cached_property
|
||||
def manifest_str(self) -> str:
|
||||
"""The Blender extension manifest TOML as a string."""
|
||||
return tomli_w.dumps(
|
||||
json.loads(
|
||||
self.model_dump_json(
|
||||
include={
|
||||
'manifest_schema_version',
|
||||
'id',
|
||||
'name',
|
||||
'version',
|
||||
'tagline',
|
||||
'maintainer',
|
||||
'type',
|
||||
'blender_version_min',
|
||||
'blender_version_max',
|
||||
'bl_platforms',
|
||||
'wheels',
|
||||
'permissions',
|
||||
'tags',
|
||||
'license',
|
||||
'copyright',
|
||||
}
|
||||
| ({'website'} if self.website is not None else set()),
|
||||
by_alias=True,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
####################
|
||||
# - Methods
|
||||
####################
|
||||
def constrain_to_bl_platform(self, bl_platform: supported.BLPlatform) -> typ.Self:
|
||||
"""Create a new `BLExtSpec`, which supports only one operating system.
|
||||
|
||||
All PyPa platform tags associated with that operating system will be transferred.
|
||||
In all other respects, the created `BLExtSpec` will be identical.
|
||||
|
||||
Parameters:
|
||||
bl_platform: The Blender platform to support exclusively.
|
||||
|
||||
"""
|
||||
pypa_platform_tags = self.bl_platform_pypa_tags[bl_platform]
|
||||
return self.model_copy(
|
||||
update={
|
||||
'bl_platform_pypa_tags': {bl_platform: pypa_platform_tags},
|
||||
'is_universal_blext': False,
|
||||
},
|
||||
deep=True,
|
||||
)
|
||||
|
||||
####################
|
||||
# - Creation
|
||||
####################
|
||||
@classmethod
|
||||
def from_proj_spec_dict(
|
||||
cls,
|
||||
proj_spec: dict[str, typ.Any],
|
||||
*,
|
||||
path_proj_root: Path,
|
||||
release_profile: supported.ReleaseProfile,
|
||||
) -> typ.Self:
|
||||
"""Parse a `BLExtSpec` from a `pyproject.toml` dictionary.
|
||||
|
||||
Args:
|
||||
proj_spec: The dictionary representation of a `pyproject.toml` file.
|
||||
|
||||
Raises:
|
||||
ValueError: If the `pyproject.toml` file does not contain the required tables and/or fields.
|
||||
|
||||
"""
|
||||
# Parse Sections
|
||||
## Parse [project]
|
||||
if proj_spec.get('project') is not None:
|
||||
project = proj_spec['project']
|
||||
else:
|
||||
msg = "'pyproject.toml' MUST define '[project]' table"
|
||||
raise ValueError(msg)
|
||||
|
||||
## Parse [tool.blext]
|
||||
if (
|
||||
proj_spec.get('tool') is not None
|
||||
or proj_spec['tool'].get('blext') is not None
|
||||
):
|
||||
blext_spec = proj_spec['tool']['blext']
|
||||
else:
|
||||
msg = "'pyproject.toml' MUST define '[tool.blext]' table"
|
||||
raise ValueError(msg)
|
||||
|
||||
## Parse [tool.blext.profiles]
|
||||
if proj_spec['tool']['blext'].get('profiles') is not None:
|
||||
release_profiles = blext_spec['profiles']
|
||||
if release_profile in release_profiles:
|
||||
release_profile_spec = release_profiles[release_profile]
|
||||
else:
|
||||
msg = f"To parse the profile '{release_profile}' from 'pyproject.toml', it MUST be defined as a key in '[tool.blext.profiles]'"
|
||||
raise ValueError(msg)
|
||||
|
||||
else:
|
||||
msg = "'pyproject.toml' MUST define '[tool.blext.profiles]'"
|
||||
raise ValueError(msg)
|
||||
|
||||
# Parse Values
|
||||
## Parse project.requires-python
|
||||
if project.get('requires-python') is not None:
|
||||
project_requires_python = project['requires-python'].replace('~= ', '')
|
||||
else:
|
||||
msg = "'pyproject.toml' MUST define 'project.requires-python'"
|
||||
raise ValueError(msg)
|
||||
|
||||
## Parse project.maintainers[0]
|
||||
if project.get('maintainers') is not None:
|
||||
first_maintainer = project.get('maintainers')[0]
|
||||
else:
|
||||
first_maintainer = {'name': None, 'email': None}
|
||||
|
||||
## Parse project.license
|
||||
if (
|
||||
project.get('license') is not None
|
||||
and project['license'].get('text') is not None
|
||||
):
|
||||
_license = project['license']['text']
|
||||
else:
|
||||
msg = "'pyproject.toml' MUST define 'project.license.text'"
|
||||
raise ValueError(msg)
|
||||
|
||||
## Parse project.urls.homepage
|
||||
if (
|
||||
project.get('urls') is not None
|
||||
and project['urls'].get('Homepage') is not None
|
||||
):
|
||||
homepage = project['urls']['Homepage']
|
||||
else:
|
||||
homepage = None
|
||||
|
||||
# Conform to BLExt Specification
|
||||
return cls(
|
||||
path_proj_root=path_proj_root,
|
||||
req_python_version=project_requires_python,
|
||||
bl_platform_pypa_tags=blext_spec.get('platforms'),
|
||||
# Path Locality
|
||||
use_path_local=release_profile_spec.get('use_path_local'),
|
||||
# File Logging
|
||||
use_log_file=release_profile_spec.get('use_log_file', False),
|
||||
log_file_path=release_profile_spec.get('log_file_path', 'addon.log'),
|
||||
log_file_level=release_profile_spec.get('log_file_level', 'DEBUG'),
|
||||
# Console Logging
|
||||
use_log_console=release_profile_spec.get('use_log_console', True),
|
||||
log_console_level=release_profile_spec.get('log_console_level', 'DEBUG'),
|
||||
# Basics
|
||||
id=project.get('name'),
|
||||
name=blext_spec.get('pretty_name'),
|
||||
version=project.get('version'),
|
||||
tagline=project.get('description'),
|
||||
maintainer=f'{first_maintainer["name"]} <{first_maintainer["email"]}>',
|
||||
# Blender Compatibility
|
||||
blender_version_min=blext_spec.get('blender_version_min'),
|
||||
blender_version_max=blext_spec.get('blender_version_max'),
|
||||
# Permissions
|
||||
permissions=blext_spec.get('permissions', {}),
|
||||
# Addon Tags
|
||||
tags=blext_spec.get('bl_tags'),
|
||||
license=(f'SPDX:{_license}',),
|
||||
copyright=blext_spec.get('copyright'),
|
||||
website=homepage,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_proj_spec(
|
||||
cls,
|
||||
path_proj_spec: Path,
|
||||
*,
|
||||
release_profile: supported.ReleaseProfile,
|
||||
) -> typ.Self:
|
||||
"""Parse a `BLExtSpec` from a `pyproject.toml` file.
|
||||
|
||||
Args:
|
||||
path_proj_spec: The path to an appropriately utilized `pyproject.toml` file.
|
||||
release_profile: The profile to load initial settings for.
|
||||
|
||||
Raises:
|
||||
ValueError: If the `pyproject.toml` file does not contain the required tables and/or fields.
|
||||
"""
|
||||
# Load File
|
||||
if path_proj_spec.is_file():
|
||||
with path_proj_spec.open('rb') as f:
|
||||
proj_spec = tomllib.load(f)
|
||||
else:
|
||||
msg = f"Could not load 'pyproject.toml' at '{path_proj_spec}"
|
||||
raise ValueError(msg)
|
||||
|
||||
# Parse Extension Specification
|
||||
return cls.from_proj_spec_dict(
|
||||
proj_spec,
|
||||
path_proj_root=path_proj_spec.parent,
|
||||
release_profile=release_profile,
|
||||
)
|
|
@ -0,0 +1,55 @@
|
|||
"""Constrained sets of strings denoting some supported set of values.
|
||||
|
||||
String enumerations are used to provide meaningful, editor-friendly choices that are enforced when appropriate ex. in the CLI interface.
|
||||
"""
|
||||
|
||||
import enum
|
||||
import logging
|
||||
|
||||
|
||||
class BLPlatform(enum.StrEnum):
|
||||
"""Operating systems supported by Blender extensions managed by BLExt.
|
||||
|
||||
Corresponds perfectly to the platforms defined in the Blender Extension Manifest.
|
||||
"""
|
||||
|
||||
linux_x64 = 'linux-x64'
|
||||
linux_arm64 = 'linux-arm64'
|
||||
macos_x64 = 'macos-x64'
|
||||
macos_arm64 = 'macos-arm64'
|
||||
windows_x64 = 'windows-x64'
|
||||
windows_arm64 = 'windows-arm64'
|
||||
|
||||
|
||||
class ReleaseProfile(enum.StrEnum):
|
||||
"""Release profiles supported by Blender extensions managed by BLExt."""
|
||||
|
||||
Test = 'test'
|
||||
Dev = 'dev'
|
||||
Release = 'release'
|
||||
ReleaseDebug = 'release-debug'
|
||||
|
||||
|
||||
class StrLogLevel(enum.StrEnum):
|
||||
"""String log-levels corresponding to log-levels in the `logging` stdlib module."""
|
||||
|
||||
Debug = 'DEBUG'
|
||||
Info = 'INFO'
|
||||
Warning = 'WARNING'
|
||||
Error = 'ERROR'
|
||||
Critical = 'CRITICAL'
|
||||
|
||||
@property
|
||||
def log_level(self) -> int:
|
||||
"""The integer corresponding to each string log-level.
|
||||
|
||||
Derived from the `logging` module of the standard library.
|
||||
"""
|
||||
SLL = self.__class__
|
||||
return {
|
||||
SLL.Debug: logging.DEBUG,
|
||||
SLL.Info: logging.INFO,
|
||||
SLL.Warning: logging.WARNING,
|
||||
SLL.Error: logging.ERROR,
|
||||
SLL.Critical: logging.CRITICAL,
|
||||
}[self]
|
|
@ -0,0 +1,189 @@
|
|||
import contextlib
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import tomllib
|
||||
|
||||
import pypdl
|
||||
import rich
|
||||
import rich.markdown
|
||||
import rich.progress
|
||||
import rich.prompt
|
||||
|
||||
from . import pack, spec, supported
|
||||
|
||||
console = rich.console.Console()
|
||||
|
||||
DELAY_DOWNLOAD_PROGRESS = 0.01
|
||||
|
||||
|
||||
####################
|
||||
# - Wheel Downloader
|
||||
####################
|
||||
def desired_wheels(blext_spec: spec.BLExtSpec) -> tuple[set[str], dict[str, str]]:
|
||||
"""Deduce the filenames and URLs of the desired wheels."""
|
||||
# Run uv Commands
|
||||
with contextlib.chdir(blext_spec.path_proj_root):
|
||||
# Generate uv.lock
|
||||
subprocess.run(
|
||||
['uv', 'lock'],
|
||||
check=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
|
||||
# Retrieve Dependencies
|
||||
## - uv must declare which dependencies are NOT dev dependencies.
|
||||
uv_tree_str = subprocess.check_output(
|
||||
['uv', 'tree', '--no-dev', '--locked'],
|
||||
stderr=subprocess.DEVNULL,
|
||||
).decode('utf-8')
|
||||
|
||||
# Find or Create uv.lock
|
||||
path_uv_lock = blext_spec.path_proj_root / 'uv.lock'
|
||||
if not path_uv_lock.is_file():
|
||||
msg = f"Couldn't find or create 'uv.lock' in project root '{blext_spec.path_proj_root}'"
|
||||
raise ValueError(msg)
|
||||
|
||||
# Parse for All Wheel Filenames
|
||||
with path_uv_lock.open('rb') as f:
|
||||
uv_lockfile = tomllib.load(f)
|
||||
|
||||
wheel_filename_to_url = {
|
||||
wheel['url'].split('/')[-1]: wheel['url']
|
||||
for pkg in uv_lockfile['package']
|
||||
for wheel in pkg.get('wheels', [])
|
||||
if 'wheels' in pkg and pkg['name'] in uv_tree_str
|
||||
}
|
||||
wheel_filename_to_url = {
|
||||
wheel_filename: wheel_url
|
||||
for wheel_filename, wheel_url in wheel_filename_to_url.items()
|
||||
if any(
|
||||
pypa_tag in wheel_filename or 'py3-none-any' in wheel_filename
|
||||
for bl_platform, pypa_tags in blext_spec.bl_platform_pypa_tags.items()
|
||||
for pypa_tag in pypa_tags
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
set(wheel_filename_to_url.keys()),
|
||||
wheel_filename_to_url,
|
||||
)
|
||||
|
||||
|
||||
def download_wheels(
|
||||
blext_spec: spec.BLExtSpec,
|
||||
*,
|
||||
bl_platform: supported.BLPlatform,
|
||||
no_prompt: bool = False,
|
||||
) -> None:
|
||||
"""Download universal and binary wheels for all platforms defined in `pyproject.toml`.
|
||||
|
||||
Each blender-supported platform requires specifying a valid list of PyPi platform constraints.
|
||||
These will be used as an allow-list when deciding which binary wheels may be selected for ex. 'mac'.
|
||||
|
||||
It is recommended to start with the most compatible platform tags, then work one's way up to the newest.
|
||||
Depending on how old the compatibility should stretch, one may have to omit / manually compile some wheels.
|
||||
|
||||
There is no exhaustive list of valid platform tags - though this should get you started:
|
||||
- https://stackoverflow.com/questions/49672621/what-are-the-valid-values-for-platform-abi-and-implementation-for-pip-do
|
||||
- Examine https://pypi.org/project/pillow/#files for some widely-supported tags.
|
||||
|
||||
Args:
|
||||
delete_existing_wheels: Whether to delete all wheels already in the directory.
|
||||
This doesn't generally require re-downloading; the pip-cache will generally be hit first.
|
||||
"""
|
||||
# Deduce Current | Desired Wheels
|
||||
wheels_current = {path.name for path in blext_spec.path_wheels.rglob('*.whl')}
|
||||
wheels_target, wheel_target_urls = desired_wheels(blext_spec)
|
||||
|
||||
# Compute Wheels to Download / Delete
|
||||
wheels_to_download = wheels_target - wheels_current
|
||||
wheels_to_delete = wheels_current - wheels_target
|
||||
|
||||
# Delete Superfluous Wheels
|
||||
if wheels_to_delete:
|
||||
console.print()
|
||||
console.rule('[bold green]Wheels to Delete')
|
||||
console.print(f'[italic]Deleting from: {blext_spec.path_wheels}:')
|
||||
console.print(
|
||||
rich.markdown.Markdown(
|
||||
'\n'.join(
|
||||
[f'- {wheel_to_delete}' for wheel_to_delete in wheels_to_delete]
|
||||
)
|
||||
),
|
||||
)
|
||||
console.print()
|
||||
|
||||
if not no_prompt:
|
||||
if rich.prompt.Confirm.ask('[bold]OK to delete?'):
|
||||
for wheel_filename in wheels_to_delete:
|
||||
wheel_path = blext_spec.path_wheels / wheel_filename
|
||||
if wheel_path.is_file():
|
||||
wheel_path.unlink()
|
||||
else:
|
||||
msg = f"While deleting superfluous wheels, a wheel path was computed that doesn't point to a valid file: {wheel_path}"
|
||||
raise RuntimeError(msg)
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
# Download Missing Wheels
|
||||
if wheels_to_download:
|
||||
console.print()
|
||||
console.rule('[bold green]Wheels to Download')
|
||||
console.print(f'[italic]Downloading to: {blext_spec.path_wheels}')
|
||||
console.print(
|
||||
rich.markdown.Markdown(
|
||||
'\n'.join(
|
||||
[
|
||||
f'- {wheel_to_download}'
|
||||
for wheel_to_download in wheels_to_download
|
||||
]
|
||||
)
|
||||
),
|
||||
)
|
||||
console.print()
|
||||
|
||||
# Start Download
|
||||
dl = pypdl.Pypdl(
|
||||
max_concurrent=8,
|
||||
allow_reuse=False,
|
||||
)
|
||||
future = dl.start(
|
||||
tasks=[
|
||||
{
|
||||
'url': wheel_url,
|
||||
'file_path': str(blext_spec.path_wheels / wheel_filename),
|
||||
}
|
||||
for wheel_filename, wheel_url in wheel_target_urls.items()
|
||||
],
|
||||
retries=3,
|
||||
display=False,
|
||||
block=False,
|
||||
)
|
||||
|
||||
# Monitor Download w/Progress Bar
|
||||
with rich.progress.Progress() as progress:
|
||||
task = progress.add_task('Downloading...', total=100)
|
||||
|
||||
# Wait for Download to Start
|
||||
while dl.progress is None:
|
||||
time.sleep(DELAY_DOWNLOAD_PROGRESS)
|
||||
|
||||
# Monitor Download
|
||||
## - 99% seems to be the maximum value.
|
||||
while dl.progress < 99: # noqa: PLR2004
|
||||
progress.update(task, completed=dl.progress)
|
||||
time.sleep(DELAY_DOWNLOAD_PROGRESS)
|
||||
|
||||
# Stop the Download @ 99%
|
||||
## - Essentially, we must wait on the future from the started download.
|
||||
## - This also merges the downloaded segments and cleans up.
|
||||
future.result()
|
||||
|
||||
# Finalize Progress Bar to 100%
|
||||
progress.update(task, completed=100)
|
||||
|
||||
# Prepack ZIP
|
||||
if wheels_to_delete or wheels_to_download:
|
||||
pack.prepack_bl_extension(blext_spec)
|
|
@ -0,0 +1,163 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
dev
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||
.pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
|
@ -0,0 +1 @@
|
|||
3.11
|
|
@ -0,0 +1,2 @@
|
|||
# blext-minimal-example
|
||||
Minimal example of a Blender extension managed with `blext`.
|
|
@ -0,0 +1,83 @@
|
|||
"""A visual DSL for electromagnetic simulation design and analysis implemented as a Blender node editor."""
|
||||
|
||||
from functools import reduce
|
||||
|
||||
from . import contracts as ct
|
||||
from . import operators, panels, preferences, registration
|
||||
from .utils import logger
|
||||
|
||||
log = logger.get(__name__)
|
||||
|
||||
|
||||
####################
|
||||
# - Load and Register Addon
|
||||
####################
|
||||
BL_REGISTER: list[ct.BLClass] = [
|
||||
*operators.BL_REGISTER,
|
||||
*panels.BL_REGISTER,
|
||||
]
|
||||
|
||||
BL_HANDLERS: ct.BLHandlers = reduce(
|
||||
lambda a, b: a + b,
|
||||
[
|
||||
operators.BL_HANDLERS,
|
||||
panels.BL_HANDLERS,
|
||||
],
|
||||
ct.BLHandlers(),
|
||||
)
|
||||
|
||||
BL_KEYMAP_ITEMS: list[ct.BLKeymapItem] = [
|
||||
*operators.BL_KEYMAP_ITEMS,
|
||||
*panels.BL_KEYMAP_ITEMS,
|
||||
]
|
||||
|
||||
|
||||
####################
|
||||
# - Registration
|
||||
####################
|
||||
def register() -> None:
|
||||
"""Implements addon registration in a way that respects the availability of addon preferences and loggers.
|
||||
|
||||
Notes:
|
||||
Called by Blender when enabling the addon.
|
||||
|
||||
Raises:
|
||||
RuntimeError: If addon preferences fail to register.
|
||||
"""
|
||||
log.info('Registering Addon Preferences: %s', ct.addon.NAME)
|
||||
registration.register_classes(preferences.BL_REGISTER)
|
||||
|
||||
addon_prefs = ct.addon.prefs()
|
||||
if addon_prefs is not None:
|
||||
# Update Loggers
|
||||
# - This updates existing loggers to use settings defined by preferences.
|
||||
addon_prefs.on_addon_logging_changed()
|
||||
|
||||
log.info('Registering Addon: %s', ct.addon.NAME)
|
||||
|
||||
registration.register_classes(BL_REGISTER)
|
||||
registration.register_handlers(BL_HANDLERS)
|
||||
registration.register_keymaps(BL_KEYMAP_ITEMS)
|
||||
|
||||
log.info('Finished Registration of Addon: %s', ct.addon.NAME)
|
||||
else:
|
||||
msg = f"Addon preferences did not register for addon '{ct.addon.NAME}' - something is very wrong!"
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
def unregister() -> None:
|
||||
"""Unregisters anything that was registered by the addon.
|
||||
|
||||
Notes:
|
||||
Run by Blender when disabling and/or uninstalling the addon.
|
||||
|
||||
This doesn't clean `sys.modules`.
|
||||
We rely on the hope that Blender has extension-extension module isolation.
|
||||
"""
|
||||
log.info('Starting %s Unregister', ct.addon.NAME)
|
||||
|
||||
registration.unregister_keymaps()
|
||||
registration.unregister_handlers()
|
||||
registration.unregister_classes()
|
||||
|
||||
log.info('Finished %s Unregister', ct.addon.NAME)
|
|
@ -0,0 +1,66 @@
|
|||
"""Independent constants and types, which represent a kind of 'social contract' governing communication between all components of the addon."""
|
||||
|
||||
from . import addon
|
||||
from .bl import (
|
||||
BLClass,
|
||||
BLColorRGBA,
|
||||
BLEnumElement,
|
||||
BLEnumID,
|
||||
BLEventType,
|
||||
BLEventValue,
|
||||
BLIcon,
|
||||
BLIconSet,
|
||||
BLIDStruct,
|
||||
BLImportMethod,
|
||||
BLModifierType,
|
||||
BLNodeTreeInterfaceID,
|
||||
BLOperatorStatus,
|
||||
BLPropFlag,
|
||||
BLRegionType,
|
||||
BLSpaceType,
|
||||
PresetName,
|
||||
PropName,
|
||||
SocketName,
|
||||
)
|
||||
from .bl_handlers import BLHandlers
|
||||
from .bl_keymap import BLKeymapItem
|
||||
from .icons import Icon
|
||||
from .operator_types import (
|
||||
OperatorType,
|
||||
)
|
||||
from .panel_types import (
|
||||
PanelType,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'addon',
|
||||
'BLClass',
|
||||
'BLColorRGBA',
|
||||
'BLEnumElement',
|
||||
'BLEnumID',
|
||||
'BLEventType',
|
||||
'BLEventValue',
|
||||
'BLIcon',
|
||||
'BLIconSet',
|
||||
'BLIDStruct',
|
||||
'BLImportMethod',
|
||||
'BLKeymapItem',
|
||||
'BLModifierType',
|
||||
'BLNodeTreeInterfaceID',
|
||||
'BLOperatorStatus',
|
||||
'BLPropFlag',
|
||||
'BLRegionType',
|
||||
'BLSpaceType',
|
||||
'KeymapItemDef',
|
||||
'ManagedObjName',
|
||||
'PresetName',
|
||||
'PropName',
|
||||
'SocketName',
|
||||
'BLHandlers',
|
||||
'Icon',
|
||||
'BLInstance',
|
||||
'InstanceID',
|
||||
'NodeTreeType',
|
||||
'OperatorType',
|
||||
'PanelType',
|
||||
]
|
|
@ -0,0 +1,49 @@
|
|||
"""Addon information deduced from the manifest and `bpy`."""
|
||||
|
||||
import tomllib
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
|
||||
from .. import __package__ as base_package
|
||||
|
||||
PATH_ADDON_ROOT: Path = Path(__file__).resolve().parent.parent
|
||||
PATH_MANIFEST: Path = PATH_ADDON_ROOT / 'blender_manifest.toml'
|
||||
|
||||
with PATH_MANIFEST.open('rb') as f:
|
||||
MANIFEST = tomllib.load(f)
|
||||
|
||||
NAME: str = MANIFEST['id']
|
||||
VERSION: str = MANIFEST['version']
|
||||
|
||||
|
||||
####################
|
||||
# - Dynamic Access
|
||||
####################
|
||||
def prefs() -> bpy.types.AddonPreferences | None:
|
||||
"""Retrieve the preferences of this addon, if they are available yet.
|
||||
|
||||
Notes:
|
||||
While registering the addon, one may wish to use the addon preferences.
|
||||
This isn't possible - not even for default values.
|
||||
|
||||
Either a bad idea is at work, or `oscillode.utils.init_settings` should be consulted until the preferences are available.
|
||||
|
||||
Returns:
|
||||
The addon preferences, if the addon is registered and loaded - otherwise None.
|
||||
"""
|
||||
addon = bpy.context.preferences.addons.get(base_package)
|
||||
if addon is None:
|
||||
return None
|
||||
return addon.preferences
|
||||
|
||||
|
||||
def addon_dir() -> Path:
|
||||
"""Absolute path to a local addon-specific directory guaranteed to be writable."""
|
||||
return Path(
|
||||
bpy.utils.extension_path_user(
|
||||
base_package,
|
||||
path='',
|
||||
create=True,
|
||||
)
|
||||
).resolve()
|
|
@ -0,0 +1,283 @@
|
|||
"""Explicit type annotations for Blender objects, making it easier to guarantee correctness in communications with Blender."""
|
||||
|
||||
import typing as typ
|
||||
|
||||
import bpy
|
||||
|
||||
####################
|
||||
# - Blender Strings
|
||||
####################
|
||||
BLEnumID: typ.TypeAlias = str
|
||||
SocketName: typ.TypeAlias = str
|
||||
PropName: typ.TypeAlias = str
|
||||
|
||||
####################
|
||||
# - Blender Enums
|
||||
####################
|
||||
BLImportMethod: typ.TypeAlias = typ.Literal['append', 'link']
|
||||
BLModifierType: typ.TypeAlias = typ.Literal['NODES', 'ARRAY']
|
||||
BLNodeTreeInterfaceID: typ.TypeAlias = str
|
||||
|
||||
BLIcon: typ.TypeAlias = str
|
||||
BLIconSet: frozenset[BLIcon] = frozenset(
|
||||
bpy.types.UILayout.bl_rna.functions['prop'].parameters['icon'].enum_items.keys()
|
||||
)
|
||||
|
||||
BLEnumElement = tuple[BLEnumID, str, str, BLIcon, int]
|
||||
|
||||
####################
|
||||
# - Blender Structs
|
||||
####################
|
||||
BLClass: typ.TypeAlias = (
|
||||
bpy.types.Panel
|
||||
| bpy.types.UIList
|
||||
| bpy.types.Menu
|
||||
| bpy.types.Header
|
||||
| bpy.types.Operator
|
||||
| bpy.types.KeyingSetInfo
|
||||
| bpy.types.RenderEngine
|
||||
| bpy.types.AssetShelf
|
||||
| bpy.types.FileHandler
|
||||
)
|
||||
BLIDStruct: typ.TypeAlias = (
|
||||
bpy.types.Action
|
||||
| bpy.types.Armature
|
||||
| bpy.types.Brush
|
||||
| bpy.types.CacheFile
|
||||
| bpy.types.Camera
|
||||
| bpy.types.Collection
|
||||
| bpy.types.Curve
|
||||
| bpy.types.Curves
|
||||
| bpy.types.FreestyleLineStyle
|
||||
| bpy.types.GreasePencil
|
||||
| bpy.types.Image
|
||||
| bpy.types.Key
|
||||
| bpy.types.Lattice
|
||||
| bpy.types.Library
|
||||
| bpy.types.Light
|
||||
| bpy.types.LightProbe
|
||||
| bpy.types.Mask
|
||||
| bpy.types.Material
|
||||
| bpy.types.Mesh
|
||||
| bpy.types.MetaBall
|
||||
| bpy.types.MovieClip
|
||||
| bpy.types.NodeTree
|
||||
| bpy.types.Object
|
||||
| bpy.types.PaintCurve
|
||||
| bpy.types.Palette
|
||||
| bpy.types.ParticleSettings
|
||||
| bpy.types.PointCloud
|
||||
| bpy.types.Scene
|
||||
| bpy.types.Screen
|
||||
| bpy.types.Sound
|
||||
| bpy.types.Speaker
|
||||
| bpy.types.Text
|
||||
| bpy.types.Texture
|
||||
| bpy.types.VectorFont
|
||||
| bpy.types.Volume
|
||||
| bpy.types.WindowManager
|
||||
| bpy.types.WorkSpace
|
||||
| bpy.types.World
|
||||
)
|
||||
BLPropFlag: typ.TypeAlias = typ.Literal[
|
||||
'HIDDEN',
|
||||
'SKIP_SAVE',
|
||||
'SKIP_PRESET',
|
||||
'ANIMATABLE',
|
||||
'LIBRARY_EDITABLE',
|
||||
'PROPORTIONAL',
|
||||
'TEXTEDIT_UPDATE',
|
||||
'OUTPUT_PATH',
|
||||
]
|
||||
BLColorRGBA = tuple[float, float, float, float]
|
||||
|
||||
|
||||
####################
|
||||
# - Operators
|
||||
####################
|
||||
BLRegionType: typ.TypeAlias = typ.Literal[
|
||||
'WINDOW',
|
||||
'HEADER',
|
||||
'CHANNELS',
|
||||
'TEMPORARY',
|
||||
'UI',
|
||||
'TOOLS',
|
||||
'TOOL_PROPS',
|
||||
'ASSET_SHELF',
|
||||
'ASSET_SHELF_HEADER',
|
||||
'PREVIEW',
|
||||
'HUD',
|
||||
'NAVIGATION_BAR',
|
||||
'EXECUTE',
|
||||
'FOOTER',
|
||||
'TOOL_HEADER',
|
||||
'XR',
|
||||
]
|
||||
BLSpaceType: typ.TypeAlias = typ.Literal[
|
||||
'EMPTY',
|
||||
'VIEW_3D',
|
||||
'IMAGE_EDITOR',
|
||||
'NODE_EDITOR',
|
||||
'SEQUENCE_EDITOR',
|
||||
'CLIP_EDITOR',
|
||||
'DOPESHEET_EDITOR',
|
||||
'GRAPH_EDITOR',
|
||||
'NLA_EDITOR',
|
||||
'TEXT_EDITOR',
|
||||
'CONSOLE',
|
||||
'INFO',
|
||||
'TOPBAR',
|
||||
'STATUSBAR',
|
||||
'OUTLINER',
|
||||
'PROPERTIES',
|
||||
'FILE_BROWSER',
|
||||
'SPREADSHEET',
|
||||
'PREFERENCES',
|
||||
]
|
||||
BLOperatorStatus: typ.TypeAlias = set[
|
||||
typ.Literal['RUNNING_MODAL', 'CANCELLED', 'FINISHED', 'PASS_THROUGH', 'INTERFACE']
|
||||
]
|
||||
|
||||
####################
|
||||
# - Operators
|
||||
####################
|
||||
## TODO: Write the rest in.
|
||||
BLEventType: typ.TypeAlias = typ.Literal[
|
||||
'NONE',
|
||||
'LEFTMOUSE',
|
||||
'MIDDLEMOUSE',
|
||||
'RIGHTMOUSE',
|
||||
'BUTTON4MOUSE',
|
||||
'BUTTON5MOUSE',
|
||||
'BUTTON6MOUSE',
|
||||
'BUTTON7MOUSE',
|
||||
'PEN',
|
||||
'ERASOR',
|
||||
'MOUSEMOVE',
|
||||
'INBETWEEN_MOUSEMOVE',
|
||||
'TRACKPADPAN',
|
||||
'TRACKPADZOOM',
|
||||
'MOUSEROTATE',
|
||||
'MOUSESMARTZOOM',
|
||||
'WHEELUPMOUSE',
|
||||
'WHEELDOWNMOUSE',
|
||||
'WHEELINMOUSE',
|
||||
'WHEELOUTMOUSE',
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'F',
|
||||
'G',
|
||||
'H',
|
||||
'I',
|
||||
'J',
|
||||
'K',
|
||||
'L',
|
||||
'M',
|
||||
'N',
|
||||
'O',
|
||||
'P',
|
||||
'Q',
|
||||
'R',
|
||||
'S',
|
||||
'T',
|
||||
'U',
|
||||
'V',
|
||||
'W',
|
||||
'X',
|
||||
'Y',
|
||||
'Z',
|
||||
'ZERO',
|
||||
'ONE',
|
||||
'TWO',
|
||||
'THREE',
|
||||
'FOUR',
|
||||
'FIVE',
|
||||
'SIX',
|
||||
'SEVEN',
|
||||
'EIGHT',
|
||||
'NINE',
|
||||
'LEFT_CTRL',
|
||||
'LEFT_ALT',
|
||||
'LEFT_SHIFT',
|
||||
'RIGHT_ALT',
|
||||
'RIGHT_CTRL',
|
||||
'RIGHT_SHIFT',
|
||||
'ESC',
|
||||
'TAB',
|
||||
'RET', ## Enter
|
||||
'SPACE',
|
||||
'LINE_FEED',
|
||||
'BACK_SPACE',
|
||||
'DEL',
|
||||
'SEMI_COLON',
|
||||
'PERIOD',
|
||||
'COMMA',
|
||||
'QUOTE',
|
||||
'ACCENT_GRAVE',
|
||||
'MINUS',
|
||||
'PLUS',
|
||||
'SLASH',
|
||||
'BACK_SLASH',
|
||||
'EQUAL',
|
||||
'LEFT_BRACKET',
|
||||
'RIGHT_BRACKET',
|
||||
'LEFT_ARROW',
|
||||
'DOWN_ARROW',
|
||||
'RIGHT_ARROW',
|
||||
'UP_ARROW',
|
||||
'NUMPAD_0',
|
||||
'NUMPAD_1',
|
||||
'NUMPAD_2',
|
||||
'NUMPAD_3',
|
||||
'NUMPAD_4',
|
||||
'NUMPAD_5',
|
||||
'NUMPAD_6',
|
||||
'NUMPAD_7',
|
||||
'NUMPAD_8',
|
||||
'NUMPAD_9',
|
||||
'NUMPAD_PERIOD',
|
||||
'NUMPAD_SLASH',
|
||||
'NUMPAD_ASTERIX',
|
||||
'NUMPAD_MINUS',
|
||||
'NUMPAD_PLUS',
|
||||
'NUMPAD_ENTER',
|
||||
'F1',
|
||||
'F2',
|
||||
'F3',
|
||||
'F4',
|
||||
'F5',
|
||||
'F6',
|
||||
'F7',
|
||||
'F8',
|
||||
'F9',
|
||||
'F10',
|
||||
'F11',
|
||||
'F12',
|
||||
'PAUSE',
|
||||
'INSERT',
|
||||
'HOME',
|
||||
'PAGE_UP',
|
||||
'PAGE_DOWN',
|
||||
'END',
|
||||
'MEDIA_PLAY',
|
||||
'MEDIA_STOP',
|
||||
'MEDIA_FIRST',
|
||||
'MEDIA_LAST',
|
||||
]
|
||||
BLEventValue: typ.TypeAlias = typ.Literal[
|
||||
'ANY',
|
||||
'PRESS',
|
||||
'RELEASE',
|
||||
'CLICK',
|
||||
'DOUBLE_CLICK',
|
||||
'CLICK_DRAG',
|
||||
'NOTHING',
|
||||
]
|
||||
|
||||
####################
|
||||
# - Blender Strings
|
||||
####################
|
||||
PresetName = str
|
|
@ -0,0 +1,115 @@
|
|||
"""Declares a structure for aggregating `bpy.app.handlers` callbacks."""
|
||||
|
||||
import typing as typ
|
||||
|
||||
import bpy
|
||||
import pydantic as pyd
|
||||
|
||||
from ..utils.staticproperty import staticproperty
|
||||
|
||||
BLHandler = typ.Callable[[], None]
|
||||
BLHandlerWithFile = typ.Callable[[str], None]
|
||||
BLHandlerWithRenderStats = typ.Callable[[typ.Any], None]
|
||||
|
||||
|
||||
class BLHandlers(pyd.BaseModel):
|
||||
"""Contains lists of handlers associated with this addon."""
|
||||
|
||||
animation_playback_post: tuple[BLHandler, ...] = ()
|
||||
animation_playback_pre: tuple[BLHandler, ...] = ()
|
||||
annotation_post: tuple[BLHandler, ...] = ()
|
||||
annotation_pre: tuple[BLHandler, ...] = ()
|
||||
composite_cancel: tuple[BLHandler, ...] = ()
|
||||
composite_post: tuple[BLHandler, ...] = ()
|
||||
composite_pre: tuple[BLHandler, ...] = ()
|
||||
depsgraph_update_post: tuple[BLHandler, ...] = ()
|
||||
depsgraph_update_pre: tuple[BLHandler, ...] = ()
|
||||
frame_change_post: tuple[BLHandler, ...] = ()
|
||||
frame_change_pre: tuple[BLHandler, ...] = ()
|
||||
load_factory_preferences_post: tuple[BLHandler, ...] = ()
|
||||
load_factory_startup_post: tuple[BLHandler, ...] = ()
|
||||
load_post: tuple[BLHandlerWithFile, ...] = ()
|
||||
load_post_fail: tuple[BLHandlerWithFile, ...] = ()
|
||||
load_pre: tuple[BLHandlerWithFile, ...] = ()
|
||||
object_bake_cancel: tuple[BLHandler, ...] = ()
|
||||
object_bake_complete: tuple[BLHandler, ...] = ()
|
||||
object_bake_pre: tuple[BLHandler, ...] = ()
|
||||
redo_post: tuple[BLHandler, ...] = ()
|
||||
redo_pre: tuple[BLHandler, ...] = ()
|
||||
render_cancel: tuple[BLHandler, ...] = ()
|
||||
render_complete: tuple[BLHandler, ...] = ()
|
||||
render_init: tuple[BLHandler, ...] = ()
|
||||
render_post: tuple[BLHandler, ...] = ()
|
||||
render_pre: tuple[BLHandler, ...] = ()
|
||||
render_stats: tuple[BLHandler, ...] = ()
|
||||
render_write: tuple[BLHandler, ...] = ()
|
||||
save_post: tuple[BLHandlerWithFile, ...] = ()
|
||||
save_post_fail: tuple[BLHandlerWithFile, ...] = ()
|
||||
save_pre: tuple[BLHandlerWithFile, ...] = ()
|
||||
version_update: tuple[BLHandler, ...] = ()
|
||||
xr_session_start_pre: tuple[BLHandler, ...] = ()
|
||||
## TODO: Verify these type signatures.
|
||||
|
||||
## TODO: A validator to check that all handlers are decorated with bpy.app.handlers.persistent
|
||||
|
||||
####################
|
||||
# - Properties
|
||||
####################
|
||||
@staticproperty # type: ignore[arg-type]
|
||||
def handler_categories() -> tuple[str, ...]: # type: ignore[misc]
|
||||
"""Returns an immutable string sequence of handler categories."""
|
||||
return (
|
||||
'animation_playback_post',
|
||||
'animation_playback_pre',
|
||||
'annotation_post',
|
||||
'annotation_pre',
|
||||
'composite_cancel',
|
||||
'composite_post',
|
||||
'composite_pre',
|
||||
'depsgraph_update_post',
|
||||
'depsgraph_update_pre',
|
||||
'frame_change_post',
|
||||
'frame_change_pre',
|
||||
'load_factory_preferences_post',
|
||||
'load_factory_startup_post',
|
||||
'load_post',
|
||||
'load_post_fail',
|
||||
'load_pre',
|
||||
'object_bake_cancel',
|
||||
'object_bake_complete',
|
||||
'object_bake_pre',
|
||||
'redo_post',
|
||||
'redo_pre',
|
||||
'render_cancel',
|
||||
'render_complete',
|
||||
'render_init',
|
||||
'render_post',
|
||||
'render_pre',
|
||||
'render_stats',
|
||||
)
|
||||
|
||||
####################
|
||||
# - Merging
|
||||
####################
|
||||
def __add__(self, other: typ.Self) -> typ.Self:
|
||||
"""Concatenate two sets of handlers."""
|
||||
return self.__class__(
|
||||
**{
|
||||
hndl_cat: getattr(self, hndl_cat) + getattr(self, hndl_cat)
|
||||
for hndl_cat in self.handler_categories
|
||||
}
|
||||
)
|
||||
|
||||
def register(self) -> None:
|
||||
"""Registers all handlers, by-category."""
|
||||
for handler_category in BLHandlers.handler_categories:
|
||||
for handler in getattr(self, handler_category):
|
||||
getattr(bpy.app.handlers, handler_category).append(handler)
|
||||
|
||||
def unregister(self) -> None:
|
||||
"""Unregisters only this object's handlers from bpy.app.handlers."""
|
||||
for handler_category in BLHandlers.handler_categories:
|
||||
for handler in getattr(self, handler_category):
|
||||
bpy_handlers = getattr(bpy.app.handlers, handler_category)
|
||||
if handler in bpy_handlers:
|
||||
bpy_handlers.remove(handler)
|
|
@ -0,0 +1,44 @@
|
|||
"""Declares types for working with `bpy.types.KeyMap`s."""
|
||||
|
||||
import bpy
|
||||
import pydantic as pyd
|
||||
|
||||
from .bl import BLEventType, BLEventValue, BLSpaceType
|
||||
from .operator_types import OperatorType
|
||||
|
||||
|
||||
class BLKeymapItem(pyd.BaseModel):
|
||||
"""Contains lists of handlers associated with this addon."""
|
||||
|
||||
operator: OperatorType
|
||||
|
||||
event_type: BLEventType
|
||||
event_value: BLEventValue
|
||||
|
||||
shift: bool = False
|
||||
ctrl: bool = False
|
||||
alt: bool = False
|
||||
key_modifier: BLEventType = 'NONE'
|
||||
|
||||
space_type: BLSpaceType = 'EMPTY'
|
||||
|
||||
def register(self, addon_keymap: bpy.types.KeyMap) -> bpy.types.KeyMapItem:
|
||||
"""Registers this hotkey with an addon keymap.
|
||||
|
||||
Raises:
|
||||
ValueError: If the `space_type` constraint of the addon keymap does not match the `space_type` constraint of this `BLKeymapItem`.
|
||||
"""
|
||||
if self.space_type == addon_keymap.space_type:
|
||||
addon_keymap.keymap_items.new(
|
||||
self.operator,
|
||||
self.event_type,
|
||||
self.event_value,
|
||||
shift=self.shift,
|
||||
ctrl=self.ctrl,
|
||||
alt=self.alt,
|
||||
key_modifier=self.key_modifier,
|
||||
)
|
||||
|
||||
msg = f'Addon keymap space type {addon_keymap.space_type} does not match space_type of BLKeymapItem to register: {self}'
|
||||
raise ValueError(msg)
|
||||
## TODO: Check if space_type matches?
|
|
@ -0,0 +1,7 @@
|
|||
"""Provides an enum that semantically constrains the use of icons throughout the addon."""
|
||||
|
||||
import enum
|
||||
|
||||
|
||||
class Icon(enum.StrEnum):
|
||||
"""Identifiers for icons used throughout this addon."""
|
|
@ -0,0 +1,11 @@
|
|||
"""Provides identifiers for Blender operators defined by this addon."""
|
||||
|
||||
import enum
|
||||
|
||||
from .addon import NAME as ADDON_NAME
|
||||
|
||||
|
||||
class OperatorType(enum.StrEnum):
|
||||
"""Identifiers for addon-defined `bpy.types.Operator`."""
|
||||
|
||||
SimpleOperator = f'{ADDON_NAME}.simple_operator'
|
|
@ -0,0 +1,13 @@
|
|||
"""Provides identifiers for Blender panels defined by this addon."""
|
||||
|
||||
import enum
|
||||
|
||||
from .addon import NAME as ADDON_NAME
|
||||
|
||||
PREFIX = f'{ADDON_NAME.upper()}_PT_'
|
||||
|
||||
|
||||
class PanelType(enum.StrEnum):
|
||||
"""Identifiers for addon-defined `bpy.types.Panel`."""
|
||||
|
||||
SimplePanel = f'{PREFIX}simple_panel'
|
|
@ -0,0 +1,20 @@
|
|||
"""Blender operators that ship with `bpy_jupyter`."""
|
||||
|
||||
from functools import reduce
|
||||
|
||||
from .. import contracts as ct
|
||||
from . import simple_operator
|
||||
|
||||
BL_REGISTER: list[ct.BLClass] = [
|
||||
*simple_operator.BL_REGISTER,
|
||||
]
|
||||
BL_HANDLERS: ct.BLHandlers = reduce(
|
||||
lambda a, b: a + b,
|
||||
[
|
||||
simple_operator.BL_HANDLERS,
|
||||
],
|
||||
ct.BLHandlers(),
|
||||
)
|
||||
BL_KEYMAP_ITEMS: list[ct.BLKeymapItem] = [
|
||||
*simple_operator.BL_KEYMAP_ITEMS,
|
||||
]
|
|
@ -0,0 +1,34 @@
|
|||
"""Defines the `StartJupyterKernel` operator.
|
||||
|
||||
Inspired by <https://github.com/cheng-chi/blender_notebook/blob/master/blender_notebook/kernel.py>
|
||||
"""
|
||||
|
||||
import bpy
|
||||
|
||||
from .. import contracts as ct
|
||||
|
||||
|
||||
class SimpleOperator(bpy.types.Operator):
|
||||
"""Operator that shows a message."""
|
||||
|
||||
bl_idname = ct.OperatorType.SimpleOperator
|
||||
bl_label = 'Other Operator'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, _: bpy.types.Context) -> bool:
|
||||
"""Always allow operator to run."""
|
||||
return True
|
||||
|
||||
def execute(self, _: bpy.types.Context) -> set[ct.BLOperatorStatus]:
|
||||
"""Display a simple message on execution."""
|
||||
self.report({'INFO'}, 'SimpleOperator was executed from blext_simple_example.')
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
BL_REGISTER = [SimpleOperator]
|
||||
BL_HANDLERS: ct.BLHandlers = ct.BLHandlers()
|
||||
BL_KEYMAP_ITEMS: list[ct.BLKeymapItem] = []
|
|
@ -0,0 +1,20 @@
|
|||
"""Blender panels that ship with `bpy_jupyter`."""
|
||||
|
||||
from functools import reduce
|
||||
|
||||
from .. import contracts as ct
|
||||
from . import simple_panel
|
||||
|
||||
BL_REGISTER: list[ct.BLClass] = [
|
||||
*simple_panel.BL_REGISTER,
|
||||
]
|
||||
BL_HANDLERS: ct.BLHandlers = reduce(
|
||||
lambda a, b: a + b,
|
||||
[
|
||||
simple_panel.BL_HANDLERS,
|
||||
],
|
||||
ct.BLHandlers(),
|
||||
)
|
||||
BL_KEYMAP_ITEMS: list[ct.BLKeymapItem] = [
|
||||
*simple_panel.BL_KEYMAP_ITEMS,
|
||||
]
|
|
@ -0,0 +1,52 @@
|
|||
import bpy
|
||||
|
||||
from .. import contracts as ct
|
||||
|
||||
bpy.types.Scene.simple_integer = bpy.props.IntProperty(
|
||||
name='Simple Integer',
|
||||
description="It's just an integer! What's the big deal?",
|
||||
default=10,
|
||||
)
|
||||
|
||||
|
||||
class SimplePanel(bpy.types.Panel):
|
||||
bl_idname = ct.PanelType.SimplePanel
|
||||
bl_label = 'Simple Panel'
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = 'scene'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, _: bpy.types.Context) -> bool:
|
||||
"""Always show panel in Scene properties.
|
||||
|
||||
Notes:
|
||||
Run by Blender when trying to show a panel.
|
||||
|
||||
Returns:
|
||||
Whether the panel can show.
|
||||
"""
|
||||
return True
|
||||
|
||||
def draw(self, _: bpy.types.Context) -> None:
|
||||
"""Draw the panel w/options.
|
||||
|
||||
Notes:
|
||||
Run by Blender when the panel needs to be displayed.
|
||||
|
||||
Parameters:
|
||||
context: The Blender context object.
|
||||
Must contain `context.window_manager` and `context.workspace`.
|
||||
"""
|
||||
layout = self.layout
|
||||
|
||||
# Operator
|
||||
layout.operator(ct.OperatorType.SimpleOperator)
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
BL_REGISTER = [SimplePanel]
|
||||
BL_HANDLERS: ct.BLHandlers = ct.BLHandlers()
|
||||
BL_KEYMAP_ITEMS: list[ct.BLKeymapItem] = []
|
|
@ -0,0 +1,185 @@
|
|||
"""Addon preferences, encapsulating the various global modifications that the user may make to how this addon functions."""
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
|
||||
from .services.init_settings import INIT_SETTINGS
|
||||
from .utils import logger
|
||||
|
||||
log = logger.get(__name__)
|
||||
|
||||
|
||||
####################
|
||||
# - Constants
|
||||
####################
|
||||
LOG_LEVEL_MAP: dict[str, logger.LogLevel] = {
|
||||
'DEBUG': logging.DEBUG,
|
||||
'INFO': logging.INFO,
|
||||
'WARNING': logging.WARNING,
|
||||
'ERROR': logging.ERROR,
|
||||
'CRITICAL': logging.CRITICAL,
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
# - Preferences
|
||||
####################
|
||||
class SimpleAddonPrefs(bpy.types.AddonPreferences):
|
||||
"""Manages user preferences and settings."""
|
||||
|
||||
bl_idname = __package__
|
||||
|
||||
####################
|
||||
# - Properties
|
||||
####################
|
||||
# Logging
|
||||
## File Logging
|
||||
use_log_file: bpy.props.BoolProperty( # type: ignore
|
||||
name='Log to File',
|
||||
description='Whether to use a file for addon logging',
|
||||
default=INIT_SETTINGS.use_log_file,
|
||||
update=lambda self, _: self.on_addon_logging_changed(),
|
||||
)
|
||||
log_level_file: bpy.props.EnumProperty( # type: ignore
|
||||
name='File Log Level',
|
||||
description='Level of addon logging to expose in the file',
|
||||
items=[
|
||||
('DEBUG', 'Debug', 'Debug'),
|
||||
('INFO', 'Info', 'Info'),
|
||||
('WARNING', 'Warning', 'Warning'),
|
||||
('ERROR', 'Error', 'Error'),
|
||||
('CRITICAL', 'Critical', 'Critical'),
|
||||
],
|
||||
default=INIT_SETTINGS.log_file_level,
|
||||
update=lambda self, _: self.on_addon_logging_changed(),
|
||||
)
|
||||
|
||||
bl__log_file_path: bpy.props.StringProperty( # type: ignore
|
||||
name='Log Path',
|
||||
description='Path to the Addon Log File',
|
||||
subtype='FILE_PATH',
|
||||
default=str(INIT_SETTINGS.log_file_path),
|
||||
update=lambda self, _: self.on_addon_logging_changed(),
|
||||
)
|
||||
|
||||
@property
|
||||
def log_file_path(self) -> Path:
|
||||
"""Retrieve the configured file-logging path as a `pathlib.Path`."""
|
||||
return Path(bpy.path.abspath(self.bl__log_file_path))
|
||||
|
||||
@log_file_path.setter
|
||||
def log_file_path(self, path: Path) -> None:
|
||||
"""Set the configured file-logging path as a `pathlib.Path`."""
|
||||
self.bl__log_file_path = str(path.resolve())
|
||||
|
||||
## Console Logging
|
||||
use_log_console: bpy.props.BoolProperty( # type: ignore
|
||||
name='Log to Console',
|
||||
description='Whether to use the console for addon logging',
|
||||
default=INIT_SETTINGS.use_log_console,
|
||||
update=lambda self, _: self.on_addon_logging_changed(),
|
||||
)
|
||||
log_level_console: bpy.props.EnumProperty( # type: ignore
|
||||
name='Console Log Level',
|
||||
description='Level of addon logging to expose in the console',
|
||||
items=[
|
||||
('DEBUG', 'Debug', 'Debug'),
|
||||
('INFO', 'Info', 'Info'),
|
||||
('WARNING', 'Warning', 'Warning'),
|
||||
('ERROR', 'Error', 'Error'),
|
||||
('CRITICAL', 'Critical', 'Critical'),
|
||||
],
|
||||
default=INIT_SETTINGS.log_console_level,
|
||||
update=lambda self, _: self.on_addon_logging_changed(),
|
||||
)
|
||||
|
||||
####################
|
||||
# - Events: Properties Changed
|
||||
####################
|
||||
def setup_logger(self, _logger: logging.Logger) -> None:
|
||||
"""Setup a logger using the settings declared in the addon preferences.
|
||||
|
||||
Args:
|
||||
_logger: The logger to configure using settings in the addon preferences.
|
||||
"""
|
||||
logger.update_logger(
|
||||
logger.console_handler,
|
||||
logger.file_handler,
|
||||
_logger,
|
||||
file_path=self.log_file_path if self.use_log_file else None,
|
||||
file_level=LOG_LEVEL_MAP[self.log_level_file],
|
||||
console_level=LOG_LEVEL_MAP[self.log_level_console]
|
||||
if self.use_log_console
|
||||
else None,
|
||||
)
|
||||
|
||||
def on_addon_logging_changed(self) -> None:
|
||||
"""Called to reconfigure all loggers to match newly-altered addon preferences.
|
||||
|
||||
This causes ex. changes to desired console log level to immediately be applied, but only the this addon's loggers.
|
||||
|
||||
Parameters:
|
||||
single_logger_to_setup: When set, only this logger will be setup.
|
||||
Otherwise, **all addon loggers will be setup**.
|
||||
"""
|
||||
log.info('Reconfiguring Loggers')
|
||||
for _logger in logger.all_addon_loggers():
|
||||
self.setup_logger(_logger)
|
||||
|
||||
log.info('Loggers Reconfigured')
|
||||
|
||||
####################
|
||||
# - UI
|
||||
####################
|
||||
def draw(self, _: bpy.types.Context) -> None:
|
||||
"""Draw the addon preferences within its panel in Blender's preferences.
|
||||
|
||||
Notes:
|
||||
Run by Blender when this addon's preferences need to be displayed.
|
||||
|
||||
Parameters:
|
||||
context: The Blender context object.
|
||||
"""
|
||||
layout = self.layout
|
||||
|
||||
####################
|
||||
# - Logging
|
||||
####################
|
||||
# Box w/Split: Log Level
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.alignment = 'CENTER'
|
||||
row.label(text='Logging')
|
||||
split = box.split(factor=0.5)
|
||||
|
||||
## Split Col: Console Logging
|
||||
col = split.column()
|
||||
row = col.row()
|
||||
row.prop(self, 'use_log_console', toggle=True)
|
||||
|
||||
row = col.row()
|
||||
row.enabled = self.use_log_console
|
||||
row.prop(self, 'log_level_console')
|
||||
|
||||
## Split Col: File Logging
|
||||
col = split.column()
|
||||
row = col.row()
|
||||
row.prop(self, 'use_log_file', toggle=True)
|
||||
|
||||
row = col.row()
|
||||
row.enabled = self.use_log_file
|
||||
row.prop(self, 'bl__log_file_path')
|
||||
|
||||
row = col.row()
|
||||
row.enabled = self.use_log_file
|
||||
row.prop(self, 'log_level_file')
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
BL_REGISTER = [
|
||||
SimpleAddonPrefs,
|
||||
]
|
|
@ -0,0 +1,143 @@
|
|||
"""Manages the registration of Blender classes, including delayed registrations that require access to Python dependencies.
|
||||
|
||||
Attributes:
|
||||
_REGISTERED_CLASSES: Blender classes currently registered by this addon.
|
||||
_REGISTERED_KEYMAPS: Addon keymaps currently registered by this addon.
|
||||
Each addon keymap is constrained to a single `space_type`, which is the key.
|
||||
_REGISTERED_KEYMAP_ITEMS: Addon keymap items currently registered by this addon.
|
||||
Each keymap item is paired to the keymap within which it is registered.
|
||||
_Each keymap is guaranteed to also be found in `_REGISTERED_KEYMAPS`._
|
||||
_REGISTERED_HANDLERS: Addon handlers currently registered by this addon.
|
||||
"""
|
||||
|
||||
import bpy
|
||||
|
||||
from . import contracts as ct
|
||||
from .utils import logger
|
||||
|
||||
log = logger.get(__name__)
|
||||
|
||||
####################
|
||||
# - Globals
|
||||
####################
|
||||
_REGISTERED_CLASSES: list[ct.BLClass] = []
|
||||
|
||||
_REGISTERED_KEYMAPS: dict[ct.BLSpaceType, bpy.types.KeyMap] = {}
|
||||
_REGISTERED_KEYMAP_ITEMS: list[tuple[bpy.types.KeyMap, bpy.types.KeyMapItem]] = []
|
||||
|
||||
_REGISTERED_HANDLERS: ct.BLHandlers | None = None
|
||||
|
||||
|
||||
####################
|
||||
# - Class Registration
|
||||
####################
|
||||
def register_classes(bl_register: list[ct.BLClass]) -> None:
|
||||
"""Registers a list of Blender classes.
|
||||
|
||||
Parameters:
|
||||
bl_register: List of Blender classes to register.
|
||||
"""
|
||||
log.info('Registering %s Classes', len(bl_register))
|
||||
for cls in bl_register:
|
||||
if cls.bl_idname in _REGISTERED_CLASSES:
|
||||
msg = f'Skipping register of {cls.bl_idname}'
|
||||
log.info(msg)
|
||||
continue
|
||||
|
||||
log.debug(
|
||||
'Registering Class %s',
|
||||
repr(cls),
|
||||
)
|
||||
bpy.utils.register_class(cls)
|
||||
_REGISTERED_CLASSES.append(cls)
|
||||
|
||||
|
||||
def unregister_classes() -> None:
|
||||
"""Unregisters all previously registered Blender classes."""
|
||||
log.info('Unregistering %s Classes', len(_REGISTERED_CLASSES))
|
||||
for cls in reversed(_REGISTERED_CLASSES):
|
||||
log.debug(
|
||||
'Unregistering Class %s',
|
||||
repr(cls),
|
||||
)
|
||||
bpy.utils.unregister_class(cls)
|
||||
|
||||
_REGISTERED_CLASSES.clear()
|
||||
|
||||
|
||||
####################
|
||||
# - Handler Registration
|
||||
####################
|
||||
def register_handlers(bl_handlers: ct.BLHandlers) -> None:
|
||||
"""Register the given Blender handlers."""
|
||||
global _REGISTERED_HANDLERS # noqa: PLW0603
|
||||
|
||||
log.info('Registering BLHandlers') ## TODO: More information
|
||||
if _REGISTERED_HANDLERS is None:
|
||||
bl_handlers.register()
|
||||
_REGISTERED_HANDLERS = bl_handlers
|
||||
else:
|
||||
msg = 'There are already BLHandlers registered; they must be unregistered before a new set can be registered.'
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
def unregister_handlers() -> None:
|
||||
"""Unregister this addon's registered Blender handlers."""
|
||||
global _REGISTERED_HANDLERS # noqa: PLW0603
|
||||
|
||||
log.info('Unregistering BLHandlers') ## TODO: More information
|
||||
if _REGISTERED_HANDLERS is not None:
|
||||
_REGISTERED_HANDLERS.register()
|
||||
_REGISTERED_HANDLERS = None
|
||||
else:
|
||||
msg = 'There are no BLHandlers registered; therefore, there is nothing to register.'
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
####################
|
||||
# - Keymap Registration
|
||||
####################
|
||||
def register_keymaps(keymap_items: list[ct.BLKeymapItem]) -> None:
|
||||
"""Registers a list of Blender hotkey definitions.
|
||||
|
||||
Parameters:
|
||||
bl_keymap_items: List of Blender hotkey definitions to register.
|
||||
"""
|
||||
# Aggregate Requested Spaces of All Keymap Items
|
||||
keymap_space_types: set[ct.BLSpaceType] = {
|
||||
keymap_item.space_type for keymap_item in keymap_items
|
||||
}
|
||||
|
||||
# Create Addon Keymap per Requested Space
|
||||
for keymap_space_type in keymap_space_types:
|
||||
log.info('Registering %s Keymap', keymap_space_type)
|
||||
bl_keymap = bpy.context.window_manager.keyconfigs.addon.keymaps.new(
|
||||
name=f'{ct.addon.NAME} - {keymap_space_type}',
|
||||
space_type=keymap_space_type,
|
||||
)
|
||||
_REGISTERED_KEYMAPS[keymap_space_type] = bl_keymap
|
||||
|
||||
# Register Keymap Items in Correct Addon Keymap
|
||||
for keymap_item in keymap_items:
|
||||
log.info('Registering %s Keymap Item', keymap_item)
|
||||
bl_keymap = _REGISTERED_KEYMAPS[keymap_item.space_type]
|
||||
bl_keymap_item = keymap_item.register(bl_keymap)
|
||||
|
||||
_REGISTERED_KEYMAP_ITEMS.append((bl_keymap, bl_keymap_item))
|
||||
|
||||
|
||||
def unregister_keymaps() -> None:
|
||||
"""Unregisters all Blender keymaps associated with the addon."""
|
||||
# Unregister Keymap Items from Correct Addon Keymap
|
||||
for bl_keymap, bl_keymap_item in reversed(_REGISTERED_KEYMAP_ITEMS):
|
||||
log.info('Unregistering %s BL Keymap Item', bl_keymap_item)
|
||||
bl_keymap.keymap_items.remove(bl_keymap_item)
|
||||
|
||||
_REGISTERED_KEYMAP_ITEMS.clear()
|
||||
|
||||
# Delete Addon Keymaps
|
||||
for bl_keymap in reversed(_REGISTERED_KEYMAPS.values()):
|
||||
log.info('Unregistering %s Keymap', bl_keymap)
|
||||
bpy.context.window_manager.keyconfigs.addon.keymaps.remove(bl_keymap)
|
||||
|
||||
_REGISTERED_KEYMAPS.clear()
|
|
@ -0,0 +1,58 @@
|
|||
"""Authoritative definition and storage of "Initialization Settings", which provide out-of-the-box defaults for settings relevant for ex. debugging.
|
||||
|
||||
These settings are transported via a special TOML file that is packed into the extension zip-file when packaging for release.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import tomllib
|
||||
import typing as typ
|
||||
from pathlib import Path
|
||||
|
||||
import pydantic as pyd
|
||||
|
||||
####################
|
||||
# - Constants
|
||||
####################
|
||||
PATH_ROOT = Path(__file__).resolve().parent.parent
|
||||
INIT_SETTINGS_FILENAME = 'init_settings.toml'
|
||||
|
||||
|
||||
####################
|
||||
# - Types
|
||||
####################
|
||||
LogLevel: typ.TypeAlias = int
|
||||
StrLogLevel: typ.TypeAlias = typ.Literal[
|
||||
'DEBUG',
|
||||
'INFO',
|
||||
'WARNING',
|
||||
'ERROR',
|
||||
'CRITICAL',
|
||||
]
|
||||
|
||||
|
||||
def str_to_loglevel(obj: typ.Any) -> LogLevel | typ.Any:
|
||||
if isinstance(obj, str) and obj in STR_LOG_LEVEL:
|
||||
return STR_LOG_LEVEL[obj]
|
||||
return obj
|
||||
|
||||
|
||||
class InitSettings(pyd.BaseModel):
|
||||
"""Model describing addon initialization settings, describing default settings baked into the release.
|
||||
|
||||
Each parameter
|
||||
|
||||
"""
|
||||
|
||||
use_log_file: bool
|
||||
log_file_path: Path
|
||||
log_file_level: StrLogLevel
|
||||
|
||||
use_log_console: bool
|
||||
log_console_level: StrLogLevel
|
||||
|
||||
|
||||
####################
|
||||
# - Init Settings Management
|
||||
####################
|
||||
with (PATH_ROOT / INIT_SETTINGS_FILENAME).open('rb') as f:
|
||||
INIT_SETTINGS: InitSettings = InitSettings(**tomllib.load(f))
|
|
@ -0,0 +1,196 @@
|
|||
"""A lightweight, `rich`-based approach to logging that is isolated from other extensions that may used the Python stdlib `logging` module."""
|
||||
|
||||
import logging
|
||||
import typing as typ
|
||||
from pathlib import Path
|
||||
|
||||
import rich.console
|
||||
import rich.logging
|
||||
import rich.traceback
|
||||
|
||||
from .. import contracts as ct
|
||||
from ..services import init_settings
|
||||
|
||||
LogLevel: typ.TypeAlias = int
|
||||
|
||||
|
||||
####################
|
||||
# - Configuration
|
||||
####################
|
||||
STREAM_LOG_FORMAT = 11 * ' ' + '%(levelname)-8s %(message)s (%(name)s)'
|
||||
FILE_LOG_FORMAT = STREAM_LOG_FORMAT
|
||||
|
||||
OUTPUT_CONSOLE = rich.console.Console(
|
||||
color_system='truecolor',
|
||||
)
|
||||
ERROR_CONSOLE = rich.console.Console(
|
||||
color_system='truecolor',
|
||||
stderr=True,
|
||||
)
|
||||
|
||||
ADDON_LOGGER_NAME = f'blext-{ct.addon.NAME}'
|
||||
ADDON_LOGGER: logging.Logger = logging.getLogger(ADDON_LOGGER_NAME)
|
||||
|
||||
rich.traceback.install(show_locals=True, console=ERROR_CONSOLE)
|
||||
|
||||
|
||||
####################
|
||||
# - Logger Access
|
||||
####################
|
||||
def all_addon_loggers() -> set[logging.Logger]:
|
||||
"""Retrieve all loggers currently declared by this addon.
|
||||
|
||||
These loggers are all children of `ADDON_LOGGER`, essentially.
|
||||
This allows for name-isolation from other Blender extensions, as well as easy cleanup.
|
||||
|
||||
Returns:
|
||||
Set of all loggers declared by this addon.
|
||||
"""
|
||||
return {
|
||||
logging.getLogger(name)
|
||||
for name in logging.root.manager.loggerDict
|
||||
if name.startswith(ADDON_LOGGER_NAME)
|
||||
}
|
||||
## TODO: Python 3.12 has a .getChildren() method that also returns sets.
|
||||
|
||||
|
||||
####################
|
||||
# - Logging Handlers
|
||||
####################
|
||||
def console_handler(level: LogLevel) -> rich.logging.RichHandler:
|
||||
"""A logging handler that prints messages to the console.
|
||||
|
||||
Parameters:
|
||||
level: The log levels (debug, info, etc.) to print.
|
||||
|
||||
Returns:
|
||||
The logging handler, which can be added to a logger.
|
||||
"""
|
||||
rich_formatter = logging.Formatter(
|
||||
'%(message)s',
|
||||
datefmt='[%X]',
|
||||
)
|
||||
rich_handler = rich.logging.RichHandler(
|
||||
level=level,
|
||||
console=ERROR_CONSOLE,
|
||||
rich_tracebacks=True,
|
||||
)
|
||||
rich_handler.setFormatter(rich_formatter)
|
||||
return rich_handler
|
||||
|
||||
|
||||
def file_handler(path_log_file: Path, level: LogLevel) -> rich.logging.RichHandler:
|
||||
"""A logging handler that prints messages to a file.
|
||||
|
||||
Parameters:
|
||||
path_log_file: The path to the log file.
|
||||
level: The log levels (debug, info, etc.) to append to the file.
|
||||
|
||||
Returns:
|
||||
The logging handler, which can be added to a logger.
|
||||
"""
|
||||
file_formatter = logging.Formatter(FILE_LOG_FORMAT)
|
||||
file_handler = logging.FileHandler(path_log_file)
|
||||
file_handler.setFormatter(file_formatter)
|
||||
file_handler.setLevel(level)
|
||||
return file_handler
|
||||
|
||||
|
||||
####################
|
||||
# - Logger Setup
|
||||
####################
|
||||
def get(module_name: str) -> logging.Logger:
|
||||
"""Retrieve and/or create a logger corresponding to a module name.
|
||||
|
||||
Warnings:
|
||||
MUST be used as `logger.get(__name__)`.
|
||||
|
||||
Parameters:
|
||||
module_name: The `__name__` of the module to return a logger for.
|
||||
"""
|
||||
log = ADDON_LOGGER.getChild(module_name)
|
||||
|
||||
# Setup Logger from Init Settings or Addon Preferences
|
||||
## - We prefer addon preferences, but they may not be setup yet.
|
||||
## - Once setup, the preferences may decide to re-configure all the loggers.
|
||||
addon_prefs = ct.addon.prefs()
|
||||
if addon_prefs is None:
|
||||
use_log_file = init_settings.INIT_SETTINGS.use_log_file
|
||||
log_file_path = init_settings.INIT_SETTINGS.log_file_path
|
||||
log_file_level = init_settings.INIT_SETTINGS.log_file_level
|
||||
use_log_console = init_settings.INIT_SETTINGS.use_log_console
|
||||
log_console_level = init_settings.INIT_SETTINGS.log_console_level
|
||||
|
||||
update_logger(
|
||||
console_handler,
|
||||
file_handler,
|
||||
log,
|
||||
file_path=log_file_path if use_log_file else None,
|
||||
file_level=log_file_level,
|
||||
console_level=log_console_level if use_log_console else None,
|
||||
)
|
||||
else:
|
||||
addon_prefs.setup_logger(log)
|
||||
|
||||
return log
|
||||
|
||||
|
||||
####################
|
||||
# - Logger Update
|
||||
####################
|
||||
def _init_logger(logger: logging.Logger) -> None:
|
||||
"""Prepare a logger for handlers to be added, ensuring normalized semantics for all loggers.
|
||||
|
||||
- Messages should not propagate to the root logger, causing double-messages.
|
||||
- Mesages should not filter by level; this is the job of the handlers.
|
||||
- No handlers must be set.
|
||||
|
||||
Args:
|
||||
logger: The logger to prepare.
|
||||
"""
|
||||
# DO NOT Propagate to Root Logger
|
||||
## - This looks like 'double messages'
|
||||
## - See SO/6729268/log-messages-appearing-twice-with-python-logging
|
||||
logger.propagate = False
|
||||
|
||||
# Let All Messages Through
|
||||
## - The individual handlers perform appropriate filtering.
|
||||
logger.setLevel(logging.NOTSET)
|
||||
|
||||
if logger.handlers:
|
||||
logger.handlers.clear()
|
||||
|
||||
|
||||
def update_logger(
|
||||
cb_console_handler: typ.Callable[[LogLevel], logging.Handler],
|
||||
cb_file_handler: typ.Callable[[Path, LogLevel], logging.Handler],
|
||||
logger: logging.Logger,
|
||||
console_level: LogLevel | None,
|
||||
file_path: Path | None,
|
||||
file_level: LogLevel,
|
||||
) -> None:
|
||||
"""Configures a single logger with given console and file handlers, individualizing the log level that triggers it.
|
||||
|
||||
This is a lower-level function - generally, modules that want to use a well-configured logger will use the `get()` function, which retrieves the parameters for this function from the addon preferences.
|
||||
This function is used by the higher-level log setup.
|
||||
|
||||
Parameters:
|
||||
cb_console_handler: A function that takes a log level threshold (inclusive), and returns a logging handler to a console-printer.
|
||||
cb_file_handler: A function that takes a log level threshold (inclusive), and returns a logging handler to a file-printer.
|
||||
logger: The logger to configure.
|
||||
console_level: The log level threshold to print to the console.
|
||||
None deactivates file logging.
|
||||
path_log_file: The path to the log file.
|
||||
None deactivates file logging.
|
||||
file_level: The log level threshold to print to the log file.
|
||||
"""
|
||||
# Initialize Logger
|
||||
_init_logger(logger)
|
||||
|
||||
# Add Console Logging Handler
|
||||
if console_level is not None:
|
||||
logger.addHandler(cb_console_handler(console_level))
|
||||
|
||||
# Add File Logging Handler
|
||||
if file_path is not None:
|
||||
logger.addHandler(cb_file_handler(file_path, file_level))
|
|
@ -0,0 +1,30 @@
|
|||
"""Provides a '@staticproperty', which is like '@property', but static. It can be very useful in specific situations."""
|
||||
|
||||
import typing as typ
|
||||
|
||||
|
||||
class staticproperty(property): # noqa: N801
|
||||
"""A read-only variant of `@property` that is entirely static, for use in specific situations.
|
||||
|
||||
The decorated method must take no arguments whatsoever, including `self`/`cls`.
|
||||
|
||||
Examples:
|
||||
Exactly as you'd expect.
|
||||
```python
|
||||
class Spam:
|
||||
@staticproperty
|
||||
def eggs():
|
||||
return 10
|
||||
|
||||
assert Spam.eggs == 10
|
||||
```
|
||||
"""
|
||||
|
||||
def __get__(self, instance: typ.Any, owner: type | None = None) -> typ.Any:
|
||||
"""Overridden getter that ignores instance and owner, and just returns the value of the evaluated (static) method.
|
||||
|
||||
Returns:
|
||||
The evaluated value of the static method that was decorated.
|
||||
"""
|
||||
return self.fget() # type: ignore
|
||||
## .fget() is guaranteed to exist by @property, so we can safely ignore the type.
|
|
@ -0,0 +1,200 @@
|
|||
[project]
|
||||
name = "blext_simple_example"
|
||||
version = "0.1.0"
|
||||
description = "Simple real-world example of a Blender extension"
|
||||
authors = [
|
||||
{ name = "John Doe", email = "john.doe@example.com" },
|
||||
]
|
||||
maintainers = [
|
||||
{ name = "John Doe", email = "john.doe@example.com" },
|
||||
]
|
||||
readme = "README.md"
|
||||
requires-python = "~= 3.11"
|
||||
license = { text = "AGPL-3.0-or-later" }
|
||||
dependencies = [
|
||||
"pydantic>=2.10.5",
|
||||
"rich>=13.9.3",
|
||||
]
|
||||
|
||||
####################
|
||||
# - Blender Extension
|
||||
####################
|
||||
[tool.blext]
|
||||
pretty_name = "BLExt Simple Example"
|
||||
blender_version_min = '4.2.0'
|
||||
blender_version_max = '4.3.10'
|
||||
bl_tags = ["Development"]
|
||||
copyright = ["2024 blext Contributors"]
|
||||
|
||||
# Platform Support
|
||||
## Map Valid Blender Platforms -> Required PyPi Platform Tags
|
||||
## Include as few PyPi tags as works on ~everything.
|
||||
[tool.blext.platforms]
|
||||
windows-amd64 = ['win_amd64']
|
||||
macos-arm64 = ['macosx_11_0_arm64', 'macosx_12_0_arm64', 'macosx_14_0_arm64']
|
||||
linux-x64 = ['manylinux1_x86_64', 'manylinux2014_x86_64', 'manylinux_2_17_x86_64', 'manylinux_2_28_x86_64']
|
||||
|
||||
# Packaging
|
||||
## Path is from the directory containing this file.
|
||||
[tool.blext.packaging]
|
||||
init_settings_filename = 'init_settings.toml'
|
||||
|
||||
# "Profiles" -> Affects Initialization Settings
|
||||
## This sets the default extension preferences for different situations.
|
||||
[tool.blext.profiles.test]
|
||||
use_path_local = true
|
||||
use_log_file = true
|
||||
log_file_path = 'addon.log'
|
||||
log_file_level = 'DEBUG'
|
||||
use_log_console = true
|
||||
log_console_level = 'INFO'
|
||||
|
||||
[tool.blext.profiles.dev]
|
||||
use_path_local = true
|
||||
use_log_file = true
|
||||
log_file_path = 'addon.log'
|
||||
log_file_level = 'DEBUG'
|
||||
use_log_console = true
|
||||
log_console_level = 'INFO'
|
||||
|
||||
[tool.blext.profiles.release]
|
||||
use_path_local = false
|
||||
use_log_file = true
|
||||
log_file_path = 'addon.log'
|
||||
log_file_level = 'INFO'
|
||||
use_log_console = true
|
||||
log_console_level = 'WARNING'
|
||||
|
||||
[tool.blext.profiles.release-debug]
|
||||
use_path_local = false
|
||||
use_log_file = true
|
||||
log_file_path = 'addon.log'
|
||||
log_file_level = 'DEBUG'
|
||||
use_log_console = true
|
||||
log_console_level = 'WARNING'
|
||||
|
||||
####################
|
||||
# - Blender Extension
|
||||
####################
|
||||
[tool.uv]
|
||||
managed = true
|
||||
dev-dependencies = [
|
||||
"mypy>=1.13.0",
|
||||
"pip>=24.2",
|
||||
"rich>=13.9.3",
|
||||
"ruff>=0.7.1",
|
||||
"tomli-w>=1.1.0",
|
||||
"typer>=0.15.1",
|
||||
]
|
||||
package = false
|
||||
|
||||
####################
|
||||
# - Tooling: Ruff
|
||||
####################
|
||||
[tool.ruff]
|
||||
target-version = "py311"
|
||||
line-length = 88
|
||||
|
||||
[tool.ruff.lint]
|
||||
task-tags = ["TODO"]
|
||||
select = [
|
||||
"E", # pycodestyle ## General Purpose
|
||||
"F", # pyflakes ## General Purpose
|
||||
"PL", # Pylint ## General Purpose
|
||||
|
||||
## Code Quality
|
||||
"TCH", # flake8-type-checking ## Type Checking Block Validator
|
||||
"C90", # mccabe ## Avoid Too-Complex Functions
|
||||
"ERA", # eradicate ## Ban Commented Code
|
||||
"TRY", # tryceratops ## Exception Handling Style
|
||||
"B", # flake8-bugbear ## Opinionated, Probable-Bug Patterns
|
||||
"N", # pep8-naming
|
||||
"D", # pydocstyle
|
||||
"SIM", # flake8-simplify ## Sanity-Check for Code Simplification
|
||||
"SLF", # flake8-self ## Ban Private Member Access
|
||||
"RUF", # Ruff-specific rules ## Extra Good-To-Have Rules
|
||||
|
||||
## Style
|
||||
"I", # isort ## Force import Sorting
|
||||
"UP", # pyupgrade ## Enforce Upgrade to Newer Python Syntaxes
|
||||
"COM", # flake8-commas ## Enforce Trailing Commas
|
||||
"Q", # flake8-quotes ## Finally - Quoting Style!
|
||||
"PTH", # flake8-use-pathlib ## Enforce pathlib usage
|
||||
"A", # flake8-builtins ## Prevent Builtin Shadowing
|
||||
"C4", # flake9-comprehensions ## Check Compehension Appropriateness
|
||||
"DTZ", # flake8-datetimez ## Ban naive Datetime Creation
|
||||
"EM", # flake8-errmsg ## Check Exception String Formatting
|
||||
"ISC", # flake8-implicit-str-concat ## Enforce Good String Literal Concat
|
||||
"G", # flake8-logging-format ## Enforce Good Logging Practices
|
||||
"INP", # flake8-no-pep420 ## Ban PEP420; Enforce __init__.py.
|
||||
"PIE", # flake8-pie ## Misc Opinionated Checks
|
||||
"T20", # flake8-print ## Ban print()
|
||||
"RSE", # flake8-raise ## Check Niche Exception Raising Pattern
|
||||
"RET", # flake8-return ## Enforce Good Returning
|
||||
"ARG", # flake8-unused-arguments ## Ban Unused Arguments
|
||||
|
||||
# Specific
|
||||
"PT", # flake8-pytest-style ## pytest-Specific Checks
|
||||
]
|
||||
ignore = [
|
||||
"COM812", # Conflicts w/Formatter
|
||||
"ISC001", # Conflicts w/Formatter
|
||||
"Q000", # Conflicts w/Formatter
|
||||
"Q001", # Conflicts w/Formatter
|
||||
"Q002", # Conflicts w/Formatter
|
||||
"Q003", # Conflicts w/Formatter
|
||||
"D206", # Conflicts w/Formatter
|
||||
"B008", # FastAPI uses this for Depends(), Security(), etc. .
|
||||
"E701", # class foo(Parent): pass or if simple: return are perfectly elegant
|
||||
"ERA001", # 'Commented-out code' seems to be just about anything to ruff
|
||||
"F722", # jaxtyping uses type annotations that ruff sees as "syntax error"
|
||||
"N806", # Sometimes we like using types w/uppercase in functions, sue me
|
||||
"RUF001", # We use a lot of unicode, yes, on purpose!
|
||||
#"RUF012", # ruff misunderstands which ClassVars are actually mutable.
|
||||
|
||||
# Line Length - Controversy Incoming
|
||||
## Hot Take: Let the Formatter Worry about Line Length
|
||||
## - Yes dear reader, I'm with you. Soft wrap can go too far.
|
||||
## - ...but also, sometimes there are real good reasons not to split.
|
||||
## - Ex. I think 'one sentence per line' docstrings are a valid thing.
|
||||
## - Overlong lines tend to be be a code smell anyway
|
||||
## - We'll see if my hot takes survive the week :)
|
||||
"E501", # Let Formatter Worry about Line Length
|
||||
]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"tests/*" = [
|
||||
"SLF001", # It's okay to not have module-level docstrings in test modules.
|
||||
"D100", # It's okay to not have module-level docstrings in test modules.
|
||||
"D104", # Same for packages.
|
||||
]
|
||||
|
||||
####################
|
||||
# - Tooling: Ruff Sublinters
|
||||
####################
|
||||
[tool.ruff.lint.flake8-bugbear]
|
||||
extend-immutable-calls = []
|
||||
|
||||
[tool.ruff.lint.pycodestyle]
|
||||
max-doc-length = 120
|
||||
ignore-overlong-task-comments = true
|
||||
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
convention = "google"
|
||||
|
||||
[tool.ruff.lint.pylint]
|
||||
max-args = 6
|
||||
|
||||
####################
|
||||
# - Tooling: Ruff Formatter
|
||||
####################
|
||||
[tool.ruff.format]
|
||||
quote-style = "single"
|
||||
indent-style = "tab"
|
||||
docstring-code-format = false
|
||||
|
||||
####################
|
||||
# - Tooling: Pytest
|
||||
####################
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
|
@ -0,0 +1,298 @@
|
|||
version = 1
|
||||
requires-python = ">=3.11, <4"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.13'",
|
||||
"python_full_version == '3.12.*'",
|
||||
"python_full_version < '3.12'",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
version = "0.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blext-simple-example"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "pydantic" },
|
||||
{ name = "rich" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "mypy" },
|
||||
{ name = "pip" },
|
||||
{ name = "rich" },
|
||||
{ name = "ruff" },
|
||||
{ name = "tomli-w" },
|
||||
{ name = "typer" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "pydantic", specifier = ">=2.10.5" },
|
||||
{ name = "rich", specifier = ">=13.9.3" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "mypy", specifier = ">=1.13.0" },
|
||||
{ name = "pip", specifier = ">=24.2" },
|
||||
{ name = "rich", specifier = ">=13.9.3" },
|
||||
{ name = "ruff", specifier = ">=0.7.1" },
|
||||
{ name = "tomli-w", specifier = ">=1.1.0" },
|
||||
{ name = "typer", specifier = ">=0.15.1" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markdown-it-py"
|
||||
version = "3.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mdurl" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mdurl"
|
||||
version = "0.1.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.14.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mypy-extensions" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b9/eb/2c92d8ea1e684440f54fa49ac5d9a5f19967b7b472a281f419e69a8d228e/mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", size = 3216051 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/da/11/a9422850fd506edbcdc7f6090682ecceaf1f87b9dd847f9df79942da8506/mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c", size = 11120432 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/9e/47e450fd39078d9c02d620545b2cb37993a8a8bdf7db3652ace2f80521ca/mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1", size = 10279515 },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/b5/6c8d33bd0f851a7692a8bfe4ee75eb82b6983a3cf39e5e32a5d2a723f0c1/mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8", size = 12025791 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/4c/e10e2c46ea37cab5c471d0ddaaa9a434dc1d28650078ac1b56c2d7b9b2e4/mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f", size = 12749203 },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/55/beacb0c69beab2153a0f57671ec07861d27d735a0faff135a494cd4f5020/mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1", size = 12885900 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/75/8c93ff7f315c4d086a2dfcde02f713004357d70a163eddb6c56a6a5eff40/mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae", size = 9777869 },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/1b/b38c079609bb4627905b74fc6a49849835acf68547ac33d8ceb707de5f52/mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", size = 11266668 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/75/2ed0d2964c1ffc9971c729f7a544e9cd34b2cdabbe2d11afd148d7838aa2/mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", size = 10254060 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/5f/7b8051552d4da3c51bbe8fcafffd76a6823779101a2b198d80886cd8f08e/mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", size = 11933167 },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/90/f53971d3ac39d8b68bbaab9a4c6c58c8caa4d5fd3d587d16f5927eeeabe1/mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", size = 12864341 },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/d2/8bc0aeaaf2e88c977db41583559319f1821c069e943ada2701e86d0430b7/mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89", size = 12972991 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/17/07815114b903b49b0f2cf7499f1c130e5aa459411596668267535fe9243c/mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", size = 9879016 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/15/bb6a686901f59222275ab228453de741185f9d54fecbaacec041679496c6/mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", size = 11252097 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/b3/8b0f74dfd072c802b7fa368829defdf3ee1566ba74c32a2cb2403f68024c/mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", size = 10239728 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/9b/4fd95ab20c52bb5b8c03cc49169be5905d931de17edfe4d9d2986800b52e/mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", size = 11924965 },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660 },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198 },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pip"
|
||||
version = "24.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f4/b1/b422acd212ad7eedddaf7981eee6e5de085154ff726459cf2da7c5a184c1/pip-24.3.1.tar.gz", hash = "sha256:ebcb60557f2aefabc2e0f918751cd24ea0d56d8ec5445fe1807f1d2109660b99", size = 1931073 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/7d/500c9ad20238fcfcb4cb9243eede163594d7020ce87bd9610c9e02771876/pip-24.3.1-py3-none-any.whl", hash = "sha256:3790624780082365f47549d032f3770eeb2b1e8bd1f7b2e02dace1afa361b4ed", size = 1822182 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.10.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "annotated-types" },
|
||||
{ name = "pydantic-core" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6a/c7/ca334c2ef6f2e046b1144fe4bb2a5da8a4c574e7f2ebf7e16b34a6a2fa92/pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff", size = 761287 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/58/26/82663c79010b28eddf29dcdd0ea723439535fa917fce5905885c0e9ba562/pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53", size = 431426 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.27.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/89/f3450af9d09d44eea1f2c369f49e8f181d742f28220f88cc4dfaae91ea6e/pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc", size = 1893421 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/e3/71fe85af2021f3f386da42d291412e5baf6ce7716bd7101ea49c810eda90/pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7", size = 1814998 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/3c/724039e0d848fd69dbf5806894e26479577316c6f0f112bacaf67aa889ac/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15", size = 1826167 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/5b/1b29e8c1fb5f3199a9a57c1452004ff39f494bbe9bdbe9a81e18172e40d3/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306", size = 1865071 },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/6c/3985203863d76bb7d7266e36970d7e3b6385148c18a68cc8915fd8c84d57/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99", size = 2036244 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/41/f15316858a246b5d723f7d7f599f79e37493b2e84bfc789e58d88c209f8a/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459", size = 2737470 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/7c/b860618c25678bbd6d1d99dbdfdf0510ccb50790099b963ff78a124b754f/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048", size = 1992291 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/73/42c3742a391eccbeab39f15213ecda3104ae8682ba3c0c28069fbcb8c10d/pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d", size = 1994613 },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/7a/941e89096d1175d56f59340f3a8ebaf20762fef222c298ea96d36a6328c5/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b", size = 2002355 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/95/2359937a73d49e336a5a19848713555605d4d8d6940c3ec6c6c0ca4dcf25/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474", size = 2126661 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/4c/ca02b7bdb6012a1adef21a50625b14f43ed4d11f1fc237f9d7490aa5078c/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6", size = 2153261 },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/9d/a241db83f973049a1092a079272ffe2e3e82e98561ef6214ab53fe53b1c7/pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c", size = 1812361 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/ef/013f07248041b74abd48a385e2110aa3a9bbfef0fbd97d4e6d07d2f5b89a/pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc", size = 1982484 },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/1c/16b3a3e3398fd29dca77cea0a1d998d6bde3902fa2706985191e2313cc76/pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4", size = 1867102 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.19.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "13.9.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markdown-it-py" },
|
||||
{ name = "pygments" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.9.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/67/3e/e89f736f01aa9517a97e2e7e0ce8d34a4d8207087b3cfdec95133fee13b5/ruff-0.9.1.tar.gz", hash = "sha256:fd2b25ecaf907d6458fa842675382c8597b3c746a2dde6717fe3415425df0c17", size = 3498844 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/05/c3a2e0feb3d5d394cdfd552de01df9d3ec8a3a3771bbff247fab7e668653/ruff-0.9.1-py3-none-linux_armv6l.whl", hash = "sha256:84330dda7abcc270e6055551aca93fdde1b0685fc4fd358f26410f9349cf1743", size = 10645241 },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/da/59f0a40e5f88ee5c054ad175caaa2319fc96571e1d29ab4730728f2aad4f/ruff-0.9.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3cae39ba5d137054b0e5b472aee3b78a7c884e61591b100aeb544bcd1fc38d4f", size = 10391066 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/fe/85e1c1acf0ba04a3f2d54ae61073da030f7a5dc386194f96f3c6ca444a78/ruff-0.9.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:50c647ff96f4ba288db0ad87048257753733763b409b2faf2ea78b45c8bb7fcb", size = 10012308 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/9b/780aa5d4bdca8dcea4309264b8faa304bac30e1ce0bcc910422bfcadd203/ruff-0.9.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0c8b149e9c7353cace7d698e1656ffcf1e36e50f8ea3b5d5f7f87ff9986a7ca", size = 10881960 },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/f4/dac4361afbfe520afa7186439e8094e4884ae3b15c8fc75fb2e759c1f267/ruff-0.9.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:beb3298604540c884d8b282fe7625651378e1986c25df51dec5b2f60cafc31ce", size = 10414803 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/a2/057a3cb7999513cb78d6cb33a7d1cc6401c82d7332583786e4dad9e38e44/ruff-0.9.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39d0174ccc45c439093971cc06ed3ac4dc545f5e8bdacf9f067adf879544d969", size = 11464929 },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/c6/1ccfcc209bee465ced4874dcfeaadc88aafcc1ea9c9f31ef66f063c187f0/ruff-0.9.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:69572926c0f0c9912288915214ca9b2809525ea263603370b9e00bed2ba56dbd", size = 12170717 },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/97/4a524027518525c7cf6931e9fd3b2382be5e4b75b2b61bec02681a7685a5/ruff-0.9.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:937267afce0c9170d6d29f01fcd1f4378172dec6760a9f4dface48cdabf9610a", size = 11708921 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/a4/4e77cf6065c700d5593b25fca6cf725b1ab6d70674904f876254d0112ed0/ruff-0.9.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:186c2313de946f2c22bdf5954b8dd083e124bcfb685732cfb0beae0c47233d9b", size = 13058074 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/d6/fcb78e0531e863d0a952c4c5600cc5cd317437f0e5f031cd2288b117bb37/ruff-0.9.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f94942a3bb767675d9a051867c036655fe9f6c8a491539156a6f7e6b5f31831", size = 11281093 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/3b/7235bbeff00c95dc2d073cfdbf2b871b5bbf476754c5d277815d286b4328/ruff-0.9.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:728d791b769cc28c05f12c280f99e8896932e9833fef1dd8756a6af2261fd1ab", size = 10882610 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/66/5599d23257c61cf038137f82999ca8f9d0080d9d5134440a461bef85b461/ruff-0.9.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2f312c86fb40c5c02b44a29a750ee3b21002bd813b5233facdaf63a51d9a85e1", size = 10489273 },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/85/de4aa057e2532db0f9761e2c2c13834991e087787b93e4aeb5f1cb10d2df/ruff-0.9.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ae017c3a29bee341ba584f3823f805abbe5fe9cd97f87ed07ecbf533c4c88366", size = 11003314 },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/42/afedcaa089116d81447347f76041ff46025849fedb0ed2b187d24cf70fca/ruff-0.9.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5dc40a378a0e21b4cfe2b8a0f1812a6572fc7b230ef12cd9fac9161aa91d807f", size = 11342982 },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/c6/fe45f3eb27e3948b41a305d8b768e949bf6a39310e9df73f6c576d7f1d9f/ruff-0.9.1-py3-none-win32.whl", hash = "sha256:46ebf5cc106cf7e7378ca3c28ce4293b61b449cd121b98699be727d40b79ba72", size = 8819750 },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/8d/580db77c3b9d5c3d9479e55b0b832d279c30c8f00ab0190d4cd8fc67831c/ruff-0.9.1-py3-none-win_amd64.whl", hash = "sha256:342a824b46ddbcdddd3abfbb332fa7fcaac5488bf18073e841236aadf4ad5c19", size = 9701331 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/94/0498cdb7316ed67a1928300dd87d659c933479f44dec51b4f62bfd1f8028/ruff-0.9.1-py3-none-win_arm64.whl", hash = "sha256:1cd76c7f9c679e6e8f2af8f778367dca82b95009bc7b1a85a47f1521ae524fa7", size = 9145708 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shellingham"
|
||||
version = "1.5.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli-w"
|
||||
version = "1.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d4/19/b65f1a088ee23e37cdea415b357843eca8b1422a7b11a9eee6e35d4ec273/tomli_w-1.1.0.tar.gz", hash = "sha256:49e847a3a304d516a169a601184932ef0f6b61623fe680f836a2aa7128ed0d33", size = 6929 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/ac/ce90573ba446a9bbe65838ded066a805234d159b4446ae9f8ec5bbd36cbd/tomli_w-1.1.0-py3-none-any.whl", hash = "sha256:1403179c78193e3184bfaade390ddbd071cba48a32a2e62ba11aae47490c63f7", size = 6440 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typer"
|
||||
version = "0.15.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "rich" },
|
||||
{ name = "shellingham" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/dca7b219718afd37a0068f4f2530a727c2b74a8b6e8e0c0080a4c0de4fcd/typer-0.15.1.tar.gz", hash = "sha256:a0588c0a7fa68a1978a069818657778f86abe6ff5ea6abf472f940a08bfe4f0a", size = 99789 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/cc/0a838ba5ca64dc832aa43f727bd586309846b0ffb2ce52422543e6075e8a/typer-0.15.1-py3-none-any.whl", hash = "sha256:7994fb7b8155b64d3402518560648446072864beefd44aa2dc36972a5972e847", size = 44908 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.12.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
|
||||
]
|
|
@ -0,0 +1,201 @@
|
|||
[project]
|
||||
name = "blext"
|
||||
version = "0.1.0"
|
||||
description = "Fast, convenient project manager for Blender Extensions."
|
||||
authors = [
|
||||
{ name = "Sofus Albert Høgsbro Rose", email = "blext@sofusrose.com" },
|
||||
]
|
||||
maintainers = [
|
||||
{ name = "Sofus Albert Høgsbro Rose", email = "blext@sofusrose.com" },
|
||||
]
|
||||
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
license = { text = "AGPL-3.0-or-later" }
|
||||
dependencies = [
|
||||
"cyclopts>=3.1.5",
|
||||
"pip>=24.3.1",
|
||||
"pydantic>=2.10.5",
|
||||
"pypdl>=1.5.1",
|
||||
"rich>=13.9.4",
|
||||
"tomli-w>=1.1.0",
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"mypy>=1.14.1",
|
||||
"pytest>=8.3.4",
|
||||
"ruff>=0.9.1",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
blext = "blext.cli:app"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["blext"]
|
||||
|
||||
[tool.uv]
|
||||
package = true
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
####################
|
||||
# - Tooling: Ruff
|
||||
####################
|
||||
[tool.ruff]
|
||||
target-version = "py311"
|
||||
line-length = 88
|
||||
|
||||
[tool.ruff.lint]
|
||||
task-tags = ["TODO"]
|
||||
select = [
|
||||
"E", # pycodestyle ## General Purpose
|
||||
"F", # pyflakes ## General Purpose
|
||||
"PL", # Pylint ## General Purpose
|
||||
|
||||
## Code Quality
|
||||
"TCH", # flake8-type-checking ## Type Checking Block Validator
|
||||
"C90", # mccabe ## Avoid Too-Complex Functions
|
||||
"ERA", # eradicate ## Ban Commented Code
|
||||
"TRY", # tryceratops ## Exception Handling Style
|
||||
"B", # flake8-bugbear ## Opinionated, Probable-Bug Patterns
|
||||
"N", # pep8-naming
|
||||
"D", # pydocstyle
|
||||
"SIM", # flake8-simplify ## Sanity-Check for Code Simplification
|
||||
"SLF", # flake8-self ## Ban Private Member Access
|
||||
"RUF", # Ruff-specific rules ## Extra Good-To-Have Rules
|
||||
|
||||
## Style
|
||||
"I", # isort ## Force import Sorting
|
||||
"UP", # pyupgrade ## Enforce Upgrade to Newer Python Syntaxes
|
||||
"COM", # flake8-commas ## Enforce Trailing Commas
|
||||
"Q", # flake8-quotes ## Finally - Quoting Style!
|
||||
"PTH", # flake8-use-pathlib ## Enforce pathlib usage
|
||||
"A", # flake8-builtins ## Prevent Builtin Shadowing
|
||||
"C4", # flake9-comprehensions ## Check Compehension Appropriateness
|
||||
"DTZ", # flake8-datetimez ## Ban naive Datetime Creation
|
||||
"EM", # flake8-errmsg ## Check Exception String Formatting
|
||||
"ISC", # flake8-implicit-str-concat ## Enforce Good String Literal Concat
|
||||
"G", # flake8-logging-format ## Enforce Good Logging Practices
|
||||
"INP", # flake8-no-pep420 ## Ban PEP420; Enforce __init__.py.
|
||||
"PIE", # flake8-pie ## Misc Opinionated Checks
|
||||
"T20", # flake8-print ## Ban print()
|
||||
"RSE", # flake8-raise ## Check Niche Exception Raising Pattern
|
||||
"RET", # flake8-return ## Enforce Good Returning
|
||||
"ARG", # flake8-unused-arguments ## Ban Unused Arguments
|
||||
|
||||
# Specific
|
||||
"PT", # flake8-pytest-style ## pytest-Specific Checks
|
||||
]
|
||||
ignore = [
|
||||
"COM812", # Conflicts w/Formatter
|
||||
"ISC001", # Conflicts w/Formatter
|
||||
"Q000", # Conflicts w/Formatter
|
||||
"Q001", # Conflicts w/Formatter
|
||||
"Q002", # Conflicts w/Formatter
|
||||
"Q003", # Conflicts w/Formatter
|
||||
"D206", # Conflicts w/Formatter
|
||||
"E701", # class foo(Parent): pass or if simple: return are perfectly elegant
|
||||
"ERA001", # 'Commented-out code' seems to be just about anything to ruff
|
||||
"F722", # jaxtyping uses type annotations that ruff sees as "syntax error"
|
||||
"N806", # Sometimes we like using types w/uppercase in functions, sue me
|
||||
"RUF001", # We use a lot of unicode, yes, on purpose!
|
||||
|
||||
# Line Length - Controversy Incoming
|
||||
## Hot Take: Let the Formatter Worry about Line Length
|
||||
## - Yes dear reader, I'm with you. Soft wrap can go too far.
|
||||
## - ...but also, sometimes there are real good reasons not to split.
|
||||
## - Ex. I think 'one sentence per line' docstrings are a valid thing.
|
||||
## - Overlong lines tend to be be a code smell anyway
|
||||
## - We'll see if my hot takes survive the week :)
|
||||
"E501", # Let Formatter Worry about Line Length
|
||||
]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"tests/*" = [
|
||||
"SLF001", # It's okay to not have module-level docstrings in test modules.
|
||||
"D100", # It's okay to not have module-level docstrings in test modules.
|
||||
"D104", # Same for packages.
|
||||
]
|
||||
|
||||
####################
|
||||
# - Tooling: Ruff Sublinters
|
||||
####################
|
||||
[tool.ruff.lint.flake8-bugbear]
|
||||
extend-immutable-calls = []
|
||||
|
||||
[tool.ruff.lint.pycodestyle]
|
||||
max-doc-length = 120
|
||||
ignore-overlong-task-comments = true
|
||||
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
convention = "google"
|
||||
|
||||
[tool.ruff.lint.pylint]
|
||||
max-args = 6
|
||||
|
||||
####################
|
||||
# - Tooling: Ruff Formatter
|
||||
####################
|
||||
[tool.ruff.format]
|
||||
quote-style = "single"
|
||||
indent-style = "tab"
|
||||
docstring-code-format = false
|
||||
|
||||
####################
|
||||
# - Tooling: MyPy
|
||||
####################
|
||||
[tool.mypy]
|
||||
python_version = '3.12'
|
||||
python_executable="./.venv/bin/python"
|
||||
|
||||
warn_redundant_casts = true
|
||||
warn_unused_ignores = true
|
||||
warn_return_any = true
|
||||
|
||||
strict_optional = true
|
||||
no_implicit_optional = true
|
||||
|
||||
disallow_subclassing_any = false
|
||||
disallow_any_generics = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_incomplete_defs = true
|
||||
|
||||
check_untyped_defs = true
|
||||
disallow_untyped_decorators = true
|
||||
|
||||
ignore_missing_imports = true
|
||||
|
||||
plugins = [
|
||||
# 'pydantic.mypy',
|
||||
# 'typing_protocol_intersection.mypy_plugin',
|
||||
]
|
||||
|
||||
|
||||
####################
|
||||
# - Tooling: Commits
|
||||
####################
|
||||
[tool.commitizen]
|
||||
# Specification
|
||||
name = "cz_conventional_commits"
|
||||
version_scheme = "semver2"
|
||||
version_provider = "pep621"
|
||||
tag_format = "v$version"
|
||||
|
||||
# Version Bumping
|
||||
retry_after_failure = true
|
||||
major_version_zero = true
|
||||
update_changelog_on_bump = true
|
||||
|
||||
# Annotations / Signature
|
||||
gpg_sign = true
|
||||
annotated_tag = true
|
||||
|
||||
####################
|
||||
# - Tooling: Pytest
|
||||
####################
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import sys
|
||||
|
||||
sys.dont_write_bytecode = True
|
|
@ -0,0 +1,7 @@
|
|||
from pathlib import Path
|
||||
|
||||
PATH_ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
PATH_EXAMPLES = {
|
||||
'simple': PATH_ROOT / 'examples' / 'simple'
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import contextlib
|
||||
|
||||
from typer.testing import CliRunner
|
||||
|
||||
from blext.cli import app
|
||||
from tests import context
|
||||
|
||||
runner = CliRunner()
|
||||
|
||||
|
||||
def test_build_simple():
|
||||
with contextlib.chdir(context.PATH_EXAMPLES['simple']):
|
||||
result = runner.invoke(app, ['build'])
|
||||
print(result)
|
||||
assert result.exit_code == 0
|
|
@ -0,0 +1,697 @@
|
|||
version = 1
|
||||
requires-python = ">=3.11"
|
||||
|
||||
[[package]]
|
||||
name = "aiofiles"
|
||||
version = "24.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c", size = 30247 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5", size = 15896 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aiohappyeyeballs"
|
||||
version = "2.4.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7f/55/e4373e888fdacb15563ef6fa9fa8c8252476ea071e96fb46defac9f18bf2/aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745", size = 21977 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/74/fbb6559de3607b3300b9be3cc64e97548d55678e44623db17820dbd20002/aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8", size = 14756 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aiohttp"
|
||||
version = "3.11.11"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aiohappyeyeballs" },
|
||||
{ name = "aiosignal" },
|
||||
{ name = "attrs" },
|
||||
{ name = "frozenlist" },
|
||||
{ name = "multidict" },
|
||||
{ name = "propcache" },
|
||||
{ name = "yarl" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fe/ed/f26db39d29cd3cb2f5a3374304c713fe5ab5a0e4c8ee25a0c45cc6adf844/aiohttp-3.11.11.tar.gz", hash = "sha256:bb49c7f1e6ebf3821a42d81d494f538107610c3a705987f53068546b0e90303e", size = 7669618 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/34/ae/e8806a9f054e15f1d18b04db75c23ec38ec954a10c0a68d3bd275d7e8be3/aiohttp-3.11.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ba74ec819177af1ef7f59063c6d35a214a8fde6f987f7661f4f0eecc468a8f76", size = 708624 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/e0/313ef1a333fb4d58d0c55a6acb3cd772f5d7756604b455181049e222c020/aiohttp-3.11.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4af57160800b7a815f3fe0eba9b46bf28aafc195555f1824555fa2cfab6c1538", size = 468507 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/60/03455476bf1f467e5b4a32a465c450548b2ce724eec39d69f737191f936a/aiohttp-3.11.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffa336210cf9cd8ed117011085817d00abe4c08f99968deef0013ea283547204", size = 455571 },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/f9/469588603bd75bf02c8ffb8c8a0d4b217eed446b49d4a767684685aa33fd/aiohttp-3.11.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b8fe282183e4a3c7a1b72f5ade1094ed1c6345a8f153506d114af5bf8accd9", size = 1685694 },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/b9/1b7fa43faf6c8616fa94c568dc1309ffee2b6b68b04ac268e5d64b738688/aiohttp-3.11.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af41686ccec6a0f2bdc66686dc0f403c41ac2089f80e2214a0f82d001052c03", size = 1743660 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/8b/0248d19dbb16b67222e75f6aecedd014656225733157e5afaf6a6a07e2e8/aiohttp-3.11.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70d1f9dde0e5dd9e292a6d4d00058737052b01f3532f69c0c65818dac26dc287", size = 1785421 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/11/f478e071815a46ca0a5ae974651ff0c7a35898c55063305a896e58aa1247/aiohttp-3.11.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:249cc6912405917344192b9f9ea5cd5b139d49e0d2f5c7f70bdfaf6b4dbf3a2e", size = 1675145 },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/5d/284d182fecbb5075ae10153ff7374f57314c93a8681666600e3a9e09c505/aiohttp-3.11.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0eb98d90b6690827dcc84c246811feeb4e1eea683c0eac6caed7549be9c84665", size = 1619804 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/78/980064c2ad685c64ce0e8aeeb7ef1e53f43c5b005edcd7d32e60809c4992/aiohttp-3.11.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec82bf1fda6cecce7f7b915f9196601a1bd1a3079796b76d16ae4cce6d0ef89b", size = 1654007 },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/8d/9e658d63b1438ad42b96f94da227f2e2c1d5c6001c9e8ffcc0bfb22e9105/aiohttp-3.11.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9fd46ce0845cfe28f108888b3ab17abff84ff695e01e73657eec3f96d72eef34", size = 1650022 },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/fd/a032bf7f2755c2df4f87f9effa34ccc1ef5cea465377dbaeef93bb56bbd6/aiohttp-3.11.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bd176afcf8f5d2aed50c3647d4925d0db0579d96f75a31e77cbaf67d8a87742d", size = 1732899 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/0c/c2b85fde167dd440c7ba50af2aac20b5a5666392b174df54c00f888c5a75/aiohttp-3.11.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ec2aa89305006fba9ffb98970db6c8221541be7bee4c1d027421d6f6df7d1ce2", size = 1755142 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/78/91ae1a3b3b3bed8b893c5d69c07023e151b1c95d79544ad04cf68f596c2f/aiohttp-3.11.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:92cde43018a2e17d48bb09c79e4d4cb0e236de5063ce897a5e40ac7cb4878773", size = 1692736 },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/89/a7ef9c4b4cdb546fcc650ca7f7395aaffbd267f0e1f648a436bec33c9b95/aiohttp-3.11.11-cp311-cp311-win32.whl", hash = "sha256:aba807f9569455cba566882c8938f1a549f205ee43c27b126e5450dc9f83cc62", size = 416418 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/db/2192489a8a51b52e06627506f8ac8df69ee221de88ab9bdea77aa793aa6a/aiohttp-3.11.11-cp311-cp311-win_amd64.whl", hash = "sha256:ae545f31489548c87b0cced5755cfe5a5308d00407000e72c4fa30b19c3220ac", size = 442509 },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/cf/4bda538c502f9738d6b95ada11603c05ec260807246e15e869fc3ec5de97/aiohttp-3.11.11-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e595c591a48bbc295ebf47cb91aebf9bd32f3ff76749ecf282ea7f9f6bb73886", size = 704666 },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/7b/87fcef2cad2fad420ca77bef981e815df6904047d0a1bd6aeded1b0d1d66/aiohttp-3.11.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ea1b59dc06396b0b424740a10a0a63974c725b1c64736ff788a3689d36c02d2", size = 464057 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/a6/789e1f17a1b6f4a38939fbc39d29e1d960d5f89f73d0629a939410171bc0/aiohttp-3.11.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8811f3f098a78ffa16e0ea36dffd577eb031aea797cbdba81be039a4169e242c", size = 455996 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/dd/485061fbfef33165ce7320db36e530cd7116ee1098e9c3774d15a732b3fd/aiohttp-3.11.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7227b87a355ce1f4bf83bfae4399b1f5bb42e0259cb9405824bd03d2f4336a", size = 1682367 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/d7/9ec5b3ea9ae215c311d88b2093e8da17e67b8856673e4166c994e117ee3e/aiohttp-3.11.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d40f9da8cabbf295d3a9dae1295c69975b86d941bc20f0a087f0477fa0a66231", size = 1736989 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/fb/ea94927f7bfe1d86178c9d3e0a8c54f651a0a655214cce930b3c679b8f64/aiohttp-3.11.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffb3dc385f6bb1568aa974fe65da84723210e5d9707e360e9ecb51f59406cd2e", size = 1793265 },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/7f/6de218084f9b653026bd7063cd8045123a7ba90c25176465f266976d8c82/aiohttp-3.11.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8f5f7515f3552d899c61202d99dcb17d6e3b0de777900405611cd747cecd1b8", size = 1691841 },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/e2/992f43d87831cbddb6b09c57ab55499332f60ad6fdbf438ff4419c2925fc/aiohttp-3.11.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3499c7ffbfd9c6a3d8d6a2b01c26639da7e43d47c7b4f788016226b1e711caa8", size = 1619317 },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/74/879b23cdd816db4133325a201287c95bef4ce669acde37f8f1b8669e1755/aiohttp-3.11.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8e2bf8029dbf0810c7bfbc3e594b51c4cc9101fbffb583a3923aea184724203c", size = 1641416 },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/98/b123f6b15d87c54e58fd7ae3558ff594f898d7f30a90899718f3215ad328/aiohttp-3.11.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b6212a60e5c482ef90f2d788835387070a88d52cf6241d3916733c9176d39eab", size = 1646514 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/38/257fda3dc99d6978ab943141d5165ec74fd4b4164baa15e9c66fa21da86b/aiohttp-3.11.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d119fafe7b634dbfa25a8c597718e69a930e4847f0b88e172744be24515140da", size = 1702095 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/f4/ddab089053f9fb96654df5505c0a69bde093214b3c3454f6bfdb1845f558/aiohttp-3.11.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:6fba278063559acc730abf49845d0e9a9e1ba74f85f0ee6efd5803f08b285853", size = 1734611 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/d6/f30b2bc520c38c8aa4657ed953186e535ae84abe55c08d0f70acd72ff577/aiohttp-3.11.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:92fc484e34b733704ad77210c7957679c5c3877bd1e6b6d74b185e9320cc716e", size = 1694576 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/97/b0a88c3f4c6d0020b34045ee6d954058abc870814f6e310c4c9b74254116/aiohttp-3.11.11-cp312-cp312-win32.whl", hash = "sha256:9f5b3c1ed63c8fa937a920b6c1bec78b74ee09593b3f5b979ab2ae5ef60d7600", size = 411363 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/23/cc36d9c398980acaeeb443100f0216f50a7cfe20c67a9fd0a2f1a5a846de/aiohttp-3.11.11-cp312-cp312-win_amd64.whl", hash = "sha256:1e69966ea6ef0c14ee53ef7a3d68b564cc408121ea56c0caa2dc918c1b2f553d", size = 437666 },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/d1/d8af164f400bad432b63e1ac857d74a09311a8334b0481f2f64b158b50eb/aiohttp-3.11.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:541d823548ab69d13d23730a06f97460f4238ad2e5ed966aaf850d7c369782d9", size = 697982 },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/d1/faad3bf9fa4bfd26b95c69fc2e98937d52b1ff44f7e28131855a98d23a17/aiohttp-3.11.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:929f3ed33743a49ab127c58c3e0a827de0664bfcda566108989a14068f820194", size = 460662 },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/61/0d71cc66d63909dabc4590f74eba71f91873a77ea52424401c2498d47536/aiohttp-3.11.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0882c2820fd0132240edbb4a51eb8ceb6eef8181db9ad5291ab3332e0d71df5f", size = 452950 },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/db/6d04bc7fd92784900704e16b745484ef45b77bd04e25f58f6febaadf7983/aiohttp-3.11.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b63de12e44935d5aca7ed7ed98a255a11e5cb47f83a9fded7a5e41c40277d104", size = 1665178 },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/5c/e95ade9ae29f375411884d9fd98e50535bf9fe316c9feb0f30cd2ac8f508/aiohttp-3.11.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa54f8ef31d23c506910c21163f22b124facb573bff73930735cf9fe38bf7dff", size = 1717939 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/1c/1e7d5c5daea9e409ed70f7986001b8c9e3a49a50b28404498d30860edab6/aiohttp-3.11.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a344d5dc18074e3872777b62f5f7d584ae4344cd6006c17ba12103759d407af3", size = 1775125 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/66/890987e44f7d2f33a130e37e01a164168e6aff06fce15217b6eaf14df4f6/aiohttp-3.11.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7fb429ab1aafa1f48578eb315ca45bd46e9c37de11fe45c7f5f4138091e2f1", size = 1677176 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/dc/e2ba57d7a52df6cdf1072fd5fa9c6301a68e1cd67415f189805d3eeb031d/aiohttp-3.11.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c341c7d868750e31961d6d8e60ff040fb9d3d3a46d77fd85e1ab8e76c3e9a5c4", size = 1603192 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/9e/8d08a57de79ca3a358da449405555e668f2c8871a7777ecd2f0e3912c272/aiohttp-3.11.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed9ee95614a71e87f1a70bc81603f6c6760128b140bc4030abe6abaa988f1c3d", size = 1618296 },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/51/89822e3ec72db352c32e7fc1c690370e24e231837d9abd056490f3a49886/aiohttp-3.11.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:de8d38f1c2810fa2a4f1d995a2e9c70bb8737b18da04ac2afbf3971f65781d87", size = 1616524 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/fa/e2e6d9398f462ffaa095e84717c1732916a57f1814502929ed67dd7568ef/aiohttp-3.11.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a9b7371665d4f00deb8f32208c7c5e652059b0fda41cf6dbcac6114a041f1cc2", size = 1685471 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/5f/6bb976e619ca28a052e2c0ca7b0251ccd893f93d7c24a96abea38e332bf6/aiohttp-3.11.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:620598717fce1b3bd14dd09947ea53e1ad510317c85dda2c9c65b622edc96b12", size = 1715312 },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/c1/756a7e65aa087c7fac724d6c4c038f2faaa2a42fe56dbc1dd62a33ca7213/aiohttp-3.11.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf8d9bfee991d8acc72d060d53860f356e07a50f0e0d09a8dfedea1c554dd0d5", size = 1672783 },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/ba/a6190ebb02176c7f75e6308da31f5d49f6477b651a3dcfaaaca865a298e2/aiohttp-3.11.11-cp313-cp313-win32.whl", hash = "sha256:9d73ee3725b7a737ad86c2eac5c57a4a97793d9f442599bea5ec67ac9f4bdc3d", size = 410229 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/62/c9fa5bafe03186a0e4699150a7fed9b1e73240996d0d2f0e5f70f3fdf471/aiohttp-3.11.11-cp313-cp313-win_amd64.whl", hash = "sha256:c7a06301c2fb096bdb0bd25fe2011531c1453b9f2c163c8031600ec73af1cc99", size = 436081 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aiosignal"
|
||||
version = "1.3.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "frozenlist" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
version = "0.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "24.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/48/c8/6260f8ccc11f0917360fc0da435c5c9c7504e3db174d5a12a1494887b045/attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", size = 805984 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/89/aa/ab0f7891a01eeb2d2e338ae8fecbe57fcebea1a24dbb64d45801bfab481d/attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308", size = 63397 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blext"
|
||||
version = "0.1.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "cyclopts" },
|
||||
{ name = "pip" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pypdl" },
|
||||
{ name = "rich" },
|
||||
{ name = "tomli-w" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "mypy" },
|
||||
{ name = "pytest" },
|
||||
{ name = "ruff" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "cyclopts", specifier = ">=3.1.5" },
|
||||
{ name = "pip", specifier = ">=24.3.1" },
|
||||
{ name = "pydantic", specifier = ">=2.10.5" },
|
||||
{ name = "pypdl", specifier = ">=1.5.1" },
|
||||
{ name = "rich", specifier = ">=13.9.4" },
|
||||
{ name = "tomli-w", specifier = ">=1.1.0" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "mypy", specifier = ">=1.14.1" },
|
||||
{ name = "pytest", specifier = ">=8.3.4" },
|
||||
{ name = "ruff", specifier = ">=0.9.1" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cyclopts"
|
||||
version = "3.1.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "attrs" },
|
||||
{ name = "docstring-parser", marker = "python_full_version < '4.0'" },
|
||||
{ name = "rich" },
|
||||
{ name = "rich-rst" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3a/bd/dfb30190921dfed99b97ec4b94f0896a477cfbe9beb6997b304bf6424603/cyclopts-3.1.5.tar.gz", hash = "sha256:a17200237c8e8b0a827ac81295ae79ee349edb6fb0b9629dc43160c4f541854e", size = 60437 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/40/594c8facedbb459cb2362e03b18e30490f7017afed592edd56391638060f/cyclopts-3.1.5-py3-none-any.whl", hash = "sha256:4bc1bdf2c7b04fb19bffea163634f3a02cba9becb883be88e073bec0c4d1b0bc", size = 69495 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docstring-parser"
|
||||
version = "0.16"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/08/12/9c22a58c0b1e29271051222d8906257616da84135af9ed167c9e28f85cb3/docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e", size = 26565 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/7c/e9fcff7623954d86bdc17782036cbf715ecab1bec4847c008557affe1ca8/docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637", size = 36533 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docutils"
|
||||
version = "0.21.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frozenlist"
|
||||
version = "1.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8f/ed/0f4cec13a93c02c47ec32d81d11c0c1efbadf4a471e3f3ce7cad366cbbd3/frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817", size = 39930 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/79/43/0bed28bf5eb1c9e4301003b74453b8e7aa85fb293b31dde352aac528dafc/frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30", size = 94987 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/bf/b74e38f09a246e8abbe1e90eb65787ed745ccab6eaa58b9c9308e052323d/frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5", size = 54584 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/31/ab01375682f14f7613a1ade30149f684c84f9b8823a4391ed950c8285656/frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778", size = 52499 },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/a8/d0ac0b9276e1404f58fec3ab6e90a4f76b778a49373ccaf6a563f100dfbc/frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a", size = 276357 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/c9/c7761084fa822f07dac38ac29f841d4587570dd211e2262544aa0b791d21/frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869", size = 287516 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/ff/cd7479e703c39df7bdab431798cef89dc75010d8aa0ca2514c5b9321db27/frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d", size = 283131 },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/a0/370941beb47d237eca4fbf27e4e91389fd68699e6f4b0ebcc95da463835b/frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45", size = 261320 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/5f/c10123e8d64867bc9b4f2f510a32042a306ff5fcd7e2e09e5ae5100ee333/frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d", size = 274877 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/79/38c505601ae29d4348f21706c5d89755ceded02a745016ba2f58bd5f1ea6/frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3", size = 269592 },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/e2/39f3a53191b8204ba9f0bb574b926b73dd2efba2a2b9d2d730517e8f7622/frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a", size = 265934 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/c9/3075eb7f7f3a91f1a6b00284af4de0a65a9ae47084930916f5528144c9dd/frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9", size = 283859 },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/f5/549f44d314c29408b962fa2b0e69a1a67c59379fb143b92a0a065ffd1f0f/frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2", size = 287560 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/f8/cb09b3c24a3eac02c4c07a9558e11e9e244fb02bf62c85ac2106d1eb0c0b/frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf", size = 277150 },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/48/38c2db3f54d1501e692d6fe058f45b6ad1b358d82cd19436efab80cfc965/frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942", size = 45244 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/8c/2ddffeb8b60a4bce3b196c32fcc30d8830d4615e7b492ec2071da801b8ad/frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d", size = 51634 },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/73/fa6d1a96ab7fd6e6d1c3500700963eab46813847f01ef0ccbaa726181dd5/frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21", size = 94026 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/04/ea8bf62c8868b8eada363f20ff1b647cf2e93377a7b284d36062d21d81d1/frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d", size = 54150 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/9a/8e479b482a6f2070b26bda572c5e6889bb3ba48977e81beea35b5ae13ece/frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e", size = 51927 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/12/2aad87deb08a4e7ccfb33600871bbe8f0e08cb6d8224371387f3303654d7/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a", size = 282647 },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/f2/07f06b05d8a427ea0060a9cef6e63405ea9e0d761846b95ef3fb3be57111/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a", size = 289052 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/9f/8bf45a2f1cd4aa401acd271b077989c9267ae8463e7c8b1eb0d3f561b65e/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee", size = 291719 },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/d1/1f20fd05a6c42d3868709b7604c9f15538a29e4f734c694c6bcfc3d3b935/frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6", size = 267433 },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/f2/64b73a9bb86f5a89fb55450e97cd5c1f84a862d4ff90d9fd1a73ab0f64a5/frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e", size = 283591 },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/e2/ffbb1fae55a791fd6c2938dd9ea779509c977435ba3940b9f2e8dc9d5316/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9", size = 273249 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/6e/008136a30798bb63618a114b9321b5971172a5abddff44a100c7edc5ad4f/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039", size = 271075 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/f0/4e71e54a026b06724cec9b6c54f0b13a4e9e298cc8db0f82ec70e151f5ce/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784", size = 285398 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/36/70ec246851478b1c0b59f11ef8ade9c482ff447c1363c2bd5fad45098b12/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631", size = 294445 },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/e0/47f87544055b3349b633a03c4d94b405956cf2437f4ab46d0928b74b7526/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f", size = 280569 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/7c/490133c160fb6b84ed374c266f42800e33b50c3bbab1652764e6e1fc498a/frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8", size = 44721 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/56/4e45136ffc6bdbfa68c29ca56ef53783ef4c2fd395f7cbf99a2624aa9aaa/frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f", size = 51329 },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/3b/915f0bca8a7ea04483622e84a9bd90033bab54bdf485479556c74fd5eaf5/frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953", size = 91538 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/d1/a7c98aad7e44afe5306a2b068434a5830f1470675f0e715abb86eb15f15b/frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0", size = 52849 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/c8/76f23bf9ab15d5f760eb48701909645f686f9c64fbb8982674c241fbef14/frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2", size = 50583 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/22/462a3dd093d11df623179d7754a3b3269de3b42de2808cddef50ee0f4f48/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f", size = 265636 },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/cf/e075e407fc2ae7328155a1cd7e22f932773c8073c1fc78016607d19cc3e5/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608", size = 270214 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/58/0642d061d5de779f39c50cbb00df49682832923f3d2ebfb0fedf02d05f7f/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b", size = 273905 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/66/3fe0f5f8f2add5b4ab7aa4e199f767fd3b55da26e3ca4ce2cc36698e50c4/frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840", size = 250542 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/b8/260791bde9198c87a465224e0e2bb62c4e716f5d198fc3a1dacc4895dbd1/frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439", size = 267026 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/a4/3d24f88c527f08f8d44ade24eaee83b2627793fa62fa07cbb7ff7a2f7d42/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de", size = 257690 },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/9a/d311d660420b2beeff3459b6626f2ab4fb236d07afbdac034a4371fe696e/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641", size = 253893 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/23/e491aadc25b56eabd0f18c53bb19f3cdc6de30b2129ee0bc39cd387cd560/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e", size = 267006 },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/c4/ab918ce636a35fb974d13d666dcbe03969592aeca6c3ab3835acff01f79c/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9", size = 276157 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/29/3b7a0bbbbe5a34833ba26f686aabfe982924adbdcafdc294a7a129c31688/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03", size = 264642 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/42/0595b3dbffc2e82d7fe658c12d5a5bafcd7516c6bf2d1d1feb5387caa9c1/frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c", size = 44914 },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/c4/b7db1206a3fea44bf3b838ca61deb6f74424a8a5db1dd53ecb21da669be6/frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28", size = 51167 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/c8/a5be5b7550c10858fcf9b0ea054baccab474da77d37f1e828ce043a3a5d4/frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3", size = 11901 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.10"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markdown-it-py"
|
||||
version = "3.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mdurl" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mdurl"
|
||||
version = "0.1.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multidict"
|
||||
version = "6.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d6/be/504b89a5e9ca731cd47487e91c469064f8ae5af93b7259758dcfc2b9c848/multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a", size = 64002 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/93/13/df3505a46d0cd08428e4c8169a196131d1b0c4b515c3649829258843dde6/multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6", size = 48570 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/e1/a215908bfae1343cdb72f805366592bdd60487b4232d039c437fe8f5013d/multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156", size = 29316 },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/0f/6dc70ddf5d442702ed74f298d69977f904960b82368532c88e854b79f72b/multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb", size = 29640 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/6d/9c87b73a13d1cdea30b321ef4b3824449866bd7f7127eceed066ccb9b9ff/multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b", size = 131067 },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/1e/1b34154fef373371fd6c65125b3d42ff5f56c7ccc6bfff91b9b3c60ae9e0/multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72", size = 138507 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/e0/0bc6b2bac6e461822b5f575eae85da6aae76d0e2a79b6665d6206b8e2e48/multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304", size = 133905 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/af/73d13b918071ff9b2205fcf773d316e0f8fefb4ec65354bbcf0b10908cc6/multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351", size = 129004 },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/21/23960627b00ed39643302d81bcda44c9444ebcdc04ee5bedd0757513f259/multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb", size = 121308 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/5c/cf282263ffce4a596ed0bb2aa1a1dddfe1996d6a62d08842a8d4b33dca13/multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3", size = 132608 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/3e/97e778c041c72063f42b290888daff008d3ab1427f5b09b714f5a8eff294/multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399", size = 127029 },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/ac/3efb7bfe2f3aefcf8d103e9a7162572f01936155ab2f7ebcc7c255a23212/multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423", size = 137594 },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/9b/6c6e9e8dc4f915fc90a9b7798c44a30773dea2995fdcb619870e705afe2b/multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3", size = 134556 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/10/8e881743b26aaf718379a14ac58572a240e8293a1c9d68e1418fb11c0f90/multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753", size = 130993 },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/84/3eb91b4b557442802d058a7579e864b329968c8d0ea57d907e7023c677f2/multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80", size = 26405 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/0b/ad879847ecbf6d27e90a6eabb7eff6b62c129eefe617ea45eae7c1f0aead/multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926", size = 28795 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/16/92057c74ba3b96d5e211b553895cd6dc7cc4d1e43d9ab8fafc727681ef71/multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa", size = 48713 },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/3d/37d1b8893ae79716179540b89fc6a0ee56b4a65fcc0d63535c6f5d96f217/multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436", size = 29516 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/12/adb6b3200c363062f805275b4c1e656be2b3681aada66c80129932ff0bae/multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761", size = 29557 },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/e9/604bb05e6e5bce1e6a5cf80a474e0f072e80d8ac105f1b994a53e0b28c42/multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e", size = 130170 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/13/9efa50801785eccbf7086b3c83b71a4fb501a4d43549c2f2f80b8787d69f/multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef", size = 134836 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/0f/93808b765192780d117814a6dfcc2e75de6dcc610009ad408b8814dca3ba/multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95", size = 133475 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/c8/529101d7176fe7dfe1d99604e48d69c5dfdcadb4f06561f465c8ef12b4df/multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925", size = 131049 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/0c/fc85b439014d5a58063e19c3a158a889deec399d47b5269a0f3b6a2e28bc/multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966", size = 120370 },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/46/d4416eb20176492d2258fbd47b4abe729ff3b6e9c829ea4236f93c865089/multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305", size = 125178 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/46/73697ad7ec521df7de5531a32780bbfd908ded0643cbe457f981a701457c/multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2", size = 119567 },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/ed/51f060e2cb0e7635329fa6ff930aa5cffa17f4c7f5c6c3ddc3500708e2f2/multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2", size = 129822 },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/9e/ee7d1954b1331da3eddea0c4e08d9142da5f14b1321c7301f5014f49d492/multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6", size = 128656 },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/00/8538f11e3356b5d95fa4b024aa566cde7a38aa7a5f08f4912b32a037c5dc/multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3", size = 125360 },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/05/5d334c1f2462d43fec2363cd00b1c44c93a78c3925d952e9a71caf662e96/multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133", size = 26382 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/bf/f332a13486b1ed0496d624bcc7e8357bb8053823e8cd4b9a18edc1d97e73/multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1", size = 28529 },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/67/1c7c0f39fe069aa4e5d794f323be24bf4d33d62d2a348acdb7991f8f30db/multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008", size = 48771 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/25/c186ee7b212bdf0df2519eacfb1981a017bda34392c67542c274651daf23/multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f", size = 29533 },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/5e/04575fd837e0958e324ca035b339cea174554f6f641d3fb2b4f2e7ff44a2/multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28", size = 29595 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/b2/e56388f86663810c07cfe4a3c3d87227f3811eeb2d08450b9e5d19d78876/multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b", size = 130094 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/ee/30ae9b4186a644d284543d55d491fbd4239b015d36b23fea43b4c94f7052/multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c", size = 134876 },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/c7/70461c13ba8ce3c779503c70ec9d0345ae84de04521c1f45a04d5f48943d/multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3", size = 133500 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/9f/002af221253f10f99959561123fae676148dd730e2daa2cd053846a58507/multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44", size = 131099 },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/42/d1c7a7301d52af79d88548a97e297f9d99c961ad76bbe6f67442bb77f097/multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2", size = 120403 },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/f3/471985c2c7ac707547553e8f37cff5158030d36bdec4414cb825fbaa5327/multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3", size = 125348 },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/2c/e6df05c77e0e433c214ec1d21ddd203d9a4770a1f2866a8ca40a545869a0/multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa", size = 119673 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/cd/bc8608fff06239c9fb333f9db7743a1b2eafe98c2666c9a196e867a3a0a4/multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa", size = 129927 },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/8e/281b69b7bc84fc963a44dc6e0bbcc7150e517b91df368a27834299a526ac/multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4", size = 128711 },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/a4/63e7cd38ed29dd9f1881d5119f272c898ca92536cdb53ffe0843197f6c85/multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6", size = 125519 },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/e0/4f5855037a72cd8a7a2f60a3952d9aa45feedb37ae7831642102604e8a37/multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81", size = 26426 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/a5/17ee3a4db1e310b7405f5d25834460073a8ccd86198ce044dfaf69eac073/multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774", size = 28531 },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/b7/b9e70fde2c0f0c9af4cc5277782a89b66d35948ea3369ec9f598358c3ac5/multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", size = 10051 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.14.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mypy-extensions" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b9/eb/2c92d8ea1e684440f54fa49ac5d9a5f19967b7b472a281f419e69a8d228e/mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", size = 3216051 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/da/11/a9422850fd506edbcdc7f6090682ecceaf1f87b9dd847f9df79942da8506/mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c", size = 11120432 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/9e/47e450fd39078d9c02d620545b2cb37993a8a8bdf7db3652ace2f80521ca/mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1", size = 10279515 },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/b5/6c8d33bd0f851a7692a8bfe4ee75eb82b6983a3cf39e5e32a5d2a723f0c1/mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8", size = 12025791 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/4c/e10e2c46ea37cab5c471d0ddaaa9a434dc1d28650078ac1b56c2d7b9b2e4/mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f", size = 12749203 },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/55/beacb0c69beab2153a0f57671ec07861d27d735a0faff135a494cd4f5020/mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1", size = 12885900 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/75/8c93ff7f315c4d086a2dfcde02f713004357d70a163eddb6c56a6a5eff40/mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae", size = 9777869 },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/1b/b38c079609bb4627905b74fc6a49849835acf68547ac33d8ceb707de5f52/mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", size = 11266668 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/75/2ed0d2964c1ffc9971c729f7a544e9cd34b2cdabbe2d11afd148d7838aa2/mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", size = 10254060 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/5f/7b8051552d4da3c51bbe8fcafffd76a6823779101a2b198d80886cd8f08e/mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", size = 11933167 },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/90/f53971d3ac39d8b68bbaab9a4c6c58c8caa4d5fd3d587d16f5927eeeabe1/mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", size = 12864341 },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/d2/8bc0aeaaf2e88c977db41583559319f1821c069e943ada2701e86d0430b7/mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89", size = 12972991 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/17/07815114b903b49b0f2cf7499f1c130e5aa459411596668267535fe9243c/mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", size = 9879016 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/15/bb6a686901f59222275ab228453de741185f9d54fecbaacec041679496c6/mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", size = 11252097 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/b3/8b0f74dfd072c802b7fa368829defdf3ee1566ba74c32a2cb2403f68024c/mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", size = 10239728 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/9b/4fd95ab20c52bb5b8c03cc49169be5905d931de17edfe4d9d2986800b52e/mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", size = 11924965 },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660 },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198 },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "24.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pip"
|
||||
version = "24.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f4/b1/b422acd212ad7eedddaf7981eee6e5de085154ff726459cf2da7c5a184c1/pip-24.3.1.tar.gz", hash = "sha256:ebcb60557f2aefabc2e0f918751cd24ea0d56d8ec5445fe1807f1d2109660b99", size = 1931073 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/7d/500c9ad20238fcfcb4cb9243eede163594d7020ce87bd9610c9e02771876/pip-24.3.1-py3-none-any.whl", hash = "sha256:3790624780082365f47549d032f3770eeb2b1e8bd1f7b2e02dace1afa361b4ed", size = 1822182 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "propcache"
|
||||
version = "0.2.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/20/c8/2a13f78d82211490855b2fb303b6721348d0787fdd9a12ac46d99d3acde1/propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64", size = 41735 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/0f/2913b6791ebefb2b25b4efd4bb2299c985e09786b9f5b19184a88e5778dd/propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16", size = 79297 },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/73/af2053aeccd40b05d6e19058419ac77674daecdd32478088b79375b9ab54/propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717", size = 45611 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/09/8386115ba7775ea3b9537730e8cf718d83bbf95bffe30757ccf37ec4e5da/propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3", size = 45146 },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/7a/793aa12f0537b2e520bf09f4c6833706b63170a211ad042ca71cbf79d9cb/propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9", size = 232136 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/38/b921b3168d72111769f648314100558c2ea1d52eb3d1ba7ea5c4aa6f9848/propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787", size = 239706 },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/29/4636f500c69b5edea7786db3c34eb6166f3384b905665ce312a6e42c720c/propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465", size = 238531 },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/14/01fe53580a8e1734ebb704a3482b7829a0ef4ea68d356141cf0994d9659b/propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af", size = 231063 },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/5c/1d961299f3c3b8438301ccfbff0143b69afcc30c05fa28673cface692305/propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7", size = 220134 },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/d0/ed735e76db279ba67a7d3b45ba4c654e7b02bc2f8050671ec365d8665e21/propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f", size = 220009 },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/90/ee8fab7304ad6533872fee982cfff5a53b63d095d78140827d93de22e2d4/propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54", size = 212199 },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/ec/977ffaf1664f82e90737275873461695d4c9407d52abc2f3c3e24716da13/propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505", size = 214827 },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/48/031fb87ab6081764054821a71b71942161619549396224cbb242922525e8/propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82", size = 228009 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/06/ef1390f2524850838f2390421b23a8b298f6ce3396a7cc6d39dedd4047b0/propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca", size = 231638 },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/2a/101e6386d5a93358395da1d41642b79c1ee0f3b12e31727932b069282b1d/propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e", size = 222788 },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/81/786f687951d0979007e05ad9346cd357e50e3d0b0f1a1d6074df334b1bbb/propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034", size = 40170 },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/59/7cc7037b295d5772eceb426358bb1b86e6cab4616d971bd74275395d100d/propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3", size = 44404 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/28/1d205fe49be8b1b4df4c50024e62480a442b1a7b818e734308bb0d17e7fb/propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a", size = 79588 },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/ee/fc4d893f8d81cd4971affef2a6cb542b36617cd1d8ce56b406112cb80bf7/propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0", size = 45825 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/de/bbe712f94d088da1d237c35d735f675e494a816fd6f54e9db2f61ef4d03f/propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d", size = 45357 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/14/7ae06a6cf2a2f1cb382586d5a99efe66b0b3d0c6f9ac2f759e6f7af9d7cf/propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4", size = 241869 },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/59/227a78be960b54a41124e639e2c39e8807ac0c751c735a900e21315f8c2b/propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d", size = 247884 },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/58/f62b4ffaedf88dc1b17f04d57d8536601e4e030feb26617228ef930c3279/propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5", size = 248486 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/07/ebe102777a830bca91bbb93e3479cd34c2ca5d0361b83be9dbd93104865e/propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24", size = 243649 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/bc/4f7aba7f08f520376c4bb6a20b9a981a581b7f2e385fa0ec9f789bb2d362/propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff", size = 229103 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/d5/04ac9cd4e51a57a96f78795e03c5a0ddb8f23ec098b86f92de028d7f2a6b/propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f", size = 226607 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/f0/24060d959ea41d7a7cc7fdbf68b31852331aabda914a0c63bdb0e22e96d6/propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec", size = 221153 },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/a7/3ac76045a077b3e4de4859a0753010765e45749bdf53bd02bc4d372da1a0/propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348", size = 222151 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/af/5e29da6f80cebab3f5a4dcd2a3240e7f56f2c4abf51cbfcc99be34e17f0b/propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6", size = 233812 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/89/ebe3ad52642cc5509eaa453e9f4b94b374d81bae3265c59d5c2d98efa1b4/propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6", size = 238829 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/2f/6b32f273fa02e978b7577159eae7471b3cfb88b48563b1c2578b2d7ca0bb/propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518", size = 230704 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/2e/f40ae6ff5624a5f77edd7b8359b208b5455ea113f68309e2b00a2e1426b6/propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246", size = 40050 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/77/a92c3ef994e47180862b9d7d11e37624fb1c00a16d61faf55115d970628b/propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1", size = 44117 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/2a/329e0547cf2def8857157f9477669043e75524cc3e6251cef332b3ff256f/propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc", size = 77002 },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/2d/c4df5415e2382f840dc2ecbca0eeb2293024bc28e57a80392f2012b4708c/propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9", size = 44639 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/5a/21aaa4ea2f326edaa4e240959ac8b8386ea31dedfdaa636a3544d9e7a408/propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439", size = 44049 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/3e/021b6cd86c0acc90d74784ccbb66808b0bd36067a1bf3e2deb0f3845f618/propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536", size = 224819 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/57/c2fdeed1b3b8918b1770a133ba5c43ad3d78e18285b0c06364861ef5cc38/propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629", size = 229625 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/81/70d4ff57bf2877b5780b466471bebf5892f851a7e2ca0ae7ffd728220281/propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b", size = 232934 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/b9/bb51ea95d73b3fb4100cb95adbd4e1acaf2cbb1fd1083f5468eeb4a099a8/propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052", size = 227361 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/20/3c6d696cd6fd70b29445960cc803b1851a1131e7a2e4ee261ee48e002bcd/propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce", size = 213904 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/cb/1593bfc5ac6d40c010fa823f128056d6bc25b667f5393781e37d62f12005/propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d", size = 212632 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/5c/e95617e222be14a34c709442a0ec179f3207f8a2b900273720501a70ec5e/propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce", size = 207897 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/3b/56c5ab3dc00f6375fbcdeefdede5adf9bee94f1fab04adc8db118f0f9e25/propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95", size = 208118 },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/25/d7ef738323fbc6ebcbce33eb2a19c5e07a89a3df2fded206065bd5e868a9/propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf", size = 217851 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/77/763e6cef1852cf1ba740590364ec50309b89d1c818e3256d3929eb92fabf/propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f", size = 222630 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/e9/0f86be33602089c701696fbed8d8c4c07b6ee9605c5b7536fd27ed540c5b/propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30", size = 216269 },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/02/5ac83217d522394b6a2e81a2e888167e7ca629ef6569a3f09852d6dcb01a/propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6", size = 39472 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/33/d6f5420252a36034bc8a3a01171bc55b4bff5df50d1c63d9caa50693662f/propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1", size = 43363 },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/b6/c5319caea262f4821995dca2107483b94a3345d4607ad797c76cb9c36bcc/propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54", size = 11818 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.10.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "annotated-types" },
|
||||
{ name = "pydantic-core" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6a/c7/ca334c2ef6f2e046b1144fe4bb2a5da8a4c574e7f2ebf7e16b34a6a2fa92/pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff", size = 761287 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/58/26/82663c79010b28eddf29dcdd0ea723439535fa917fce5905885c0e9ba562/pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53", size = 431426 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.27.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/89/f3450af9d09d44eea1f2c369f49e8f181d742f28220f88cc4dfaae91ea6e/pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc", size = 1893421 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/e3/71fe85af2021f3f386da42d291412e5baf6ce7716bd7101ea49c810eda90/pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7", size = 1814998 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/3c/724039e0d848fd69dbf5806894e26479577316c6f0f112bacaf67aa889ac/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15", size = 1826167 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/5b/1b29e8c1fb5f3199a9a57c1452004ff39f494bbe9bdbe9a81e18172e40d3/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306", size = 1865071 },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/6c/3985203863d76bb7d7266e36970d7e3b6385148c18a68cc8915fd8c84d57/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99", size = 2036244 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/41/f15316858a246b5d723f7d7f599f79e37493b2e84bfc789e58d88c209f8a/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459", size = 2737470 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/7c/b860618c25678bbd6d1d99dbdfdf0510ccb50790099b963ff78a124b754f/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048", size = 1992291 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/73/42c3742a391eccbeab39f15213ecda3104ae8682ba3c0c28069fbcb8c10d/pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d", size = 1994613 },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/7a/941e89096d1175d56f59340f3a8ebaf20762fef222c298ea96d36a6328c5/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b", size = 2002355 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/95/2359937a73d49e336a5a19848713555605d4d8d6940c3ec6c6c0ca4dcf25/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474", size = 2126661 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/4c/ca02b7bdb6012a1adef21a50625b14f43ed4d11f1fc237f9d7490aa5078c/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6", size = 2153261 },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/9d/a241db83f973049a1092a079272ffe2e3e82e98561ef6214ab53fe53b1c7/pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c", size = 1812361 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/ef/013f07248041b74abd48a385e2110aa3a9bbfef0fbd97d4e6d07d2f5b89a/pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc", size = 1982484 },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/1c/16b3a3e3398fd29dca77cea0a1d998d6bde3902fa2706985191e2313cc76/pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4", size = 1867102 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.19.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pypdl"
|
||||
version = "1.5.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aiofiles" },
|
||||
{ name = "aiohttp" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0a/5a/cd399b1b36fa5e1fc61f6d3a39aff12345aaac8e187073a5ba01e418a515/pypdl-1.5.1.tar.gz", hash = "sha256:975c2c8b58ab94154f2afb5308ffdd5e89f3944d3d149a9f1f6ba1de412a0355", size = 23944 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/5e/ac68c41650d7b29f1c52e4fc0923f6a098a4070d44e340c3c570fb15005d/pypdl-1.5.1-py3-none-any.whl", hash = "sha256:e95d5ea48a56bf807b73b96ebd48e918648f7cb432dbbe8b8968f93d9ad47e76", size = 20712 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "8.3.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
{ name = "iniconfig" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pluggy" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "13.9.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markdown-it-py" },
|
||||
{ name = "pygments" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rich-rst"
|
||||
version = "1.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "docutils" },
|
||||
{ name = "rich" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b0/69/5514c3a87b5f10f09a34bb011bc0927bc12c596c8dae5915604e71abc386/rich_rst-1.3.1.tar.gz", hash = "sha256:fad46e3ba42785ea8c1785e2ceaa56e0ffa32dbe5410dec432f37e4107c4f383", size = 13839 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/bc/cc4e3dbc5e7992398dcb7a8eda0cbcf4fb792a0cdb93f857b478bf3cf884/rich_rst-1.3.1-py3-none-any.whl", hash = "sha256:498a74e3896507ab04492d326e794c3ef76e7cda078703aa592d1853d91098c1", size = 11621 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.9.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/67/3e/e89f736f01aa9517a97e2e7e0ce8d34a4d8207087b3cfdec95133fee13b5/ruff-0.9.1.tar.gz", hash = "sha256:fd2b25ecaf907d6458fa842675382c8597b3c746a2dde6717fe3415425df0c17", size = 3498844 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/05/c3a2e0feb3d5d394cdfd552de01df9d3ec8a3a3771bbff247fab7e668653/ruff-0.9.1-py3-none-linux_armv6l.whl", hash = "sha256:84330dda7abcc270e6055551aca93fdde1b0685fc4fd358f26410f9349cf1743", size = 10645241 },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/da/59f0a40e5f88ee5c054ad175caaa2319fc96571e1d29ab4730728f2aad4f/ruff-0.9.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3cae39ba5d137054b0e5b472aee3b78a7c884e61591b100aeb544bcd1fc38d4f", size = 10391066 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/fe/85e1c1acf0ba04a3f2d54ae61073da030f7a5dc386194f96f3c6ca444a78/ruff-0.9.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:50c647ff96f4ba288db0ad87048257753733763b409b2faf2ea78b45c8bb7fcb", size = 10012308 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/9b/780aa5d4bdca8dcea4309264b8faa304bac30e1ce0bcc910422bfcadd203/ruff-0.9.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0c8b149e9c7353cace7d698e1656ffcf1e36e50f8ea3b5d5f7f87ff9986a7ca", size = 10881960 },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/f4/dac4361afbfe520afa7186439e8094e4884ae3b15c8fc75fb2e759c1f267/ruff-0.9.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:beb3298604540c884d8b282fe7625651378e1986c25df51dec5b2f60cafc31ce", size = 10414803 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/a2/057a3cb7999513cb78d6cb33a7d1cc6401c82d7332583786e4dad9e38e44/ruff-0.9.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39d0174ccc45c439093971cc06ed3ac4dc545f5e8bdacf9f067adf879544d969", size = 11464929 },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/c6/1ccfcc209bee465ced4874dcfeaadc88aafcc1ea9c9f31ef66f063c187f0/ruff-0.9.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:69572926c0f0c9912288915214ca9b2809525ea263603370b9e00bed2ba56dbd", size = 12170717 },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/97/4a524027518525c7cf6931e9fd3b2382be5e4b75b2b61bec02681a7685a5/ruff-0.9.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:937267afce0c9170d6d29f01fcd1f4378172dec6760a9f4dface48cdabf9610a", size = 11708921 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/a4/4e77cf6065c700d5593b25fca6cf725b1ab6d70674904f876254d0112ed0/ruff-0.9.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:186c2313de946f2c22bdf5954b8dd083e124bcfb685732cfb0beae0c47233d9b", size = 13058074 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/d6/fcb78e0531e863d0a952c4c5600cc5cd317437f0e5f031cd2288b117bb37/ruff-0.9.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f94942a3bb767675d9a051867c036655fe9f6c8a491539156a6f7e6b5f31831", size = 11281093 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/3b/7235bbeff00c95dc2d073cfdbf2b871b5bbf476754c5d277815d286b4328/ruff-0.9.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:728d791b769cc28c05f12c280f99e8896932e9833fef1dd8756a6af2261fd1ab", size = 10882610 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/66/5599d23257c61cf038137f82999ca8f9d0080d9d5134440a461bef85b461/ruff-0.9.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2f312c86fb40c5c02b44a29a750ee3b21002bd813b5233facdaf63a51d9a85e1", size = 10489273 },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/85/de4aa057e2532db0f9761e2c2c13834991e087787b93e4aeb5f1cb10d2df/ruff-0.9.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ae017c3a29bee341ba584f3823f805abbe5fe9cd97f87ed07ecbf533c4c88366", size = 11003314 },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/42/afedcaa089116d81447347f76041ff46025849fedb0ed2b187d24cf70fca/ruff-0.9.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5dc40a378a0e21b4cfe2b8a0f1812a6572fc7b230ef12cd9fac9161aa91d807f", size = 11342982 },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/c6/fe45f3eb27e3948b41a305d8b768e949bf6a39310e9df73f6c576d7f1d9f/ruff-0.9.1-py3-none-win32.whl", hash = "sha256:46ebf5cc106cf7e7378ca3c28ce4293b61b449cd121b98699be727d40b79ba72", size = 8819750 },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/8d/580db77c3b9d5c3d9479e55b0b832d279c30c8f00ab0190d4cd8fc67831c/ruff-0.9.1-py3-none-win_amd64.whl", hash = "sha256:342a824b46ddbcdddd3abfbb332fa7fcaac5488bf18073e841236aadf4ad5c19", size = 9701331 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/94/0498cdb7316ed67a1928300dd87d659c933479f44dec51b4f62bfd1f8028/ruff-0.9.1-py3-none-win_arm64.whl", hash = "sha256:1cd76c7f9c679e6e8f2af8f778367dca82b95009bc7b1a85a47f1521ae524fa7", size = 9145708 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli-w"
|
||||
version = "1.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d4/19/b65f1a088ee23e37cdea415b357843eca8b1422a7b11a9eee6e35d4ec273/tomli_w-1.1.0.tar.gz", hash = "sha256:49e847a3a304d516a169a601184932ef0f6b61623fe680f836a2aa7128ed0d33", size = 6929 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/ac/ce90573ba446a9bbe65838ded066a805234d159b4446ae9f8ec5bbd36cbd/tomli_w-1.1.0-py3-none-any.whl", hash = "sha256:1403179c78193e3184bfaade390ddbd071cba48a32a2e62ba11aae47490c63f7", size = 6440 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.12.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yarl"
|
||||
version = "1.18.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "idna" },
|
||||
{ name = "multidict" },
|
||||
{ name = "propcache" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b7/9d/4b94a8e6d2b51b599516a5cb88e5bc99b4d8d4583e468057eaa29d5f0918/yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1", size = 181062 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/40/93/282b5f4898d8e8efaf0790ba6d10e2245d2c9f30e199d1a85cae9356098c/yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069", size = 141555 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/9c/0a49af78df099c283ca3444560f10718fadb8a18dc8b3edf8c7bd9fd7d89/yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193", size = 94351 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/a1/205ab51e148fdcedad189ca8dd587794c6f119882437d04c33c01a75dece/yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889", size = 92286 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/fe/88b690b30f3f59275fb674f5f93ddd4a3ae796c2b62e5bb9ece8a4914b83/yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8", size = 340649 },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/eb/3b65499b568e01f36e847cebdc8d7ccb51fff716dbda1ae83c3cbb8ca1c9/yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca", size = 356623 },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/46/f559dc184280b745fc76ec6b1954de2c55595f0ec0a7614238b9ebf69618/yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8", size = 354007 },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/ba/1865d85212351ad160f19fb99808acf23aab9a0f8ff31c8c9f1b4d671fc9/yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae", size = 344145 },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/cb/5c3e975d77755d7b3d5193e92056b19d83752ea2da7ab394e22260a7b824/yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3", size = 336133 },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/89/b77d3fd249ab52a5c40859815765d35c91425b6bb82e7427ab2f78f5ff55/yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb", size = 347967 },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/bd/f6b7630ba2cc06c319c3235634c582a6ab014d52311e7d7c22f9518189b5/yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e", size = 346397 },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/1a/0b4e367d5a72d1f095318344848e93ea70da728118221f84f1bf6c1e39e7/yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59", size = 350206 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/cf/320fff4367341fb77809a2d8d7fe75b5d323a8e1b35710aafe41fdbf327b/yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d", size = 362089 },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/cf/aadba261d8b920253204085268bad5e8cdd86b50162fcb1b10c10834885a/yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e", size = 366267 },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/58/fb4cadd81acdee6dafe14abeb258f876e4dd410518099ae9a35c88d8097c/yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a", size = 359141 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/7a/4c571597589da4cd5c14ed2a0b17ac56ec9ee7ee615013f74653169e702d/yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1", size = 84402 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/7b/8600250b3d89b625f1121d897062f629883c2f45339623b69b1747ec65fa/yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5", size = 91030 },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/85/bd2e2729752ff4c77338e0102914897512e92496375e079ce0150a6dc306/yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50", size = 142644 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/74/1178322cc0f10288d7eefa6e4a85d8d2e28187ccab13d5b844e8b5d7c88d/yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576", size = 94962 },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/75/79c6acc0261e2c2ae8a1c41cf12265e91628c8c58ae91f5ff59e29c0787f/yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640", size = 92795 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/32/927b2d67a412c31199e83fefdce6e645247b4fb164aa1ecb35a0f9eb2058/yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2", size = 332368 },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/e5/859fca07169d6eceeaa4fde1997c91d8abde4e9a7c018e371640c2da2b71/yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75", size = 342314 },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/75/76b63ccd91c9e03ab213ef27ae6add2e3400e77e5cdddf8ed2dbc36e3f21/yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512", size = 341987 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/e1/a097d5755d3ea8479a42856f51d97eeff7a3a7160593332d98f2709b3580/yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba", size = 336914 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/42/e1b4d0e396b7987feceebe565286c27bc085bf07d61a59508cdaf2d45e63/yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb", size = 325765 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/18/03a5834ccc9177f97ca1bbb245b93c13e58e8225276f01eedc4cc98ab820/yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272", size = 344444 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/03/a713633bdde0640b0472aa197b5b86e90fbc4c5bc05b727b714cd8a40e6d/yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6", size = 340760 },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/99/f6567e3f3bbad8fd101886ea0276c68ecb86a2b58be0f64077396cd4b95e/yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e", size = 346484 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/a9/84717c896b2fc6cb15bd4eecd64e34a2f0a9fd6669e69170c73a8b46795a/yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb", size = 359864 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/2e/d0f5f1bef7ee93ed17e739ec8dbcb47794af891f7d165fa6014517b48169/yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393", size = 364537 },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/8a/568d07c5d4964da5b02621a517532adb8ec5ba181ad1687191fffeda0ab6/yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285", size = 357861 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/e3/924c3f64b6b3077889df9a1ece1ed8947e7b61b0a933f2ec93041990a677/yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2", size = 84097 },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/45/0e055320daaabfc169b21ff6174567b2c910c45617b0d79c68d7ab349b02/yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477", size = 90399 },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/c7/c790513d5328a8390be8f47be5d52e141f78b66c6c48f48d241ca6bd5265/yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb", size = 140789 },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/aa/a2f84e93554a578463e2edaaf2300faa61c8701f0898725842c704ba5444/yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa", size = 94144 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/fc/d68d8f83714b221a85ce7866832cba36d7c04a68fa6a960b908c2c84f325/yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782", size = 91974 },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/4e/d2563d8323a7e9a414b5b25341b3942af5902a2263d36d20fb17c40411e2/yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0", size = 333587 },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/c9/cfec0bc0cac8d054be223e9f2c7909d3e8442a856af9dbce7e3442a8ec8d/yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482", size = 344386 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/5d/4c532190113b25f1364d25f4c319322e86232d69175b91f27e3ebc2caf9a/yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186", size = 345421 },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/d1/6cdd1632da013aa6ba18cee4d750d953104a5e7aac44e249d9410a972bf5/yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58", size = 339384 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/c4/6b3c39bec352e441bd30f432cda6ba51681ab19bb8abe023f0d19777aad1/yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53", size = 326689 },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/30/07fb088f2eefdc0aa4fc1af4e3ca4eb1a3aadd1ce7d866d74c0f124e6a85/yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2", size = 345453 },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/09/d54befb48f9cd8eec43797f624ec37783a0266855f4930a91e3d5c7717f8/yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8", size = 341872 },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/26/fd0ef9bf29dd906a84b59f0cd1281e65b0c3e08c6aa94b57f7d11f593518/yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1", size = 347497 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/b5/14ac7a256d0511b2ac168d50d4b7d744aea1c1aa20c79f620d1059aab8b2/yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a", size = 359981 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/b3/d493221ad5cbd18bc07e642894030437e405e1413c4236dd5db6e46bcec9/yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10", size = 366229 },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/56/6a3e2a5d9152c56c346df9b8fb8edd2c8888b1e03f96324d457e5cf06d34/yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8", size = 360383 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/b7/4b3c7c7913a278d445cc6284e59b2e62fa25e72758f888b7a7a39eb8423f/yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d", size = 310152 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/d5/688db678e987c3e0fb17867970700b92603cadf36c56e5fb08f23e822a0c/yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c", size = 315723 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/4b/a06e0ec3d155924f77835ed2d167ebd3b211a7b0853da1cf8d8414d784ef/yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b", size = 45109 },
|
||||
]
|
Loading…
Reference in New Issue