add new binproviders and binaries args to install and version, bump pydantic-pkgr version

This commit is contained in:
Nick Sweeting 2024-10-11 00:45:59 -07:00
parent fbd2c458c3
commit 6e7071bd19
No known key found for this signature in database
24 changed files with 318 additions and 235 deletions

View file

@ -1,15 +1,14 @@
__package__ = "abx.archivebox" __package__ = "abx.archivebox"
import os import os
from typing import Dict, List, Optional from typing import Optional, cast
from typing_extensions import Self from typing_extensions import Self
from pydantic import Field, InstanceOf, validate_call from pydantic import validate_call
from pydantic_pkgr import ( from pydantic_pkgr import (
Binary, Binary,
BinProvider, BinProvider,
BinProviderName, BinProviderName,
ProviderLookupDict,
AptProvider, AptProvider,
BrewProvider, BrewProvider,
EnvProvider, EnvProvider,
@ -25,18 +24,6 @@ from .base_hook import BaseHook, HookType
class BaseBinProvider(BaseHook, BinProvider): class BaseBinProvider(BaseHook, BinProvider):
hook_type: HookType = "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 # TODO: add install/load/load_or_install methods as abx.hookimpl methods
@ -52,9 +39,6 @@ class BaseBinProvider(BaseHook, BinProvider):
class BaseBinary(BaseHook, Binary): class BaseBinary(BaseHook, Binary):
hook_type: HookType = "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 @staticmethod
def symlink_to_lib(binary, bin_dir=None) -> None: def symlink_to_lib(binary, bin_dir=None) -> None:
bin_dir = bin_dir or CONSTANTS.LIB_BIN_DIR bin_dir = bin_dir or CONSTANTS.LIB_BIN_DIR
@ -82,13 +66,13 @@ class BaseBinary(BaseHook, Binary):
# get cached binary from db # get cached binary from db
try: try:
from machine.models import InstalledBinary 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) binary = InstalledBinary.load_from_db(installed_binary)
except Exception: except Exception:
# maybe we are not in a DATA dir so there is no db, fallback to reading from fs # 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) # (e.g. when archivebox version is run outside of a DATA dir)
binary = super().load(**kwargs) binary = super().load(**kwargs)
return binary return cast(Self, binary)
@validate_call @validate_call
def install(self, **kwargs) -> Self: def install(self, **kwargs) -> Self:

View file

@ -9,7 +9,7 @@ from pydantic import model_validator, TypeAdapter
from pydantic_settings import BaseSettings, SettingsConfigDict, PydanticBaseSettingsSource from pydantic_settings import BaseSettings, SettingsConfigDict, PydanticBaseSettingsSource
from pydantic_settings.sources import TomlConfigSettingsSource 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 import abx

View file

@ -22,17 +22,33 @@ def main(args: Optional[List[str]]=None, stdin: Optional[IO]=None, pwd: Optional
add_help=True, add_help=True,
formatter_class=SmartFormatter, formatter_class=SmartFormatter,
) )
# parser.add_argument( parser.add_argument(
# '--force', # '-f', '--binproviders', '-p',
# action='store_true', type=str,
# help='Overwrite any existing packages that conflict with the ones ArchiveBox is trying to install', 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 command = parser.parse_args(args or ()) # noqa
reject_stdin(__command__, stdin) reject_stdin(__command__, stdin)
install( install(
# force=command.force, # force=command.force,
out_dir=Path(pwd) if pwd else DATA_DIR, 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,
) )

View file

@ -27,6 +27,18 @@ def main(args: Optional[List[str]]=None, stdin: Optional[IO]=None, pwd: Optional
action='store_true', action='store_true',
help='Only print ArchiveBox version number and nothing else.', 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 ()) command = parser.parse_args(args or ())
reject_stdin(__command__, stdin) reject_stdin(__command__, stdin)
@ -40,6 +52,8 @@ def main(args: Optional[List[str]]=None, stdin: Optional[IO]=None, pwd: Optional
version( version(
quiet=command.quiet, quiet=command.quiet,
out_dir=Path(pwd) if pwd else DATA_DIR, 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,
) )

View file

@ -111,9 +111,9 @@ def binaries_list_view(request: HttpRequest, **kwargs) -> TableContext:
or config_value.lower().endswith(binary.name.lower()) or config_value.lower().endswith(binary.name.lower())
# or binary.name.lower().replace('-', '').replace('_', '') in str(config_value).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() # 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) # rows['Description'].append(binary.description)
return TableContext( return TableContext(
@ -153,7 +153,7 @@ def binary_detail_view(request: HttpRequest, key: str, **kwargs) -> ItemContext:
'binprovider': binary.loaded_binprovider, 'binprovider': binary.loaded_binprovider,
'abspath': binary.loaded_abspath, 'abspath': binary.loaded_abspath,
'version': binary.loaded_version, 'version': binary.loaded_version,
'overrides': obj_to_yaml(binary.provider_overrides), 'overrides': obj_to_yaml(binary.overrides),
'providers': obj_to_yaml(binary.binproviders_supported), 'providers': obj_to_yaml(binary.binproviders_supported),
}, },
"help_texts": { "help_texts": {

View file

@ -356,7 +356,7 @@ class InstalledBinary(ABIDModel, ModelWithHealthStats):
'sha256': self.sha256, 'sha256': self.sha256,
'loaded_binprovider': self.BINPROVIDER, 'loaded_binprovider': self.BINPROVIDER,
'binproviders_supported': self.BINARY.binproviders_supported, 'binproviders_supported': self.BINARY.binproviders_supported,
'provider_overrides': self.BINARY.provider_overrides, 'overrides': self.BINARY.overrides,
}) })
def load_fresh(self) -> BaseBinary: def load_fresh(self) -> BaseBinary:

