From 6e7071bd198ea235f4e497069e10de4ce3eaf407 Mon Sep 17 00:00:00 2001 From: Nick Sweeting Date: Fri, 11 Oct 2024 00:45:59 -0700 Subject: [PATCH] add new binproviders and binaries args to install and version, bump pydantic-pkgr version --- archivebox/abx/archivebox/base_binary.py | 24 +--- archivebox/abx/archivebox/base_configset.py | 2 +- archivebox/cli/archivebox_install.py | 26 +++- archivebox/cli/archivebox_version.py | 14 ++ archivebox/config/views.py | 6 +- archivebox/machine/models.py | 2 +- archivebox/main.py | 112 +++++++++++---- archivebox/plugins_auth/ldap/apps.py | 14 +- archivebox/plugins_extractor/chrome/apps.py | 8 +- archivebox/plugins_extractor/mercury/apps.py | 12 +- .../plugins_extractor/readability/apps.py | 25 +--- .../plugins_extractor/singlefile/apps.py | 25 +--- archivebox/plugins_extractor/ytdlp/apps.py | 8 +- archivebox/plugins_pkg/npm/apps.py | 12 +- archivebox/plugins_pkg/pip/apps.py | 60 ++++----- archivebox/plugins_pkg/playwright/apps.py | 26 ++-- archivebox/plugins_pkg/puppeteer/apps.py | 12 +- archivebox/plugins_search/ripgrep/apps.py | 10 +- archivebox/plugins_search/sonic/apps.py | 10 +- archivebox/plugins_search/sqlite/apps.py | 4 +- archivebox/vendor/pydantic-pkgr | 2 +- pyproject.toml | 4 +- requirements.txt | 8 +- uv.lock | 127 ++++++++++-------- 24 files changed, 318 insertions(+), 235 deletions(-) diff --git a/archivebox/abx/archivebox/base_binary.py b/archivebox/abx/archivebox/base_binary.py index 0bce1da5..45735a1b 100644 --- a/archivebox/abx/archivebox/base_binary.py +++ b/archivebox/abx/archivebox/base_binary.py @@ -1,15 +1,14 @@ __package__ = "abx.archivebox" import os -from typing import Dict, List, Optional +from typing import Optional, cast from typing_extensions import Self -from pydantic import Field, InstanceOf, validate_call +from pydantic import validate_call from pydantic_pkgr import ( Binary, BinProvider, BinProviderName, - ProviderLookupDict, AptProvider, BrewProvider, EnvProvider, @@ -25,18 +24,6 @@ from .base_hook import BaseHook, HookType class BaseBinProvider(BaseHook, BinProvider): hook_type: HookType = "BINPROVIDER" - # def on_get_abspath(self, bin_name: BinName, **context) -> Optional[HostBinPath]: - # Class = super() - # get_abspath_func = lambda: Class.on_get_abspath(bin_name, **context) - # # return cache.get_or_set(f'bin:abspath:{bin_name}', get_abspath_func) - # return get_abspath_func() - - # def on_get_version(self, bin_name: BinName, abspath: Optional[HostBinPath]=None, **context) -> SemVer | None: - # Class = super() - # get_version_func = lambda: Class.on_get_version(bin_name, abspath, **context) - # # return cache.get_or_set(f'bin:version:{bin_name}:{abspath}', get_version_func) - # return get_version_func() - # TODO: add install/load/load_or_install methods as abx.hookimpl methods @@ -52,9 +39,6 @@ class BaseBinProvider(BaseHook, BinProvider): class BaseBinary(BaseHook, Binary): hook_type: HookType = "BINARY" - binproviders_supported: List[InstanceOf[BinProvider]] = Field(default_factory=list, alias="binproviders") - provider_overrides: Dict[BinProviderName, ProviderLookupDict] = Field(default_factory=dict, alias="overrides") - @staticmethod def symlink_to_lib(binary, bin_dir=None) -> None: bin_dir = bin_dir or CONSTANTS.LIB_BIN_DIR @@ -82,13 +66,13 @@ class BaseBinary(BaseHook, Binary): # get cached binary from db try: from machine.models import InstalledBinary - installed_binary = InstalledBinary.objects.get_from_db_or_cache(self) + installed_binary = InstalledBinary.objects.get_from_db_or_cache(self) # type: ignore binary = InstalledBinary.load_from_db(installed_binary) except Exception: # maybe we are not in a DATA dir so there is no db, fallback to reading from fs # (e.g. when archivebox version is run outside of a DATA dir) binary = super().load(**kwargs) - return binary + return cast(Self, binary) @validate_call def install(self, **kwargs) -> Self: diff --git a/archivebox/abx/archivebox/base_configset.py b/archivebox/abx/archivebox/base_configset.py index 4e6cbd36..be7b89c3 100644 --- a/archivebox/abx/archivebox/base_configset.py +++ b/archivebox/abx/archivebox/base_configset.py @@ -9,7 +9,7 @@ from pydantic import model_validator, TypeAdapter from pydantic_settings import BaseSettings, SettingsConfigDict, PydanticBaseSettingsSource from pydantic_settings.sources import TomlConfigSettingsSource -from pydantic_pkgr.base_types import func_takes_args_or_kwargs +from pydantic_pkgr import func_takes_args_or_kwargs import abx diff --git a/archivebox/cli/archivebox_install.py b/archivebox/cli/archivebox_install.py index 28615cc2..2f68c57d 100755 --- a/archivebox/cli/archivebox_install.py +++ b/archivebox/cli/archivebox_install.py @@ -22,17 +22,33 @@ def main(args: Optional[List[str]]=None, stdin: Optional[IO]=None, pwd: Optional add_help=True, formatter_class=SmartFormatter, ) - # parser.add_argument( - # '--force', # '-f', - # action='store_true', - # help='Overwrite any existing packages that conflict with the ones ArchiveBox is trying to install', - # ) + parser.add_argument( + '--binproviders', '-p', + type=str, + help='Select binproviders to use DEFAULT=env,apt,brew,sys_pip,venv_pip,lib_pip,pipx,sys_npm,lib_npm,puppeteer,playwright (all)', + default=None, + ) + parser.add_argument( + '--binaries', '-b', + type=str, + help='Select binaries to install DEFAULT=curl,wget,git,yt-dlp,chrome,single-file,readability-extractor,postlight-parser,... (all)', + default=None, + ) + parser.add_argument( + '--dry-run', '-d', + action='store_true', + help='Show what would be installed without actually installing anything', + default=False, + ) command = parser.parse_args(args or ()) # noqa reject_stdin(__command__, stdin) install( # force=command.force, out_dir=Path(pwd) if pwd else DATA_DIR, + binaries=command.binaries.split(',') if command.binaries else None, + binproviders=command.binproviders.split(',') if command.binproviders else None, + dry_run=command.dry_run, ) diff --git a/archivebox/cli/archivebox_version.py b/archivebox/cli/archivebox_version.py index 09906355..4229ff5a 100755 --- a/archivebox/cli/archivebox_version.py +++ b/archivebox/cli/archivebox_version.py @@ -27,6 +27,18 @@ def main(args: Optional[List[str]]=None, stdin: Optional[IO]=None, pwd: Optional action='store_true', help='Only print ArchiveBox version number and nothing else.', ) + parser.add_argument( + '--binproviders', '-p', + type=str, + help='Select binproviders to detect DEFAULT=env,apt,brew,sys_pip,venv_pip,lib_pip,pipx,sys_npm,lib_npm,puppeteer,playwright (all)', + default=None, + ) + parser.add_argument( + '--binaries', '-b', + type=str, + help='Select binaries to detect DEFAULT=curl,wget,git,yt-dlp,chrome,single-file,readability-extractor,postlight-parser,... (all)', + default=None, + ) command = parser.parse_args(args or ()) reject_stdin(__command__, stdin) @@ -40,6 +52,8 @@ def main(args: Optional[List[str]]=None, stdin: Optional[IO]=None, pwd: Optional version( quiet=command.quiet, out_dir=Path(pwd) if pwd else DATA_DIR, + binproviders=command.binproviders.split(',') if command.binproviders else None, + binaries=command.binaries.split(',') if command.binaries else None, ) diff --git a/archivebox/config/views.py b/archivebox/config/views.py index b0f1a8c9..eb1adbe8 100644 --- a/archivebox/config/views.py +++ b/archivebox/config/views.py @@ -111,9 +111,9 @@ def binaries_list_view(request: HttpRequest, **kwargs) -> TableContext: or config_value.lower().endswith(binary.name.lower()) # or binary.name.lower().replace('-', '').replace('_', '') in str(config_value).lower() ))) - # if not binary.provider_overrides: + # if not binary.overrides: # import ipdb; ipdb.set_trace() - # rows['Overrides'].append(str(obj_to_yaml(binary.provider_overrides) or str(binary.provider_overrides))[:200]) + # rows['Overrides'].append(str(obj_to_yaml(binary.overrides) or str(binary.overrides))[:200]) # rows['Description'].append(binary.description) return TableContext( @@ -153,7 +153,7 @@ def binary_detail_view(request: HttpRequest, key: str, **kwargs) -> ItemContext: 'binprovider': binary.loaded_binprovider, 'abspath': binary.loaded_abspath, 'version': binary.loaded_version, - 'overrides': obj_to_yaml(binary.provider_overrides), + 'overrides': obj_to_yaml(binary.overrides), 'providers': obj_to_yaml(binary.binproviders_supported), }, "help_texts": { diff --git a/archivebox/machine/models.py b/archivebox/machine/models.py index b91953fe..b1854d44 100644 --- a/archivebox/machine/models.py +++ b/archivebox/machine/models.py @@ -356,7 +356,7 @@ class InstalledBinary(ABIDModel, ModelWithHealthStats): 'sha256': self.sha256, 'loaded_binprovider': self.BINPROVIDER, 'binproviders_supported': self.BINARY.binproviders_supported, - 'provider_overrides': self.BINARY.provider_overrides, + 'overrides': self.BINARY.overrides, }) def load_fresh(self) -> BaseBinary: diff --git a/archivebox/main.py b/archivebox/main.py index 1b62693a..fd278b07 100755 --- a/archivebox/main.py +++ b/archivebox/main.py @@ -179,7 +179,10 @@ def help(out_dir: Path=DATA_DIR) -> None: @enforce_types def version(quiet: bool=False, - out_dir: Path=DATA_DIR) -> None: + out_dir: Path=DATA_DIR, + binproviders: Optional[List[str]]=None, + binaries: Optional[List[str]]=None, + ) -> None: """Print the ArchiveBox version and dependency information""" print(VERSION) @@ -244,6 +247,14 @@ def version(quiet: bool=False, if binary.name == 'archivebox': continue + # skip if the binary is not in the requested list of binaries + if binaries and binary.name not in binaries: + continue + + # skip if the binary is not supported by any of the requested binproviders + if binproviders and binary.binproviders_supported and not any(provider.name in binproviders for provider in binary.binproviders_supported): + continue + err = None try: loaded_bin = binary.load() @@ -266,6 +277,9 @@ def version(quiet: bool=False, for name, binprovider in reversed(list(settings.BINPROVIDERS.items())): err = None + if binproviders and binprovider.name not in binproviders: + continue + # TODO: implement a BinProvider.BINARY() method that gets the loaded binary for a binprovider's INSTALLER_BIN loaded_bin = binprovider.INSTALLER_BINARY or BaseBinary(name=binprovider.INSTALLER_BIN, binproviders=[env, apt, brew]) @@ -278,25 +292,28 @@ def version(quiet: bool=False, PATH = str(binprovider.PATH).replace(str(DATA_DIR), '[light_slate_blue].[/light_slate_blue]').replace(str(Path('~').expanduser()), '~') ownership_summary = f'UID=[blue]{str(binprovider.EUID).ljust(4)}[/blue]' provider_summary = f'[dark_sea_green3]{str(abspath).ljust(52)}[/dark_sea_green3]' if abspath else f'[grey23]{"not available".ljust(52)}[/grey23]' - prnt('', '[green]√[/green]' if binprovider.is_valid else '[red]X[/red]', '', binprovider.name.ljust(11), provider_summary, ownership_summary, f'PATH={PATH}', overflow='ellipsis', soft_wrap=True) + prnt('', '[green]√[/green]' if binprovider.is_valid else '[grey53]-[/grey53]', '', binprovider.name.ljust(11), provider_summary, ownership_summary, f'PATH={PATH}', overflow='ellipsis', soft_wrap=True) - prnt() - prnt('[deep_sky_blue3][i] Source-code locations:[/deep_sky_blue3]') - for name, path in CONSTANTS.CODE_LOCATIONS.items(): - prnt(printable_folder_status(name, path), overflow='ignore', crop=False) - - prnt() - if os.access(CONSTANTS.ARCHIVE_DIR, os.R_OK) or os.access(CONSTANTS.CONFIG_FILE, os.R_OK): - prnt('[bright_yellow][i] Data locations:[/bright_yellow]') - for name, path in CONSTANTS.DATA_LOCATIONS.items(): - prnt(printable_folder_status(name, path), overflow='ignore', crop=False) - - from archivebox.misc.checks import check_data_dir_permissions + if not (binaries or binproviders): + # dont show source code / data dir info if we just want to get version info for a binary or binprovider - check_data_dir_permissions() - else: prnt() - prnt('[red][i] Data locations:[/red] (not in a data directory)') + prnt('[deep_sky_blue3][i] Code locations:[/deep_sky_blue3]') + for name, path in CONSTANTS.CODE_LOCATIONS.items(): + prnt(printable_folder_status(name, path), overflow='ignore', crop=False) + + prnt() + if os.access(CONSTANTS.ARCHIVE_DIR, os.R_OK) or os.access(CONSTANTS.CONFIG_FILE, os.R_OK): + prnt('[bright_yellow][i] Data locations:[/bright_yellow]') + for name, path in CONSTANTS.DATA_LOCATIONS.items(): + prnt(printable_folder_status(name, path), overflow='ignore', crop=False) + + from archivebox.misc.checks import check_data_dir_permissions + + check_data_dir_permissions() + else: + prnt() + prnt('[red][i] Data locations:[/red] (not in a data directory)') prnt() @@ -986,7 +1003,7 @@ def list_folders(links: List[Link], raise ValueError('Status not recognized.') @enforce_types -def install(out_dir: Path=DATA_DIR) -> None: +def install(out_dir: Path=DATA_DIR, binproviders: Optional[List[str]]=None, binaries: Optional[List[str]]=None, dry_run: bool=False) -> None: """Automatically install all ArchiveBox dependencies and extras""" # if running as root: @@ -1021,9 +1038,15 @@ def install(out_dir: Path=DATA_DIR) -> None: print() - package_manager_names = ', '.join(f'[yellow]{binprovider.name}[/yellow]' for binprovider in reversed(list(settings.BINPROVIDERS.values()))) + package_manager_names = ', '.join( + f'[yellow]{binprovider.name}[/yellow]' + for binprovider in reversed(list(settings.BINPROVIDERS.values())) + if not binproviders or (binproviders and binprovider.name in binproviders) + ) print(f'[+] Setting up package managers {package_manager_names}...') for binprovider in reversed(list(settings.BINPROVIDERS.values())): + if binproviders and binprovider.name not in binproviders: + continue try: binprovider.setup() except Exception: @@ -1035,12 +1058,46 @@ def install(out_dir: Path=DATA_DIR) -> None: print() for binary in reversed(list(settings.BINARIES.values())): - providers = ' [grey53]or[/grey53] '.join(provider.name for provider in binary.binproviders_supported) + if binary.name in ('archivebox', 'django', 'sqlite', 'python', 'pipx'): + # obviously must already be installed if we are running + continue + + if binaries and binary.name not in binaries: + continue + + providers = ' [grey53]or[/grey53] '.join( + provider.name for provider in binary.binproviders_supported + if not binproviders or (binproviders and provider.name in binproviders) + ) + if not providers: + continue print(f'[+] Detecting / Installing [yellow]{binary.name.ljust(22)}[/yellow] using [red]{providers}[/red]...') try: with SudoPermission(uid=0, fallback=True): - # print(binary.load_or_install(fresh=True).model_dump(exclude={'provider_overrides', 'bin_dir', 'hook_type'})) - binary.load_or_install(fresh=True).model_dump(exclude={'provider_overrides', 'bin_dir', 'hook_type'}) + # print(binary.load_or_install(fresh=True).model_dump(exclude={'overrides', 'bin_dir', 'hook_type'})) + if binproviders: + providers_supported_by_binary = [provider.name for provider in binary.binproviders_supported] + for binprovider_name in binproviders: + + if binprovider_name not in providers_supported_by_binary: + continue + + if dry_run: + # always show install commands when doing a dry run + sys.stderr.write("\033[2;49;90m") # grey53 + result = binary.install(binproviders=[binprovider_name], dry_run=dry_run).model_dump(exclude={'overrides', 'bin_dir', 'hook_type'}) + sys.stderr.write("\033[00m\n") # reset + else: + result = binary.load_or_install(binproviders=[binprovider_name], fresh=True, dry_run=dry_run).model_dump(exclude={'overrides', 'bin_dir', 'hook_type'}) + if result and result['loaded_version']: + break + else: + if dry_run: + sys.stderr.write("\033[2;49;90m") # grey53 + binary.install(dry_run=dry_run).model_dump(exclude={'overrides', 'bin_dir', 'hook_type'}) + sys.stderr.write("\033[00m\n") # reset + else: + binary.load_or_install(fresh=True, dry_run=dry_run).model_dump(exclude={'overrides', 'bin_dir', 'hook_type'}) if IS_ROOT: with SudoPermission(uid=0): if ARCHIVEBOX_USER == 0: @@ -1049,6 +1106,9 @@ def install(out_dir: Path=DATA_DIR) -> None: os.system(f'chown -R {ARCHIVEBOX_USER} "{CONSTANTS.LIB_DIR.resolve()}"') except Exception as e: print(f'[red]:cross_mark: Failed to install {binary.name} as user {ARCHIVEBOX_USER}: {e}[/red]') + if binaries and len(binaries) == 1: + # if we are only installing a single binary, raise the exception so the user can see what went wrong + raise from django.contrib.auth import get_user_model @@ -1063,7 +1123,13 @@ def install(out_dir: Path=DATA_DIR) -> None: from plugins_pkg.pip.apps import ARCHIVEBOX_BINARY - proc = run_shell([ARCHIVEBOX_BINARY.load().abspath, 'version'], capture_output=False, cwd=out_dir) + extra_args = [] + if binproviders: + extra_args.append(f'--binproviders={",".join(binproviders)}') + if binaries: + extra_args.append(f'--binaries={",".join(binaries)}') + + proc = run_shell([ARCHIVEBOX_BINARY.load().abspath, 'version', *extra_args], capture_output=False, cwd=out_dir) raise SystemExit(proc.returncode) diff --git a/archivebox/plugins_auth/ldap/apps.py b/archivebox/plugins_auth/ldap/apps.py index f2dce29a..0cb74da1 100644 --- a/archivebox/plugins_auth/ldap/apps.py +++ b/archivebox/plugins_auth/ldap/apps.py @@ -3,11 +3,11 @@ __package__ = 'archivebox.plugins_auth.ldap' import inspect -from typing import List, Dict +from typing import List from pathlib import Path from pydantic import InstanceOf -from pydantic_pkgr import BinProviderName, ProviderLookupDict, SemVer +from pydantic_pkgr import BinaryOverrides, SemVer from abx.archivebox.base_plugin import BasePlugin from abx.archivebox.base_hook import BaseHook @@ -43,26 +43,26 @@ class LdapBinary(BaseBinary): description: str = 'LDAP Authentication' binproviders_supported: List[InstanceOf[BaseBinProvider]] = [VENV_PIP_BINPROVIDER, SYS_PIP_BINPROVIDER, LIB_PIP_BINPROVIDER, apt] - provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { + overrides: BinaryOverrides = { LIB_PIP_BINPROVIDER.name: { "abspath": lambda: get_LDAP_LIB_path(LIB_SITE_PACKAGES), "version": lambda: get_LDAP_LIB_version(), - "packages": lambda: ['python-ldap>=3.4.3', 'django-auth-ldap>=4.1.0'], + "packages": ['python-ldap>=3.4.3', 'django-auth-ldap>=4.1.0'], }, VENV_PIP_BINPROVIDER.name: { "abspath": lambda: get_LDAP_LIB_path(VENV_SITE_PACKAGES), "version": lambda: get_LDAP_LIB_version(), - "packages": lambda: ['python-ldap>=3.4.3', 'django-auth-ldap>=4.1.0'], + "packages": ['python-ldap>=3.4.3', 'django-auth-ldap>=4.1.0'], }, SYS_PIP_BINPROVIDER.name: { "abspath": lambda: get_LDAP_LIB_path((*USER_SITE_PACKAGES, *SYS_SITE_PACKAGES)), "version": lambda: get_LDAP_LIB_version(), - "packages": lambda: ['python-ldap>=3.4.3', 'django-auth-ldap>=4.1.0'], + "packages": ['python-ldap>=3.4.3', 'django-auth-ldap>=4.1.0'], }, apt.name: { "abspath": lambda: get_LDAP_LIB_path(), "version": lambda: get_LDAP_LIB_version(), - "packages": lambda: ['libssl-dev', 'libldap2-dev', 'libsasl2-dev', 'python3-ldap', 'python3-msgpack', 'python3-mutagen'], + "packages": ['libssl-dev', 'libldap2-dev', 'libsasl2-dev', 'python3-ldap', 'python3-msgpack', 'python3-mutagen'], }, } diff --git a/archivebox/plugins_extractor/chrome/apps.py b/archivebox/plugins_extractor/chrome/apps.py index 1c0bee25..3651ad51 100644 --- a/archivebox/plugins_extractor/chrome/apps.py +++ b/archivebox/plugins_extractor/chrome/apps.py @@ -13,7 +13,7 @@ from pydantic_pkgr import ( BinProvider, BinName, BinProviderName, - ProviderLookupDict, + BinaryOverrides, bin_abspath, ) @@ -204,15 +204,15 @@ class ChromeBinary(BaseBinary): name: BinName = CHROME_CONFIG.CHROME_BINARY binproviders_supported: List[InstanceOf[BinProvider]] = [PUPPETEER_BINPROVIDER, env, PLAYWRIGHT_BINPROVIDER] - provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { + overrides: BinaryOverrides = { env.name: { 'abspath': lambda: autodetect_system_chrome_install(PATH=env.PATH), # /usr/bin/google-chrome-stable }, PUPPETEER_BINPROVIDER.name: { - 'packages': lambda: ['chrome@stable'], # npx @puppeteer/browsers install chrome@stable + 'packages': ['chrome@stable'], # npx @puppeteer/browsers install chrome@stable }, PLAYWRIGHT_BINPROVIDER.name: { - 'packages': lambda: ['chromium'], # playwright install chromium + 'packages': ['chromium'], # playwright install chromium }, } diff --git a/archivebox/plugins_extractor/mercury/apps.py b/archivebox/plugins_extractor/mercury/apps.py index 84caad28..926bbdca 100644 --- a/archivebox/plugins_extractor/mercury/apps.py +++ b/archivebox/plugins_extractor/mercury/apps.py @@ -1,10 +1,10 @@ __package__ = 'plugins_extractor.mercury' -from typing import List, Optional, Dict +from typing import List, Optional from pathlib import Path from pydantic import InstanceOf, Field -from pydantic_pkgr import BinProvider, BinName, BinProviderName, ProviderLookupDict, bin_abspath +from pydantic_pkgr import BinProvider, BinName, BinaryOverrides, bin_abspath from abx.archivebox.base_plugin import BasePlugin, BaseHook from abx.archivebox.base_configset import BaseConfigSet @@ -38,13 +38,13 @@ class MercuryBinary(BaseBinary): name: BinName = MERCURY_CONFIG.MERCURY_BINARY binproviders_supported: List[InstanceOf[BinProvider]] = [LIB_NPM_BINPROVIDER, SYS_NPM_BINPROVIDER, env] - provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { + overrides: BinaryOverrides = { LIB_NPM_BINPROVIDER.name: { - 'packages': lambda: ['@postlight/parser@^2.2.3'], + 'packages': ['@postlight/parser@^2.2.3'], }, SYS_NPM_BINPROVIDER.name: { - 'packages': lambda: ['@postlight/parser@^2.2.3'], - 'install': lambda: False, # never try to install things into global prefix + 'packages': ['@postlight/parser@^2.2.3'], + 'install': lambda: None, # never try to install things into global prefix }, env.name: { 'version': lambda: '999.999.999' if bin_abspath('postlight-parser', PATH=env.PATH) else None, diff --git a/archivebox/plugins_extractor/readability/apps.py b/archivebox/plugins_extractor/readability/apps.py index c61efb21..bf215c5f 100644 --- a/archivebox/plugins_extractor/readability/apps.py +++ b/archivebox/plugins_extractor/readability/apps.py @@ -1,12 +1,12 @@ __package__ = 'archivebox.plugins_extractor.readability' from pathlib import Path -from typing import List, Dict, Optional +from typing import List # from typing_extensions import Self # Depends on other PyPI/vendor packages: -from pydantic import InstanceOf, Field, validate_call -from pydantic_pkgr import BinProvider, BinProviderName, ProviderLookupDict, BinName, ShallowBinary +from pydantic import InstanceOf, Field +from pydantic_pkgr import BinProvider, BinaryOverrides, BinName # Depends on other Django apps: from abx.archivebox.base_plugin import BasePlugin @@ -39,23 +39,10 @@ class ReadabilityBinary(BaseBinary): name: BinName = READABILITY_CONFIG.READABILITY_BINARY binproviders_supported: List[InstanceOf[BinProvider]] = [LIB_NPM_BINPROVIDER, SYS_NPM_BINPROVIDER, env] - provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { - LIB_NPM_BINPROVIDER.name: {"packages": lambda: [READABILITY_PACKAGE_NAME]}, - SYS_NPM_BINPROVIDER.name: {"packages": lambda: []}, # prevent modifying system global npm packages + overrides: BinaryOverrides = { + LIB_NPM_BINPROVIDER.name: {"packages": [READABILITY_PACKAGE_NAME]}, + SYS_NPM_BINPROVIDER.name: {"packages": [READABILITY_PACKAGE_NAME], "install": lambda: None}, # prevent modifying system global npm packages } - - @validate_call - def install(self, binprovider_name: Optional[BinProviderName]=None, **kwargs) -> ShallowBinary: - # force install to only use lib/npm provider, we never want to modify global NPM packages - return BaseBinary.install(self, binprovider_name=binprovider_name or LIB_NPM_BINPROVIDER.name, **kwargs) - - @validate_call - def load_or_install(self, binprovider_name: Optional[BinProviderName] = None, fresh=False, **kwargs) -> ShallowBinary: - try: - return self.load(fresh=fresh) - except Exception: - # force install to only use lib/npm provider, we never want to modify global NPM packages - return BaseBinary.install(self, binprovider_name=binprovider_name or LIB_NPM_BINPROVIDER.name, **kwargs) diff --git a/archivebox/plugins_extractor/singlefile/apps.py b/archivebox/plugins_extractor/singlefile/apps.py index c0e91116..a160f9bd 100644 --- a/archivebox/plugins_extractor/singlefile/apps.py +++ b/archivebox/plugins_extractor/singlefile/apps.py @@ -1,12 +1,12 @@ __package__ = 'archivebox.plugins_extractor.singlefile' from pathlib import Path -from typing import List, Dict, Optional +from typing import List, Optional # from typing_extensions import Self # Depends on other PyPI/vendor packages: from pydantic import InstanceOf, Field -from pydantic_pkgr import BinProvider, BinProviderName, ProviderLookupDict, BinName, bin_abspath, ShallowBinary +from pydantic_pkgr import BinProvider, BinaryOverrides, BinName, bin_abspath # Depends on other Django apps: from abx.archivebox.base_plugin import BasePlugin @@ -45,22 +45,21 @@ class SinglefileBinary(BaseBinary): name: BinName = SINGLEFILE_CONFIG.SINGLEFILE_BINARY binproviders_supported: List[InstanceOf[BinProvider]] = [LIB_NPM_BINPROVIDER, SYS_NPM_BINPROVIDER, env] - provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { + overrides: BinaryOverrides = { LIB_NPM_BINPROVIDER.name: { "abspath": lambda: bin_abspath(SINGLEFILE_CONFIG.SINGLEFILE_BINARY, PATH=LIB_NPM_BINPROVIDER.PATH) or bin_abspath("single-file", PATH=LIB_NPM_BINPROVIDER.PATH) or bin_abspath("single-file-node.js", PATH=LIB_NPM_BINPROVIDER.PATH), - "packages": lambda: - [f"single-file-cli@>={SINGLEFILE_MIN_VERSION} <{SINGLEFILE_MAX_VERSION}"], + "packages": [f"single-file-cli@>={SINGLEFILE_MIN_VERSION} <{SINGLEFILE_MAX_VERSION}"], }, SYS_NPM_BINPROVIDER.name: { "abspath": lambda: bin_abspath(SINGLEFILE_CONFIG.SINGLEFILE_BINARY, PATH=SYS_NPM_BINPROVIDER.PATH) or bin_abspath("single-file", PATH=SYS_NPM_BINPROVIDER.PATH) or bin_abspath("single-file-node.js", PATH=SYS_NPM_BINPROVIDER.PATH), - "packages": lambda: - [], # prevent modifying system global npm packages + "packages": [f"single-file-cli@>={SINGLEFILE_MIN_VERSION} <{SINGLEFILE_MAX_VERSION}"], + "install": lambda: None, }, env.name: { 'abspath': lambda: @@ -69,18 +68,6 @@ class SinglefileBinary(BaseBinary): or bin_abspath('single-file-node.js', PATH=env.PATH), }, } - - def install(self, binprovider_name: Optional[BinProviderName]=None, **kwargs) -> ShallowBinary: - # force install to only use lib/npm provider, we never want to modify global NPM packages - return BaseBinary.install(self, binprovider_name=binprovider_name or LIB_NPM_BINPROVIDER.name, **kwargs) - - def load_or_install(self, binprovider_name: Optional[BinProviderName]=None, fresh=False, **kwargs) -> ShallowBinary: - try: - return self.load(fresh=fresh) - except Exception: - # force install to only use lib/npm provider, we never want to modify global NPM packages - return BaseBinary.install(self, binprovider_name=binprovider_name or LIB_NPM_BINPROVIDER.name, **kwargs) - SINGLEFILE_BINARY = SinglefileBinary() diff --git a/archivebox/plugins_extractor/ytdlp/apps.py b/archivebox/plugins_extractor/ytdlp/apps.py index 2c935797..742c742b 100644 --- a/archivebox/plugins_extractor/ytdlp/apps.py +++ b/archivebox/plugins_extractor/ytdlp/apps.py @@ -1,10 +1,10 @@ import sys -from typing import List, Dict +from typing import List from subprocess import run, PIPE from rich import print from pydantic import InstanceOf, Field, model_validator, AliasChoices -from pydantic_pkgr import BinProvider, BinName, BinProviderName, ProviderLookupDict +from pydantic_pkgr import BinProvider, BinName, BinaryOverrides from abx.archivebox.base_plugin import BasePlugin from abx.archivebox.base_configset import BaseConfigSet @@ -54,10 +54,10 @@ class FfmpegBinary(BaseBinary): name: BinName = 'ffmpeg' binproviders_supported: List[InstanceOf[BinProvider]] = [apt, brew, env] - provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { + overrides: BinaryOverrides = { 'env': { # 'abspath': lambda: shutil.which('ffmpeg', PATH=env.PATH), - # 'version': lambda: run(['ffmpeg', '-version'], stdout=PIPE, stderr=PIPE, text=True).stdout, + 'version': lambda: run(['ffmpeg', '-version'], stdout=PIPE, stderr=PIPE, text=True).stdout, }, 'apt': { # 'abspath': lambda: shutil.which('ffmpeg', PATH=apt.PATH), diff --git a/archivebox/plugins_pkg/npm/apps.py b/archivebox/plugins_pkg/npm/apps.py index e74c9854..586f7c3c 100644 --- a/archivebox/plugins_pkg/npm/apps.py +++ b/archivebox/plugins_pkg/npm/apps.py @@ -1,11 +1,11 @@ __package__ = 'archivebox.plugins_pkg.npm' from pathlib import Path -from typing import List, Optional, Dict +from typing import List, Optional from pydantic import InstanceOf, model_validator -from pydantic_pkgr import BinProvider, NpmProvider, BinName, PATHStr, BinProviderName, ProviderLookupDict +from pydantic_pkgr import BinProvider, NpmProvider, BinName, PATHStr, BinProviderName, BinaryOverrides from archivebox.config import DATA_DIR, CONSTANTS @@ -60,8 +60,8 @@ class NodeBinary(BaseBinary): name: BinName = 'node' binproviders_supported: List[InstanceOf[BinProvider]] = [apt, brew, env] - overrides: Dict[BinProviderName, ProviderLookupDict] = { - apt.name: {'packages': lambda c: ['nodejs']}, + overrides: BinaryOverrides = { + apt.name: {'packages': ['nodejs']}, } @@ -72,7 +72,7 @@ class NpmBinary(BaseBinary): name: BinName = 'npm' binproviders_supported: List[InstanceOf[BinProvider]] = [apt, brew, env] - overrides: Dict[BinProviderName, ProviderLookupDict] = { + overrides: BinaryOverrides = { apt.name: {'install': lambda: None}, # already installed when nodejs is installed brew.name: {'install': lambda: None}, # already installed when nodejs is installed } @@ -84,7 +84,7 @@ class NpxBinary(BaseBinary): name: BinName = 'npx' binproviders_supported: List[InstanceOf[BinProvider]] = [apt, brew, env] - overrides: Dict[BinProviderName, ProviderLookupDict] = { + overrides: BinaryOverrides = { apt.name: {'install': lambda: None}, # already installed when nodejs is installed brew.name: {'install': lambda: None}, # already installed when nodejs is installed } diff --git a/archivebox/plugins_pkg/pip/apps.py b/archivebox/plugins_pkg/pip/apps.py index 0ca700f0..6ad1a5da 100644 --- a/archivebox/plugins_pkg/pip/apps.py +++ b/archivebox/plugins_pkg/pip/apps.py @@ -4,14 +4,14 @@ import os import sys import site from pathlib import Path -from typing import List, Dict, Optional +from typing import List, Optional from pydantic import InstanceOf, Field, model_validator, validate_call import django import django.db.backends.sqlite3.base from django.db.backends.sqlite3.base import Database as django_sqlite3 # type: ignore[import-type] -from pydantic_pkgr import BinProvider, PipProvider, BinName, BinProviderName, ProviderLookupDict, SemVer +from pydantic_pkgr import BinProvider, PipProvider, BinName, BinProviderName, BinaryOverrides, SemVer from archivebox.config import CONSTANTS, VERSION @@ -105,18 +105,18 @@ class ArchiveboxBinary(BaseBinary): name: BinName = 'archivebox' binproviders_supported: List[InstanceOf[BinProvider]] = [VENV_PIP_BINPROVIDER, SYS_PIP_BINPROVIDER, apt, brew, env] - provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { - VENV_PIP_BINPROVIDER.name: {'packages': lambda: [], 'version': lambda: VERSION}, - SYS_PIP_BINPROVIDER.name: {'packages': lambda: [], 'version': lambda: VERSION}, - apt.name: {'packages': lambda: [], 'version': lambda: VERSION}, - brew.name: {'packages': lambda: [], 'version': lambda: VERSION}, + overrides: BinaryOverrides = { + VENV_PIP_BINPROVIDER.name: {'packages': [], 'version': VERSION}, + SYS_PIP_BINPROVIDER.name: {'packages': [], 'version': VERSION}, + apt.name: {'packages': [], 'version': VERSION}, + brew.name: {'packages': [], 'version': VERSION}, } - @validate_call + # @validate_call def install(self, **kwargs): return self.load() # obviously it's already installed if we are running this ;) - @validate_call + # @validate_call def load_or_install(self, **kwargs): return self.load() # obviously it's already installed if we are running this ;) @@ -127,18 +127,18 @@ class PythonBinary(BaseBinary): name: BinName = 'python' binproviders_supported: List[InstanceOf[BinProvider]] = [VENV_PIP_BINPROVIDER, SYS_PIP_BINPROVIDER, apt, brew, env] - provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { + overrides: BinaryOverrides = { SYS_PIP_BINPROVIDER.name: { - 'abspath': lambda: sys.executable, - 'version': lambda: '{}.{}.{}'.format(*sys.version_info[:3]), + 'abspath': sys.executable, + 'version': '{}.{}.{}'.format(*sys.version_info[:3]), }, } - @validate_call + # @validate_call def install(self, **kwargs): return self.load() # obviously it's already installed if we are running this ;) - @validate_call + # @validate_call def load_or_install(self, **kwargs): return self.load() # obviously it's already installed if we are running this ;) @@ -152,14 +152,14 @@ LOADED_SQLITE_FROM_VENV = str(LOADED_SQLITE_PATH.absolute().resolve()).startswit class SqliteBinary(BaseBinary): name: BinName = 'sqlite' binproviders_supported: List[InstanceOf[BaseBinProvider]] = Field(default=[VENV_PIP_BINPROVIDER, SYS_PIP_BINPROVIDER]) - provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { + overrides: BinaryOverrides = { VENV_PIP_BINPROVIDER.name: { - "abspath": lambda: LOADED_SQLITE_PATH if LOADED_SQLITE_FROM_VENV else None, - "version": lambda: LOADED_SQLITE_VERSION if LOADED_SQLITE_FROM_VENV else None, + "abspath": LOADED_SQLITE_PATH if LOADED_SQLITE_FROM_VENV else None, + "version": LOADED_SQLITE_VERSION if LOADED_SQLITE_FROM_VENV else None, }, SYS_PIP_BINPROVIDER.name: { - "abspath": lambda: LOADED_SQLITE_PATH if not LOADED_SQLITE_FROM_VENV else None, - "version": lambda: LOADED_SQLITE_VERSION if not LOADED_SQLITE_FROM_VENV else None, + "abspath": LOADED_SQLITE_PATH if not LOADED_SQLITE_FROM_VENV else None, + "version": LOADED_SQLITE_VERSION if not LOADED_SQLITE_FROM_VENV else None, }, } @@ -177,11 +177,11 @@ class SqliteBinary(BaseBinary): ]) return self - @validate_call + # @validate_call def install(self, **kwargs): return self.load() # obviously it's already installed if we are running this ;) - @validate_call + # @validate_call def load_or_install(self, **kwargs): return self.load() # obviously it's already installed if we are running this ;) @@ -196,22 +196,22 @@ class DjangoBinary(BaseBinary): name: BinName = 'django' binproviders_supported: List[InstanceOf[BaseBinProvider]] = Field(default=[VENV_PIP_BINPROVIDER, SYS_PIP_BINPROVIDER]) - provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { + overrides: BinaryOverrides = { VENV_PIP_BINPROVIDER.name: { - "abspath": lambda: LOADED_DJANGO_PATH if LOADED_DJANGO_FROM_VENV else None, - "version": lambda: LOADED_DJANGO_VERSION if LOADED_DJANGO_FROM_VENV else None, + "abspath": LOADED_DJANGO_PATH if LOADED_DJANGO_FROM_VENV else None, + "version": LOADED_DJANGO_VERSION if LOADED_DJANGO_FROM_VENV else None, }, SYS_PIP_BINPROVIDER.name: { - "abspath": lambda: LOADED_DJANGO_PATH if not LOADED_DJANGO_FROM_VENV else None, - "version": lambda: LOADED_DJANGO_VERSION if not LOADED_DJANGO_FROM_VENV else None, + "abspath": LOADED_DJANGO_PATH if not LOADED_DJANGO_FROM_VENV else None, + "version": LOADED_DJANGO_VERSION if not LOADED_DJANGO_FROM_VENV else None, }, } - @validate_call + # @validate_call def install(self, **kwargs): return self.load() # obviously it's already installed if we are running this ;) - @validate_call + # @validate_call def load_or_install(self, **kwargs): return self.load() # obviously it's already installed if we are running this ;) @@ -221,11 +221,11 @@ class PipBinary(BaseBinary): name: BinName = "pip" binproviders_supported: List[InstanceOf[BinProvider]] = [LIB_PIP_BINPROVIDER, VENV_PIP_BINPROVIDER, SYS_PIP_BINPROVIDER, apt, brew, env] - @validate_call + # @validate_call def install(self, **kwargs): return self.load() # obviously it's already installed if we are running this ;) - @validate_call + # @validate_call def load_or_install(self, **kwargs): return self.load() # obviously it's already installed if we are running this ;) diff --git a/archivebox/plugins_pkg/playwright/apps.py b/archivebox/plugins_pkg/playwright/apps.py index 5d0a3c7f..a0804023 100644 --- a/archivebox/plugins_pkg/playwright/apps.py +++ b/archivebox/plugins_pkg/playwright/apps.py @@ -11,7 +11,7 @@ from pydantic_pkgr import ( BinName, BinProvider, BinProviderName, - ProviderLookupDict, + BinProviderOverrides, InstallArgs, PATHStr, HostBinPath, @@ -66,15 +66,15 @@ class PlaywrightBinProvider(BaseBinProvider): PATH: PATHStr = f"{CONSTANTS.LIB_BIN_DIR}:{DEFAULT_ENV_PATH}" - playwright_browsers_dir: Optional[Path] = ( + playwright_browsers_dir: Path = ( Path("~/Library/Caches/ms-playwright").expanduser() # macos playwright cache dir if OPERATING_SYSTEM == "darwin" else Path("~/.cache/ms-playwright").expanduser() # linux playwright cache dir ) playwright_install_args: List[str] = ["install"] # --with-deps - packages_handler: ProviderLookupDict = Field(default={ - "chrome": lambda: ["chromium"], + packages_handler: BinProviderOverrides = Field(default={ + "chrome": ["chromium"], }, exclude=True) _browser_abspaths: ClassVar[Dict[str, HostBinPath]] = {} @@ -104,9 +104,17 @@ class PlaywrightBinProvider(BaseBinProvider): ) # ~/Library/caches/ms-playwright/chromium-1097/chrome-linux/chromium - return sorted(self.playwright_browsers_dir.glob(f"{browser_name}-*/*-linux/*")) + paths = [] + for path in sorted(self.playwright_browsers_dir.glob(f"{browser_name}-*/*-linux/*")): + if 'xdg-settings' in str(path): + continue + if 'ffmpeg' in str(path): + continue + if '/chrom' in str(path) and 'chrom' in path.name.lower(): + paths.append(path) + return paths - def on_get_abspath(self, bin_name: BinName, **context) -> Optional[HostBinPath]: + def default_abspath_handler(self, bin_name: BinName, **context) -> Optional[HostBinPath]: assert bin_name == "chrome", "Only chrome is supported using the @puppeteer/browsers install method currently." # already loaded, return abspath from cache @@ -128,7 +136,7 @@ class PlaywrightBinProvider(BaseBinProvider): return None - def on_install(self, bin_name: str, packages: Optional[InstallArgs] = None, **context) -> str: + def default_install_handler(self, bin_name: str, packages: Optional[InstallArgs] = None, **context) -> str: """playwright install chrome""" self.setup() assert bin_name == "chrome", "Only chrome is supported using the playwright install method currently." @@ -137,7 +145,7 @@ class PlaywrightBinProvider(BaseBinProvider): raise Exception( f"{self.__class__.__name__} install method is not available on this host ({self.INSTALLER_BIN} not found in $PATH)" ) - packages = packages or self.on_get_packages(bin_name) + packages = packages or self.get_packages(bin_name) # print(f'[*] {self.__class__.__name__}: Installing {bin_name}: {self.INSTALLER_BIN_ABSPATH} install {packages}') @@ -155,7 +163,7 @@ class PlaywrightBinProvider(BaseBinProvider): output_lines = [ line for line in proc.stdout.strip().split('\n') if '/chrom' in line - and 'chrom' in line.rsplit('/', 1)[-1].lower() # make final path segment (filename) contains chrome or chromium + and 'chrom' in line.rsplit('/', 1)[-1].lower() # if final path segment (filename) contains chrome or chromium and 'xdg-settings' not in line and 'ffmpeg' not in line ] diff --git a/archivebox/plugins_pkg/puppeteer/apps.py b/archivebox/plugins_pkg/puppeteer/apps.py index e11be4df..f9ee3206 100644 --- a/archivebox/plugins_pkg/puppeteer/apps.py +++ b/archivebox/plugins_pkg/puppeteer/apps.py @@ -11,7 +11,7 @@ from pydantic_pkgr import ( BinProvider, BinName, BinProviderName, - ProviderLookupDict, + BinProviderOverrides, InstallArgs, PATHStr, HostBinPath, @@ -65,10 +65,10 @@ class PuppeteerBinProvider(BaseBinProvider): euid: Optional[int] = ARCHIVEBOX_USER - puppeteer_browsers_dir: Optional[Path] = LIB_DIR_BROWSERS + puppeteer_browsers_dir: Path = LIB_DIR_BROWSERS puppeteer_install_args: List[str] = ["@puppeteer/browsers", "install", "--path", str(LIB_DIR_BROWSERS)] - packages_handler: ProviderLookupDict = Field(default={ + packages_handler: BinProviderOverrides = Field(default={ "chrome": lambda: ['chrome@stable'], }, exclude=True) @@ -90,7 +90,7 @@ class PuppeteerBinProvider(BaseBinProvider): # /data/lib/browsers/chrome/linux-131.0.6730.0/chrome-linux64/chrome return sorted(self.puppeteer_browsers_dir.glob(f"{browser_name}/linux*/chrome*/chrome")) - def on_get_abspath(self, bin_name: BinName, **context) -> Optional[HostBinPath]: + def default_abspath_handler(self, bin_name: BinName, **context) -> Optional[HostBinPath]: assert bin_name == 'chrome', 'Only chrome is supported using the @puppeteer/browsers install method currently.' # already loaded, return abspath from cache @@ -106,7 +106,7 @@ class PuppeteerBinProvider(BaseBinProvider): return None - def on_install(self, bin_name: str, packages: Optional[InstallArgs] = None, **context) -> str: + def default_install_handler(self, bin_name: str, packages: Optional[InstallArgs] = None, **context) -> str: """npx @puppeteer/browsers install chrome@stable""" self.setup() assert bin_name == 'chrome', 'Only chrome is supported using the @puppeteer/browsers install method currently.' @@ -115,7 +115,7 @@ class PuppeteerBinProvider(BaseBinProvider): raise Exception( f"{self.__class__.__name__} install method is not available on this host ({self.INSTALLER_BIN} not found in $PATH)" ) - packages = packages or self.on_get_packages(bin_name) + packages = packages or self.get_packages(bin_name) assert packages, f"No packages specified for installation of {bin_name}" # print(f'[*] {self.__class__.__name__}: Installing {bin_name}: {self.INSTALLER_BIN_ABSPATH} install {packages}') diff --git a/archivebox/plugins_search/ripgrep/apps.py b/archivebox/plugins_search/ripgrep/apps.py index cc94a807..27d0f5e1 100644 --- a/archivebox/plugins_search/ripgrep/apps.py +++ b/archivebox/plugins_search/ripgrep/apps.py @@ -3,12 +3,12 @@ __package__ = 'archivebox.plugins_search.ripgrep' import re from pathlib import Path from subprocess import run -from typing import List, Dict, Iterable +from typing import List, Iterable # from typing_extensions import Self # Depends on other PyPI/vendor packages: from pydantic import InstanceOf, Field -from pydantic_pkgr import BinProvider, BinProviderName, ProviderLookupDict, BinName +from pydantic_pkgr import BinProvider, BinaryOverrides, BinName # Depends on other Django apps: from abx.archivebox.base_plugin import BasePlugin @@ -45,9 +45,9 @@ class RipgrepBinary(BaseBinary): name: BinName = RIPGREP_CONFIG.RIPGREP_BINARY binproviders_supported: List[InstanceOf[BinProvider]] = [apt, brew, env] - provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { - apt.name: {'packages': lambda: ['ripgrep']}, - brew.name: {'packages': lambda: ['ripgrep']}, + overrides: BinaryOverrides = { + apt.name: {'packages': ['ripgrep']}, + brew.name: {'packages': ['ripgrep']}, } RIPGREP_BINARY = RipgrepBinary() diff --git a/archivebox/plugins_search/sonic/apps.py b/archivebox/plugins_search/sonic/apps.py index 1d034bb5..d62d1f12 100644 --- a/archivebox/plugins_search/sonic/apps.py +++ b/archivebox/plugins_search/sonic/apps.py @@ -1,11 +1,11 @@ __package__ = 'archivebox.plugins_search.sonic' import sys -from typing import List, Dict, Generator, cast +from typing import List, Generator, cast # Depends on other PyPI/vendor packages: from pydantic import InstanceOf, Field, model_validator -from pydantic_pkgr import BinProvider, BinProviderName, ProviderLookupDict, BinName +from pydantic_pkgr import BinProvider, BinaryOverrides, BinName # Depends on other Django apps: from abx.archivebox.base_plugin import BasePlugin @@ -55,9 +55,9 @@ class SonicBinary(BaseBinary): name: BinName = SONIC_CONFIG.SONIC_BINARY binproviders_supported: List[InstanceOf[BinProvider]] = [brew, env] # TODO: add cargo - provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { - brew.name: {'packages': lambda: ['sonic']}, - # cargo.name: {'packages': lambda: ['sonic-server']}, # TODO: add cargo + overrides: BinaryOverrides = { + brew.name: {'packages': ['sonic']}, + # cargo.name: {'packages': ['sonic-server']}, # TODO: add cargo } # TODO: add version checking over protocol? for when sonic backend is on remote server and binary is not installed locally diff --git a/archivebox/plugins_search/sqlite/apps.py b/archivebox/plugins_search/sqlite/apps.py index 9f34bfd8..67917f19 100644 --- a/archivebox/plugins_search/sqlite/apps.py +++ b/archivebox/plugins_search/sqlite/apps.py @@ -66,11 +66,11 @@ class SqliteftsConfig(BaseConfigSet): # Only Python >= 3.11 supports sqlite3.Connection.getlimit(), # so fall back to the default if the API to get the real value isn't present try: - limit_id = sqlite3.SQLITE_LIMIT_LENGTH + limit_id = sqlite3.SQLITE_LIMIT_LENGTH # type: ignore[attr-defined] if self.SQLITEFTS_SEPARATE_DATABASE: cursor = self.get_connection() - return cursor.connection.getlimit(limit_id) + return cursor.connection.getlimit(limit_id) # type: ignore[attr-defined] else: with database.temporary_connection() as cursor: # type: ignore[attr-defined] return cursor.connection.getlimit(limit_id) diff --git a/archivebox/vendor/pydantic-pkgr b/archivebox/vendor/pydantic-pkgr index 9d33c8c7..ad3c0ca4 160000 --- a/archivebox/vendor/pydantic-pkgr +++ b/archivebox/vendor/pydantic-pkgr @@ -1 +1 @@ -Subproject commit 9d33c8c75ebfc7ea99e29fcc8126d081a8026cda +Subproject commit ad3c0ca457951d4d0852b46020fc6365b75e5065 diff --git a/pyproject.toml b/pyproject.toml index 6c650ec1..83d94020 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "archivebox" -version = "0.8.5rc37" +version = "0.8.5rc42" requires-python = ">=3.10" description = "Self-hosted internet archiving solution." authors = [{name = "Nick Sweeting", email = "pyproject.toml@archivebox.io"}] @@ -78,7 +78,7 @@ dependencies = [ "django-taggit==1.3.0", "base32-crockford==0.3.0", # "pocket@git+https://github.com/tapanpandita/pocket.git@v0.3.7", - "pydantic-pkgr>=0.4.24", + "pydantic-pkgr>=0.5.2", ############# Plugin Dependencies ################ "sonic-client>=1.0.0", "yt-dlp>=2024.8.6", # for: media" diff --git a/requirements.txt b/requirements.txt index 7a8d417b..8b60cddc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -119,7 +119,7 @@ executing==2.1.0 # via stack-data feedparser==6.0.11 # via archivebox (pyproject.toml) -ftfy==6.2.3 +ftfy==6.3.0 # via python-benedict h11==0.14.0 # via httpcore @@ -168,6 +168,8 @@ pexpect==4.9.0 # via ipython phonenumbers==8.13.47 # via python-benedict +platformdirs==4.3.6 + # via pydantic-pkgr pluggy==1.5.0 # via archivebox (pyproject.toml) prompt-toolkit==3.0.48 @@ -203,7 +205,7 @@ pydantic-core==2.23.4 # via # pydantic # pydantic-pkgr -pydantic-pkgr==0.4.24 +pydantic-pkgr==0.5.2 # via archivebox (pyproject.toml) pydantic-settings==2.5.2 # via archivebox (pyproject.toml) @@ -332,5 +334,5 @@ xmltodict==0.14.1 # via python-benedict yt-dlp==2024.10.7 # via archivebox (pyproject.toml) -zope-interface==7.0.3 +zope-interface==7.1.0 # via twisted diff --git a/uv.lock b/uv.lock index f2db34f2..3a11c518 100644 --- a/uv.lock +++ b/uv.lock @@ -41,7 +41,7 @@ wheels = [ [[package]] name = "archivebox" -version = "0.8.5rc36" +version = "0.8.5rc42" source = { editable = "." } dependencies = [ { name = "atomicwrites" }, @@ -148,7 +148,7 @@ requires-dist = [ { name = "pluggy", specifier = ">=1.5.0" }, { name = "psutil", specifier = ">=6.0.0" }, { name = "py-machineid", specifier = ">=0.6.0" }, - { name = "pydantic-pkgr", specifier = ">=0.4.24" }, + { name = "pydantic-pkgr", specifier = ">=0.5.2" }, { name = "pydantic-settings", specifier = ">=2.5.2" }, { name = "python-benedict", extras = ["io", "parse"], specifier = ">=0.33.2" }, { name = "python-crontab", specifier = ">=3.2.0" }, @@ -965,14 +965,14 @@ wheels = [ [[package]] name = "ftfy" -version = "6.2.3" +version = "6.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/da/a9/59f4354257e8350a25be1774021991fb3a99a2fb87d0c1f367592548aed3/ftfy-6.2.3.tar.gz", hash = "sha256:79b505988f29d577a58a9069afe75553a02a46e42de6091c0660cdc67812badc", size = 64165 } +sdist = { url = "https://files.pythonhosted.org/packages/85/c3/63753eca4c5257ce0561cb5f8e9cd0d45d97848c73c56e33a0a764319e5b/ftfy-6.3.0.tar.gz", hash = "sha256:1c7d6418e72b25a7760feb150acf574b86924dbb2e95b32c0b3abbd1ba3d7ad6", size = 362118 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/46/14d230ad057048aea7ccd2f96a80905830866d281ea90a6662a825490659/ftfy-6.2.3-py3-none-any.whl", hash = "sha256:f15761b023f3061a66207d33f0c0149ad40a8319fd16da91796363e2c049fdf8", size = 43011 }, + { url = "https://files.pythonhosted.org/packages/76/0f/d8a8152e720cbcad890e56ee98639ff489f1992869b4cf304c3fa24d4bcc/ftfy-6.3.0-py3-none-any.whl", hash = "sha256:17aca296801f44142e3ff2c16f93fbf6a87609ebb3704a9a41dd5d4903396caf", size = 44778 }, ] [[package]] @@ -1170,31 +1170,37 @@ wheels = [ [[package]] name = "libcst" -version = "1.4.0" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e4/bd/ff41d7a8efc4f60a61d903c3f9823565006f44f2b8b11c99701f552b0851/libcst-1.4.0.tar.gz", hash = "sha256:449e0b16604f054fa7f27c3ffe86ea7ef6c409836fe68fe4e752a1894175db00", size = 771364 } +sdist = { url = "https://files.pythonhosted.org/packages/4d/c4/5577b92173199299e0d32404aa92a156d353d6ec0f74148f6e418e0defef/libcst-1.5.0.tar.gz", hash = "sha256:8478abf21ae3861a073e898d80b822bd56e578886331b33129ba77fec05b8c24", size = 772970 } wheels = [ - { url = "https://files.pythonhosted.org/packages/09/a2/00a395a95518626cfd67aeed1d3e9f39b82b5e42e025bea897e1226db41b/libcst-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:279b54568ea1f25add50ea4ba3d76d4f5835500c82f24d54daae4c5095b986aa", size = 2110691 }, - { url = "https://files.pythonhosted.org/packages/53/4d/8353b566a9c338b46af01f3758296d5646808dd314c0b686f77384c0d323/libcst-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3401dae41fe24565387a65baee3887e31a44e3e58066b0250bc3f3ccf85b1b5a", size = 2036754 }, - { url = "https://files.pythonhosted.org/packages/e6/c9/9cea10a2c2dcb120a793616ceac0ab9548c05edb06e4f824f6e88c86c8e8/libcst-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1989fa12d3cd79118ebd29ebe2a6976d23d509b1a4226bc3d66fcb7cb50bd5d", size = 2199222 }, - { url = "https://files.pythonhosted.org/packages/25/5f/0df8f628122a5cd114b9edfbc673cb56070fdb295e355048a076a40d5974/libcst-1.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:addc6d585141a7677591868886f6bda0577529401a59d210aa8112114340e129", size = 2251349 }, - { url = "https://files.pythonhosted.org/packages/3f/0d/2db8d0df21eab1a10c89218123cabb667d7c546dff6253bdc56480d707e0/libcst-1.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17d71001cb25e94cfe8c3d997095741a8c4aa7a6d234c0f972bc42818c88dfaf", size = 2335344 }, - { url = "https://files.pythonhosted.org/packages/b2/1b/1a2b83d208ea4d91b955be2a4e6b3cec0a647e6d6aa032d3b59f1585de31/libcst-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:2d47de16d105e7dd5f4e01a428d9f4dc1e71efd74f79766daf54528ce37f23c3", size = 2029201 }, - { url = "https://files.pythonhosted.org/packages/85/2c/6bf8e4710afe1e0d45643e3726c0a956f5965555425cd7efa31e97cc7a6b/libcst-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e6227562fc5c9c1efd15dfe90b0971ae254461b8b6b23c1b617139b6003de1c1", size = 2110723 }, - { url = "https://files.pythonhosted.org/packages/5d/82/652e041aa6e14751a2ce41e68e281d9d5a32864ba11a363e103c429bf0e8/libcst-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3399e6c95df89921511b44d8c5bf6a75bcbc2d51f1f6429763609ba005c10f6b", size = 2036982 }, - { url = "https://files.pythonhosted.org/packages/b8/d7/515b6187a900033467a4001bf8e2ed95f4961aa9bedf2bf39dfd68659157/libcst-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48601e3e590e2d6a7ab8c019cf3937c70511a78d778ab3333764531253acdb33", size = 2199286 }, - { url = "https://files.pythonhosted.org/packages/50/a1/2093f74a3f8936fcdaac01f86d1c5fa8f586202afa466a92332b9a461b14/libcst-1.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f42797309bb725f0f000510d5463175ccd7155395f09b5e7723971b0007a976d", size = 2251591 }, - { url = "https://files.pythonhosted.org/packages/0a/6c/1eb258b0eba8f337e1e9bd40574247310670c036a3913c9b650d6d9cd4de/libcst-1.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb4e42ea107a37bff7f9fdbee9532d39f9ea77b89caa5c5112b37057b12e0838", size = 2335434 }, - { url = "https://files.pythonhosted.org/packages/6a/56/1c5a8385e9cc2d95d278cb8df48d11587c1c93b3b78c2edafd16b2bf11fa/libcst-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:9d0cc3c5a2a51fa7e1d579a828c0a2e46b2170024fd8b1a0691c8a52f3abb2d9", size = 2029195 }, - { url = "https://files.pythonhosted.org/packages/2f/09/e4374c8e9bde82a6197860b67ed0b0cd07c0fbc95fff035886382165a279/libcst-1.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7ece51d935bc9bf60b528473d2e5cc67cbb88e2f8146297e40ee2c7d80be6f13", size = 2106058 }, - { url = "https://files.pythonhosted.org/packages/61/8a/84810ea960ede8d15266cc5e135165d92aadb08956136e53926b3e037829/libcst-1.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:81653dea1cdfa4c6520a7c5ffb95fa4d220cbd242e446c7a06d42d8636bfcbba", size = 2032124 }, - { url = "https://files.pythonhosted.org/packages/08/1d/3e2ab936e4195df82b764b02631a865b65dcf252772ddfe5265d384a883d/libcst-1.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6abce0e66bba2babfadc20530fd3688f672d565674336595b4623cd800b91ef", size = 2195173 }, - { url = "https://files.pythonhosted.org/packages/11/38/30206bbcf31425f6fd01dae3cf23e35df790969243d39757ae743d8e6d67/libcst-1.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da9d7dc83801aba3b8d911f82dc1a375db0d508318bad79d9fb245374afe068", size = 2248523 }, - { url = "https://files.pythonhosted.org/packages/8c/02/1c9c908724c732f09b11493ee5d61893060ecc9a3dc4bc80032d1be87b37/libcst-1.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c54aa66c86d8ece9c93156a2cf5ca512b0dce40142fe9e072c86af2bf892411", size = 2326040 }, - { url = "https://files.pythonhosted.org/packages/04/32/7345f10a2dc728015920d689d5c1b8dc0232db321e172cdad2611e73c5b3/libcst-1.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:62e2682ee1567b6a89c91853865372bf34f178bfd237853d84df2b87b446e654", size = 2026263 }, + { url = "https://files.pythonhosted.org/packages/85/44/c8f1e0d83bbdabc240c05d5bedddfd4e095a0031b8df473d8eb004f12554/libcst-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:23d0e07fd3ed11480f8993a1e99d58a45f914a711b14f858b8db08ae861a8a34", size = 2112640 }, + { url = "https://files.pythonhosted.org/packages/20/d5/3d5819da92a8f997ecf0b5a77d65865d4d2aa4209b34e32835b555218689/libcst-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d92c5ae2e2dc9356ad7e3d05077d9b7e5065423e45788fd86729c88729e45c6e", size = 2026866 }, + { url = "https://files.pythonhosted.org/packages/74/19/d2ebded5990f2f5ab4c86412df75338b9d8b386fbb5e430669f287bc8d9c/libcst-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96adc45e96476350df6b8a5ddbb1e1d6a83a7eb3f13087e52eb7cd2f9b65bcc7", size = 2203742 }, + { url = "https://files.pythonhosted.org/packages/87/98/d47a9a88df48cc33db7e1219cd7c29bfdfd8d695634f3f2e86ff04bbd58d/libcst-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d5978fd60c66794bb60d037b2e6427ea52d032636e84afce32b0f04e1cf500a", size = 2253801 }, + { url = "https://files.pythonhosted.org/packages/b8/ca/7fdcbab8f8e8c46336099af7929d0f0e5873222830010aae0160d16544c1/libcst-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6502aeb11412afc759036160c686be1107eb5a4466db56b207c786b9b4da7c4", size = 2324610 }, + { url = "https://files.pythonhosted.org/packages/24/fb/db7c696b7bf8e295aa9bf37091fbd1bad35e491be44926da2b20907c3452/libcst-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9cccfc0a78e110c0d0a9d2c6fdeb29feb5274c9157508a8baef7edf352420f6d", size = 2030364 }, + { url = "https://files.pythonhosted.org/packages/b5/82/5b9d1f89bdba4106de6080ab3384157581af4f0b94e04a7150b917b5b945/libcst-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:585b3aa705b3767d717d2100935d8ef557275ecdd3fac81c3e28db0959efb0ea", size = 2112655 }, + { url = "https://files.pythonhosted.org/packages/17/4d/c6ed4323e77717edf3f47af8cabbdd4a7de7983fc5a1cc20130947f65f9d/libcst-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8935dd3393e30c2f97344866a4cb14efe560200e232166a8db1de7865c2ef8b2", size = 2026906 }, + { url = "https://files.pythonhosted.org/packages/eb/ad/10cffc6a69da4320cc75f7f031a48292b61ad5ba0ba94fa9f963cb0b5f67/libcst-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc80ea16c7d44e38f193e4d4ef7ff1e0ba72d8e60e8b61ac6f4c87f070a118bd", size = 2203824 }, + { url = "https://files.pythonhosted.org/packages/e8/88/016b3feb75a3b16896e27691439c3bd493ae7d896bb4e31d6bd4c2e5c20b/libcst-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02be4aab728261bb76d16e77c9a457884cebb60d09c8edee844de43b0e08aff7", size = 2253854 }, + { url = "https://files.pythonhosted.org/packages/69/8e/5a60d53493e259743fd574abe442dd7f3b497ebb58dee168473a03f90d3e/libcst-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8fcd78be4d9ce3c36d0c5d0bdd384e0c7d5f72970a9e4ebd56070141972b4ad", size = 2324725 }, + { url = "https://files.pythonhosted.org/packages/65/86/ddf0d593f4ef5994f456e00e99a1eb28b661aab5df960034199f4d8bbeb4/libcst-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:52b6aadfe54e3ae52c3b815eaaa17ba4da9ff010d5e8adf6a70697872886dd10", size = 2030364 }, + { url = "https://files.pythonhosted.org/packages/a7/23/9cdb3362ad75490108a03abeaae8d7f7fb0d86586d806102ae9d9690d6b8/libcst-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:83bc5fbe34d33597af1d5ea113dcb9b5dd5afe5a5f4316bac4293464d5e3971a", size = 2108563 }, + { url = "https://files.pythonhosted.org/packages/48/ec/4a1a34c3dbe6d51815700a0c14991f4124f10e82f9959d4fb5a9b0b06c74/libcst-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f10124bf99a0b075eae136ef0ce06204e5f6b8da4596a9c4853a0663e80ddf3", size = 2024056 }, + { url = "https://files.pythonhosted.org/packages/da/b7/1976377c19f9477267daac2ea8e2d5a72ce12d5b523ff147d404fb7ae74e/libcst-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48e581af6127c5af4c9f483e5986d94f0c6b2366967ee134f0a8eba0aa4c8c12", size = 2199473 }, + { url = "https://files.pythonhosted.org/packages/63/c4/e056f3f34642f294421bd4a4d4b40aeccaf153a456bcb4d7e54f4337143f/libcst-1.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dba93cca0a5c6d771ed444c44d21ce8ea9b277af7036cea3743677aba9fbbb8", size = 2251411 }, + { url = "https://files.pythonhosted.org/packages/e8/d6/574fc6c8b0ca81586ee05f284ef6987730b841b31ce246ef9d3c45f17ec4/libcst-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b5c4d87721a7bab265c202575809b810815ab81d5e2e7a5d4417a087975840", size = 2323144 }, + { url = "https://files.pythonhosted.org/packages/b1/92/5cb62834eec397f4b3218c03acc28b6b8470f87c8dad9e9b0fd738c3948c/libcst-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:b48bf71d52c1e891a0948465a94d9817b5fc1ec1a09603566af90585f3b11948", size = 2029603 }, + { url = "https://files.pythonhosted.org/packages/60/5e/dd156f628fed03a273d995008f1669e1964727df6a8818bbedaac51f9ae5/libcst-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:88520b6dea59eaea0cae80f77c0a632604a82c5b2d23dedb4b5b34035cbf1615", size = 2108562 }, + { url = "https://files.pythonhosted.org/packages/2c/54/f63bf0bd2d70179e0557c9474a0511e33e646d398945b5a01de36237ce60/libcst-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:208ea92d80b2eeed8cbc879d5f39f241582a5d56b916b1b65ed2be2f878a2425", size = 2024057 }, + { url = "https://files.pythonhosted.org/packages/dc/37/ce62947fd7305fb501589e4b8f6e82e3cf61fca2d62392e281c17a2112f5/libcst-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4592872aaf5b7fa5c2727a7d73c0985261f1b3fe7eff51f4fd5b8174f30b4e2", size = 2199474 }, + { url = "https://files.pythonhosted.org/packages/c9/95/b878c95af17f3e341ac5dc18e3160d45d86b2c05a0cafd866ceb0b766bbd/libcst-1.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2788b2b5838b78fe15df8e9fa6b6903195ea49b2d2ba43e8f423f6c90e4b69f", size = 2251410 }, + { url = "https://files.pythonhosted.org/packages/e1/26/697b54aa839c4dc6ea2787d5e977ed4be0636149f85df1a0cba7a29bd188/libcst-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b5b5bcd3a9ba92840f27ad34eaa038acbee195ec337da39536c0a2efbbf28efd", size = 2323144 }, + { url = "https://files.pythonhosted.org/packages/a0/9f/5b5481d716670ed5fbd8d06dfa94b7108272b645da2f2406eb909cb6a450/libcst-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:4d6acb0bdee1e55b44c6215c59755ec4693ac01e74bb1fde04c37358b378835d", size = 2029600 }, ] [[package]] @@ -1608,6 +1614,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/55/90db48d85f7689ec6f81c0db0622d704306c5284850383c090e6c7195a5c/pip-24.2-py3-none-any.whl", hash = "sha256:2cd581cf58ab7fcfca4ce8efa6dcacd0de5bf8d0a3eb9ec927e07405f4d9e2a2", size = 1815170 }, ] +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + [[package]] name = "pluggy" version = "1.5.0" @@ -1834,16 +1849,17 @@ wheels = [ [[package]] name = "pydantic-pkgr" -version = "0.4.24" +version = "0.5.2" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "platformdirs" }, { name = "pydantic" }, { name = "pydantic-core" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/65/9a3d801d19686de0d940d131a135bdb1a79ed6ac430c0f7901dcd45f710a/pydantic_pkgr-0.4.24.tar.gz", hash = "sha256:5a4de016478ecd7c0aec0818f5d2e72255a2f625a5c200af217aa70c4524fa90", size = 38785 } +sdist = { url = "https://files.pythonhosted.org/packages/0c/6c/ed0e6d519ecd4ac7cb36c8d74344a26260f7f1878c590a9f3cfb34057bec/pydantic_pkgr-0.5.2.tar.gz", hash = "sha256:8cd01cef9db94e6b97222c1e44b8a7a4b73ca0b8c51a7fddece501094c422d3e", size = 42303 } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/0d/84080a63ebcb112fe7a96bc681aee91ccdf0c334598e2016984aa4c74d6e/pydantic_pkgr-0.4.24-py3-none-any.whl", hash = "sha256:cec121b5b0fc73421af9915e45dfb09cdc776734fcee87f08b501053f5a36259", size = 41791 }, + { url = "https://files.pythonhosted.org/packages/c0/94/1db0817fd8fa234c9696625e314e44b91a9cee7f37cc715328cd57d2454d/pydantic_pkgr-0.5.2-py3-none-any.whl", hash = "sha256:7511830af65a75c03d9e4320d73640429ae53c1f1c2d39f28067857369f142fd", size = 45050 }, ] [[package]] @@ -2301,7 +2317,7 @@ wheels = [ [[package]] name = "sphinx" -version = "8.0.2" +version = "8.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "alabaster" }, @@ -2322,9 +2338,9 @@ dependencies = [ { name = "sphinxcontrib-serializinghtml" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/a7/3cc3d6dcad70aba2e32a3ae8de5a90026a0a2fdaaa0756925e3a120249b6/sphinx-8.0.2.tar.gz", hash = "sha256:0cce1ddcc4fd3532cf1dd283bc7d886758362c5c1de6598696579ce96d8ffa5b", size = 8189041 } +sdist = { url = "https://files.pythonhosted.org/packages/9d/f3/e3c6fb6d015d6b0c5215d1a6e45276aa89b6685fc63a1b7ac230bcebcb4f/sphinx-8.1.0.tar.gz", hash = "sha256:109454425dbf4c78ecfdd481e56f078376d077edbda29804dba05c5161c8de06", size = 8183960 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/61/2ad169c6ff1226b46e50da0e44671592dbc6d840a52034a0193a99b28579/sphinx-8.0.2-py3-none-any.whl", hash = "sha256:56173572ae6c1b9a38911786e206a110c9749116745873feae4f9ce88e59391d", size = 3498950 }, + { url = "https://files.pythonhosted.org/packages/fb/21/143e5e4666432668fbd669f89ee0abc50040787f932bd30befd0f7a42a6e/sphinx-8.1.0-py3-none-any.whl", hash = "sha256:3202bba95697b9fc4371a07d6d457239de9860244ce235283149f817c253fd2f", size = 3486829 }, ] [[package]] @@ -2829,32 +2845,35 @@ wheels = [ [[package]] name = "zope-interface" -version = "7.0.3" +version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c8/83/7de03efae7fc9a4ec64301d86e29a324f32fe395022e3a5b1a79e376668e/zope.interface-7.0.3.tar.gz", hash = "sha256:cd2690d4b08ec9eaf47a85914fe513062b20da78d10d6d789a792c0b20307fb1", size = 252504 } +sdist = { url = "https://files.pythonhosted.org/packages/e4/1f/8bb0739aba9a8909bcfa2e12dc20443ebd5bd773b6796603f1a126211e18/zope_interface-7.1.0.tar.gz", hash = "sha256:3f005869a1a05e368965adb2075f97f8ee9a26c61898a9e52a9764d93774f237", size = 300239 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/33/a55311169d3d41b61da7c5b7d528ebb0469263252a71d9510849c0d66201/zope.interface-7.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9b9369671a20b8d039b8e5a1a33abd12e089e319a3383b4cc0bf5c67bd05fe7b", size = 207912 }, - { url = "https://files.pythonhosted.org/packages/6b/c3/7d18af6971634087a4ddc436e37fc47988c31635cd01948ff668d11c96c4/zope.interface-7.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db6237e8fa91ea4f34d7e2d16d74741187e9105a63bbb5686c61fea04cdbacca", size = 208416 }, - { url = "https://files.pythonhosted.org/packages/8a/64/2922134a93978b6a8b823f3e784d6af3d5d165fad1f66388b0f89b5695fc/zope.interface-7.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53d678bb1c3b784edbfb0adeebfeea6bf479f54da082854406a8f295d36f8386", size = 254614 }, - { url = "https://files.pythonhosted.org/packages/5a/a9/9665ba3aa7c6173ea2c3249c85546139119eaf3146f280cea8053e0047b9/zope.interface-7.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3aa8fcbb0d3c2be1bfd013a0f0acd636f6ed570c287743ae2bbd467ee967154d", size = 249026 }, - { url = "https://files.pythonhosted.org/packages/45/58/890cf943c9a7dd82d096a11872c7efb3f0e97e86f71b886018044fb01972/zope.interface-7.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6195c3c03fef9f87c0dbee0b3b6451df6e056322463cf35bca9a088e564a3c58", size = 254134 }, - { url = "https://files.pythonhosted.org/packages/f9/41/b126c98cc8a72b807cecab5ba483444e573ef9c7ca7f71449e96afd14d4d/zope.interface-7.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:11fa1382c3efb34abf16becff8cb214b0b2e3144057c90611621f2d186b7e1b7", size = 211591 }, - { url = "https://files.pythonhosted.org/packages/80/ff/66b5cd662b177de4082cac412a877c7a528ef79a392d90e504f50c041dda/zope.interface-7.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:af94e429f9d57b36e71ef4e6865182090648aada0cb2d397ae2b3f7fc478493a", size = 208441 }, - { url = "https://files.pythonhosted.org/packages/c1/a3/a890f35a62aa25233c95e2af4510aa1df0553be48450bb0840b8d3b2a62c/zope.interface-7.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dd647fcd765030638577fe6984284e0ebba1a1008244c8a38824be096e37fe3", size = 208954 }, - { url = "https://files.pythonhosted.org/packages/9e/1b/79bcfbdc7d621c410a188f25d78f6e07aff7f608c9589cfba77003769f98/zope.interface-7.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bee1b722077d08721005e8da493ef3adf0b7908e0cd85cc7dc836ac117d6f32", size = 261132 }, - { url = "https://files.pythonhosted.org/packages/c6/91/d3e665df6837629e2eec9cdc8cd1118f1a0e74b586bbec2e6cfc6a1b6c3a/zope.interface-7.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2545d6d7aac425d528cd9bf0d9e55fcd47ab7fd15f41a64b1c4bf4c6b24946dc", size = 255243 }, - { url = "https://files.pythonhosted.org/packages/2c/c2/39964ef5fed7ac1523bab2d1bba244290965da6f720164b603ec07adf0a7/zope.interface-7.0.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d04b11ea47c9c369d66340dbe51e9031df2a0de97d68f442305ed7625ad6493", size = 259957 }, - { url = "https://files.pythonhosted.org/packages/6b/68/3937ac6cd0299694102d71721efd38fd1ceba7eaef20aefed3cdbb22527c/zope.interface-7.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:064ade95cb54c840647205987c7b557f75d2b2f7d1a84bfab4cf81822ef6e7d1", size = 211972 }, - { url = "https://files.pythonhosted.org/packages/ec/be/6640eb57c4b84a471d691082d0207434d1524e428fba1231c335a4cad446/zope.interface-7.0.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3fcdc76d0cde1c09c37b7c6b0f8beba2d857d8417b055d4f47df9c34ec518bdd", size = 208567 }, - { url = "https://files.pythonhosted.org/packages/2d/45/a891ee78ba5ef5b5437394f8c2c56c094ed1ab41a80ef7afe50191dce3d2/zope.interface-7.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3d4b91821305c8d8f6e6207639abcbdaf186db682e521af7855d0bea3047c8ca", size = 208972 }, - { url = "https://files.pythonhosted.org/packages/14/44/d12683e823ced271ae2ca3976f16066634911e02540a9559b09444a4b2d3/zope.interface-7.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35062d93bc49bd9b191331c897a96155ffdad10744ab812485b6bad5b588d7e4", size = 266389 }, - { url = "https://files.pythonhosted.org/packages/db/35/c83308ac84552c2242d5d59488dbea9a91c64765e117a71c566ddf896e31/zope.interface-7.0.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c96b3e6b0d4f6ddfec4e947130ec30bd2c7b19db6aa633777e46c8eecf1d6afd", size = 261112 }, - { url = "https://files.pythonhosted.org/packages/3d/ed/0ac414f9373d742d2eb2f436b595ed281031780a405621a4d906096092ea/zope.interface-7.0.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e0c151a6c204f3830237c59ee4770cc346868a7a1af6925e5e38650141a7f05", size = 267044 }, - { url = "https://files.pythonhosted.org/packages/38/92/e9fe2a8cb53cffc73f923da84e50e0ee3a8d38a64bef6965428d5b5c4910/zope.interface-7.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:3de1d553ce72868b77a7e9d598c9bff6d3816ad2b4cc81c04f9d8914603814f3", size = 212064 }, - { url = "https://files.pythonhosted.org/packages/2b/6f/059521297028f3037f2b19a711be845983151acbdeda1031749a91d07048/zope.interface-7.0.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab985c566a99cc5f73bc2741d93f1ed24a2cc9da3890144d37b9582965aff996", size = 266369 }, - { url = "https://files.pythonhosted.org/packages/ce/bb/51ab7785b2ad3123d5eb85b548f98fe2c0809c6bd452e677b1aca71c3c79/zope.interface-7.0.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d976fa7b5faf5396eb18ce6c132c98e05504b52b60784e3401f4ef0b2e66709b", size = 261119 }, - { url = "https://files.pythonhosted.org/packages/be/56/6a57ef0699b857b33a407162f29eade4062596870d335f53e914bb98fd0e/zope.interface-7.0.3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a207c6b2c58def5011768140861a73f5240f4f39800625072ba84e76c9da0b", size = 267059 }, + { url = "https://files.pythonhosted.org/packages/52/cf/6fe78d1748ade8bde9e0afa0b7a6dc53427fa817c44c0c67937f4a3890ca/zope.interface-7.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2bd9e9f366a5df08ebbdc159f8224904c1c5ce63893984abb76954e6fbe4381a", size = 207992 }, + { url = "https://files.pythonhosted.org/packages/98/6a/7583a3bf0ba508d7454b69928ced99f516af674be7a2781d681bbdf3e439/zope.interface-7.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:661d5df403cd3c5b8699ac480fa7f58047a3253b029db690efa0c3cf209993ef", size = 208498 }, + { url = "https://files.pythonhosted.org/packages/f2/d7/acae0a46ff4494ade2478335aeb2dec2ec024b7761915b82887cb04f207d/zope.interface-7.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91b6c30689cfd87c8f264acb2fc16ad6b3c72caba2aec1bf189314cf1a84ca33", size = 254730 }, + { url = "https://files.pythonhosted.org/packages/76/78/42201e0e6150a14d6aaf138f969186a89ec31d25a5860b7c054191cfefa6/zope.interface-7.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b6a4924f5bad9fe21d99f66a07da60d75696a136162427951ec3cb223a5570d", size = 249135 }, + { url = "https://files.pythonhosted.org/packages/3f/1e/a2bb69085db973bc936493e1a870c708b4e61496c4c1f04033a9aeb2dcce/zope.interface-7.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80a3c00b35f6170be5454b45abe2719ea65919a2f09e8a6e7b1362312a872cd3", size = 254254 }, + { url = "https://files.pythonhosted.org/packages/4f/cf/a5cb40b19f52c100d0ce22797f63ac865ced81fbf3a75a7ae0ecf2c45810/zope.interface-7.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:b936d61dbe29572fd2cfe13e30b925e5383bed1aba867692670f5a2a2eb7b4e9", size = 211705 }, + { url = "https://files.pythonhosted.org/packages/9a/0b/c9dd45c073109fcaa63d5e167cae9e364fcb25f3626350127258a678ff0f/zope.interface-7.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ac20581fc6cd7c754f6dff0ae06fedb060fa0e9ea6309d8be8b2701d9ea51c4", size = 208524 }, + { url = "https://files.pythonhosted.org/packages/e0/34/57afb328bcced4d0472c11cfab5581cc1e6bb91adf1bb87509a4f5690755/zope.interface-7.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:848b6fa92d7c8143646e64124ed46818a0049a24ecc517958c520081fd147685", size = 209032 }, + { url = "https://files.pythonhosted.org/packages/e9/a4/b2e4900f6d4a572979b5e8aa95f1ff9296b458978537f51ba546da51c108/zope.interface-7.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec1ef1fdb6f014d5886b97e52b16d0f852364f447d2ab0f0c6027765777b6667", size = 261251 }, + { url = "https://files.pythonhosted.org/packages/c3/89/2cd0a6b24819c024b340fa67f0dda65d0ac8bbd81f35a1fa7c468b681d55/zope.interface-7.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bcff5c09d0215f42ba64b49205a278e44413d9bf9fa688fd9e42bfe472b5f4f", size = 255366 }, + { url = "https://files.pythonhosted.org/packages/9e/00/e58be3067025ffbeed48094a07c1972d8150f6d628151fde66f16fa0d4ae/zope.interface-7.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07add15de0cc7e69917f7d286b64d54125c950aeb43efed7a5ea7172f000fbc1", size = 260078 }, + { url = "https://files.pythonhosted.org/packages/d1/b6/56436f9f6b74c13c9cd3dbd8345f47823d72b7c9ba2b39872cb7bee4cf42/zope.interface-7.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:9940d5bc441f887c5f375ec62bcf7e7e495a2d5b1da97de1184a88fb567f06af", size = 212092 }, + { url = "https://files.pythonhosted.org/packages/ee/d7/0ab8291230cf4fa05fa6f7bb26e0206d799a922070bc3a102f88133edc1e/zope.interface-7.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f245d039f72e6f802902375755846f5de1ee1e14c3e8736c078565599bcab621", size = 208649 }, + { url = "https://files.pythonhosted.org/packages/4e/ce/598d623faeca8a7ccb120a7d94f707efb61d21a57324a905c9a2bdb7b4b9/zope.interface-7.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6159e767d224d8f18deff634a1d3722e68d27488c357f62ebeb5f3e2f5288b1f", size = 209053 }, + { url = "https://files.pythonhosted.org/packages/ea/d0/c88caffdf6cf99e9b5d1fad9bdfa94d9eee21f72c2f9f4768bced100aab7/zope.interface-7.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e956b1fd7f3448dd5e00f273072e73e50dfafcb35e4227e6d5af208075593c9", size = 266506 }, + { url = "https://files.pythonhosted.org/packages/1d/bd/2b665bb66b18169828f0e3d0865eabdb3c8f59556db90367950edccfc072/zope.interface-7.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff115ef91c0eeac69cd92daeba36a9d8e14daee445b504eeea2b1c0b55821984", size = 261229 }, + { url = "https://files.pythonhosted.org/packages/04/a0/9a0595057002784395990b5e5a5e84e71905f5c110ea5ecae469dc831468/zope.interface-7.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bec001798ab62c3fc5447162bf48496ae9fba02edc295a9e10a0b0c639a6452e", size = 267167 }, + { url = "https://files.pythonhosted.org/packages/fb/64/cf1a22aad65dc9746fdc6705042c066011e3fe80f9c73aea9a53b0b3642d/zope.interface-7.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:124149e2d42067b9c6597f4dafdc7a0983d0163868f897b7bb5dc850b14f9a87", size = 212207 }, + { url = "https://files.pythonhosted.org/packages/43/39/75d4e59474ec7aeb8eebb01fae88e97ee8b0b3144d7a445679f000001977/zope.interface-7.1.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:9733a9a0f94ef53d7aa64661811b20875b5bc6039034c6e42fb9732170130573", size = 208650 }, + { url = "https://files.pythonhosted.org/packages/c9/24/929b5530508a39a842fe50e159681b3dd36800604252940662268c3a8551/zope.interface-7.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5fcf379b875c610b5a41bc8a891841533f98de0520287d7f85e25386cd10d3e9", size = 209057 }, + { url = "https://files.pythonhosted.org/packages/fa/a3/07c120b40d47a3b28faadbacea579db8d7dc9214c909da13d72fd55395f7/zope.interface-7.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0a45b5af9f72c805ee668d1479480ca85169312211bed6ed18c343e39307d5f", size = 266466 }, + { url = "https://files.pythonhosted.org/packages/4f/fa/e1925c8737787887a2801a45aadbc1ca8367fd9f135e721a2ce5a020e14d/zope.interface-7.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af4a12b459a273b0b34679a5c3dc5e34c1847c3dd14a628aa0668e19e638ea2", size = 261220 }, + { url = "https://files.pythonhosted.org/packages/d5/79/d7828b915edf77f8f7849e0ab4380084d07c3d09ef86f9763f1490661d66/zope.interface-7.1.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a735f82d2e3ed47ca01a20dfc4c779b966b16352650a8036ab3955aad151ed8a", size = 267157 }, + { url = "https://files.pythonhosted.org/packages/98/ac/012f18dc9b35e8547975f6e0512bcb6a1e97901d7a5e4e4cb5899dee6304/zope.interface-7.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:5501e772aff595e3c54266bc1bfc5858e8f38974ce413a8f1044aae0f32a83a3", size = 212213 }, ]