From 560525eec08f58ee018f3c72b46040dd20e91426 Mon Sep 17 00:00:00 2001 From: mbeaulne Date: Tue, 24 Feb 2026 19:35:55 -0500 Subject: [PATCH] run ruff on codebase --- api_server_main.py | 11 +- cloud_pipelines_backend/api_router.py | 16 +- cloud_pipelines_backend/api_server_sql.py | 58 +++-- cloud_pipelines_backend/backend_types_sql.py | 2 +- .../component_library_api_server.py | 22 +- .../component_structures.py | 128 +++++------ cloud_pipelines_backend/database_ops.py | 2 +- .../instrumentation/contextual_logging.py | 7 +- .../instrumentation/otel_tracing.py | 4 +- .../launchers/container_component_utils.py | 10 +- .../launchers/huggingface_launchers.py | 28 +-- .../launchers/interfaces.py | 4 +- .../launchers/kubernetes_launchers.py | 35 ++- .../launchers/local_docker_launchers.py | 16 +- cloud_pipelines_backend/orchestrator_sql.py | 62 +++--- .../huggingface_repo_storage.py | 5 +- orchestrator_main.py | 9 +- pyproject.toml | 7 + start_local.py | 31 +-- tests/test_component_library_api_server.py | 71 +++--- .../test_context_aware_formatter_exception.py | 36 ++-- tests/test_instrumentation_logging_context.py | 11 +- ...test_instrumentation_request_middleware.py | 4 +- tests/test_request_id_concurrency.py | 1 + tests/test_secrets.py | 6 +- uv.lock | 202 +++++++++--------- 26 files changed, 385 insertions(+), 403 deletions(-) diff --git a/api_server_main.py b/api_server_main.py index d94cf22..641c8d5 100644 --- a/api_server_main.py +++ b/api_server_main.py @@ -3,11 +3,12 @@ import fastapi -from cloud_pipelines_backend import api_router -from cloud_pipelines_backend import database_ops -from cloud_pipelines_backend.instrumentation import api_tracing -from cloud_pipelines_backend.instrumentation import contextual_logging -from cloud_pipelines_backend.instrumentation import otel_tracing +from cloud_pipelines_backend import api_router, database_ops +from cloud_pipelines_backend.instrumentation import ( + api_tracing, + contextual_logging, + otel_tracing, +) app = fastapi.FastAPI( title="Cloud Pipelines API", diff --git a/cloud_pipelines_backend/api_router.py b/cloud_pipelines_backend/api_router.py index e4a331e..d40c817 100644 --- a/cloud_pipelines_backend/api_router.py +++ b/cloud_pipelines_backend/api_router.py @@ -1,20 +1,16 @@ -from collections import abc import contextlib import dataclasses import typing -import typing_extensions +from collections import abc import fastapi import sqlalchemy -from sqlalchemy import orm import starlette.types +import typing_extensions +from sqlalchemy import orm - -from . import api_server_sql -from . import backend_types_sql +from . import api_server_sql, backend_types_sql, database_ops, errors from . import component_library_api_server as components_api -from . import database_ops -from . import errors from .instrumentation import contextual_logging if typing.TYPE_CHECKING: @@ -137,7 +133,7 @@ def get_user_name( def user_has_admin_permission( user_details: typing.Annotated[UserDetails, get_user_details_dependency], ): - return user_details.permissions.get("admin") == True + return user_details.permissions.get("admin") user_has_admin_permission_dependency = fastapi.Depends(user_has_admin_permission) @@ -401,7 +397,7 @@ def get_current_user( permissions = list( permission for permission, is_granted in (user_details.permissions or {}).items() - if is_granted == True + if is_granted ) return GetUserResponse( id=user_details.name, diff --git a/cloud_pipelines_backend/api_server_sql.py b/cloud_pipelines_backend/api_server_sql.py index 9341db4..6877c36 100644 --- a/cloud_pipelines_backend/api_server_sql.py +++ b/cloud_pipelines_backend/api_server_sql.py @@ -4,12 +4,13 @@ import json import logging import typing -from typing import Any, Optional +from typing import Any if typing.TYPE_CHECKING: from cloud_pipelines.orchestration.storage_providers import ( interfaces as storage_provider_interfaces, ) + from .launchers import interfaces as launcher_interfaces @@ -26,8 +27,8 @@ def _get_current_time() -> datetime.datetime: return datetime.datetime.now(tz=datetime.timezone.utc) -from . import component_structures as structures from . import backend_types_sql as bts +from . import component_structures as structures from . import errors from .errors import ItemNotFoundError @@ -77,9 +78,9 @@ def create( session: orm.Session, root_task: structures.TaskSpec, # Component library to avoid repeating component specs inside task specs - components: Optional[list[structures.ComponentReference]] = None, + components: list[structures.ComponentReference] | None = None, # Arbitrary metadata. Can be used to specify user. - annotations: Optional[dict[str, Any]] = None, + annotations: dict[str, Any] | None = None, created_by: str | None = None, ) -> PipelineRunResponse: # TODO: Validate the pipeline spec @@ -89,7 +90,6 @@ def create( pipeline_name = root_task.component_ref.spec.name with session.begin(): - root_execution_node = _recursively_create_all_executions_and_artifacts_root( session=session, root_task_spec=root_task, @@ -201,7 +201,7 @@ def list( if value: where_clauses.append(bts.PipelineRun.created_by == value) else: - where_clauses.append(bts.PipelineRun.created_by == None) + where_clauses.append(bts.PipelineRun.created_by.is_(None)) else: raise NotImplementedError(f"Unsupported filter {filter}.") pipeline_runs = list( @@ -275,7 +275,7 @@ def _calculate_execution_status_stats( bts.ExecutionToAncestorExecutionLink.ancestor_execution_id == root_execution_id ) - .where(bts.ExecutionNode.container_execution_status != None) + .where(bts.ExecutionNode.container_execution_status.isnot(None)) .group_by( bts.ExecutionNode.container_execution_status, ) @@ -386,7 +386,7 @@ def _calculate_hash(s: str) -> str: def _split_type_spec( type_spec: structures.TypeSpecType | None, -) -> typing.Tuple[str | None, dict[str, Any] | None]: +) -> tuple[str | None, dict[str, Any] | None]: if type_spec is None: return None, None if isinstance(type_spec, str): @@ -520,7 +520,6 @@ class GetContainerExecutionLogResponse: class ExecutionNodesApiService_Sql: - def get(self, session: orm.Session, id: bts.IdType) -> GetExecutionInfoResponse: execution_node = session.get(bts.ExecutionNode, id) if execution_node is None: @@ -631,7 +630,7 @@ def get_graph_execution_state( ExecutionNode_Descendant.id == bts.ExecutionToAncestorExecutionLink.execution_id, ) - .where(ExecutionNode_Descendant.container_execution_status != None) + .where(ExecutionNode_Descendant.container_execution_status.isnot(None)) .group_by( ExecutionNode_Child.id, ExecutionNode_Descendant.container_execution_status, @@ -644,7 +643,7 @@ def get_graph_execution_state( sql.func.count().label("count"), ) .where(ExecutionNode_Child.parent_execution_id == id) - .where(ExecutionNode_Child.container_execution_status != None) + .where(ExecutionNode_Child.container_execution_status.isnot(None)) .group_by( ExecutionNode_Child.id, ExecutionNode_Child.container_execution_status, @@ -790,7 +789,7 @@ def get_container_execution_log( log_uri=container_execution.log_uri, storage_provider=storage_provider, ) - except: + except Exception: # Do not raise exception if the execution is in SYSTEM_ERROR state # We want to return the system error exception. if ( @@ -801,11 +800,11 @@ def get_container_execution_log( elif container_execution.status == bts.ContainerExecutionStatus.RUNNING: if not container_launcher: raise ApiServiceError( - f"Reading log of an unfinished container requires `container_launcher`." + "Reading log of an unfinished container requires `container_launcher`." ) if not container_execution.launcher_data: raise ApiServiceError( - f"Execution does not have container launcher data." + "Execution does not have container launcher data." ) launched_container = ( @@ -833,11 +832,11 @@ def stream_container_execution_log( container_execution = execution.container_execution if not container_execution: raise ApiServiceError( - f"Execution does not have container execution information." + "Execution does not have container execution information." ) if not container_execution.launcher_data: raise ApiServiceError( - f"Execution does not have container launcher information." + "Execution does not have container launcher information." ) if container_execution.status == bts.ContainerExecutionStatus.RUNNING: launched_container = ( @@ -882,7 +881,7 @@ def _read_container_execution_log_from_uri( if "://" not in log_uri: # Consider the URL to be an absolute local path (`/path` or `C:\path` or `C:/path`) - with open(log_uri, "r") as reader: + with open(log_uri) as reader: return reader.read() elif log_uri.startswith("gs://"): # TODO: Switch to using storage providers. @@ -966,7 +965,6 @@ class GetArtifactSignedUrlResponse: class ArtifactNodesApiService_Sql: - def get(self, session: orm.Session, id: bts.IdType) -> GetArtifactInfoResponse: artifact_node = session.get(bts.ArtifactNode, id) if artifact_node is None: @@ -990,14 +988,14 @@ def get_signed_artifact_url( if not artifact_data.uri: raise ValueError(f"Artifact node with {id=} does not have artifact URI.") if artifact_data.is_dir: - raise ValueError(f"Cannot generate signer URL for a directory artifact.") + raise ValueError("Cannot generate signer URL for a directory artifact.") if not artifact_data.uri.startswith("gs://"): raise ValueError( f"The get_signed_artifact_url method only supports Google Cloud Storage URIs, but got {artifact_data.uri=}." ) - from google.cloud import storage from google import auth + from google.cloud import storage # Avoiding error: "you need a private key to sign credentials." # "the credentials you are currently using just contains a token. @@ -1040,7 +1038,6 @@ class ListSecretsResponse: class SecretsApiService: - def create_secret( self, *, @@ -1053,7 +1050,7 @@ def create_secret( ) -> SecretInfoResponse: secret_name = secret_name.strip() if not secret_name: - raise ApiServiceError(f"Secret name must not be empty.") + raise ApiServiceError("Secret name must not be empty.") return self._create_or_update_secret( session=session, user_id=user_id, @@ -1166,9 +1163,7 @@ def list_secrets( # No. Decided to first do topological sort and then 1-stage generation. -_ArtifactNodeOrDynamicDataType = typing.Union[ - bts.ArtifactNode, structures.DynamicDataArgument -] +_ArtifactNodeOrDynamicDataType = bts.ArtifactNode | structures.DynamicDataArgument def _recursively_create_all_executions_and_artifacts_root( @@ -1262,7 +1257,6 @@ def _recursively_create_all_executions_and_artifacts( # FIX: Handle ExecutionNode.constant_arguments # We do not touch root_task_spec.arguments. We use graph_input_artifact_nodes instead - constant_input_artifacts: dict[str, bts.ArtifactData] = {} input_artifact_nodes = dict(input_artifact_nodes) for input_spec in root_component_spec.inputs or []: input_artifact_node = input_artifact_nodes.get(input_spec.name) @@ -1490,13 +1484,11 @@ def _toposort_tasks( dependencies[argument.task_output.task_id] = True if argument.task_output.task_id not in tasks: raise TypeError( - 'Argument "{}" references non-existing task.'.format( - argument - ) + f'Argument "{argument}" references non-existing task.' ) # Topologically sorting tasks to detect cycles - task_dependents = {k: {} for k in task_dependencies.keys()} + task_dependents = {k: {} for k in task_dependencies} for task_id, dependencies in task_dependencies.items(): for dependency in dependencies: task_dependents[dependency][task_id] = True @@ -1515,7 +1507,7 @@ def process_task(task_id): task_number_of_remaining_dependencies[dependent_task] -= 1 process_task(dependent_task) - for task_id in task_dependencies.keys(): + for task_id in task_dependencies: process_task(task_id) if len(sorted_tasks) != len(task_dependencies): tasks_with_unsatisfied_dependencies = { @@ -1526,9 +1518,7 @@ def process_task(task_id): key=lambda task_id: tasks_with_unsatisfied_dependencies[task_id], ) raise ValueError( - 'Task "{}" has cyclical dependency.'.format( - task_with_minimal_number_of_unsatisfied_dependencies - ) + f'Task "{task_with_minimal_number_of_unsatisfied_dependencies}" has cyclical dependency.' ) return sorted_tasks diff --git a/cloud_pipelines_backend/backend_types_sql.py b/cloud_pipelines_backend/backend_types_sql.py index 04a8bf5..f642383 100644 --- a/cloud_pipelines_backend/backend_types_sql.py +++ b/cloud_pipelines_backend/backend_types_sql.py @@ -55,7 +55,7 @@ def generate_unique_id() -> str: nanoseconds = time.time_ns() milliseconds = nanoseconds // 1_000_000 - return ("%012x" % milliseconds) + random_bytes.hex() + return f"{milliseconds:012x}" + random_bytes.hex() id_column = orm.mapped_column( diff --git a/cloud_pipelines_backend/component_library_api_server.py b/cloud_pipelines_backend/component_library_api_server.py index f316ec1..94560a9 100644 --- a/cloud_pipelines_backend/component_library_api_server.py +++ b/cloud_pipelines_backend/component_library_api_server.py @@ -4,13 +4,12 @@ import hashlib from typing import Any -import yaml import sqlalchemy as sql +import yaml from sqlalchemy import orm from . import backend_types_sql as bts -from . import errors -from . import component_structures +from . import component_structures, errors def calculate_digest_for_component_text(text: str) -> str: @@ -192,7 +191,7 @@ def list( if digest: query = query.filter(PublishedComponentRow.digest == digest) if not include_deprecated: - query = query.filter(PublishedComponentRow.deprecated == False) + query = query.filter(PublishedComponentRow.deprecated.is_(False)) if name_substring: query = query.filter( PublishedComponentRow.name.icontains(name_substring, autoescape=True) @@ -243,7 +242,7 @@ def publish( digest = component_ref.digest if not (digest and component_text): raise ValueError( - f"Component text is missing, cannot get component by digest (or digest is missing). Currently we cannot get component by URL for security reasons (you can get text from url yourself before publishing)." + "Component text is missing, cannot get component by digest (or digest is missing). Currently we cannot get component by URL for security reasons (you can get text from url yourself before publishing)." ) component_spec = load_component_spec_from_text_and_validate(component_text) @@ -372,7 +371,7 @@ def make_empty_user_library(user_name: str) -> "ComponentLibraryRow": user_name = id.partition(":")[2] else: raise ValueError( - f"make_empty_user_library only supports user component libraries." + "make_empty_user_library only supports user component libraries." ) name = f"{user_name} components" @@ -443,7 +442,7 @@ def list( # TODO: Implement filtering by user, URL # TODO: Implement visibility/access control query = sql.select(ComponentLibraryRow).filter( - ComponentLibraryRow.hide_from_search == False + ComponentLibraryRow.hide_from_search.is_(False) ) if name_substring: query = query.filter( @@ -520,14 +519,12 @@ def _prepare_new_library_and_publish_components( service = PublishedComponentService() session.rollback() component_count = 0 - for ( - component_ref - ) in ComponentLibraryService._recursively_iterate_over_all_component_refs_in_library_folder( + for component_ref in ComponentLibraryService._recursively_iterate_over_all_component_refs_in_library_folder( library.root_folder ): if not component_ref.text: # TODO: Support publishing component from URL - raise ValueError(f"Currently every library component must have text.") + raise ValueError("Currently every library component must have text.") digest = calculate_digest_for_component_text(component_ref.text) if publish_components: try: @@ -669,8 +666,7 @@ def _recursively_iterate_over_all_component_refs_in_library_folder( yield from ComponentLibraryService._recursively_iterate_over_all_component_refs_in_library_folder( child_folder ) - for component_ref in library_folder.components or []: - yield component_ref + yield from library_folder.components or [] ### UserService diff --git a/cloud_pipelines_backend/component_structures.py b/cloud_pipelines_backend/component_structures.py index 1bc4550..1a6f287 100644 --- a/cloud_pipelines_backend/component_structures.py +++ b/cloud_pipelines_backend/component_structures.py @@ -46,20 +46,17 @@ ] import dataclasses -from collections import OrderedDict - -from typing import Any, Dict, List, Mapping, Optional, Sequence, Union +from collections.abc import Mapping +from typing import Any, Union import pydantic import pydantic.alias_generators -from pydantic.dataclasses import dataclass as pydantic_dataclasses - # PrimitiveTypes = Union[str, int, float, bool] PrimitiveTypes = str -TypeSpecType = Union[str, Dict, List] +TypeSpecType = str | dict | list # class _BaseModel1(pydantic.BaseModel): @@ -105,11 +102,11 @@ class InputSpec(_BaseModel): """Describes the component input specification""" name: str - type: Optional[TypeSpecType] = None - description: Optional[str] = None - default: Optional[PrimitiveTypes] = None - optional: Optional[bool] = False - annotations: Optional[Dict[str, Any]] = None + type: TypeSpecType | None = None + description: str | None = None + default: PrimitiveTypes | None = None + optional: bool | None = False + annotations: dict[str, Any] | None = None @dataclasses.dataclass @@ -117,9 +114,9 @@ class OutputSpec(_BaseModel): """Describes the component output specification""" name: str - type: Optional[TypeSpecType] = None - description: Optional[str] = None - annotations: Optional[Dict[str, Any]] = None + type: TypeSpecType | None = None + description: str | None = None + annotations: dict[str, Any] | None = None @dataclasses.dataclass @@ -130,8 +127,10 @@ class InputValuePlaceholder(_BaseModel): # Non-standard attr names "input_name": "inputValue", } __pydantic_config__ = _default_pydantic_config | pydantic.ConfigDict( - alias_generator=lambda s: InputValuePlaceholder._serialized_names.get(s) - or pydantic.alias_generators.to_camel(s), + alias_generator=lambda s: ( + InputValuePlaceholder._serialized_names.get(s) + or pydantic.alias_generators.to_camel(s) + ), ) input_name: str @@ -145,8 +144,10 @@ class InputPathPlaceholder(_BaseModel): # Non-standard attr names "input_name": "inputPath", } __pydantic_config__ = _default_pydantic_config | pydantic.ConfigDict( - alias_generator=lambda s: InputPathPlaceholder._serialized_names.get(s) - or pydantic.alias_generators.to_camel(s), + alias_generator=lambda s: ( + InputPathPlaceholder._serialized_names.get(s) + or pydantic.alias_generators.to_camel(s) + ), ) input_name: str @@ -159,8 +160,10 @@ class OutputPathPlaceholder(_BaseModel): # Non-standard attr names "output_name": "outputPath", } __pydantic_config__ = _default_pydantic_config | pydantic.ConfigDict( - alias_generator=lambda s: OutputPathPlaceholder._serialized_names.get(s) - or pydantic.alias_generators.to_camel(s), + alias_generator=lambda s: ( + OutputPathPlaceholder._serialized_names.get(s) + or pydantic.alias_generators.to_camel(s) + ), ) output_name: str @@ -179,7 +182,7 @@ class OutputPathPlaceholder(_BaseModel): # Non-standard attr names class ConcatPlaceholder(_BaseModel): """Represents the command-line argument placeholder that will be replaced at run-time by the concatenated values of its items.""" - concat: List[CommandlineArgumentType] + concat: list[CommandlineArgumentType] @dataclasses.dataclass @@ -189,7 +192,7 @@ class IsPresentPlaceholder(_BaseModel): is_present: str # Input name that must be present -IfConditionArgumentType = Union[bool, str, IsPresentPlaceholder, InputValuePlaceholder] +IfConditionArgumentType = bool | str | IsPresentPlaceholder | InputValuePlaceholder @dataclasses.dataclass @@ -202,13 +205,15 @@ class IfPlaceholderStructure(_BaseModel): # Non-standard attr names "else_value": "else", } __pydantic_config__ = _default_pydantic_config | pydantic.ConfigDict( - alias_generator=lambda s: IfPlaceholderStructure._serialized_names.get(s) - or pydantic.alias_generators.to_camel(s), + alias_generator=lambda s: ( + IfPlaceholderStructure._serialized_names.get(s) + or pydantic.alias_generators.to_camel(s) + ), ) condition: IfConditionArgumentType - then_value: List[CommandlineArgumentType] - else_value: Optional[List[CommandlineArgumentType]] = None + then_value: list[CommandlineArgumentType] + else_value: list[CommandlineArgumentType] | None = None @dataclasses.dataclass @@ -219,8 +224,10 @@ class IfPlaceholder(_BaseModel): # Non-standard attr names "if_structure": "if", } __pydantic_config__ = _default_pydantic_config | pydantic.ConfigDict( - alias_generator=lambda s: IfPlaceholder._serialized_names.get(s) - or pydantic.alias_generators.to_camel(s), + alias_generator=lambda s: ( + IfPlaceholder._serialized_names.get(s) + or pydantic.alias_generators.to_camel(s) + ), ) if_structure: IfPlaceholderStructure @@ -231,9 +238,9 @@ class ContainerSpec(_BaseModel): """Describes the container component implementation.""" image: str - command: Optional[List[CommandlineArgumentType]] = None - args: Optional[List[CommandlineArgumentType]] = None - env: Optional[Mapping[str, str]] = None + command: list[CommandlineArgumentType] | None = None + args: list[CommandlineArgumentType] | None = None + env: Mapping[str, str] | None = None @dataclasses.dataclass @@ -248,20 +255,20 @@ class ContainerImplementation(_BaseModel): @dataclasses.dataclass class MetadataSpec(_BaseModel): - annotations: Optional[Dict[str, str]] = None - labels: Optional[Dict[str, str]] = None + annotations: dict[str, str] | None = None + labels: dict[str, str] | None = None @dataclasses.dataclass class ComponentSpec(_BaseModel): """Component specification. Describes the metadata (name, description, annotations and labels), the interface (inputs and outputs) and the implementation of the component.""" - name: Optional[str] = None # ? Move to metadata? - description: Optional[str] = None # ? Move to metadata? - metadata: Optional[MetadataSpec] = None - inputs: Optional[List[InputSpec]] = None - outputs: Optional[List[OutputSpec]] = None - implementation: Optional[ImplementationType] = None + name: str | None = None # ? Move to metadata? + description: str | None = None # ? Move to metadata? + metadata: MetadataSpec | None = None + inputs: list[InputSpec] | None = None + outputs: list[OutputSpec] | None = None + implementation: ImplementationType | None = None def __post_init__(self): # _component_spec_post_init(self) @@ -272,12 +279,12 @@ def __post_init__(self): class ComponentReference(_BaseModel): """Component reference. Contains information that can be used to locate and load a component by name, digest or URL""" - name: Optional[str] = None - digest: Optional[str] = None - tag: Optional[str] = None - url: Optional[str] = None - spec: Optional[ComponentSpec] = None - text: Optional[str] = None + name: str | None = None + digest: str | None = None + tag: str | None = None + url: str | None = None + spec: ComponentSpec | None = None + text: str | None = None def __post_init__(self) -> None: if not any([self.name, self.digest, self.url, self.spec, self.text]): @@ -289,9 +296,7 @@ class GraphInputReference(_BaseModel): """References the input of the graph (the scope is a single graph).""" input_name: str - type: Optional[TypeSpecType] = ( - None # Can be used to override the reference data type - ) + type: TypeSpecType | None = None # Can be used to override the reference data type @dataclasses.dataclass @@ -334,30 +339,25 @@ class DynamicDataArgument(_BaseModel): dynamic_data: DynamicDataReference -ArgumentType = Union[ - PrimitiveTypes, GraphInputArgument, TaskOutputArgument, DynamicDataArgument -] +ArgumentType = ( + PrimitiveTypes | GraphInputArgument | TaskOutputArgument | DynamicDataArgument +) @dataclasses.dataclass class RetryStrategySpec(_BaseModel): - max_retries: int @dataclasses.dataclass class CachingStrategySpec(_BaseModel): - - max_cache_staleness: Optional[str] = ( - None # RFC3339 compliant duration: P30DT1H22M3S - ) + max_cache_staleness: str | None = None # RFC3339 compliant duration: P30DT1H22M3S @dataclasses.dataclass class ExecutionOptionsSpec(_BaseModel): - - retry_strategy: Optional[RetryStrategySpec] = None - caching_strategy: Optional[CachingStrategySpec] = None + retry_strategy: RetryStrategySpec | None = None + caching_strategy: CachingStrategySpec | None = None @dataclasses.dataclass @@ -365,10 +365,10 @@ class TaskSpec(_BaseModel): """Task specification. Task is a "configured" component - a component supplied with arguments and other applied configuration changes.""" component_ref: ComponentReference - arguments: Optional[Mapping[str, ArgumentType]] = None - is_enabled: Optional[ArgumentType] = None - execution_options: Optional[ExecutionOptionsSpec] = None - annotations: Optional[Dict[str, Any]] = None + arguments: Mapping[str, ArgumentType] | None = None + is_enabled: ArgumentType | None = None + execution_options: ExecutionOptionsSpec | None = None + annotations: dict[str, Any] | None = None @dataclasses.dataclass @@ -376,7 +376,7 @@ class GraphSpec(_BaseModel): """Describes the graph component implementation. It represents a graph of component tasks connected to the upstream sources of data using the argument specifications. It also describes the sources of graph output values.""" tasks: Mapping[str, TaskSpec] - output_values: Optional[Mapping[str, ArgumentType]] = None + output_values: Mapping[str, ArgumentType] | None = None def __post_init__(self): # _graph_spec_post_init(self) diff --git a/cloud_pipelines_backend/database_ops.py b/cloud_pipelines_backend/database_ops.py index 3d94ed1..eb47855 100644 --- a/cloud_pipelines_backend/database_ops.py +++ b/cloud_pipelines_backend/database_ops.py @@ -24,7 +24,7 @@ def create_db_engine( ) -> sqlalchemy.Engine: if database_uri.startswith("mysql://"): try: - import MySQLdb + import MySQLdb # noqa: F401 except ImportError: # Using PyMySQL instead of missing MySQLdb database_uri = database_uri.replace("mysql://", "mysql+pymysql://") diff --git a/cloud_pipelines_backend/instrumentation/contextual_logging.py b/cloud_pipelines_backend/instrumentation/contextual_logging.py index d9414ab..171b07f 100644 --- a/cloud_pipelines_backend/instrumentation/contextual_logging.py +++ b/cloud_pipelines_backend/instrumentation/contextual_logging.py @@ -23,11 +23,12 @@ import contextvars from contextlib import contextmanager -from typing import Any, Optional +from typing import Any # Single context variable to store all metadata as a dictionary _context_metadata: contextvars.ContextVar[dict[str, Any]] = contextvars.ContextVar( - "context_metadata", default={} + "context_metadata", + default={}, # noqa: B039 ) @@ -57,7 +58,7 @@ def delete_context_metadata(key: str) -> None: _context_metadata.set(metadata) -def get_context_metadata(key: str) -> Optional[Any]: +def get_context_metadata(key: str) -> Any | None: """Get a metadata value from the current context. Args: diff --git a/cloud_pipelines_backend/instrumentation/otel_tracing.py b/cloud_pipelines_backend/instrumentation/otel_tracing.py index 08f9afa..c2a2191 100644 --- a/cloud_pipelines_backend/instrumentation/otel_tracing.py +++ b/cloud_pipelines_backend/instrumentation/otel_tracing.py @@ -5,10 +5,10 @@ to an OpenTelemetry collector endpoint. """ -import fastapi import logging import os +import fastapi from opentelemetry import trace from opentelemetry.exporter.otlp.proto.grpc import ( trace_exporter as otel_grpc_trace_exporter, @@ -79,7 +79,7 @@ def setup_api_tracing(app: fastapi.FastAPI) -> None: # https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/fastapi/fastapi.html otel_fastapi.FastAPIInstrumentor.instrument_app(app) - _logger.info(f"OpenTelemetry tracing configured successfully.") + _logger.info("OpenTelemetry tracing configured successfully.") except Exception as e: _logger.exception(f"Failed to configure OpenTelemetry tracing: {e}") diff --git a/cloud_pipelines_backend/launchers/container_component_utils.py b/cloud_pipelines_backend/launchers/container_component_utils.py index 7e1c533..09436f1 100644 --- a/cloud_pipelines_backend/launchers/container_component_utils.py +++ b/cloud_pipelines_backend/launchers/container_component_utils.py @@ -1,6 +1,6 @@ import dataclasses import typing -from typing import List, Mapping, Sequence, Union +from collections.abc import Mapping, Sequence from .. import component_structures as structures @@ -38,7 +38,7 @@ def resolve_container_command_line( input_paths = dict() inputs_consumed_by_value = {} - def expand_command_part(arg) -> Union[str, List[str], None]: + def expand_command_part(arg) -> str | list[str] | None: if arg is None: return None if isinstance(arg, (str, int, float, bool)): @@ -74,9 +74,7 @@ def expand_command_part(arg) -> Union[str, List[str], None]: if arg.output_name in output_paths: if output_paths[output_name] != output_filename: raise ValueError( - "Conflicting output files specified for port {}: {} and {}".format( - output_name, output_paths[output_name], output_filename - ) + f"Conflicting output files specified for port {output_name}: {output_paths[output_name]} and {output_filename}" ) else: output_paths[output_name] = output_filename @@ -107,7 +105,7 @@ def expand_command_part(arg) -> Union[str, List[str], None]: argument_is_present = input_name in provided_input_names return str(argument_is_present) else: - raise TypeError("Unrecognized argument type: {}".format(arg)) + raise TypeError(f"Unrecognized argument type: {arg}") def expand_argument_list(argument_list): expanded_list = [] diff --git a/cloud_pipelines_backend/launchers/huggingface_launchers.py b/cloud_pipelines_backend/launchers/huggingface_launchers.py index 9ff1ac0..5a0adcd 100644 --- a/cloud_pipelines_backend/launchers/huggingface_launchers.py +++ b/cloud_pipelines_backend/launchers/huggingface_launchers.py @@ -4,16 +4,15 @@ import logging import pathlib import typing -from typing import Any, Optional +from typing import Any import huggingface_hub from cloud_pipelines.orchestration.launchers import naming_utils -from ..storage_providers import huggingface_repo_storage -from .. import component_structures as structures -from . import container_component_utils -from . import interfaces +from .. import component_structures as structures +from ..storage_providers import huggingface_repo_storage +from . import container_component_utils, interfaces _logger = logging.getLogger(__name__) @@ -30,11 +29,11 @@ class HuggingFaceJobsContainerLauncher( def __init__( self, *, - client: Optional[huggingface_hub.HfApi] = None, - namespace: Optional[str] = None, - hf_token: Optional[str] = None, - hf_job_token: Optional[str] = None, - job_timeout: Optional[int | float | str] = None, + client: huggingface_hub.HfApi | None = None, + namespace: str | None = None, + hf_token: str | None = None, + hf_job_token: str | None = None, + job_timeout: int | float | str | None = None, ): # The HF Jobs that we launch need token to write the output artifacts and logs hf_token = hf_token or huggingface_hub.get_token() @@ -354,15 +353,16 @@ def status(self) -> interfaces.ContainerStatus: return interfaces.ContainerStatus.RUNNING elif status_str == huggingface_hub.JobStage.COMPLETED: return interfaces.ContainerStatus.SUCCEEDED - elif status_str == huggingface_hub.JobStage.ERROR: - return interfaces.ContainerStatus.FAILED - elif status_str == huggingface_hub.JobStage.CANCELED: + elif ( + status_str == huggingface_hub.JobStage.ERROR + or status_str == huggingface_hub.JobStage.CANCELED + ): return interfaces.ContainerStatus.FAILED else: # "DELETED" return interfaces.ContainerStatus.ERROR @property - def exit_code(self) -> Optional[int]: + def exit_code(self) -> int | None: # HF Jobs do not provide exit code if not self.has_ended: return None diff --git a/cloud_pipelines_backend/launchers/interfaces.py b/cloud_pipelines_backend/launchers/interfaces.py index f5807f9..0ad1946 100644 --- a/cloud_pipelines_backend/launchers/interfaces.py +++ b/cloud_pipelines_backend/launchers/interfaces.py @@ -4,7 +4,6 @@ import dataclasses import datetime import enum - import typing from typing import Any @@ -71,8 +70,7 @@ class ContainerStatus(str, enum.Enum): ERROR = "ERROR" -class LaunchedContainer(abc.ABC): - +class LaunchedContainer(abc.ABC): # noqa: B024 # @classmethod # def get(cls: typing.Type[_TLaunchedContainer]) -> _TLaunchedContainer: # raise NotImplementedError() diff --git a/cloud_pipelines_backend/launchers/kubernetes_launchers.py b/cloud_pipelines_backend/launchers/kubernetes_launchers.py index 5287c4a..953a1c8 100644 --- a/cloud_pipelines_backend/launchers/kubernetes_launchers.py +++ b/cloud_pipelines_backend/launchers/kubernetes_launchers.py @@ -7,7 +7,7 @@ import os import pathlib import typing -from typing import Any, Optional +from typing import Any from kubernetes import client as k8s_client_lib from kubernetes import watch as k8s_watch_lib @@ -17,9 +17,9 @@ interfaces as storage_provider_interfaces, ) from cloud_pipelines.orchestration.storage_providers import local_storage + from .. import component_structures as structures -from . import container_component_utils -from . import interfaces +from . import container_component_utils, interfaces if typing.TYPE_CHECKING: from google.cloud import storage @@ -417,7 +417,7 @@ def launch_container_task( output_uris: dict[str, str], log_uri: str, annotations: dict[str, Any] | None = None, - ) -> "LaunchedKubernetesContainer": + ) -> LaunchedKubernetesContainer: namespace = self._choose_namespace(annotations=annotations) pod = self._prepare_kubernetes_pod( @@ -466,7 +466,7 @@ def launch_container_task( def get_refreshed_launched_container_from_dict( self, launched_container_dict: dict - ) -> "LaunchedKubernetesContainer": + ) -> LaunchedKubernetesContainer: launched_container = LaunchedKubernetesContainer.from_dict( launched_container_dict, launcher=self ) @@ -474,7 +474,7 @@ def get_refreshed_launched_container_from_dict( def deserialize_launched_container_from_dict( self, launched_container_dict: dict - ) -> "LaunchedKubernetesContainer": + ) -> LaunchedKubernetesContainer: launched_container = LaunchedKubernetesContainer.from_dict( launched_container_dict, launcher=self ) @@ -582,7 +582,7 @@ def __init__( service_account_name: str | None = None, request_timeout: int | tuple[int, int] = 10, pod_name_prefix: str = "task-pod-", - gcs_client: "storage.Client | None" = None, + gcs_client: storage.Client | None = None, pod_labels: dict[str, str] | None = None, pod_annotations: dict[str, str] | None = None, pod_postprocessor: PodPostProcessor | None = None, @@ -616,7 +616,6 @@ class KubernetesWithGcsFuseContainerLauncher(GoogleKubernetesEngineLauncher): class LaunchedKubernetesContainer(interfaces.LaunchedContainer): - def __init__( self, pod_name: str, @@ -681,7 +680,7 @@ def _get_main_container_terminated_state( @property def status(self) -> interfaces.ContainerStatus: phase_str = self._debug_pod.status.phase - if phase_str == "Pending": + if phase_str == "Pending": # noqa: SIM116 return interfaces.ContainerStatus.PENDING elif phase_str == "Running": return interfaces.ContainerStatus.RUNNING @@ -693,7 +692,7 @@ def status(self) -> interfaces.ContainerStatus: return interfaces.ContainerStatus.ERROR @property - def exit_code(self) -> Optional[int]: + def exit_code(self) -> int | None: main_container_terminated_state = self._get_main_container_terminated_state() if main_container_terminated_state is None: return None @@ -782,7 +781,7 @@ def from_dict( launcher=launcher, ) - def get_refreshed(self) -> "LaunchedKubernetesContainer": + def get_refreshed(self) -> LaunchedKubernetesContainer: launcher = self._get_launcher() core_api_client = k8s_client_lib.CoreV1Api(api_client=launcher._api_client) pod: k8s_client_lib.V1Pod = core_api_client.read_namespaced_pod( @@ -891,7 +890,7 @@ def launch_container_task( output_uris: dict[str, str], log_uri: str, annotations: dict[str, Any] | None = None, - ) -> "LaunchedKubernetesJob": + ) -> LaunchedKubernetesJob: namespace = self._choose_namespace(annotations=annotations) pod = self._prepare_kubernetes_pod( @@ -1020,7 +1019,7 @@ def _transform_job_before_launching( def get_refreshed_launched_container_from_dict( self, launched_container_dict: dict - ) -> "LaunchedKubernetesJob": + ) -> LaunchedKubernetesJob: launched_container = LaunchedKubernetesJob.from_dict( launched_container_dict, launcher=self ) @@ -1028,7 +1027,7 @@ def get_refreshed_launched_container_from_dict( def deserialize_launched_container_from_dict( self, launched_container_dict: dict - ) -> "LaunchedKubernetesJob": + ) -> LaunchedKubernetesJob: launched_container = LaunchedKubernetesJob.from_dict( launched_container_dict, launcher=self ) @@ -1036,7 +1035,6 @@ def deserialize_launched_container_from_dict( class LaunchedKubernetesJob(interfaces.LaunchedContainer): - def __init__( self, job_name: str, @@ -1091,7 +1089,7 @@ def status(self) -> interfaces.ContainerStatus: return interfaces.ContainerStatus.RUNNING @property - def exit_code(self) -> Optional[int]: + def exit_code(self) -> int | None: if not self.has_ended: return None # Shortcut for succeeded jobs @@ -1144,7 +1142,6 @@ def started_at(self) -> datetime.datetime | None: @property def ended_at(self) -> datetime.datetime | None: - job = self._debug_job job_status = self._debug_job.status if not job_status: return None @@ -1210,7 +1207,7 @@ def from_dict( launcher=launcher, ) - def get_refreshed(self) -> "LaunchedKubernetesJob": + def get_refreshed(self) -> LaunchedKubernetesJob: launcher = self._get_launcher() batch_api_client = k8s_client_lib.BatchV1Api(api_client=launcher._api_client) job: k8s_client_lib.V1Job = batch_api_client.read_namespaced_job( @@ -1407,7 +1404,7 @@ def _kubernetes_serialize(obj) -> dict[str, Any]: return shallow_client.sanitize_for_serialization(obj) -def _kubernetes_deserialize(obj_dict: dict[str, Any], cls: typing.Type[_T]) -> _T: +def _kubernetes_deserialize(obj_dict: dict[str, Any], cls: type[_T]) -> _T: shallow_client = k8s_client_lib.ApiClient.__new__(k8s_client_lib.ApiClient) return shallow_client._ApiClient__deserialize(obj_dict, cls) diff --git a/cloud_pipelines_backend/launchers/local_docker_launchers.py b/cloud_pipelines_backend/launchers/local_docker_launchers.py index e794fbb..b8cf70e 100644 --- a/cloud_pipelines_backend/launchers/local_docker_launchers.py +++ b/cloud_pipelines_backend/launchers/local_docker_launchers.py @@ -3,18 +3,17 @@ import logging import pathlib import typing -from typing import Any, Optional +from typing import Any import docker -import docker.types import docker.models.containers +import docker.types from cloud_pipelines.orchestration.launchers import naming_utils from cloud_pipelines.orchestration.storage_providers import local_storage -from .. import component_structures as structures -from . import container_component_utils -from . import interfaces +from .. import component_structures as structures +from . import container_component_utils, interfaces _logger = logging.getLogger(__name__) @@ -66,7 +65,7 @@ class DockerContainerLauncher( ): """Launcher that uses Docker installed locally""" - def __init__(self, client: Optional[docker.DockerClient] = None): + def __init__(self, client: docker.DockerClient | None = None): try: self._docker_client = client or docker.from_env(timeout=5) except Exception as ex: @@ -87,8 +86,6 @@ def launch_container_task( annotations: dict[str, Any] | None = None, ) -> "LaunchedDockerContainer": container_spec = component_spec.implementation.container - input_names = list(input_arguments.keys()) - output_names = list(output_uris.keys()) # TODO: Validate the output URIs. Don't forget about (`C:\*` and `C:/*` paths) @@ -281,7 +278,7 @@ def status(self) -> interfaces.ContainerStatus: return interfaces.ContainerStatus.ERROR @property - def exit_code(self) -> Optional[int]: + def exit_code(self) -> int | None: if not self.has_ended: return None return self._container.attrs["State"]["ExitCode"] @@ -371,6 +368,7 @@ def from_dict( log_uri=log_uri, ) + def _parse_docker_time(date_string: str) -> datetime.datetime: # Workaround for Python <3.11 failing to parse timestamps that include nanoseconds: # datetime.datetime.fromisoformat("2025-10-07T04:48:35.585991509+00:00") diff --git a/cloud_pipelines_backend/orchestrator_sql.py b/cloud_pipelines_backend/orchestrator_sql.py index 9b231e4..b98c832 100644 --- a/cloud_pipelines_backend/orchestrator_sql.py +++ b/cloud_pipelines_backend/orchestrator_sql.py @@ -1,26 +1,25 @@ import copy -import json import datetime +import json import logging import time import traceback import typing from typing import Any - import sqlalchemy as sql from sqlalchemy import orm +from cloud_pipelines.orchestration.launchers import naming_utils from cloud_pipelines.orchestration.storage_providers import ( interfaces as storage_provider_interfaces, ) -from cloud_pipelines.orchestration.launchers import naming_utils from . import backend_types_sql as bts from . import component_structures as structures +from .instrumentation import contextual_logging from .launchers import common_annotations from .launchers import interfaces as launcher_interfaces -from .instrumentation import contextual_logging _logger = logging.getLogger(__name__) @@ -64,7 +63,7 @@ def run_loop(self): try: self.process_each_queue_once() time.sleep(self._sleep_seconds_between_queue_sweeps) - except: + except Exception: _logger.exception("Error while calling `process_each_queue_once`") def process_each_queue_once(self): @@ -76,12 +75,13 @@ def process_each_queue_once(self): try: with self._session_factory() as session: queue_handler(session=session) - except: + except Exception: _logger.exception(f"Error while executing {queue_handler=}") def internal_process_queued_executions_queue(self, session: orm.Session): query = ( - sql.select(bts.ExecutionNode).where( + sql.select(bts.ExecutionNode) + .where( bts.ExecutionNode.container_execution_status.in_( ( bts.ContainerExecutionStatus.UNINITIALIZED, @@ -125,7 +125,7 @@ def internal_process_queued_executions_queue(self, session: orm.Session): else: if not self._queued_executions_queue_idle: self._queued_executions_queue_idle = True - _logger.debug(f"No queued executions found") + _logger.debug("No queued executions found") return False def internal_process_running_executions_queue(self, session: orm.Session): @@ -201,7 +201,7 @@ def internal_process_running_executions_queue(self, session: orm.Session): return True else: if not self._running_executions_queue_idle: - _logger.debug(f"No running container executions found") + _logger.debug("No running container executions found") self._running_executions_queue_idle = True return False @@ -656,7 +656,7 @@ def internal_process_one_running_execution( # We should preserve the logs before terminating/deleting the container try: _retry(lambda: launched_container.upload_log()) - except: + except Exception: _logger.exception("Error uploading logs before termination.") # Requesting container termination. # Termination might not happen immediately (e.g. Kubernetes has grace period). @@ -707,7 +707,7 @@ def internal_process_one_running_execution( execution_nodes = container_execution.execution_nodes if not execution_nodes: raise OrchestratorError( - f"Could not find ExecutionNode associated with ContainerExecution." + "Could not find ExecutionNode associated with ContainerExecution." ) if len(execution_nodes) > 1: execution_node_ids = [execution.id for execution in execution_nodes] @@ -752,15 +752,15 @@ def _maybe_preload_value( # Those values may be useful for preservation, but not so important that we should fail a successfully completed container execution. try: data = uri_reader.download_as_bytes() - except Exception as ex: + except Exception: _logger.exception( - f"Error during preloading small artifact values." + "Error during preloading small artifact values." ) return None try: text = data.decode("utf-8") return text - except: + except Exception: pass output_artifact_uris: dict[str, str] = { @@ -774,7 +774,9 @@ def _maybe_preload_value( output_name for output_name, uri in output_artifact_uris.items() if not _retry( - lambda: self._storage_provider.make_uri(uri).get_reader().exists() + lambda uri=uri: ( + self._storage_provider.make_uri(uri).get_reader().exists() + ) ) ] @@ -799,9 +801,9 @@ def _maybe_preload_value( else: output_artifact_data_info_map = { output_name: _retry( - lambda: self._storage_provider.make_uri(uri) - .get_reader() - .get_info() + lambda uri=uri: ( + self._storage_provider.make_uri(uri).get_reader().get_info() + ) ) for output_name, uri in output_artifact_uris.items() } @@ -818,11 +820,13 @@ def _maybe_preload_value( uri=output_artifact_uris[output_name], # Preloading artifact value is it's small enough (e.g. <=255 bytes) value=_retry( - lambda: _maybe_get_small_artifact_value( - uri_reader=self._storage_provider.make_uri( - output_artifact_uris[output_name] - ).get_reader(), - data_info=data_info, + lambda output_name=output_name, data_info=data_info: ( + _maybe_get_small_artifact_value( + uri_reader=self._storage_provider.make_uri( + output_artifact_uris[output_name] + ).get_reader(), + data_info=data_info, + ) ) ), created_at=current_time, @@ -932,7 +936,7 @@ def _mark_all_downstream_executions_as_skipped( ) -def _assert_type(value: typing.Any, typ: typing.Type[_T]) -> _T: +def _assert_type(value: typing.Any, typ: type[_T]) -> _T: if not isinstance(value, typ): raise TypeError(f"Expected type {typ}, but got {type(value)}: {value}") return value @@ -982,7 +986,7 @@ def _generate_random_id() -> str: nanoseconds = time.time_ns() milliseconds = nanoseconds // 1_000_000 - return ("%012x" % milliseconds) + random_bytes.hex() + return f"{milliseconds:012x}" + random_bytes.hex() def _update_dict_recursive(d1: dict, d2: dict): @@ -1003,7 +1007,7 @@ def _retry( for i in range(max_retries): try: return func() - except: + except Exception: _logger.exception(f"Exception calling {func}.") time.sleep(wait_seconds) if i == max_retries - 1: @@ -1054,11 +1058,11 @@ def _maybe_get_small_artifact_value( # Those values may be useful for preservation, but not so important that we should fail a successfully completed container execution. try: data = uri_reader.download_as_bytes() - except Exception as ex: - _logger.exception(f"Error during preloading small artifact values.") + except Exception: + _logger.exception("Error during preloading small artifact values.") return None try: text = data.decode("utf-8") return text - except: + except Exception: pass diff --git a/cloud_pipelines_backend/storage_providers/huggingface_repo_storage.py b/cloud_pipelines_backend/storage_providers/huggingface_repo_storage.py index f402f4d..1b7bb73 100644 --- a/cloud_pipelines_backend/storage_providers/huggingface_repo_storage.py +++ b/cloud_pipelines_backend/storage_providers/huggingface_repo_storage.py @@ -2,7 +2,6 @@ import dataclasses import logging import pathlib -from typing import Optional import huggingface_hub from huggingface_hub import hf_api @@ -72,7 +71,7 @@ def repo_id(self): class HuggingFaceRepoStorageProvider(interfaces.StorageProvider): - def __init__(self, client: Optional[huggingface_hub.HfApi] = None) -> None: + def __init__(self, client: huggingface_hub.HfApi | None = None) -> None: self._client = client or huggingface_hub.HfApi() def make_uri(self, uri: str) -> interfaces.UriAccessor: @@ -142,7 +141,7 @@ def exists(self, uri: HuggingFaceRepoUri) -> bool: def calculate_data_hash(self, *, data: bytes) -> dict[str, str]: import hashlib - header = f"blob {len(data)}\0".encode("utf-8") + header = f"blob {len(data)}\0".encode() hasher = hashlib.sha1(header) hasher.update(data) return {"GitBlobHash": hasher.hexdigest()} diff --git a/orchestrator_main.py b/orchestrator_main.py index d27c73d..7d956ba 100644 --- a/orchestrator_main.py +++ b/orchestrator_main.py @@ -1,13 +1,13 @@ import logging -import pathlib import os +import pathlib import sqlalchemy from sqlalchemy import orm +from cloud_pipelines.orchestration.storage_providers import local_storage from cloud_pipelines_backend import orchestrator_sql from cloud_pipelines_backend.launchers import kubernetes_launchers -from cloud_pipelines.orchestration.storage_providers import local_storage def main(): @@ -46,12 +46,12 @@ def main(): artifact_store_root_dir = (pathlib.Path.cwd() / "tmp" / "artifacts").as_posix() log_store_root_dir = (pathlib.Path.cwd() / "tmp" / "logs").as_posix() - from kubernetes import config as k8s_config_lib from kubernetes import client as k8s_client_lib + from kubernetes import config as k8s_config_lib try: k8s_config_lib.load_incluster_config() - except: + except Exception: k8s_config_lib.load_kube_config() k8s_client = k8s_client_lib.ApiClient() @@ -81,7 +81,6 @@ def main(): if __name__ == "__main__": - # This sets the root logger to write to stdout (your console). # Your script/app needs to call this somewhere at least once. # logging.basicConfig() diff --git a/pyproject.toml b/pyproject.toml index 48db352..06075a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,13 @@ select = [ "B", # flake8-bugbear "SIM", # flake8-simplify ] +ignore = [ + "E501", # line too long (let the formatter handle what it can) +] + +[tool.ruff.lint.per-file-ignores] +"start_local.py" = ["E402"] # imports not at top (by design, uses region blocks) +"cloud_pipelines_backend/api_server_sql.py" = ["E402"] # imports after early class/function definitions [tool.ruff.lint.isort] known-first-party = ["cloud_pipelines", "cloud_pipelines_backend"] diff --git a/start_local.py b/start_local.py index 1155c15..52a3278 100644 --- a/start_local.py +++ b/start_local.py @@ -37,6 +37,7 @@ # region: Launcher configuration import docker + from cloud_pipelines_backend.launchers import local_docker_launchers docker_client = docker.DockerClient.from_env(timeout=5) @@ -53,7 +54,6 @@ # endregion # region: Authentication configuration -import fastapi ADMIN_USER_NAME = "admin" default_component_library_owner_username = ADMIN_USER_NAME @@ -78,6 +78,7 @@ def get_user_details(request: fastapi.Request): # region: Logging configuration import logging.config + from cloud_pipelines_backend.instrumentation import structured_logging LOGGING_CONFIG = { @@ -195,13 +196,16 @@ def run_orchestrator( orchestrator.run_loop() -run_configured_orchestrator = lambda: run_orchestrator( - db_engine=db_engine, - storage_provider=storage_provider, - data_root_uri=artifacts_root_uri, - logs_root_uri=logs_root_uri, - sleep_seconds_between_queue_sweeps=sleep_seconds_between_queue_sweeps, -) +def run_configured_orchestrator(): + run_orchestrator( + db_engine=db_engine, + storage_provider=storage_provider, + data_root_uri=artifacts_root_uri, + logs_root_uri=logs_root_uri, + sleep_seconds_between_queue_sweeps=sleep_seconds_between_queue_sweeps, + ) + + # endregion @@ -213,11 +217,12 @@ def run_orchestrator( import fastapi from fastapi import staticfiles -from cloud_pipelines_backend import api_router -from cloud_pipelines_backend import database_ops -from cloud_pipelines_backend.instrumentation import api_tracing -from cloud_pipelines_backend.instrumentation import contextual_logging -from cloud_pipelines_backend.instrumentation import otel_tracing +from cloud_pipelines_backend import api_router, database_ops +from cloud_pipelines_backend.instrumentation import ( + api_tracing, + contextual_logging, + otel_tracing, +) @contextlib.asynccontextmanager diff --git a/tests/test_component_library_api_server.py b/tests/test_component_library_api_server.py index 1b3497f..fd7b874 100644 --- a/tests/test_component_library_api_server.py +++ b/tests/test_component_library_api_server.py @@ -1,11 +1,9 @@ -from sqlalchemy import orm -import yaml import pytest +import yaml +from sqlalchemy import orm -from cloud_pipelines_backend import component_structures from cloud_pipelines_backend import component_library_api_server as components_api -from cloud_pipelines_backend import database_ops -from cloud_pipelines_backend import errors +from cloud_pipelines_backend import component_structures, database_ops, errors def _make_component_spec(name: str): @@ -43,8 +41,8 @@ def test_published_component_service(): assert published_component.digest assert published_component.published_by == user_name assert published_component.name == component_name - assert published_component.deprecated == False - assert published_component.superseded_by == None + assert not published_component.deprecated + assert published_component.superseded_by is None # Test listing with session_factory() as session: @@ -93,15 +91,14 @@ def test_published_component_service(): user_name=user_name, deprecated=True, ) - assert published_component_2.deprecated == True - with session_factory() as session: - with pytest.raises(errors.ItemNotFoundError): - published_component_service.update( - session=session, - digest=published_component.digest, - user_name="XXX", - deprecated=True, - ) + assert published_component_2.deprecated + with session_factory() as session, pytest.raises(errors.ItemNotFoundError): + published_component_service.update( + session=session, + digest=published_component.digest, + user_name="XXX", + deprecated=True, + ) # Test listing deprecated components with session_factory() as session: @@ -170,7 +167,7 @@ def test_component_library_service(): ) assert library_2.id assert library_2.name == library_name - assert library_2.hide_from_search == False + assert not library_2.hide_from_search assert library_2.component_count == 1 assert library_2.published_by == user_name assert library_2.root_folder @@ -183,8 +180,8 @@ def test_component_library_service(): assert component_ref_2.name == component_name assert component_ref_2.url == component_url # By default, the returned library does not include component text or spec attributes - assert component_ref_2.text == None - assert component_ref_2.spec == None + assert component_ref_2.text is None + assert component_ref_2.spec is None # Test: Test `get()`, `include_component_texts` with session_factory() as session: @@ -241,14 +238,13 @@ def test_component_library_service(): name=library_name_7, root_folder=library_folder_7, ) - with pytest.raises(errors.PermissionError): - with session_factory() as session: - component_library_service.replace( - session=session, - id=library_2.id, - library=library_request_7, - user_name="XXX", - ) + with pytest.raises(errors.PermissionError), session_factory() as session: + component_library_service.replace( + session=session, + id=library_2.id, + library=library_request_7, + user_name="XXX", + ) with session_factory() as session: library_7 = component_library_service.replace( @@ -264,11 +260,9 @@ def test_component_library_service(): user_name=user_name ) with session_factory() as session: - library_8 = component_library_service.get( - session=session, id=user_library_id - ) + library_8 = component_library_service.get(session=session, id=user_library_id) assert library_8.id == user_library_id - assert library_8.hide_from_search == True + assert library_8.hide_from_search assert user_name in library_8.name # Test: Replacing user library @@ -277,14 +271,13 @@ def test_component_library_service(): name=library_name_9, root_folder=library_folder_7, ) - with pytest.raises(errors.PermissionError): - with session_factory() as session: - component_library_service.replace( - session=session, - id=user_library_id, - library=library_request_9, - user_name="XXX", - ) + with pytest.raises(errors.PermissionError), session_factory() as session: + component_library_service.replace( + session=session, + id=user_library_id, + library=library_request_9, + user_name="XXX", + ) with session_factory() as session: library_9 = component_library_service.replace( session=session, diff --git a/tests/test_context_aware_formatter_exception.py b/tests/test_context_aware_formatter_exception.py index 75e7375..312d987 100644 --- a/tests/test_context_aware_formatter_exception.py +++ b/tests/test_context_aware_formatter_exception.py @@ -5,11 +5,11 @@ including full tracebacks. """ +import io import logging import logging.config -import io -import sys import os +import sys # Add the parent directory to the path so we can import the module sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -68,7 +68,7 @@ def test_logger_exception_with_context_aware_formatter(): try: # Simulate an error - result = 1 / 0 + _ = 1 / 0 except ZeroDivisionError: test_logger.exception("An error occurred while dividing") @@ -111,20 +111,20 @@ def outer_function(): # Verify context is included assert "execution_id=exec-12345" in output2, "execution_id should be in logs" - assert ( - "container_execution_id=container-67890" in output2 - ), "container_execution_id should be in logs" + assert "container_execution_id=container-67890" in output2, ( + "container_execution_id should be in logs" + ) assert "pipeline_run_id=run-abc123" in output2, "pipeline_run_id should be in logs" # Verify the traceback is still present with context assert "Traceback" in output2, "Traceback should be present even with context" assert "NameError" in output2, "Exception type should be in logs" - assert ( - "inner_function" in output2 - ), "Function names from traceback should be present" - assert ( - "outer_function" in output2 - ), "Function names from traceback should be present" + assert "inner_function" in output2, ( + "Function names from traceback should be present" + ) + assert "outer_function" in output2, ( + "Function names from traceback should be present" + ) # Clear buffer for next test log_buffer.truncate(0) @@ -147,9 +147,9 @@ def outer_function(): # Verify .error() does NOT include traceback assert "Traceback" not in output3, "logger.error() should NOT include traceback" - assert ( - "ValueError" not in output3 - ), "logger.error() should NOT include exception details" + assert "ValueError" not in output3, ( + "logger.error() should NOT include exception details" + ) # Clear buffer log_buffer.truncate(0) @@ -167,9 +167,9 @@ def outer_function(): # Verify .exception() DOES include traceback assert "Traceback" in output4, "logger.exception() SHOULD include traceback" - assert ( - "ValueError" in output4 - ), "logger.exception() SHOULD include exception details" + assert "ValueError" in output4, ( + "logger.exception() SHOULD include exception details" + ) print("\n" + "=" * 80) print("SUMMARY") diff --git a/tests/test_instrumentation_logging_context.py b/tests/test_instrumentation_logging_context.py index dfd5959..1f5ef10 100644 --- a/tests/test_instrumentation_logging_context.py +++ b/tests/test_instrumentation_logging_context.py @@ -1,6 +1,7 @@ """Tests for the logging_context module in instrumentation.""" import pytest + from cloud_pipelines_backend.instrumentation import contextual_logging from cloud_pipelines_backend.instrumentation.api_tracing import generate_request_id @@ -106,10 +107,12 @@ def test_context_manager_clears_on_exception(self): """Test that context manager restores metadata even when exception occurs.""" test_id = "exception_test" - with pytest.raises(ValueError): - with contextual_logging.logging_context(request_id=test_id): - assert contextual_logging.get_context_metadata("request_id") == test_id - raise ValueError("Test exception") + with ( + pytest.raises(ValueError), + contextual_logging.logging_context(request_id=test_id), + ): + assert contextual_logging.get_context_metadata("request_id") == test_id + raise ValueError("Test exception") # Metadata should be cleared even after exception assert contextual_logging.get_context_metadata("request_id") is None diff --git a/tests/test_instrumentation_request_middleware.py b/tests/test_instrumentation_request_middleware.py index 70dfeb3..2bf6ee5 100644 --- a/tests/test_instrumentation_request_middleware.py +++ b/tests/test_instrumentation_request_middleware.py @@ -1,10 +1,8 @@ """Tests for the request_middleware module in instrumentation.""" import pytest -from unittest.mock import AsyncMock, MagicMock -from starlette.requests import Request -from starlette.responses import Response from starlette.applications import Starlette +from starlette.responses import Response from starlette.testclient import TestClient from cloud_pipelines_backend.instrumentation import contextual_logging diff --git a/tests/test_request_id_concurrency.py b/tests/test_request_id_concurrency.py index 97dce6c..c82b010 100644 --- a/tests/test_request_id_concurrency.py +++ b/tests/test_request_id_concurrency.py @@ -1,6 +1,7 @@ """Test that request_id works correctly with concurrent requests.""" import asyncio + import pytest from starlette.applications import Starlette from starlette.responses import JSONResponse diff --git a/tests/test_secrets.py b/tests/test_secrets.py index 6d4cb67..6b894e3 100644 --- a/tests/test_secrets.py +++ b/tests/test_secrets.py @@ -1,11 +1,9 @@ -from typing import Callable +from collections.abc import Callable from unittest import mock from sqlalchemy import orm -from cloud_pipelines_backend import api_server_sql -from cloud_pipelines_backend import component_structures -from cloud_pipelines_backend import database_ops +from cloud_pipelines_backend import api_server_sql, component_structures, database_ops from cloud_pipelines_backend.launchers import interfaces as launcher_interfaces diff --git a/uv.lock b/uv.lock index 5f83fa4..5419396 100644 --- a/uv.lock +++ b/uv.lock @@ -9,7 +9,7 @@ resolution-markers = [ [[package]] name = "annotated-types" version = "0.7.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, @@ -18,7 +18,7 @@ wheels = [ [[package]] name = "anyio" version = "4.9.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "idna" }, @@ -33,7 +33,7 @@ wheels = [ [[package]] name = "asgiref" version = "3.11.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] @@ -45,7 +45,7 @@ wheels = [ [[package]] name = "authlib" version = "1.6.5" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "cryptography" }, ] @@ -57,7 +57,7 @@ wheels = [ [[package]] name = "backports-asyncio-runner" version = "1.2.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, @@ -66,7 +66,7 @@ wheels = [ [[package]] name = "cachetools" version = "5.5.2" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380, upload-time = "2025-02-20T21:01:19.524Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" }, @@ -75,7 +75,7 @@ wheels = [ [[package]] name = "certifi" version = "2025.4.26" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" }, @@ -84,7 +84,7 @@ wheels = [ [[package]] name = "cffi" version = "2.0.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] @@ -166,7 +166,7 @@ wheels = [ [[package]] name = "charset-normalizer" version = "3.4.2" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, @@ -227,7 +227,7 @@ wheels = [ [[package]] name = "click" version = "8.2.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] @@ -239,7 +239,7 @@ wheels = [ [[package]] name = "cloud-pipelines" version = "0.23.2.4" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "docker" }, { name = "docstring-parser" }, @@ -307,7 +307,7 @@ huggingface = [{ name = "huggingface-hub", extras = ["oauth"], specifier = ">=0. [[package]] name = "colorama" version = "0.4.6" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, @@ -316,7 +316,7 @@ wheels = [ [[package]] name = "cryptography" version = "46.0.3" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, @@ -381,7 +381,7 @@ wheels = [ [[package]] name = "dnspython" version = "2.7.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197, upload-time = "2024-10-05T20:14:59.362Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632, upload-time = "2024-10-05T20:14:57.687Z" }, @@ -390,7 +390,7 @@ wheels = [ [[package]] name = "docker" version = "6.1.3" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "packaging" }, { name = "pywin32", marker = "sys_platform == 'win32'" }, @@ -406,7 +406,7 @@ wheels = [ [[package]] name = "docstring-parser" version = "0.16" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/08/12/9c22a58c0b1e29271051222d8906257616da84135af9ed167c9e28f85cb3/docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e", size = 26565, upload-time = "2024-03-15T10:39:44.419Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d5/7c/e9fcff7623954d86bdc17782036cbf715ecab1bec4847c008557affe1ca8/docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637", size = 36533, upload-time = "2024-03-15T10:39:41.527Z" }, @@ -415,7 +415,7 @@ wheels = [ [[package]] name = "durationpy" version = "0.10" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/9d/a4/e44218c2b394e31a6dd0d6b095c4e1f32d0be54c2a4b250032d717647bab/durationpy-0.10.tar.gz", hash = "sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba", size = 3335, upload-time = "2025-05-17T13:52:37.26Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b0/0d/9feae160378a3553fa9a339b0e9c1a048e147a4127210e286ef18b730f03/durationpy-0.10-py3-none-any.whl", hash = "sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286", size = 3922, upload-time = "2025-05-17T13:52:36.463Z" }, @@ -424,7 +424,7 @@ wheels = [ [[package]] name = "email-validator" version = "2.2.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "dnspython" }, { name = "idna" }, @@ -437,7 +437,7 @@ wheels = [ [[package]] name = "exceptiongroup" version = "1.3.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] @@ -449,7 +449,7 @@ wheels = [ [[package]] name = "fastapi" version = "0.115.12" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "pydantic" }, { name = "starlette" }, @@ -473,7 +473,7 @@ standard = [ [[package]] name = "fastapi-cli" version = "0.0.7" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "rich-toolkit" }, { name = "typer" }, @@ -492,7 +492,7 @@ standard = [ [[package]] name = "filelock" version = "3.20.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" }, @@ -501,7 +501,7 @@ wheels = [ [[package]] name = "fsspec" version = "2025.9.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/de/e0/bab50af11c2d75c9c4a2a26a5254573c0bd97cea152254401510950486fa/fsspec-2025.9.0.tar.gz", hash = "sha256:19fd429483d25d28b65ec68f9f4adc16c17ea2c7c7bf54ec61360d478fb19c19", size = 304847, upload-time = "2025-09-02T19:10:49.215Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl", hash = "sha256:530dc2a2af60a414a832059574df4a6e10cce927f6f4a78209390fe38955cfb7", size = 199289, upload-time = "2025-09-02T19:10:47.708Z" }, @@ -510,7 +510,7 @@ wheels = [ [[package]] name = "google-auth" version = "2.40.3" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "cachetools" }, { name = "pyasn1-modules" }, @@ -524,7 +524,7 @@ wheels = [ [[package]] name = "googleapis-common-protos" version = "1.72.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "protobuf" }, ] @@ -536,7 +536,7 @@ wheels = [ [[package]] name = "greenlet" version = "3.2.3" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/c9/92/bb85bd6e80148a4d2e0c59f7c0c2891029f8fd510183afc7d8d2feeed9b6/greenlet-3.2.3.tar.gz", hash = "sha256:8b0dd8ae4c0d6f5e54ee55ba935eeb3d735a9b58a8a1e5b5cbab64e01a39f365", size = 185752, upload-time = "2025-06-05T16:16:09.955Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/92/db/b4c12cff13ebac2786f4f217f06588bccd8b53d260453404ef22b121fc3a/greenlet-3.2.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:1afd685acd5597349ee6d7a88a8bec83ce13c106ac78c196ee9dde7c04fe87be", size = 268977, upload-time = "2025-06-05T16:10:24.001Z" }, @@ -582,7 +582,7 @@ wheels = [ [[package]] name = "grpcio" version = "1.76.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "typing-extensions" }, ] @@ -643,7 +643,7 @@ wheels = [ [[package]] name = "h11" version = "0.16.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, @@ -652,7 +652,7 @@ wheels = [ [[package]] name = "hf-xet" version = "1.1.10" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/74/31/feeddfce1748c4a233ec1aa5b7396161c07ae1aa9b7bdbc9a72c3c7dd768/hf_xet-1.1.10.tar.gz", hash = "sha256:408aef343800a2102374a883f283ff29068055c111f003ff840733d3b715bb97", size = 487910, upload-time = "2025-09-12T20:10:27.12Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/f7/a2/343e6d05de96908366bdc0081f2d8607d61200be2ac802769c4284cc65bd/hf_xet-1.1.10-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:686083aca1a6669bc85c21c0563551cbcdaa5cf7876a91f3d074a030b577231d", size = 2761466, upload-time = "2025-09-12T20:10:22.836Z" }, @@ -667,7 +667,7 @@ wheels = [ [[package]] name = "httpcore" version = "1.0.9" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "certifi" }, { name = "h11" }, @@ -680,7 +680,7 @@ wheels = [ [[package]] name = "httptools" version = "0.6.4" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639, upload-time = "2024-10-16T19:45:08.902Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/3b/6f/972f8eb0ea7d98a1c6be436e2142d51ad2a64ee18e02b0e7ff1f62171ab1/httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0", size = 198780, upload-time = "2024-10-16T19:44:06.882Z" }, @@ -716,7 +716,7 @@ wheels = [ [[package]] name = "httpx" version = "0.28.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "anyio" }, { name = "certifi" }, @@ -731,7 +731,7 @@ wheels = [ [[package]] name = "huggingface-hub" version = "0.35.3" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "filelock" }, { name = "fsspec" }, @@ -758,7 +758,7 @@ oauth = [ [[package]] name = "idna" version = "3.10" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, @@ -767,7 +767,7 @@ wheels = [ [[package]] name = "importlib-metadata" version = "8.7.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "zipp" }, ] @@ -779,7 +779,7 @@ wheels = [ [[package]] name = "iniconfig" version = "2.3.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, @@ -788,7 +788,7 @@ wheels = [ [[package]] name = "isodate" version = "0.6.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "six" }, ] @@ -800,7 +800,7 @@ wheels = [ [[package]] name = "itsdangerous" version = "2.2.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, @@ -809,7 +809,7 @@ wheels = [ [[package]] name = "jinja2" version = "3.1.6" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "markupsafe" }, ] @@ -821,7 +821,7 @@ wheels = [ [[package]] name = "kubernetes" version = "33.1.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "certifi" }, { name = "durationpy" }, @@ -843,7 +843,7 @@ wheels = [ [[package]] name = "librt" version = "0.8.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/56/9c/b4b0c54d84da4a94b37bd44151e46d5e583c9534c7e02250b961b1b6d8a8/librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73", size = 177471, upload-time = "2026-02-17T16:13:06.101Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/7c/5f/63f5fa395c7a8a93558c0904ba8f1c8d1b997ca6a3de61bc7659970d66bf/librt-0.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc", size = 65697, upload-time = "2026-02-17T16:11:06.903Z" }, @@ -928,7 +928,7 @@ wheels = [ [[package]] name = "markdown-it-py" version = "3.0.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "mdurl" }, ] @@ -940,7 +940,7 @@ wheels = [ [[package]] name = "markupsafe" version = "3.0.2" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357, upload-time = "2024-10-18T15:20:51.44Z" }, @@ -998,7 +998,7 @@ wheels = [ [[package]] name = "mdurl" version = "0.1.2" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, @@ -1007,7 +1007,7 @@ wheels = [ [[package]] name = "mypy" version = "1.19.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, { name = "mypy-extensions" }, @@ -1053,7 +1053,7 @@ wheels = [ [[package]] name = "mypy-extensions" version = "1.1.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, @@ -1062,7 +1062,7 @@ wheels = [ [[package]] name = "oauthlib" version = "3.2.2" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/6d/fa/fbf4001037904031639e6bfbfc02badfc7e12f137a8afa254df6c4c8a670/oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918", size = 177352, upload-time = "2022-10-17T20:04:27.471Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688, upload-time = "2022-10-17T20:04:24.037Z" }, @@ -1071,7 +1071,7 @@ wheels = [ [[package]] name = "opentelemetry-api" version = "1.39.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "importlib-metadata" }, { name = "typing-extensions" }, @@ -1084,7 +1084,7 @@ wheels = [ [[package]] name = "opentelemetry-exporter-otlp-proto-common" version = "1.39.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "opentelemetry-proto" }, ] @@ -1096,7 +1096,7 @@ wheels = [ [[package]] name = "opentelemetry-exporter-otlp-proto-grpc" version = "1.39.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "googleapis-common-protos" }, { name = "grpcio" }, @@ -1114,7 +1114,7 @@ wheels = [ [[package]] name = "opentelemetry-exporter-otlp-proto-http" version = "1.39.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "googleapis-common-protos" }, { name = "opentelemetry-api" }, @@ -1132,7 +1132,7 @@ wheels = [ [[package]] name = "opentelemetry-instrumentation" version = "0.60b1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, @@ -1147,7 +1147,7 @@ wheels = [ [[package]] name = "opentelemetry-instrumentation-asgi" version = "0.60b1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "asgiref" }, { name = "opentelemetry-api" }, @@ -1163,7 +1163,7 @@ wheels = [ [[package]] name = "opentelemetry-instrumentation-fastapi" version = "0.60b1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-instrumentation" }, @@ -1179,7 +1179,7 @@ wheels = [ [[package]] name = "opentelemetry-proto" version = "1.39.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "protobuf" }, ] @@ -1191,7 +1191,7 @@ wheels = [ [[package]] name = "opentelemetry-sdk" version = "1.39.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, @@ -1205,7 +1205,7 @@ wheels = [ [[package]] name = "opentelemetry-semantic-conventions" version = "0.60b1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "opentelemetry-api" }, { name = "typing-extensions" }, @@ -1218,7 +1218,7 @@ wheels = [ [[package]] name = "opentelemetry-util-http" version = "0.60b1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/50/fc/c47bb04a1d8a941a4061307e1eddfa331ed4d0ab13d8a9781e6db256940a/opentelemetry_util_http-0.60b1.tar.gz", hash = "sha256:0d97152ca8c8a41ced7172d29d3622a219317f74ae6bb3027cfbdcf22c3cc0d6", size = 11053, upload-time = "2025-12-11T13:37:25.115Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/16/5c/d3f1733665f7cd582ef0842fb1d2ed0bc1fba10875160593342d22bba375/opentelemetry_util_http-0.60b1-py3-none-any.whl", hash = "sha256:66381ba28550c91bee14dcba8979ace443444af1ed609226634596b4b0faf199", size = 8947, upload-time = "2025-12-11T13:36:37.151Z" }, @@ -1227,7 +1227,7 @@ wheels = [ [[package]] name = "packaging" version = "25.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, @@ -1236,7 +1236,7 @@ wheels = [ [[package]] name = "pathspec" version = "1.0.3" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/4c/b2/bb8e495d5262bfec41ab5cb18f522f1012933347fb5d9e62452d446baca2/pathspec-1.0.3.tar.gz", hash = "sha256:bac5cf97ae2c2876e2d25ebb15078eb04d76e4b98921ee31c6f85ade8b59444d", size = 130841, upload-time = "2026-01-09T15:46:46.009Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/32/2b/121e912bd60eebd623f873fd090de0e84f322972ab25a7f9044c056804ed/pathspec-1.0.3-py3-none-any.whl", hash = "sha256:e80767021c1cc524aa3fb14bedda9c34406591343cc42797b386ce7b9354fb6c", size = 55021, upload-time = "2026-01-09T15:46:44.652Z" }, @@ -1245,7 +1245,7 @@ wheels = [ [[package]] name = "pluggy" version = "1.6.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, @@ -1254,7 +1254,7 @@ wheels = [ [[package]] name = "protobuf" version = "6.33.5" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/ba/25/7c72c307aafc96fa87062aa6291d9f7c94836e43214d43722e86037aac02/protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c", size = 444465, upload-time = "2026-01-29T21:51:33.494Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b1/79/af92d0a8369732b027e6d6084251dd8e782c685c72da161bd4a2e00fbabb/protobuf-6.33.5-cp310-abi3-win32.whl", hash = "sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b", size = 425769, upload-time = "2026-01-29T21:51:21.751Z" }, @@ -1269,7 +1269,7 @@ wheels = [ [[package]] name = "pyasn1" version = "0.6.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, @@ -1278,7 +1278,7 @@ wheels = [ [[package]] name = "pyasn1-modules" version = "0.4.2" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "pyasn1" }, ] @@ -1290,7 +1290,7 @@ wheels = [ [[package]] name = "pycparser" version = "2.23" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, @@ -1299,7 +1299,7 @@ wheels = [ [[package]] name = "pydantic" version = "2.11.5" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, @@ -1314,7 +1314,7 @@ wheels = [ [[package]] name = "pydantic-core" version = "2.33.2" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "typing-extensions" }, ] @@ -1401,7 +1401,7 @@ wheels = [ [[package]] name = "pygments" version = "2.19.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, @@ -1410,7 +1410,7 @@ wheels = [ [[package]] name = "pytest" version = "8.4.2" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, @@ -1428,7 +1428,7 @@ wheels = [ [[package]] name = "pytest-asyncio" version = "1.3.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, { name = "pytest" }, @@ -1442,7 +1442,7 @@ wheels = [ [[package]] name = "python-dateutil" version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "six" }, ] @@ -1454,7 +1454,7 @@ wheels = [ [[package]] name = "python-dotenv" version = "1.1.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920, upload-time = "2025-03-25T10:14:56.835Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256, upload-time = "2025-03-25T10:14:55.034Z" }, @@ -1463,7 +1463,7 @@ wheels = [ [[package]] name = "python-multipart" version = "0.0.20" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, @@ -1472,7 +1472,7 @@ wheels = [ [[package]] name = "pywin32" version = "310" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } wheels = [ { url = "https://files.pythonhosted.org/packages/95/da/a5f38fffbba2fb99aa4aa905480ac4b8e83ca486659ac8c95bce47fb5276/pywin32-310-cp310-cp310-win32.whl", hash = "sha256:6dd97011efc8bf51d6793a82292419eba2c71cf8e7250cfac03bba284454abc1", size = 8848240, upload-time = "2025-03-17T00:55:46.783Z" }, { url = "https://files.pythonhosted.org/packages/aa/fe/d873a773324fa565619ba555a82c9dabd677301720f3660a731a5d07e49a/pywin32-310-cp310-cp310-win_amd64.whl", hash = "sha256:c3e78706e4229b915a0821941a84e7ef420bf2b77e08c9dae3c76fd03fd2ae3d", size = 9601854, upload-time = "2025-03-17T00:55:48.783Z" }, @@ -1491,7 +1491,7 @@ wheels = [ [[package]] name = "pyyaml" version = "6.0.2" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, @@ -1535,7 +1535,7 @@ wheels = [ [[package]] name = "requests" version = "2.31.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "certifi" }, { name = "charset-normalizer" }, @@ -1550,7 +1550,7 @@ wheels = [ [[package]] name = "requests-oauthlib" version = "2.0.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "oauthlib" }, { name = "requests" }, @@ -1563,7 +1563,7 @@ wheels = [ [[package]] name = "rich" version = "14.0.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, @@ -1577,7 +1577,7 @@ wheels = [ [[package]] name = "rich-toolkit" version = "0.14.7" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "click" }, { name = "rich" }, @@ -1591,7 +1591,7 @@ wheels = [ [[package]] name = "rsa" version = "4.9.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "pyasn1" }, ] @@ -1603,7 +1603,7 @@ wheels = [ [[package]] name = "ruff" version = "0.15.2" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/06/04/eab13a954e763b0606f460443fcbf6bb5a0faf06890ea3754ff16523dce5/ruff-0.15.2.tar.gz", hash = "sha256:14b965afee0969e68bb871eba625343b8673375f457af4abe98553e8bbb98342", size = 4558148, upload-time = "2026-02-19T22:32:20.271Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2f/70/3a4dc6d09b13cb3e695f28307e5d889b2e1a66b7af9c5e257e796695b0e6/ruff-0.15.2-py3-none-linux_armv6l.whl", hash = "sha256:120691a6fdae2f16d65435648160f5b81a9625288f75544dc40637436b5d3c0d", size = 10430565, upload-time = "2026-02-19T22:32:41.824Z" }, @@ -1628,7 +1628,7 @@ wheels = [ [[package]] name = "shellingham" version = "1.5.4" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, @@ -1637,7 +1637,7 @@ wheels = [ [[package]] name = "six" version = "1.17.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, @@ -1646,7 +1646,7 @@ wheels = [ [[package]] name = "sniffio" version = "1.3.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, @@ -1655,7 +1655,7 @@ wheels = [ [[package]] name = "sqlalchemy" version = "2.0.41" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "greenlet", marker = "(python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64')" }, { name = "typing-extensions" }, @@ -1700,7 +1700,7 @@ wheels = [ [[package]] name = "starlette" version = "0.46.2" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "anyio" }, ] @@ -1712,7 +1712,7 @@ wheels = [ [[package]] name = "strip-hints" version = "0.1.13" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "wheel" }, ] @@ -1724,7 +1724,7 @@ wheels = [ [[package]] name = "tomli" version = "2.3.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" }, @@ -1773,7 +1773,7 @@ wheels = [ [[package]] name = "tqdm" version = "4.67.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] @@ -1785,7 +1785,7 @@ wheels = [ [[package]] name = "typer" version = "0.16.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "click" }, { name = "rich" }, @@ -1800,7 +1800,7 @@ wheels = [ [[package]] name = "typing-extensions" version = "4.14.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" }, @@ -1809,7 +1809,7 @@ wheels = [ [[package]] name = "typing-inspection" version = "0.4.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "typing-extensions" }, ] @@ -1821,7 +1821,7 @@ wheels = [ [[package]] name = "urllib3" version = "2.4.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" }, @@ -1830,7 +1830,7 @@ wheels = [ [[package]] name = "uvicorn" version = "0.34.3" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "click" }, { name = "h11" }, @@ -1855,7 +1855,7 @@ standard = [ [[package]] name = "uvloop" version = "0.21.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3", size = 2492741, upload-time = "2024-10-14T23:38:35.489Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/3d/76/44a55515e8c9505aa1420aebacf4dd82552e5e15691654894e90d0bd051a/uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f", size = 1442019, upload-time = "2024-10-14T23:37:20.068Z" }, @@ -1887,7 +1887,7 @@ wheels = [ [[package]] name = "watchfiles" version = "1.0.5" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } dependencies = [ { name = "anyio" }, ] @@ -1952,7 +1952,7 @@ wheels = [ [[package]] name = "websocket-client" version = "1.8.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648, upload-time = "2024-04-23T22:16:16.976Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826, upload-time = "2024-04-23T22:16:14.422Z" }, @@ -1961,7 +1961,7 @@ wheels = [ [[package]] name = "websockets" version = "15.0.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423, upload-time = "2025-03-05T20:01:35.363Z" }, @@ -2020,7 +2020,7 @@ wheels = [ [[package]] name = "wheel" version = "0.45.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/8a/98/2d9906746cdc6a6ef809ae6338005b3f21bb568bea3165cfc6a243fdc25c/wheel-0.45.1.tar.gz", hash = "sha256:661e1abd9198507b1409a20c02106d9670b2576e916d58f520316666abca6729", size = 107545, upload-time = "2024-11-23T00:18:23.513Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0b/2c/87f3254fd8ffd29e4c02732eee68a83a1d3c346ae39bc6822dcbcb697f2b/wheel-0.45.1-py3-none-any.whl", hash = "sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248", size = 72494, upload-time = "2024-11-23T00:18:21.207Z" }, @@ -2029,7 +2029,7 @@ wheels = [ [[package]] name = "wrapt" version = "1.17.3" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/3f/23/bb82321b86411eb51e5a5db3fb8f8032fd30bd7c2d74bfe936136b2fa1d6/wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04", size = 53482, upload-time = "2025-08-12T05:51:44.467Z" }, @@ -2098,7 +2098,7 @@ wheels = [ [[package]] name = "zipp" version = "3.23.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://pkgs.shopify.io/basic/data/python/simple/" } sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" },