View file

@ -179,7 +179,10 @@ def help(out_dir: Path=DATA_DIR) -> None:
@enforce_types @enforce_types
def version(quiet: bool=False, 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 the ArchiveBox version and dependency information"""
print(VERSION) print(VERSION)
@ -244,6 +247,14 @@ def version(quiet: bool=False,
if binary.name == 'archivebox': if binary.name == 'archivebox':
continue 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 err = None
try: try:
loaded_bin = binary.load() loaded_bin = binary.load()
@ -266,6 +277,9 @@ def version(quiet: bool=False,
for name, binprovider in reversed(list(settings.BINPROVIDERS.items())): for name, binprovider in reversed(list(settings.BINPROVIDERS.items())):
err = None 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 # 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]) 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()), '~') 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]' 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]' 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() if not (binaries or binproviders):
prnt('[deep_sky_blue3][i] Source-code locations:[/deep_sky_blue3]') # dont show source code / data dir info if we just want to get version info for a binary or binprovider
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()
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() prnt()
@ -986,7 +1003,7 @@ def list_folders(links: List[Link],
raise ValueError('Status not recognized.') raise ValueError('Status not recognized.')
@enforce_types @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""" """Automatically install all ArchiveBox dependencies and extras"""
# if running as root: # if running as root:
@ -1021,9 +1038,15 @@ def install(out_dir: Path=DATA_DIR) -> None:
print() 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}...') print(f'[+] Setting up package managers {package_manager_names}...')
for binprovider in reversed(list(settings.BINPROVIDERS.values())): for binprovider in reversed(list(settings.BINPROVIDERS.values())):
if binproviders and binprovider.name not in binproviders:
continue
try: try:
binprovider.setup() binprovider.setup()
except Exception: except Exception:
@ -1035,12 +1058,46 @@ def install(out_dir: Path=DATA_DIR) -> None:
print() print()
for binary in reversed(list(settings.BINARIES.values())): 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]...') print(f'[+] Detecting / Installing [yellow]{binary.name.ljust(22)}[/yellow] using [red]{providers}[/red]...')
try: try:
with SudoPermission(uid=0, fallback=True): with SudoPermission(uid=0, fallback=True):
# print(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'}))
binary.load_or_install(fresh=True).model_dump(exclude={'provider_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: if IS_ROOT:
with SudoPermission(uid=0): with SudoPermission(uid=0):
if ARCHIVEBOX_USER == 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()}"') os.system(f'chown -R {ARCHIVEBOX_USER} "{CONSTANTS.LIB_DIR.resolve()}"')
except Exception as e: except Exception as e:
print(f'[red]:cross_mark: Failed to install {binary.name} as user {ARCHIVEBOX_USER}: {e}[/red]') 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 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 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) raise SystemExit(proc.returncode)

View file

@ -3,11 +3,11 @@ __package__ = 'archivebox.plugins_auth.ldap'
import inspect import inspect
from typing import List, Dict from typing import List
from pathlib import Path from pathlib import Path
from pydantic import InstanceOf 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_plugin import BasePlugin
from abx.archivebox.base_hook import BaseHook from abx.archivebox.base_hook import BaseHook
@ -43,26 +43,26 @@ class LdapBinary(BaseBinary):
description: str = 'LDAP Authentication' description: str = 'LDAP Authentication'
binproviders_supported: List[InstanceOf[BaseBinProvider]] = [VENV_PIP_BINPROVIDER, SYS_PIP_BINPROVIDER, LIB_PIP_BINPROVIDER, apt] 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: { LIB_PIP_BINPROVIDER.name: {
"abspath": lambda: get_LDAP_LIB_path(LIB_SITE_PACKAGES), "abspath": lambda: get_LDAP_LIB_path(LIB_SITE_PACKAGES),
"version": lambda: get_LDAP_LIB_version(), "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: { VENV_PIP_BINPROVIDER.name: {
"abspath": lambda: get_LDAP_LIB_path(VENV_SITE_PACKAGES), "abspath": lambda: get_LDAP_LIB_path(VENV_SITE_PACKAGES),
"version": lambda: get_LDAP_LIB_version(), "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: { SYS_PIP_BINPROVIDER.name: {
"abspath": lambda: get_LDAP_LIB_path((*USER_SITE_PACKAGES, *SYS_SITE_PACKAGES)), "abspath": lambda: get_LDAP_LIB_path((*USER_SITE_PACKAGES, *SYS_SITE_PACKAGES)),
"version": lambda: get_LDAP_LIB_version(), "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: { apt.name: {
"abspath": lambda: get_LDAP_LIB_path(), "abspath": lambda: get_LDAP_LIB_path(),
"version": lambda: get_LDAP_LIB_version(), "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'],
}, },
} }

View file

@ -13,7 +13,7 @@ from pydantic_pkgr import (
BinProvider, BinProvider,
BinName, BinName,
BinProviderName, BinProviderName,
ProviderLookupDict, BinaryOverrides,
bin_abspath, bin_abspath,
) )
@ -204,15 +204,15 @@ class ChromeBinary(BaseBinary):
name: BinName = CHROME_CONFIG.CHROME_BINARY name: BinName = CHROME_CONFIG.CHROME_BINARY
binproviders_supported: List[InstanceOf[BinProvider]] = [PUPPETEER_BINPROVIDER, env, PLAYWRIGHT_BINPROVIDER] binproviders_supported: List[InstanceOf[BinProvider]] = [PUPPETEER_BINPROVIDER, env, PLAYWRIGHT_BINPROVIDER]
provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { overrides: BinaryOverrides = {
env.name: { env.name: {
'abspath': lambda: autodetect_system_chrome_install(PATH=env.PATH), # /usr/bin/google-chrome-stable 'abspath': lambda: autodetect_system_chrome_install(PATH=env.PATH), # /usr/bin/google-chrome-stable
}, },
PUPPETEER_BINPROVIDER.name: { 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: { PLAYWRIGHT_BINPROVIDER.name: {
'packages': lambda: ['chromium'], # playwright install chromium 'packages': ['chromium'], # playwright install chromium
}, },
} }

View file

@ -1,10 +1,10 @@
__package__ = 'plugins_extractor.mercury' __package__ = 'plugins_extractor.mercury'
from typing import List, Optional, Dict from typing import List, Optional
from pathlib import Path from pathlib import Path
from pydantic import InstanceOf, Field 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_plugin import BasePlugin, BaseHook
from abx.archivebox.base_configset import BaseConfigSet from abx.archivebox.base_configset import BaseConfigSet
@ -38,13 +38,13 @@ class MercuryBinary(BaseBinary):
name: BinName = MERCURY_CONFIG.MERCURY_BINARY name: BinName = MERCURY_CONFIG.MERCURY_BINARY
binproviders_supported: List[InstanceOf[BinProvider]] = [LIB_NPM_BINPROVIDER, SYS_NPM_BINPROVIDER, env] binproviders_supported: List[InstanceOf[BinProvider]] = [LIB_NPM_BINPROVIDER, SYS_NPM_BINPROVIDER, env]
provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { overrides: BinaryOverrides = {
LIB_NPM_BINPROVIDER.name: { LIB_NPM_BINPROVIDER.name: {
'packages': lambda: ['@postlight/parser@^2.2.3'], 'packages': ['@postlight/parser@^2.2.3'],
}, },
SYS_NPM_BINPROVIDER.name: { SYS_NPM_BINPROVIDER.name: {
'packages': lambda: ['@postlight/parser@^2.2.3'], 'packages': ['@postlight/parser@^2.2.3'],
'install': lambda: False, # never try to install things into global prefix 'install': lambda: None, # never try to install things into global prefix
}, },
env.name: { env.name: {
'version': lambda: '999.999.999' if bin_abspath('postlight-parser', PATH=env.PATH) else None, 'version': lambda: '999.999.999' if bin_abspath('postlight-parser', PATH=env.PATH) else None,

View file

@ -1,12 +1,12 @@
__package__ = 'archivebox.plugins_extractor.readability' __package__ = 'archivebox.plugins_extractor.readability'
from pathlib import Path from pathlib import Path
from typing import List, Dict, Optional from typing import List
# from typing_extensions import Self # from typing_extensions import Self
# Depends on other PyPI/vendor packages: # Depends on other PyPI/vendor packages:
from pydantic import InstanceOf, Field, validate_call from pydantic import InstanceOf, Field
from pydantic_pkgr import BinProvider, BinProviderName, ProviderLookupDict, BinName, ShallowBinary from pydantic_pkgr import BinProvider, BinaryOverrides, BinName
# Depends on other Django apps: # Depends on other Django apps:
from abx.archivebox.base_plugin import BasePlugin from abx.archivebox.base_plugin import BasePlugin
@ -39,23 +39,10 @@ class ReadabilityBinary(BaseBinary):
name: BinName = READABILITY_CONFIG.READABILITY_BINARY name: BinName = READABILITY_CONFIG.READABILITY_BINARY
binproviders_supported: List[InstanceOf[BinProvider]] = [LIB_NPM_BINPROVIDER, SYS_NPM_BINPROVIDER, env] 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: [READABILITY_PACKAGE_NAME]}, LIB_NPM_BINPROVIDER.name: {"packages": [READABILITY_PACKAGE_NAME]},
SYS_NPM_BINPROVIDER.name: {"packages": lambda: []}, # prevent modifying system global npm packages 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)

View file

@ -1,12 +1,12 @@
__package__ = 'archivebox.plugins_extractor.singlefile' __package__ = 'archivebox.plugins_extractor.singlefile'
from pathlib import Path from pathlib import Path
from typing import List, Dict, Optional from typing import List, Optional
# from typing_extensions import Self # from typing_extensions import Self
# Depends on other PyPI/vendor packages: # Depends on other PyPI/vendor packages:
from pydantic import InstanceOf, Field 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: # Depends on other Django apps:
from abx.archivebox.base_plugin import BasePlugin from abx.archivebox.base_plugin import BasePlugin
@ -45,22 +45,21 @@ class SinglefileBinary(BaseBinary):
name: BinName = SINGLEFILE_CONFIG.SINGLEFILE_BINARY name: BinName = SINGLEFILE_CONFIG.SINGLEFILE_BINARY
binproviders_supported: List[InstanceOf[BinProvider]] = [LIB_NPM_BINPROVIDER, SYS_NPM_BINPROVIDER, env] binproviders_supported: List[InstanceOf[BinProvider]] = [LIB_NPM_BINPROVIDER, SYS_NPM_BINPROVIDER, env]
provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { overrides: BinaryOverrides = {
LIB_NPM_BINPROVIDER.name: { LIB_NPM_BINPROVIDER.name: {
"abspath": lambda: "abspath": lambda:
bin_abspath(SINGLEFILE_CONFIG.SINGLEFILE_BINARY, PATH=LIB_NPM_BINPROVIDER.PATH) 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", PATH=LIB_NPM_BINPROVIDER.PATH)
or bin_abspath("single-file-node.js", PATH=LIB_NPM_BINPROVIDER.PATH), or bin_abspath("single-file-node.js", PATH=LIB_NPM_BINPROVIDER.PATH),
"packages": lambda: "packages": [f"single-file-cli@>={SINGLEFILE_MIN_VERSION} <{SINGLEFILE_MAX_VERSION}"],
[f"single-file-cli@>={SINGLEFILE_MIN_VERSION} <{SINGLEFILE_MAX_VERSION}"],
}, },
SYS_NPM_BINPROVIDER.name: { SYS_NPM_BINPROVIDER.name: {
"abspath": lambda: "abspath": lambda:
bin_abspath(SINGLEFILE_CONFIG.SINGLEFILE_BINARY, PATH=SYS_NPM_BINPROVIDER.PATH) 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", PATH=SYS_NPM_BINPROVIDER.PATH)
or bin_abspath("single-file-node.js", PATH=SYS_NPM_BINPROVIDER.PATH), or bin_abspath("single-file-node.js", PATH=SYS_NPM_BINPROVIDER.PATH),
"packages": lambda: "packages": [f"single-file-cli@>={SINGLEFILE_MIN_VERSION} <{SINGLEFILE_MAX_VERSION}"],
[], # prevent modifying system global npm packages "install": lambda: None,
}, },
env.name: { env.name: {
'abspath': lambda: 'abspath': lambda:
@ -69,18 +68,6 @@ class SinglefileBinary(BaseBinary):
or bin_abspath('single-file-node.js', PATH=env.PATH), 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() SINGLEFILE_BINARY = SinglefileBinary()

View file

@ -1,10 +1,10 @@
import sys import sys
from typing import List, Dict from typing import List
from subprocess import run, PIPE from subprocess import run, PIPE
from rich import print from rich import print
from pydantic import InstanceOf, Field, model_validator, AliasChoices 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_plugin import BasePlugin
from abx.archivebox.base_configset import BaseConfigSet from abx.archivebox.base_configset import BaseConfigSet
@ -54,10 +54,10 @@ class FfmpegBinary(BaseBinary):
name: BinName = 'ffmpeg' name: BinName = 'ffmpeg'
binproviders_supported: List[InstanceOf[BinProvider]] = [apt, brew, env] binproviders_supported: List[InstanceOf[BinProvider]] = [apt, brew, env]
provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { overrides: BinaryOverrides = {
'env': { 'env': {
# 'abspath': lambda: shutil.which('ffmpeg', PATH=env.PATH), # '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': { 'apt': {
# 'abspath': lambda: shutil.which('ffmpeg', PATH=apt.PATH), # 'abspath': lambda: shutil.which('ffmpeg', PATH=apt.PATH),

View file

@ -1,11 +1,11 @@
__package__ = 'archivebox.plugins_pkg.npm' __package__ = 'archivebox.plugins_pkg.npm'
from pathlib import Path from pathlib import Path
from typing import List, Optional, Dict from typing import List, Optional
from pydantic import InstanceOf, model_validator 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 from archivebox.config import DATA_DIR, CONSTANTS
@ -60,8 +60,8 @@ class NodeBinary(BaseBinary):
name: BinName = 'node' name: BinName = 'node'
binproviders_supported: List[InstanceOf[BinProvider]] = [apt, brew, env] binproviders_supported: List[InstanceOf[BinProvider]] = [apt, brew, env]
overrides: Dict[BinProviderName, ProviderLookupDict] = { overrides: BinaryOverrides = {
apt.name: {'packages': lambda c: ['nodejs']}, apt.name: {'packages': ['nodejs']},
} }
@ -72,7 +72,7 @@ class NpmBinary(BaseBinary):
name: BinName = 'npm' name: BinName = 'npm'
binproviders_supported: List[InstanceOf[BinProvider]] = [apt, brew, env] 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 apt.name: {'install': lambda: None}, # already installed when nodejs is installed
brew.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' name: BinName = 'npx'
binproviders_supported: List[InstanceOf[BinProvider]] = [apt, brew, env] 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 apt.name: {'install': lambda: None}, # already installed when nodejs is installed
brew.name: {'install': lambda: None}, # already installed when nodejs is installed brew.name: {'install': lambda: None}, # already installed when nodejs is installed
} }

View file

@ -4,14 +4,14 @@ import os
import sys import sys
import site import site
from pathlib import Path from pathlib import Path
from typing import List, Dict, Optional from typing import List, Optional
from pydantic import InstanceOf, Field, model_validator, validate_call from pydantic import InstanceOf, Field, model_validator, validate_call
import django import django
import django.db.backends.sqlite3.base import django.db.backends.sqlite3.base
from django.db.backends.sqlite3.base import Database as django_sqlite3 # type: ignore[import-type] 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 from archivebox.config import CONSTANTS, VERSION
@ -105,18 +105,18 @@ class ArchiveboxBinary(BaseBinary):
name: BinName = 'archivebox' name: BinName = 'archivebox'
binproviders_supported: List[InstanceOf[BinProvider]] = [VENV_PIP_BINPROVIDER, SYS_PIP_BINPROVIDER, apt, brew, env] binproviders_supported: List[InstanceOf[BinProvider]] = [VENV_PIP_BINPROVIDER, SYS_PIP_BINPROVIDER, apt, brew, env]
provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { overrides: BinaryOverrides = {
VENV_PIP_BINPROVIDER.name: {'packages': lambda: [], 'version': lambda: VERSION}, VENV_PIP_BINPROVIDER.name: {'packages': [], 'version': VERSION},
SYS_PIP_BINPROVIDER.name: {'packages': lambda: [], 'version': lambda: VERSION}, SYS_PIP_BINPROVIDER.name: {'packages': [], 'version': VERSION},
apt.name: {'packages': lambda: [], 'version': lambda: VERSION}, apt.name: {'packages': [], 'version': VERSION},
brew.name: {'packages': lambda: [], 'version': lambda: VERSION}, brew.name: {'packages': [], 'version': VERSION},
} }
@validate_call # @validate_call
def install(self, **kwargs): def install(self, **kwargs):
return self.load() # obviously it's already installed if we are running this ;) return self.load() # obviously it's already installed if we are running this ;)
@validate_call # @validate_call
def load_or_install(self, **kwargs): def load_or_install(self, **kwargs):
return self.load() # obviously it's already installed if we are running this ;) return self.load() # obviously it's already installed if we are running this ;)
@ -127,18 +127,18 @@ class PythonBinary(BaseBinary):
name: BinName = 'python' name: BinName = 'python'
binproviders_supported: List[InstanceOf[BinProvider]] = [VENV_PIP_BINPROVIDER, SYS_PIP_BINPROVIDER, apt, brew, env] 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: { SYS_PIP_BINPROVIDER.name: {
'abspath': lambda: sys.executable, 'abspath': sys.executable,
'version': lambda: '{}.{}.{}'.format(*sys.version_info[:3]), 'version': '{}.{}.{}'.format(*sys.version_info[:3]),
}, },
} }
@validate_call # @validate_call
def install(self, **kwargs): def install(self, **kwargs):
return self.load() # obviously it's already installed if we are running this ;) return self.load() # obviously it's already installed if we are running this ;)
@validate_call # @validate_call
def load_or_install(self, **kwargs): def load_or_install(self, **kwargs):
return self.load() # obviously it's already installed if we are running this ;) 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): class SqliteBinary(BaseBinary):
name: BinName = 'sqlite' name: BinName = 'sqlite'
binproviders_supported: List[InstanceOf[BaseBinProvider]] = Field(default=[VENV_PIP_BINPROVIDER, SYS_PIP_BINPROVIDER]) binproviders_supported: List[InstanceOf[BaseBinProvider]] = Field(default=[VENV_PIP_BINPROVIDER, SYS_PIP_BINPROVIDER])
provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { overrides: BinaryOverrides = {
VENV_PIP_BINPROVIDER.name: { VENV_PIP_BINPROVIDER.name: {
"abspath": lambda: LOADED_SQLITE_PATH if LOADED_SQLITE_FROM_VENV else None, "abspath": LOADED_SQLITE_PATH if LOADED_SQLITE_FROM_VENV else None,
"version": lambda: LOADED_SQLITE_VERSION if LOADED_SQLITE_FROM_VENV else None, "version": LOADED_SQLITE_VERSION if LOADED_SQLITE_FROM_VENV else None,
}, },
SYS_PIP_BINPROVIDER.name: { SYS_PIP_BINPROVIDER.name: {
"abspath": lambda: LOADED_SQLITE_PATH if not LOADED_SQLITE_FROM_VENV else None, "abspath": LOADED_SQLITE_PATH if not LOADED_SQLITE_FROM_VENV else None,
"version": lambda: LOADED_SQLITE_VERSION 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 return self
@validate_call # @validate_call
def install(self, **kwargs): def install(self, **kwargs):
return self.load() # obviously it's already installed if we are running this ;) return self.load() # obviously it's already installed if we are running this ;)
@validate_call # @validate_call
def load_or_install(self, **kwargs): def load_or_install(self, **kwargs):
return self.load() # obviously it's already installed if we are running this ;) return self.load() # obviously it's already installed if we are running this ;)
@ -196,22 +196,22 @@ class DjangoBinary(BaseBinary):
name: BinName = 'django' name: BinName = 'django'
binproviders_supported: List[InstanceOf[BaseBinProvider]] = Field(default=[VENV_PIP_BINPROVIDER, SYS_PIP_BINPROVIDER]) binproviders_supported: List[InstanceOf[BaseBinProvider]] = Field(default=[VENV_PIP_BINPROVIDER, SYS_PIP_BINPROVIDER])
provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { overrides: BinaryOverrides = {
VENV_PIP_BINPROVIDER.name: { VENV_PIP_BINPROVIDER.name: {
"abspath": lambda: LOADED_DJANGO_PATH if LOADED_DJANGO_FROM_VENV else None, "abspath": LOADED_DJANGO_PATH if LOADED_DJANGO_FROM_VENV else None,
"version": lambda: LOADED_DJANGO_VERSION if LOADED_DJANGO_FROM_VENV else None, "version": LOADED_DJANGO_VERSION if LOADED_DJANGO_FROM_VENV else None,
}, },
SYS_PIP_BINPROVIDER.name: { SYS_PIP_BINPROVIDER.name: {
"abspath": lambda: LOADED_DJANGO_PATH if not LOADED_DJANGO_FROM_VENV else None, "abspath": LOADED_DJANGO_PATH if not LOADED_DJANGO_FROM_VENV else None,
"version": lambda: LOADED_DJANGO_VERSION 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): def install(self, **kwargs):
return self.load() # obviously it's already installed if we are running this ;) return self.load() # obviously it's already installed if we are running this ;)
@validate_call # @validate_call
def load_or_install(self, **kwargs): def load_or_install(self, **kwargs):
return self.load() # obviously it's already installed if we are running this ;) return self.load() # obviously it's already installed if we are running this ;)
@ -221,11 +221,11 @@ class PipBinary(BaseBinary):
name: BinName = "pip" name: BinName = "pip"
binproviders_supported: List[InstanceOf[BinProvider]] = [LIB_PIP_BINPROVIDER, VENV_PIP_BINPROVIDER, SYS_PIP_BINPROVIDER, apt, brew, env] 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): def install(self, **kwargs):
return self.load() # obviously it's already installed if we are running this ;) return self.load() # obviously it's already installed if we are running this ;)
@validate_call # @validate_call
def load_or_install(self, **kwargs): def load_or_install(self, **kwargs):
return self.load() # obviously it's already installed if we are running this ;) return self.load() # obviously it's already installed if we are running this ;)

View file

@ -11,7 +11,7 @@ from pydantic_pkgr import (
BinName, BinName,
BinProvider, BinProvider,
BinProviderName, BinProviderName,
ProviderLookupDict, BinProviderOverrides,
InstallArgs, InstallArgs,
PATHStr, PATHStr,
HostBinPath, HostBinPath,
@ -66,15 +66,15 @@ class PlaywrightBinProvider(BaseBinProvider):
PATH: PATHStr = f"{CONSTANTS.LIB_BIN_DIR}:{DEFAULT_ENV_PATH}" 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 Path("~/Library/Caches/ms-playwright").expanduser() # macos playwright cache dir
if OPERATING_SYSTEM == "darwin" else if OPERATING_SYSTEM == "darwin" else
Path("~/.cache/ms-playwright").expanduser() # linux playwright cache dir Path("~/.cache/ms-playwright").expanduser() # linux playwright cache dir
) )
playwright_install_args: List[str] = ["install"] # --with-deps playwright_install_args: List[str] = ["install"] # --with-deps
packages_handler: ProviderLookupDict = Field(default={ packages_handler: BinProviderOverrides = Field(default={
"chrome": lambda: ["chromium"], "chrome": ["chromium"],
}, exclude=True) }, exclude=True)
_browser_abspaths: ClassVar[Dict[str, HostBinPath]] = {} _browser_abspaths: ClassVar[Dict[str, HostBinPath]] = {}
@ -104,9 +104,17 @@ class PlaywrightBinProvider(BaseBinProvider):
) )
# ~/Library/caches/ms-playwright/chromium-1097/chrome-linux/chromium # ~/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." assert bin_name == "chrome", "Only chrome is supported using the @puppeteer/browsers install method currently."
# already loaded, return abspath from cache # already loaded, return abspath from cache
@ -128,7 +136,7 @@ class PlaywrightBinProvider(BaseBinProvider):
return None 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""" """playwright install chrome"""
self.setup() self.setup()
assert bin_name == "chrome", "Only chrome is supported using the playwright install method currently." assert bin_name == "chrome", "Only chrome is supported using the playwright install method currently."
@ -137,7 +145,7 @@ class PlaywrightBinProvider(BaseBinProvider):
raise Exception( raise Exception(
f"{self.__class__.__name__} install method is not available on this host ({self.INSTALLER_BIN} not found in $PATH)" 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}') # print(f'[*] {self.__class__.__name__}: Installing {bin_name}: {self.INSTALLER_BIN_ABSPATH} install {packages}')
@ -155,7 +163,7 @@ class PlaywrightBinProvider(BaseBinProvider):
output_lines = [ output_lines = [
line for line in proc.stdout.strip().split('\n') line for line in proc.stdout.strip().split('\n')
if '/chrom' in line 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 'xdg-settings' not in line
and 'ffmpeg' not in line and 'ffmpeg' not in line
] ]

View file

@ -11,7 +11,7 @@ from pydantic_pkgr import (
BinProvider, BinProvider,
BinName, BinName,
BinProviderName, BinProviderName,
ProviderLookupDict, BinProviderOverrides,
InstallArgs, InstallArgs,
PATHStr, PATHStr,
HostBinPath, HostBinPath,
@ -65,10 +65,10 @@ class PuppeteerBinProvider(BaseBinProvider):
euid: Optional[int] = ARCHIVEBOX_USER 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)] 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": lambda:
['chrome@stable'], ['chrome@stable'],
}, exclude=True) }, exclude=True)
@ -90,7 +90,7 @@ class PuppeteerBinProvider(BaseBinProvider):
# /data/lib/browsers/chrome/linux-131.0.6730.0/chrome-linux64/chrome # /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")) 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.' assert bin_name == 'chrome', 'Only chrome is supported using the @puppeteer/browsers install method currently.'
# already loaded, return abspath from cache # already loaded, return abspath from cache
@ -106,7 +106,7 @@ class PuppeteerBinProvider(BaseBinProvider):
return None 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""" """npx @puppeteer/browsers install chrome@stable"""
self.setup() self.setup()
assert bin_name == 'chrome', 'Only chrome is supported using the @puppeteer/browsers install method currently.' assert bin_name == 'chrome', 'Only chrome is supported using the @puppeteer/browsers install method currently.'
@ -115,7 +115,7 @@ class PuppeteerBinProvider(BaseBinProvider):
raise Exception( raise Exception(
f"{self.__class__.__name__} install method is not available on this host ({self.INSTALLER_BIN} not found in $PATH)" 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}" 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}') # print(f'[*] {self.__class__.__name__}: Installing {bin_name}: {self.INSTALLER_BIN_ABSPATH} install {packages}')

View file

@ -3,12 +3,12 @@ __package__ = 'archivebox.plugins_search.ripgrep'
import re import re
from pathlib import Path from pathlib import Path
from subprocess import run from subprocess import run
from typing import List, Dict, Iterable from typing import List, Iterable
# from typing_extensions import Self # from typing_extensions import Self
# Depends on other PyPI/vendor packages: # Depends on other PyPI/vendor packages:
from pydantic import InstanceOf, Field 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: # Depends on other Django apps:
from abx.archivebox.base_plugin import BasePlugin from abx.archivebox.base_plugin import BasePlugin
@ -45,9 +45,9 @@ class RipgrepBinary(BaseBinary):
name: BinName = RIPGREP_CONFIG.RIPGREP_BINARY name: BinName = RIPGREP_CONFIG.RIPGREP_BINARY
binproviders_supported: List[InstanceOf[BinProvider]] = [apt, brew, env] binproviders_supported: List[InstanceOf[BinProvider]] = [apt, brew, env]
provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { overrides: BinaryOverrides = {
apt.name: {'packages': lambda: ['ripgrep']}, apt.name: {'packages': ['ripgrep']},
brew.name: {'packages': lambda: ['ripgrep']}, brew.name: {'packages': ['ripgrep']},
} }
RIPGREP_BINARY = RipgrepBinary() RIPGREP_BINARY = RipgrepBinary()

View file

@ -1,11 +1,11 @@
__package__ = 'archivebox.plugins_search.sonic' __package__ = 'archivebox.plugins_search.sonic'
import sys import sys
from typing import List, Dict, Generator, cast from typing import List, Generator, cast
# Depends on other PyPI/vendor packages: # Depends on other PyPI/vendor packages:
from pydantic import InstanceOf, Field, model_validator 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: # Depends on other Django apps:
from abx.archivebox.base_plugin import BasePlugin from abx.archivebox.base_plugin import BasePlugin
@ -55,9 +55,9 @@ class SonicBinary(BaseBinary):
name: BinName = SONIC_CONFIG.SONIC_BINARY name: BinName = SONIC_CONFIG.SONIC_BINARY
binproviders_supported: List[InstanceOf[BinProvider]] = [brew, env] # TODO: add cargo binproviders_supported: List[InstanceOf[BinProvider]] = [brew, env] # TODO: add cargo
provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { overrides: BinaryOverrides = {
brew.name: {'packages': lambda: ['sonic']}, brew.name: {'packages': ['sonic']},
# cargo.name: {'packages': lambda: ['sonic-server']}, # TODO: add cargo # 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 # TODO: add version checking over protocol? for when sonic backend is on remote server and binary is not installed locally

View file

@ -66,11 +66,11 @@ class SqliteftsConfig(BaseConfigSet):
# Only Python >= 3.11 supports sqlite3.Connection.getlimit(), # 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 # so fall back to the default if the API to get the real value isn't present
try: try:
limit_id = sqlite3.SQLITE_LIMIT_LENGTH limit_id = sqlite3.SQLITE_LIMIT_LENGTH # type: ignore[attr-defined]
if self.SQLITEFTS_SEPARATE_DATABASE: if self.SQLITEFTS_SEPARATE_DATABASE:
cursor = self.get_connection() cursor = self.get_connection()
return cursor.connection.getlimit(limit_id) return cursor.connection.getlimit(limit_id) # type: ignore[attr-defined]
else: else:
with database.temporary_connection() as cursor: # type: ignore[attr-defined] with database.temporary_connection() as cursor: # type: ignore[attr-defined]
return cursor.connection.getlimit(limit_id) return cursor.connection.getlimit(limit_id)

@ -1 +1 @@
Subproject commit 9d33c8c75ebfc7ea99e29fcc8126d081a8026cda Subproject commit ad3c0ca457951d4d0852b46020fc6365b75e5065

View file

@ -1,6 +1,6 @@
[project] [project]
name = "archivebox" name = "archivebox"
version = "0.8.5rc37" version = "0.8.5rc42"
requires-python = ">=3.10" requires-python = ">=3.10"
description = "Self-hosted internet archiving solution." description = "Self-hosted internet archiving solution."
authors = [{name = "Nick Sweeting", email = "pyproject.toml@archivebox.io"}] authors = [{name = "Nick Sweeting", email = "pyproject.toml@archivebox.io"}]
@ -78,7 +78,7 @@ dependencies = [
"django-taggit==1.3.0", "django-taggit==1.3.0",
"base32-crockford==0.3.0", "base32-crockford==0.3.0",
# "pocket@git+https://github.com/tapanpandita/pocket.git@v0.3.7", # "pocket@git+https://github.com/tapanpandita/pocket.git@v0.3.7",
"pydantic-pkgr>=0.4.24", "pydantic-pkgr>=0.5.2",
############# Plugin Dependencies ################ ############# Plugin Dependencies ################
"sonic-client>=1.0.0", "sonic-client>=1.0.0",
"yt-dlp>=2024.8.6", # for: media" "yt-dlp>=2024.8.6", # for: media"

View file

@ -119,7 +119,7 @@ executing==2.1.0
# via stack-data # via stack-data
feedparser==6.0.11 feedparser==6.0.11
# via archivebox (pyproject.toml) # via archivebox (pyproject.toml)
ftfy==6.2.3 ftfy==6.3.0
# via python-benedict # via python-benedict
h11==0.14.0 h11==0.14.0
# via httpcore # via httpcore
@ -168,6 +168,8 @@ pexpect==4.9.0
# via ipython # via ipython
phonenumbers==8.13.47 phonenumbers==8.13.47
# via python-benedict # via python-benedict
platformdirs==4.3.6
# via pydantic-pkgr
pluggy==1.5.0 pluggy==1.5.0
# via archivebox (pyproject.toml) # via archivebox (pyproject.toml)
prompt-toolkit==3.0.48 prompt-toolkit==3.0.48
@ -203,7 +205,7 @@ pydantic-core==2.23.4
# via # via
# pydantic # pydantic
# pydantic-pkgr # pydantic-pkgr
pydantic-pkgr==0.4.24 pydantic-pkgr==0.5.2
# via archivebox (pyproject.toml) # via archivebox (pyproject.toml)
pydantic-settings==2.5.2 pydantic-settings==2.5.2
# via archivebox (pyproject.toml) # via archivebox (pyproject.toml)
@ -332,5 +334,5 @@ xmltodict==0.14.1
# via python-benedict # via python-benedict
yt-dlp==2024.10.7 yt-dlp==2024.10.7
# via archivebox (pyproject.toml) # via archivebox (pyproject.toml)
zope-interface==7.0.3 zope-interface==7.1.0
# via twisted # via twisted

127
uv.lock
View file

@ -41,7 +41,7 @@ wheels = [
[[package]] [[package]]
name = "archivebox" name = "archivebox"
version = "0.8.5rc36" version = "0.8.5rc42"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "atomicwrites" }, { name = "atomicwrites" },
@ -148,7 +148,7 @@ requires-dist = [
{ name = "pluggy", specifier = ">=1.5.0" }, { name = "pluggy", specifier = ">=1.5.0" },
{ name = "psutil", specifier = ">=6.0.0" }, { name = "psutil", specifier = ">=6.0.0" },
{ name = "py-machineid", specifier = ">=0.6.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 = "pydantic-settings", specifier = ">=2.5.2" },
{ name = "python-benedict", extras = ["io", "parse"], specifier = ">=0.33.2" }, { name = "python-benedict", extras = ["io", "parse"], specifier = ">=0.33.2" },
{ name = "python-crontab", specifier = ">=3.2.0" }, { name = "python-crontab", specifier = ">=3.2.0" },
@ -965,14 +965,14 @@ wheels = [
[[package]] [[package]]
name = "ftfy" name = "ftfy"
version = "6.2.3" version = "6.3.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "wcwidth" }, { 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 = [ 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]] [[package]]
@ -1170,31 +1170,37 @@ wheels = [
[[package]] [[package]]
name = "libcst" name = "libcst"
version = "1.4.0" version = "1.5.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "pyyaml" }, { 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 = [ 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/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/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/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/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/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/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/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/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/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/b2/1b/1a2b83d208ea4d91b955be2a4e6b3cec0a647e6d6aa032d3b59f1585de31/libcst-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:2d47de16d105e7dd5f4e01a428d9f4dc1e71efd74f79766daf54528ce37f23c3", size = 2029201 }, { 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/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/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/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/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/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/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/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/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/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/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/6a/56/1c5a8385e9cc2d95d278cb8df48d11587c1c93b3b78c2edafd16b2bf11fa/libcst-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:9d0cc3c5a2a51fa7e1d579a828c0a2e46b2170024fd8b1a0691c8a52f3abb2d9", size = 2029195 }, { 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/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/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/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/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/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/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/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/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/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/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/04/32/7345f10a2dc728015920d689d5c1b8dc0232db321e172cdad2611e73c5b3/libcst-1.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:62e2682ee1567b6a89c91853865372bf34f178bfd237853d84df2b87b446e654", size = 2026263 }, { 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]] [[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 }, { 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]] [[package]]
name = "pluggy" name = "pluggy"
version = "1.5.0" version = "1.5.0"
@ -1834,16 +1849,17 @@ wheels = [
[[package]] [[package]]
name = "pydantic-pkgr" name = "pydantic-pkgr"
version = "0.4.24" version = "0.5.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "platformdirs" },
{ name = "pydantic" }, { name = "pydantic" },
{ name = "pydantic-core" }, { name = "pydantic-core" },
{ name = "typing-extensions" }, { 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 = [ 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]] [[package]]
@ -2301,7 +2317,7 @@ wheels = [
[[package]] [[package]]
name = "sphinx" name = "sphinx"
version = "8.0.2" version = "8.1.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "alabaster" }, { name = "alabaster" },
@ -2322,9 +2338,9 @@ dependencies = [
{ name = "sphinxcontrib-serializinghtml" }, { name = "sphinxcontrib-serializinghtml" },
{ name = "tomli", marker = "python_full_version < '3.11'" }, { 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 = [ 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]] [[package]]
@ -2829,32 +2845,35 @@ wheels = [
[[package]] [[package]]
name = "zope-interface" name = "zope-interface"
version = "7.0.3" version = "7.1.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "setuptools" }, { 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 = [ 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/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/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/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/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/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/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/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/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/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/f9/41/b126c98cc8a72b807cecab5ba483444e573ef9c7ca7f71449e96afd14d4d/zope.interface-7.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:11fa1382c3efb34abf16becff8cb214b0b2e3144057c90611621f2d186b7e1b7", size = 211591 }, { 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/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/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/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/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/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/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/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/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/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/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/6b/68/3937ac6cd0299694102d71721efd38fd1ceba7eaef20aefed3cdbb22527c/zope.interface-7.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:064ade95cb54c840647205987c7b557f75d2b2f7d1a84bfab4cf81822ef6e7d1", size = 211972 }, { 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/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/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/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/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/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/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/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/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/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/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/38/92/e9fe2a8cb53cffc73f923da84e50e0ee3a8d38a64bef6965428d5b5c4910/zope.interface-7.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:3de1d553ce72868b77a7e9d598c9bff6d3816ad2b4cc81c04f9d8914603814f3", size = 212064 }, { 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/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/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/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/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/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/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 },
] ]