diff --git a/.gitattributes b/.gitattributes index a5feef9..d448f5b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -src/athena_client/generated/* linguist-generated=true +src/resolver_athena_client/generated/* linguist-generated=true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b55397c..4a9de56 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,13 +52,13 @@ jobs: - name: Install the project run: uv sync --locked --all-extras --dev - - name: Ensure no differences in generated code (Unix) + - name: Ensure no differences in generated code if: runner.os != 'Windows' shell: bash run: | source .venv/bin/activate - GENERATED_DIR="src/athena_client/generated" - BACKUP_DIR="src/athena_client/generated_backup" + GENERATED_DIR="src/resolver_athena_client/generated" + BACKUP_DIR="src/resolver_athena_client/generated_backup" cp -r $GENERATED_DIR $BACKUP_DIR @@ -66,8 +66,8 @@ jobs: # Fix imports in generated files if [[ -f "$GENERATED_DIR/athena/athena_pb2_grpc.py" && -f "$GENERATED_DIR/athena/athena_pb2.py" ]]; then - sed -i.bak 's/^from athena /from athena_client.generated.athena /' "$GENERATED_DIR/athena/athena_pb2_grpc.py" - sed -i.bak 's/^from athena /from athena_client.generated.athena /' "$GENERATED_DIR/athena/athena_pb2.py" + sed -i.bak 's/^from athena /from resolver_athena_client.generated.athena /' "$GENERATED_DIR/athena/athena_pb2_grpc.py" + sed -i.bak 's/^from athena /from resolver_athena_client.generated.athena /' "$GENERATED_DIR/athena/athena_pb2.py" rm -f "$GENERATED_DIR/athena/athena_pb2_grpc.py.bak" "$GENERATED_DIR/athena/athena_pb2.py.bak" else echo "Error: Expected files not found in $GENERATED_DIR/athena" @@ -78,44 +78,6 @@ jobs: rm -rf $BACKUP_DIR - - name: Ensure no differences in generated code (Windows) - if: runner.os == 'Windows' - shell: powershell - run: | - & .venv\Scripts\Activate.ps1 - $GENERATED_DIR = "src\athena_client\generated" - $BACKUP_DIR = "src\athena_client\generated_backup" - - Copy-Item -Recurse -Path $GENERATED_DIR -Destination $BACKUP_DIR - - # Compile protobuf files directly since compile_proto.sh won't work on Windows - if (!(Test-Path "athena-protobufs\athena\athena.proto")) { - Write-Host "Error: Protobuf file not found at athena-protobufs\athena\athena.proto. Ensure the submodule is initialized and updated." - exit 1 - } - - New-Item -ItemType Directory -Force -Path $GENERATED_DIR | Out-Null - - python -m grpc_tools.protoc --proto_path="athena-protobufs" --python_out=$GENERATED_DIR --grpc_python_out=$GENERATED_DIR --mypy_out=$GENERATED_DIR "athena-protobufs\athena\athena.proto" - - # Fix imports in generated files - if ((Test-Path "$GENERATED_DIR\athena\athena_pb2_grpc.py") -and (Test-Path "$GENERATED_DIR\athena\athena_pb2.py")) { - (Get-Content "$GENERATED_DIR\athena\athena_pb2_grpc.py") -replace '^from athena ', 'from athena_client.generated.athena ' | Set-Content "$GENERATED_DIR\athena\athena_pb2_grpc.py" - (Get-Content "$GENERATED_DIR\athena\athena_pb2.py") -replace '^from athena ', 'from athena_client.generated.athena ' | Set-Content "$GENERATED_DIR\athena\athena_pb2.py" - } else { - Write-Host "Error: Expected files not found in $GENERATED_DIR\athena" - exit 1 - } - - # Simple directory comparison for Windows - $differences = Compare-Object -ReferenceObject (Get-ChildItem -Recurse $GENERATED_DIR) -DifferenceObject (Get-ChildItem -Recurse $BACKUP_DIR) -Property Name, Length - if ($differences) { - Write-Host "Generated code differs. Please commit the changes after running compile_proto.sh." - exit 1 - } - - Remove-Item -Recurse -Force $BACKUP_DIR - - name: Run linter run: | uv run ruff check @@ -130,7 +92,7 @@ jobs: - name: Run tests run: | - uv run pytest --junitxml=pytest.xml --tb=auto --cov-report=term-missing:skip-covered --cov=src tests/ | tee pytest-coverage.txt + uv run pytest --junitxml=pytest.xml --tb=auto --cov-report=term-missing:skip-covered --cov=src/resolver_athena_client tests/ | tee pytest-coverage.txt - name: Upload test results uses: actions/upload-artifact@v4 @@ -188,7 +150,7 @@ jobs: if: github.event_name == 'release' && github.event.action == 'published' environment: name: pypi - url: https://pypi.org/p/athena-client + url: https://pypi.org/p/resolver-athena-client permissions: id-token: write diff --git a/README.md b/README.md index 47a4162..5e87ad1 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ The Athena client supports two authentication methods: ### Static Token Authentication ```python -from athena_client.client.channel import create_channel +from resolver_athena_client.client.channel import create_channel # Use a pre-existing authentication token channel = create_channel(host="your-host", auth_token="your-token") @@ -20,7 +20,7 @@ The credential helper automatically handles OAuth token acquisition and refresh: ```python import asyncio -from athena_client.client.channel import CredentialHelper, create_channel_with_credentials +from resolver_athena_client.client.channel import CredentialHelper, create_channel_with_credentials async def main(): # Create credential helper with OAuth settings diff --git a/docs/api/client.rst b/docs/api/client.rst index 492e2c9..0d5a6a5 100644 --- a/docs/api/client.rst +++ b/docs/api/client.rst @@ -1,7 +1,7 @@ Client Interface ================ -.. currentmodule:: athena_client.client +.. currentmodule:: resolver_athena_client.client The client module provides the main interface for interacting with Athena services, including the primary client class and authentication helpers. @@ -19,7 +19,7 @@ Example: ... print(result.outputs) -.. currentmodule:: athena_client.client.athena_client +.. currentmodule:: resolver_athena_client.client.athena_client .. autoclass:: AthenaClient :members: @@ -30,7 +30,7 @@ Example: Channel Management ------------------ -.. currentmodule:: athena_client.client.channel +.. currentmodule:: resolver_athena_client.client.channel .. autofunction:: create_channel_with_credentials @@ -55,14 +55,14 @@ Channel Management Deployment Selection -------------------- -.. currentmodule:: athena_client.client.deployment_selector +.. currentmodule:: resolver_athena_client.client.deployment_selector See :doc:`deployment_selector` for deployment discovery and selection functionality. Correlation and Tracing ----------------------- -.. currentmodule:: athena_client.client.correlation +.. currentmodule:: resolver_athena_client.client.correlation .. autoclass:: CorrelationProvider :members: @@ -87,7 +87,7 @@ Correlation and Tracing Utility Functions ----------------- -.. currentmodule:: athena_client.client.utils +.. currentmodule:: resolver_athena_client.client.utils .. autofunction:: process_classification_outputs @@ -109,7 +109,7 @@ Utility Functions Constants --------- -.. currentmodule:: athena_client.client.consts +.. currentmodule:: resolver_athena_client.client.consts Common constants used throughout the client library. @@ -127,9 +127,9 @@ Basic Classification .. code-block:: python import asyncio - from athena_client.client.athena_client import AthenaClient - from athena_client.client.athena_options import AthenaOptions - from athena_client.client.channel import create_channel + from resolver_athena_client.client.athena_client import AthenaClient + from resolver_athena_client.client.athena_options import AthenaOptions + from resolver_athena_client.client.channel import create_channel async def main(): # Create channel with static token @@ -167,9 +167,9 @@ OAuth Authentication import asyncio import os - from athena_client.client.athena_client import AthenaClient - from athena_client.client.athena_options import AthenaOptions - from athena_client.client.channel import ( + from resolver_athena_client.client.athena_client import AthenaClient + from resolver_athena_client.client.athena_options import AthenaOptions + from resolver_athena_client.client.channel import ( CredentialHelper, create_channel_with_credentials ) @@ -205,12 +205,12 @@ Error Handling .. code-block:: python - from athena_client.client.exceptions import ( + from resolver_athena_client.client.exceptions import ( AthenaClientError, AuthenticationError, ConnectionError ) - from athena_client.client.utils import ( + from resolver_athena_client.client.utils import ( process_classification_outputs, has_output_errors ) diff --git a/docs/api/correlation.rst b/docs/api/correlation.rst index 92ad3ec..92c11ee 100644 --- a/docs/api/correlation.rst +++ b/docs/api/correlation.rst @@ -5,7 +5,7 @@ The correlation system provides mechanisms for generating and managing correlation IDs for requests. This system enables request tracing, deduplication, and logging correlation across the service. -.. module:: athena_client.client.correlation +.. module:: resolver_athena_client.client.correlation Correlation Providers ~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/api/deployment_selector.rst b/docs/api/deployment_selector.rst index 4bb36f5..6e6d407 100644 --- a/docs/api/deployment_selector.rst +++ b/docs/api/deployment_selector.rst @@ -5,7 +5,7 @@ The deployment selector provides functionality for discovering and selecting Athena service deployments. It abstracts the complexities of deployment management and provides a simple interface for listing deployments. -.. module:: athena_client.client.deployment_selector +.. module:: resolver_athena_client.client.deployment_selector .. autoclass:: DeploymentSelector :members: diff --git a/docs/api/exceptions.rst b/docs/api/exceptions.rst index a03d010..4c5720a 100644 --- a/docs/api/exceptions.rst +++ b/docs/api/exceptions.rst @@ -1,7 +1,7 @@ Error Handling ============== -.. currentmodule:: athena_client.client.exceptions +.. currentmodule:: resolver_athena_client.client.exceptions The exceptions module provides a comprehensive hierarchy of exception types for handling various error conditions that can occur when using the Athena Client library. @@ -88,7 +88,7 @@ Basic Error Handling .. code-block:: python - from athena_client.client.exceptions import ( + from resolver_athena_client.client.exceptions import ( AthenaError, InvalidAuthError, InvalidRequestError @@ -118,7 +118,7 @@ OAuth Error Handling .. code-block:: python - from athena_client.client.exceptions import ( + from resolver_athena_client.client.exceptions import ( OAuthError, TokenExpiredError, CredentialError @@ -145,8 +145,8 @@ Classification Error Handling .. code-block:: python - from athena_client.client.exceptions import ClassificationOutputError - from athena_client.client.utils import process_classification_outputs + from resolver_athena_client.client.exceptions import ClassificationOutputError + from resolver_athena_client.client.utils import process_classification_outputs try: async for result in results: @@ -174,7 +174,7 @@ Connection Error Handling .. code-block:: python - from athena_client.client.exceptions import InvalidHostError + from resolver_athena_client.client.exceptions import InvalidHostError import asyncio try: @@ -201,7 +201,7 @@ Retry Logic .. code-block:: python import asyncio - from athena_client.client.exceptions import ( + from resolver_athena_client.client.exceptions import ( AthenaError, TokenExpiredError, InvalidRequestError @@ -239,7 +239,7 @@ Graceful Degradation .. code-block:: python - from athena_client.client.exceptions import AthenaError + from resolver_athena_client.client.exceptions import AthenaError async def classify_with_fallback(client, image_iterator, fallback_handler=None): """Classify images with fallback handling.""" diff --git a/docs/api/grpc_wrappers.rst b/docs/api/grpc_wrappers.rst index 0b95c75..69804d0 100644 --- a/docs/api/grpc_wrappers.rst +++ b/docs/api/grpc_wrappers.rst @@ -5,14 +5,14 @@ The GRPC wrappers module provides low-level components for interacting with the Athena service via GRPC. These are consumed by the higher-level clients, you should only use them directly if you are implementing a new client. -See the :doc:`athena_protobufs:index` for more information about the protobuf definitions. +See the athena-protobufs repository for more information about the protobuf definitions. -.. module:: athena_client.grpc_wrappers +.. module:: resolver_athena_client.grpc_wrappers Service Clients --------------- -.. autoclass:: athena_client.grpc_wrappers.classifier_service.ClassifierServiceClient +.. autoclass:: resolver_athena_client.grpc_wrappers.classifier_service.ClassifierServiceClient :members: :special-members: __init__ :show-inheritance: @@ -24,6 +24,6 @@ The ClassifierServiceClient provides direct access to Athena service methods:: classify_response = await client.classify(request) -The ``list_deployments(request)`` method corresponds to :doc:`athena_protobufs:api_reference#listdeployments`. +The ``list_deployments(request)`` method lists available deployments for classification. -The ``classify(request)`` method corresponds to :doc:`athena_protobufs:api_reference#classify`. +The ``classify(request)`` method performs image classification using the configured deployment. diff --git a/docs/api/index.rst b/docs/api/index.rst index cb91b02..3447660 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -60,6 +60,7 @@ Core Modules client options + transformers exceptions grpc_wrappers deployment_selector @@ -68,8 +69,8 @@ Core Modules Module Details -------------- -athena_client.client -~~~~~~~~~~~~~~~~~~~~ +resolver_athena_client.client +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The main client module containing: @@ -78,8 +79,8 @@ The main client module containing: * ``CredentialHelper``: OAuth authentication helper * ``create_channel``: Channel creation utilities -athena_client.client.transformers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +resolver_athena_client.client.transformers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Image processing pipeline components: @@ -88,8 +89,8 @@ Image processing pipeline components: * ``ImageValidator``: Validate image formats and data * ``Pipeline``: Combine multiple transformers -athena_client.client.exceptions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +resolver_athena_client.client.exceptions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Exception hierarchy for error handling: @@ -99,8 +100,8 @@ Exception hierarchy for error handling: * ``ValidationError``: Input validation failures * ``ProcessingError``: Image processing errors -athena_client.grpc_wrappers -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +resolver_athena_client.grpc_wrappers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Low-level gRPC integration: @@ -117,9 +118,9 @@ Basic Client Usage .. code-block:: python - from athena_client.client.athena_client import AthenaClient - from athena_client.client.athena_options import AthenaOptions - from athena_client.client.channel import create_channel_with_credentials + from resolver_athena_client.client.athena_client import AthenaClient + from resolver_athena_client.client.athena_options import AthenaOptions + from resolver_athena_client.client.channel import create_channel_with_credentials # Setup options = AthenaOptions( @@ -141,7 +142,7 @@ Error Handling .. code-block:: python - from athena_client.client.exceptions import ( + from resolver_athena_client.client.exceptions import ( AthenaClientError, AuthenticationError, ConnectionError @@ -166,7 +167,7 @@ Pipeline Processing .. code-block:: python - from athena_client.client.transformers import ImageResizer, BrotliCompressor + from resolver_athena_client.client.transformers import ImageResizer, BrotliCompressor # Create processing pipeline resizer = ImageResizer(target_size=(512, 512)) @@ -223,7 +224,7 @@ Extend the processing pipeline with custom transformers: .. code-block:: python - from athena_client.client.transformers.base import BaseTransformer + from resolver_athena_client.client.transformers.base import BaseTransformer class CustomTransformer(BaseTransformer): async def transform(self, data: bytes) -> bytes: @@ -237,7 +238,7 @@ Dynamically select deployments: .. code-block:: python - from athena_client.client.deployment_selector import DeploymentSelector + from resolver_athena_client.client.deployment_selector import DeploymentSelector async with DeploymentSelector(channel) as selector: deployments = await selector.list_deployments() @@ -250,7 +251,7 @@ Track requests across the system: .. code-block:: python - from athena_client.client.correlation import generate_correlation_id + from resolver_athena_client.client.correlation import generate_correlation_id correlation_id = generate_correlation_id() # Use correlation_id for request tracing diff --git a/docs/api/options.rst b/docs/api/options.rst index d558645..d2f0514 100644 --- a/docs/api/options.rst +++ b/docs/api/options.rst @@ -1,7 +1,7 @@ Configuration Options ===================== -.. currentmodule:: athena_client.client.athena_options +.. currentmodule:: resolver_athena_client.client.athena_options The options module provides configuration classes for customizing client behavior, connection settings, and processing options. @@ -85,7 +85,7 @@ Development Configuration .. code-block:: python - from athena_client.client.athena_options import AthenaOptions + from resolver_athena_client.client.athena_options import AthenaOptions # Development setup dev_options = AthenaOptions( @@ -153,7 +153,7 @@ Environment-Based Configuration .. code-block:: python import os - from athena_client.client.athena_options import AthenaOptions + from resolver_athena_client.client.athena_options import AthenaOptions # Load from environment variables options = AthenaOptions( @@ -281,7 +281,7 @@ Configuration from Files import json from pathlib import Path - from athena_client.client.athena_options import AthenaOptions + from resolver_athena_client.client.athena_options import AthenaOptions def load_options_from_file(config_path: Path) -> AthenaOptions: """Load configuration from a JSON file.""" diff --git a/docs/api/transformers.rst b/docs/api/transformers.rst new file mode 100644 index 0000000..22aff0c --- /dev/null +++ b/docs/api/transformers.rst @@ -0,0 +1,257 @@ +Image Transformers +================== + +The transformers module provides a pipeline of image processing components that prepare images for classification. These transformers work asynchronously and can be chained together to create custom processing pipelines. + +Overview +-------- + +Transformers are designed around the async/await pattern and implement a common interface for processing image data. They can be used individually or combined into processing pipelines. + +Base Classes +------------ + +.. automodule:: resolver_athena_client.client.transformers.async_transformer + :members: + :undoc-members: + :show-inheritance: + +Core Transformers +----------------- + +Image Resizer +~~~~~~~~~~~~~ + +.. automodule:: resolver_athena_client.client.transformers.image_resizer + :members: + :undoc-members: + :show-inheritance: + +The ``ImageResizer`` automatically resizes images to optimal dimensions for classification while maintaining aspect ratio and image quality. + +**Key Features:** + +* Automatic aspect ratio preservation +* Multiple resampling algorithms +* Configurable target dimensions +* Format preservation + +**Example Usage:** + +.. code-block:: python + + from resolver_athena_client.client.transformers import ImageResizer + + resizer = ImageResizer(target_size=(512, 512)) + resized_data = await resizer.transform(image_data) + +Brotli Compressor +~~~~~~~~~~~~~~~~~ + +.. automodule:: resolver_athena_client.client.transformers.brotli_compressor + :members: + :undoc-members: + :show-inheritance: + +The ``BrotliCompressor`` provides efficient compression of image data to reduce bandwidth usage during transmission. + +**Key Features:** + +* High compression ratios +* Fast compression/decompression +* Configurable compression levels +* Automatic format detection + +**Example Usage:** + +.. code-block:: python + + from resolver_athena_client.client.transformers import BrotliCompressor + + compressor = BrotliCompressor(quality=6) + compressed_data = await compressor.transform(image_data) + +Classification Input Transformer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: resolver_athena_client.client.transformers.classification_input + :members: + :undoc-members: + :show-inheritance: + +The ``ClassificationInputTransformer`` converts processed image data into the protobuf format required by the classification service. + +**Key Features:** + +* Automatic format conversion +* Metadata preservation +* Correlation ID management +* Error validation + +Request Batcher +~~~~~~~~~~~~~~~ + +.. automodule:: resolver_athena_client.client.transformers.request_batcher + :members: + :undoc-members: + :show-inheritance: + +The ``RequestBatcher`` groups multiple images into batches for efficient processing. + +**Key Features:** + +* Configurable batch sizes +* Timeout-based batching +* Memory-efficient streaming +* Automatic flush on completion + +**Example Usage:** + +.. code-block:: python + + from resolver_athena_client.client.transformers import RequestBatcher + + batcher = RequestBatcher(batch_size=10, timeout=1.0) + async for batch in batcher.transform(image_stream): + # Process batch + pass + +Pipeline Usage +-------------- + +Transformers can be chained together to create custom processing pipelines: + +.. code-block:: python + + from resolver_athena_client.client.transformers import ( + ImageResizer, + BrotliCompressor, + ClassificationInputTransformer + ) + + # Create processing pipeline + resizer = ImageResizer(target_size=(512, 512)) + compressor = BrotliCompressor(quality=6) + input_transformer = ClassificationInputTransformer( + deployment_id="your-deployment", + affiliate="your-affiliate" + ) + + # Process images through pipeline + async for image_data in image_iterator: + # Resize image + resized = await resizer.transform(image_data) + + # Compress data + compressed = await compressor.transform(resized) + + # Convert to classification input + classification_input = await input_transformer.transform(compressed) + + # Send for classification + yield classification_input + +Custom Transformers +------------------- + +You can create custom transformers by extending the base ``AsyncTransformer`` class: + +.. code-block:: python + + from resolver_athena_client.client.transformers.async_transformer import AsyncTransformer + from resolver_athena_client.client.models import ImageData + + class CustomTransformer(AsyncTransformer): + """Custom image transformer example.""" + + def __init__(self, param1: str, param2: int = 100): + self.param1 = param1 + self.param2 = param2 + + async def transform(self, data: ImageData) -> ImageData: + """Transform image data with custom logic.""" + # Your custom transformation logic here + processed_data = self._process_image(data.content) + + return ImageData( + content=processed_data, + format=data.format, + correlation_id=data.correlation_id + ) + + def _process_image(self, image_bytes: bytes) -> bytes: + """Custom image processing implementation.""" + # Implement your custom processing + return image_bytes + +Performance Considerations +-------------------------- + +When using transformers: + +* **Memory Usage**: Transformers process images in memory. For large batches, consider streaming approaches. +* **CPU Usage**: Image resizing and compression are CPU-intensive. Consider using appropriate batch sizes. +* **Compression Trade-offs**: Higher compression levels reduce bandwidth but increase CPU usage. +* **Pipeline Order**: Order transformers efficiently (e.g., resize before compression). + +**Recommended Pipeline Order:** + +1. **ImageResizer** - Reduce image size early to minimize processing overhead +2. **Custom transformers** - Apply any custom processing to optimally-sized images +3. **BrotliCompressor** - Compress final image data +4. **ClassificationInputTransformer** - Convert to service format + +Error Handling +-------------- + +Transformers can raise various exceptions during processing: + +.. code-block:: python + + from resolver_athena_client.client.exceptions import ( + ImageProcessingError, + ValidationError + ) + + try: + transformed = await transformer.transform(image_data) + except ImageProcessingError as e: + # Handle image processing failures + logger.error(f"Image processing failed: {e}") + except ValidationError as e: + # Handle validation failures + logger.error(f"Invalid image data: {e}") + +For robust applications, implement retry logic and fallback strategies for transformation failures. + +Type Information +---------------- + +All transformers provide comprehensive type hints: + +* Input types: ``ImageData``, ``bytes``, or custom data types +* Output types: ``ImageData``, ``ClassificationInput``, or transformed data +* Async methods: Return ``Awaitable[T]`` where ``T`` is the output type + +Configuration +------------- + +Transformers accept configuration through their constructors: + +**ImageResizer Configuration:** + +* ``target_size``: Tuple of (width, height) for output dimensions +* ``resampling``: PIL resampling algorithm (default: ``Image.LANCZOS``) +* ``maintain_aspect_ratio``: Whether to preserve aspect ratio (default: ``True``) + +**BrotliCompressor Configuration:** + +* ``quality``: Compression quality level 0-11 (default: 6) +* ``window_bits``: Compression window size (default: 22) + +**RequestBatcher Configuration:** + +* ``batch_size``: Maximum items per batch (default: 10) +* ``timeout``: Maximum time to wait for batch completion (default: 1.0) + +See individual transformer documentation for complete configuration options. diff --git a/docs/authentication.rst b/docs/authentication.rst index 38a6700..607de68 100644 --- a/docs/authentication.rst +++ b/docs/authentication.rst @@ -22,7 +22,7 @@ The OAuth credential helper is the recommended authentication method for product .. code-block:: python - from athena_client.client.channel import CredentialHelper, create_channel_with_credentials + from resolver_athena_client.client.channel import CredentialHelper, create_channel_with_credentials # Create credential helper credential_helper = CredentialHelper( @@ -61,9 +61,9 @@ Set these environment variables for OAuth authentication: import os from dotenv import load_dotenv - from athena_client.client.channel import CredentialHelper, create_channel_with_credentials - from athena_client.client.athena_client import AthenaClient - from athena_client.client.athena_options import AthenaOptions + from resolver_athena_client.client.channel import CredentialHelper, create_channel_with_credentials + from resolver_athena_client.client.athena_client import AthenaClient + from resolver_athena_client.client.athena_options import AthenaOptions async def main(): load_dotenv() @@ -119,7 +119,7 @@ Static token authentication is suitable for simple use cases or when you already .. code-block:: python - from athena_client.client.channel import create_channel + from resolver_athena_client.client.channel import create_channel # Use existing authentication token channel = create_channel( @@ -132,9 +132,9 @@ Static token authentication is suitable for simple use cases or when you already .. code-block:: python import asyncio - from athena_client.client.channel import create_channel - from athena_client.client.athena_client import AthenaClient - from athena_client.client.athena_options import AthenaOptions + from resolver_athena_client.client.channel import create_channel + from resolver_athena_client.client.athena_client import AthenaClient + from resolver_athena_client.client.athena_options import AthenaOptions async def main(): # Create channel with static token @@ -258,7 +258,7 @@ Handle OAuth-specific errors gracefully: .. code-block:: python - from athena_client.client.exceptions import AuthenticationError + from resolver_athena_client.client.exceptions import AuthenticationError try: token = await credential_helper.get_token() diff --git a/docs/conf.py b/docs/conf.py index cd7d077..89a4ccc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -10,7 +10,7 @@ sys.path.insert(0, str(Path("../src").resolve())) # Project information -pkg_metadata = metadata("athena_client") +pkg_metadata = metadata("resolver_athena_client") project = pkg_metadata["Name"].replace("-", " ").title() copyright = str(datetime.now(timezone.utc).year) # noqa: A001 - required Sphinx config variable @@ -92,8 +92,8 @@ html_favicon = "_static/images/favicon/favicon-32x32.png" html_css_files = ["css/custom.css"] -# Intersphinx configuration intersphinx_mapping = { + # Disabled due to SSL certificate issues in development "python": ("https://docs.python.org/3", None), "aiohttp": ("https://docs.aiohttp.org/en/stable/", None), "grpc": ("https://grpc.github.io/grpc/python/", None), diff --git a/docs/development.rst b/docs/development.rst index 7d314e7..28215c6 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -228,7 +228,7 @@ Use structured error handling with custom exception types: .. code-block:: python - from athena_client.client.exceptions import AthenaClientError + from resolver_athena_client.client.exceptions import AthenaClientError try: result = await client.classify_image(image_data) @@ -285,7 +285,7 @@ Use pytest with async support: import pytest from unittest.mock import AsyncMock, Mock - from athena_client.client.athena_client import AthenaClient + from resolver_athena_client.client.athena_client import AthenaClient @pytest.fixture async def mock_channel(): diff --git a/docs/examples.rst b/docs/examples.rst index f3b3ab2..00ceb2d 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -30,13 +30,13 @@ This example shows: import os from dotenv import load_dotenv - from athena_client.client.athena_client import AthenaClient - from athena_client.client.athena_options import AthenaOptions - from athena_client.client.channel import ( + from resolver_athena_client.client.athena_client import AthenaClient + from resolver_athena_client.client.athena_options import AthenaOptions + from resolver_athena_client.client.channel import ( CredentialHelper, create_channel_with_credentials, ) - from athena_client.client.deployment_selector import DeploymentSelector + from resolver_athena_client.client.deployment_selector import DeploymentSelector async def main(): load_dotenv() @@ -152,7 +152,7 @@ For simpler use cases, you can use static token authentication: .. code-block:: python - from athena_client.client.channel import create_channel + from resolver_athena_client.client.channel import create_channel # Use a pre-existing authentication token channel = create_channel(host="your-host", auth_token="your-token") @@ -179,7 +179,7 @@ The examples demonstrate comprehensive error handling: .. code-block:: python - from athena_client.client.utils import ( + from resolver_athena_client.client.utils import ( get_output_error_summary, has_output_errors, process_classification_outputs, @@ -323,7 +323,7 @@ Implement retry logic for robust applications: .. code-block:: python import asyncio - from athena_client.client.exceptions import AthenaClientError + from resolver_athena_client.client.exceptions import AthenaClientError max_retries = 3 retry_delay = 1.0 diff --git a/docs/index.rst b/docs/index.rst index 7e0ca77..edce5f0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,9 +23,9 @@ Quick Start .. code-block:: python import asyncio - from athena_client.client.channel import CredentialHelper, create_channel_with_credentials - from athena_client.client.athena_client import AthenaClient - from athena_client.client.athena_options import AthenaOptions + from resolver_athena_client.client.channel import CredentialHelper, create_channel_with_credentials + from resolver_athena_client.client.athena_client import AthenaClient + from resolver_athena_client.client.athena_options import AthenaOptions async def main(): credential_helper = CredentialHelper( @@ -109,7 +109,7 @@ Support * Review the :doc:`examples` for common usage patterns * Check the :doc:`api/index` for detailed API documentation -* See :doc:`athena_protobufs:index` for gRPC API documentation. +* See the gRPC protobuf definitions in the athena-protobufs repository for API details. * Report issues on GitHub * Follow development guidelines in :doc:`contributing` diff --git a/docs/installation.rst b/docs/installation.rst index 317aad9..90451db 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -127,8 +127,8 @@ To verify your installation works correctly: .. code-block:: python - import athena_client - print(athena_client.__version__) + import resolver_athena_client + print(resolver_athena_client.__version__) 2. **Run the test suite**: diff --git a/examples/create_image.py b/examples/create_image.py index 6f26a87..06b75b0 100644 --- a/examples/create_image.py +++ b/examples/create_image.py @@ -6,7 +6,7 @@ from PIL import Image, ImageDraw -from athena_client.client.models import ImageData +from resolver_athena_client.client.models import ImageData # Global cache for reusable objects and constants _image_cache = {} diff --git a/examples/example.py b/examples/example.py index 6341df3..63a7df8 100755 --- a/examples/example.py +++ b/examples/example.py @@ -11,14 +11,14 @@ from create_image import iter_images from dotenv import load_dotenv -from athena_client.client.athena_client import AthenaClient -from athena_client.client.athena_options import AthenaOptions -from athena_client.client.channel import ( +from resolver_athena_client.client.athena_client import AthenaClient +from resolver_athena_client.client.athena_options import AthenaOptions +from resolver_athena_client.client.channel import ( CredentialHelper, create_channel_with_credentials, ) -from athena_client.client.deployment_selector import DeploymentSelector -from athena_client.client.utils import ( +from resolver_athena_client.client.deployment_selector import DeploymentSelector +from resolver_athena_client.client.utils import ( get_output_error_summary, has_output_errors, process_classification_outputs, diff --git a/pyproject.toml b/pyproject.toml index f2d5966..fdcfd8f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "athena_client" +name = "resolver-athena-client" version = "0.0.0" description = "Python client library for Athena API - CSAM detection and content classification" readme = "README.md" @@ -58,7 +58,7 @@ build-backend = "uv_build" [tool.ruff] line-length = 80 -exclude = ["src/athena_client/generated/*"] +exclude = ["src/resolver_athena_client/generated/*"] [tool.ruff.lint] select = ["ALL"] @@ -69,9 +69,9 @@ ignore = ["COM812", "D213", "D211", "D203", "S324"] "tests/**" = ["D", "S106", "S105", "S101", "SLF001"] [tool.pyright] -exclude = ["src/athena_client/generated/*", ".venv"] +exclude = ["src/resolver_athena_client/generated/*", ".venv"] venvPath = "." venv = ".venv" [tool.coverage.run] -omit = ["src/athena_client/generated/*"] +omit = ["src/resolver_athena_client/generated/*"] diff --git a/scripts/compile_proto.sh b/scripts/compile_proto.sh index 975e918..1cec6de 100755 --- a/scripts/compile_proto.sh +++ b/scripts/compile_proto.sh @@ -2,7 +2,7 @@ # Define paths PROTO_DIR="athena-protobufs" -OUT_DIR="src/athena_client/generated" +OUT_DIR="src/resolver_athena_client/generated" # Ensure output directory exists mkdir -p "$OUT_DIR" @@ -15,6 +15,7 @@ fi python -m grpc_tools.protoc \ --proto_path="$PROTO_DIR" \ + --proto_path="$(python -c "import grpc_tools; print(grpc_tools.__path__[0])")" \ --python_out="$OUT_DIR" \ --grpc_python_out="$OUT_DIR" \ --mypy_out="$OUT_DIR" \ @@ -22,11 +23,11 @@ python -m grpc_tools.protoc \ # Fix imports in generated files if [[ "$OSTYPE" == "darwin"* ]]; then - sed -i '' -e 's/^from athena /from athena_client.generated.athena /' "$OUT_DIR/athena/athena_pb2_grpc.py" - sed -i '' -e 's/^from athena /from athena_client.generated.athena /' "$OUT_DIR/athena/athena_pb2.py" + sed -i '' -e 's/^from athena /from resolver_athena_client.generated.athena /' "$OUT_DIR/athena/athena_pb2_grpc.py" + sed -i '' -e 's/^from athena /from resolver_athena_client.generated.athena /' "$OUT_DIR/athena/athena_pb2.py" else - sed -i -e 's/^from athena /from athena_client.generated.athena /' "$OUT_DIR/athena/athena_pb2_grpc.py" - sed -i -e 's/^from athena /from athena_client.generated.athena /' "$OUT_DIR/athena/athena_pb2.py" + sed -i -e 's/^from athena /from resolver_athena_client.generated.athena /' "$OUT_DIR/athena/athena_pb2_grpc.py" + sed -i -e 's/^from athena /from resolver_athena_client.generated.athena /' "$OUT_DIR/athena/athena_pb2.py" fi if [ ! -f "$OUT_DIR/athena/athena_pb2.py" ] || [ ! -f "$OUT_DIR/athena/athena_pb2_grpc.py" ]; then diff --git a/src/athena_client/client/transformers/__init__.py b/src/athena_client/client/transformers/__init__.py deleted file mode 100644 index 2fd7f20..0000000 --- a/src/athena_client/client/transformers/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -"""AsyncIterable transformers for AthenaClient.""" - -from athena_client.client.transformers.async_transformer import AsyncTransformer -from athena_client.client.transformers.brotli_compressor import BrotliCompressor -from athena_client.client.transformers.classification_input import ( - ClassificationInputTransformer, -) -from athena_client.client.transformers.image_resizer import ImageResizer -from athena_client.client.transformers.request_batcher import RequestBatcher - -__all__ = [ - "AsyncTransformer", - "BrotliCompressor", - "ClassificationInputTransformer", - "ImageResizer", - "RequestBatcher", -] diff --git a/src/athena_client/__init__.py b/src/resolver_athena_client/__init__.py similarity index 100% rename from src/athena_client/__init__.py rename to src/resolver_athena_client/__init__.py diff --git a/src/athena_client/client/__init__.py b/src/resolver_athena_client/client/__init__.py similarity index 81% rename from src/athena_client/client/__init__.py rename to src/resolver_athena_client/client/__init__.py index 374c877..94be685 100644 --- a/src/athena_client/client/__init__.py +++ b/src/resolver_athena_client/client/__init__.py @@ -3,17 +3,17 @@ This module provides a client for interacting with the Athena API. """ -from athena_client.client.channel import ( +from resolver_athena_client.client.channel import ( CredentialHelper, create_channel_with_credentials, ) -from athena_client.client.exceptions import ( +from resolver_athena_client.client.exceptions import ( ClassificationOutputError, CredentialError, OAuthError, TokenExpiredError, ) -from athena_client.client.utils import ( +from resolver_athena_client.client.utils import ( get_output_error_summary, get_successful_outputs, has_output_errors, diff --git a/src/athena_client/client/athena_client.py b/src/resolver_athena_client/client/athena_client.py similarity index 93% rename from src/athena_client/client/athena_client.py rename to src/resolver_athena_client/client/athena_client.py index 0e0fe38..2c82897 100644 --- a/src/athena_client/client/athena_client.py +++ b/src/resolver_athena_client/client/athena_client.py @@ -7,21 +7,27 @@ import grpc -from athena_client.client.athena_options import AthenaOptions -from athena_client.client.exceptions import AthenaError -from athena_client.client.models import ImageData -from athena_client.client.transformers.brotli_compressor import BrotliCompressor -from athena_client.client.transformers.classification_input import ( +from resolver_athena_client.client.athena_options import AthenaOptions +from resolver_athena_client.client.exceptions import AthenaError +from resolver_athena_client.client.models import ImageData +from resolver_athena_client.client.transformers.brotli_compressor import ( + BrotliCompressor, +) +from resolver_athena_client.client.transformers.classification_input import ( ClassificationInputTransformer, ) -from athena_client.client.transformers.image_resizer import ImageResizer -from athena_client.client.transformers.request_batcher import RequestBatcher -from athena_client.generated.athena.athena_pb2 import ( +from resolver_athena_client.client.transformers.image_resizer import ( + ImageResizer, +) +from resolver_athena_client.client.transformers.request_batcher import ( + RequestBatcher, +) +from resolver_athena_client.generated.athena.athena_pb2 import ( ClassifyRequest, ClassifyResponse, RequestEncoding, ) -from athena_client.grpc_wrappers.classifier_service import ( +from resolver_athena_client.grpc_wrappers.classifier_service import ( ClassifierServiceClient, ) diff --git a/src/athena_client/client/athena_options.py b/src/resolver_athena_client/client/athena_options.py similarity index 97% rename from src/athena_client/client/athena_options.py rename to src/resolver_athena_client/client/athena_options.py index 2227c51..d0502ea 100644 --- a/src/athena_client/client/athena_options.py +++ b/src/resolver_athena_client/client/athena_options.py @@ -2,7 +2,7 @@ from dataclasses import dataclass -from athena_client.client.correlation import ( +from resolver_athena_client.client.correlation import ( CorrelationProvider, HashCorrelationProvider, ) diff --git a/src/athena_client/client/channel.py b/src/resolver_athena_client/client/channel.py similarity index 99% rename from src/athena_client/client/channel.py rename to src/resolver_athena_client/client/channel.py index 53a22be..8eeaa16 100644 --- a/src/athena_client/client/channel.py +++ b/src/resolver_athena_client/client/channel.py @@ -8,7 +8,7 @@ import httpx from grpc.aio import Channel -from athena_client.client.exceptions import ( +from resolver_athena_client.client.exceptions import ( CredentialError, InvalidHostError, OAuthError, diff --git a/src/athena_client/client/consts.py b/src/resolver_athena_client/client/consts.py similarity index 60% rename from src/athena_client/client/consts.py rename to src/resolver_athena_client/client/consts.py index 48ab963..6884f0f 100644 --- a/src/athena_client/client/consts.py +++ b/src/resolver_athena_client/client/consts.py @@ -1,4 +1,4 @@ -"""Constants for the athena_client's image properties.""" +"""Constants for the resolver_athena_client's image properties.""" # Athena's classifier expects images to be 448x448 pixels. EXPECTED_WIDTH = 448 diff --git a/src/athena_client/client/correlation.py b/src/resolver_athena_client/client/correlation.py similarity index 100% rename from src/athena_client/client/correlation.py rename to src/resolver_athena_client/client/correlation.py diff --git a/src/athena_client/client/deployment_selector.py b/src/resolver_athena_client/client/deployment_selector.py similarity index 94% rename from src/athena_client/client/deployment_selector.py rename to src/resolver_athena_client/client/deployment_selector.py index 36c60aa..020f7bd 100644 --- a/src/athena_client/client/deployment_selector.py +++ b/src/resolver_athena_client/client/deployment_selector.py @@ -5,8 +5,10 @@ import grpc -from athena_client.generated.athena.athena_pb2 import ListDeploymentsResponse -from athena_client.grpc_wrappers.classifier_service import ( +from resolver_athena_client.generated.athena.athena_pb2 import ( + ListDeploymentsResponse, +) +from resolver_athena_client.grpc_wrappers.classifier_service import ( ClassifierServiceClient, ) diff --git a/src/athena_client/client/exceptions.py b/src/resolver_athena_client/client/exceptions.py similarity index 95% rename from src/athena_client/client/exceptions.py rename to src/resolver_athena_client/client/exceptions.py index 1c27a5e..0066414 100644 --- a/src/athena_client/client/exceptions.py +++ b/src/resolver_athena_client/client/exceptions.py @@ -3,7 +3,9 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from athena_client.generated.athena.athena_pb2 import ClassificationError + from resolver_athena_client.generated.athena.athena_pb2 import ( + ClassificationError, + ) class AthenaError(Exception): diff --git a/src/athena_client/client/models/__init__.py b/src/resolver_athena_client/client/models/__init__.py similarity index 100% rename from src/athena_client/client/models/__init__.py rename to src/resolver_athena_client/client/models/__init__.py diff --git a/src/athena_client/client/models/input_model.py b/src/resolver_athena_client/client/models/input_model.py similarity index 100% rename from src/athena_client/client/models/input_model.py rename to src/resolver_athena_client/client/models/input_model.py diff --git a/src/resolver_athena_client/client/transformers/__init__.py b/src/resolver_athena_client/client/transformers/__init__.py new file mode 100644 index 0000000..cac3ca3 --- /dev/null +++ b/src/resolver_athena_client/client/transformers/__init__.py @@ -0,0 +1,25 @@ +"""AsyncIterable transformers for AthenaClient.""" + +from resolver_athena_client.client.transformers.async_transformer import ( + AsyncTransformer, +) +from resolver_athena_client.client.transformers.brotli_compressor import ( + BrotliCompressor, +) +from resolver_athena_client.client.transformers.classification_input import ( + ClassificationInputTransformer, +) +from resolver_athena_client.client.transformers.image_resizer import ( + ImageResizer, +) +from resolver_athena_client.client.transformers.request_batcher import ( + RequestBatcher, +) + +__all__ = [ + "AsyncTransformer", + "BrotliCompressor", + "ClassificationInputTransformer", + "ImageResizer", + "RequestBatcher", +] diff --git a/src/athena_client/client/transformers/async_transformer.py b/src/resolver_athena_client/client/transformers/async_transformer.py similarity index 100% rename from src/athena_client/client/transformers/async_transformer.py rename to src/resolver_athena_client/client/transformers/async_transformer.py diff --git a/src/athena_client/client/transformers/brotli_compressor.py b/src/resolver_athena_client/client/transformers/brotli_compressor.py similarity index 84% rename from src/athena_client/client/transformers/brotli_compressor.py rename to src/resolver_athena_client/client/transformers/brotli_compressor.py index 85e5d84..1e12248 100644 --- a/src/athena_client/client/transformers/brotli_compressor.py +++ b/src/resolver_athena_client/client/transformers/brotli_compressor.py @@ -2,8 +2,8 @@ import brotli -from athena_client.client.models import ImageData -from athena_client.client.transformers.async_transformer import ( +from resolver_athena_client.client.models import ImageData +from resolver_athena_client.client.transformers.async_transformer import ( AsyncTransformer, ) diff --git a/src/athena_client/client/transformers/classification_input.py b/src/resolver_athena_client/client/transformers/classification_input.py similarity index 87% rename from src/athena_client/client/transformers/classification_input.py rename to src/resolver_athena_client/client/transformers/classification_input.py index 907b728..2341b3c 100644 --- a/src/athena_client/client/transformers/classification_input.py +++ b/src/resolver_athena_client/client/transformers/classification_input.py @@ -2,12 +2,12 @@ from collections.abc import AsyncIterator -from athena_client.client.correlation import CorrelationProvider -from athena_client.client.models import ImageData -from athena_client.client.transformers.async_transformer import ( +from resolver_athena_client.client.correlation import CorrelationProvider +from resolver_athena_client.client.models import ImageData +from resolver_athena_client.client.transformers.async_transformer import ( AsyncTransformer, ) -from athena_client.generated.athena.athena_pb2 import ( +from resolver_athena_client.generated.athena.athena_pb2 import ( ClassificationInput, ImageFormat, RequestEncoding, diff --git a/src/athena_client/client/transformers/image_resizer.py b/src/resolver_athena_client/client/transformers/image_resizer.py similarity index 92% rename from src/athena_client/client/transformers/image_resizer.py rename to src/resolver_athena_client/client/transformers/image_resizer.py index cef867c..999aede 100644 --- a/src/athena_client/client/transformers/image_resizer.py +++ b/src/resolver_athena_client/client/transformers/image_resizer.py @@ -6,9 +6,9 @@ from PIL import Image -from athena_client.client.consts import EXPECTED_HEIGHT, EXPECTED_WIDTH -from athena_client.client.models import ImageData -from athena_client.client.transformers.async_transformer import ( +from resolver_athena_client.client.consts import EXPECTED_HEIGHT, EXPECTED_WIDTH +from resolver_athena_client.client.models import ImageData +from resolver_athena_client.client.transformers.async_transformer import ( AsyncTransformer, ) diff --git a/src/athena_client/client/transformers/request_batcher.py b/src/resolver_athena_client/client/transformers/request_batcher.py similarity index 98% rename from src/athena_client/client/transformers/request_batcher.py rename to src/resolver_athena_client/client/transformers/request_batcher.py index 88186e6..df24263 100644 --- a/src/athena_client/client/transformers/request_batcher.py +++ b/src/resolver_athena_client/client/transformers/request_batcher.py @@ -5,7 +5,7 @@ import time from collections.abc import AsyncIterator -from athena_client.generated.athena.athena_pb2 import ( +from resolver_athena_client.generated.athena.athena_pb2 import ( ClassificationInput, ClassifyRequest, ) diff --git a/src/athena_client/client/utils.py b/src/resolver_athena_client/client/utils.py similarity index 96% rename from src/athena_client/client/utils.py rename to src/resolver_athena_client/client/utils.py index 7f412fe..4bb1d1a 100644 --- a/src/athena_client/client/utils.py +++ b/src/resolver_athena_client/client/utils.py @@ -3,10 +3,10 @@ import logging from typing import TYPE_CHECKING -from athena_client.client.exceptions import ClassificationOutputError +from resolver_athena_client.client.exceptions import ClassificationOutputError if TYPE_CHECKING: - from athena_client.generated.athena.athena_pb2 import ( + from resolver_athena_client.generated.athena.athena_pb2 import ( ClassificationOutput, ClassifyResponse, ) diff --git a/src/athena_client/generated/__init__.py b/src/resolver_athena_client/generated/__init__.py similarity index 100% rename from src/athena_client/generated/__init__.py rename to src/resolver_athena_client/generated/__init__.py diff --git a/src/athena_client/generated/athena/__init__.py b/src/resolver_athena_client/generated/athena/__init__.py similarity index 100% rename from src/athena_client/generated/athena/__init__.py rename to src/resolver_athena_client/generated/athena/__init__.py diff --git a/src/athena_client/generated/athena/athena_pb2.py b/src/resolver_athena_client/generated/athena/athena_pb2.py similarity index 100% rename from src/athena_client/generated/athena/athena_pb2.py rename to src/resolver_athena_client/generated/athena/athena_pb2.py diff --git a/src/athena_client/generated/athena/athena_pb2.pyi b/src/resolver_athena_client/generated/athena/athena_pb2.pyi similarity index 100% rename from src/athena_client/generated/athena/athena_pb2.pyi rename to src/resolver_athena_client/generated/athena/athena_pb2.pyi diff --git a/src/athena_client/generated/athena/athena_pb2_grpc.py b/src/resolver_athena_client/generated/athena/athena_pb2_grpc.py similarity index 98% rename from src/athena_client/generated/athena/athena_pb2_grpc.py rename to src/resolver_athena_client/generated/athena/athena_pb2_grpc.py index 139e39e..4dc4e03 100644 --- a/src/athena_client/generated/athena/athena_pb2_grpc.py +++ b/src/resolver_athena_client/generated/athena/athena_pb2_grpc.py @@ -3,7 +3,7 @@ import grpc import warnings -from athena_client.generated.athena import athena_pb2 as athena_dot_athena__pb2 +from resolver_athena_client.generated.athena import athena_pb2 as athena_dot_athena__pb2 from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 GRPC_GENERATED_VERSION = '1.74.0' diff --git a/src/athena_client/grpc_wrappers/__init__.py b/src/resolver_athena_client/grpc_wrappers/__init__.py similarity index 100% rename from src/athena_client/grpc_wrappers/__init__.py rename to src/resolver_athena_client/grpc_wrappers/__init__.py diff --git a/src/athena_client/grpc_wrappers/classifier_service.py b/src/resolver_athena_client/grpc_wrappers/classifier_service.py similarity index 92% rename from src/athena_client/grpc_wrappers/classifier_service.py rename to src/resolver_athena_client/grpc_wrappers/classifier_service.py index 215dbc4..f3ddc20 100644 --- a/src/athena_client/grpc_wrappers/classifier_service.py +++ b/src/resolver_athena_client/grpc_wrappers/classifier_service.py @@ -6,12 +6,14 @@ from google.protobuf.empty_pb2 import Empty from grpc import aio -from athena_client.generated.athena.athena_pb2 import ( +from resolver_athena_client.generated.athena.athena_pb2 import ( ClassifyRequest, ClassifyResponse, ListDeploymentsResponse, ) -from athena_client.generated.athena.athena_pb2_grpc import ClassifierServiceStub +from resolver_athena_client.generated.athena.athena_pb2_grpc import ( + ClassifierServiceStub, +) if TYPE_CHECKING: from grpc.aio import StreamStreamCall diff --git a/src/athena_client/py.typed b/src/resolver_athena_client/py.typed similarity index 100% rename from src/athena_client/py.typed rename to src/resolver_athena_client/py.typed diff --git a/src/athena_client/version.py b/src/resolver_athena_client/version.py similarity index 53% rename from src/athena_client/version.py rename to src/resolver_athena_client/version.py index 461bab2..3edca84 100644 --- a/src/athena_client/version.py +++ b/src/resolver_athena_client/version.py @@ -1,4 +1,4 @@ -"""Single point of truth for the version of the athena_client package.""" +"""Single point of truth for the version of the package.""" import importlib.metadata diff --git a/tests/client/test_athena_client.py b/tests/client/test_athena_client.py index 7d9e40b..8c6e49c 100644 --- a/tests/client/test_athena_client.py +++ b/tests/client/test_athena_client.py @@ -5,17 +5,17 @@ import pytest from grpc import aio -from athena_client.client.athena_client import AthenaClient -from athena_client.client.athena_options import AthenaOptions -from athena_client.client.exceptions import AthenaError -from athena_client.client.models import ImageData -from athena_client.generated.athena.athena_pb2 import ( +from resolver_athena_client.client.athena_client import AthenaClient +from resolver_athena_client.client.athena_options import AthenaOptions +from resolver_athena_client.client.exceptions import AthenaError +from resolver_athena_client.client.models import ImageData +from resolver_athena_client.generated.athena.athena_pb2 import ( ClassificationError, ClassificationOutput, ClassifyResponse, ErrorCode, ) -from athena_client.grpc_wrappers.classifier_service import ( +from resolver_athena_client.grpc_wrappers.classifier_service import ( ClassifierServiceClient, ) from tests.utils.mock_async_iterator import MockAsyncIterator @@ -61,7 +61,7 @@ async def test_classify_images_success( # Setup mock classifier client with mock.patch( - "athena_client.client.athena_client.ClassifierServiceClient" + "resolver_athena_client.client.athena_client.ClassifierServiceClient" ) as mock_client_cls: mock_client = mock_client_cls.return_value @@ -99,7 +99,7 @@ async def test_client_context_manager_success( ) # Success response will have default empty global_error with mock.patch( - "athena_client.client.athena_client.ClassifierServiceClient" + "resolver_athena_client.client.athena_client.ClassifierServiceClient" ) as mock_client_cls: mock_client = mock_client_cls.return_value @@ -138,7 +138,7 @@ async def test_client_context_manager_error( ) with mock.patch( - "athena_client.client.athena_client.ClassifierServiceClient" + "resolver_athena_client.client.athena_client.ClassifierServiceClient" ) as mock_client_cls: mock_client = mock_client_cls.return_value @@ -171,7 +171,7 @@ async def test_client_transformers_disabled( ) with mock.patch( - "athena_client.client.athena_client.ClassifierServiceClient" + "resolver_athena_client.client.athena_client.ClassifierServiceClient" ) as mock_client_cls: mock_client = mock_client_cls.return_value mock_classify = MockAsyncIterator([test_response]) @@ -210,7 +210,7 @@ async def test_client_transformers_enabled( ) with mock.patch( - "athena_client.client.athena_client.ClassifierServiceClient" + "resolver_athena_client.client.athena_client.ClassifierServiceClient" ) as mock_client_cls: mock_client = mock_client_cls.return_value mock_classify = MockAsyncIterator([test_response]) diff --git a/tests/client/test_channel.py b/tests/client/test_channel.py index d612c44..00f0ae3 100644 --- a/tests/client/test_channel.py +++ b/tests/client/test_channel.py @@ -7,12 +7,12 @@ import pytest from grpc.aio import Channel -from athena_client.client.channel import ( +from resolver_athena_client.client.channel import ( CredentialHelper, TokenMetadataPlugin, create_channel_with_credentials, ) -from athena_client.client.exceptions import ( +from resolver_athena_client.client.exceptions import ( CredentialError, InvalidHostError, OAuthError, diff --git a/tests/client/test_deployment_selector.py b/tests/client/test_deployment_selector.py index 2f126f5..f622ec1 100644 --- a/tests/client/test_deployment_selector.py +++ b/tests/client/test_deployment_selector.py @@ -5,12 +5,12 @@ import pytest from grpc import aio -from athena_client.client.deployment_selector import DeploymentSelector -from athena_client.generated.athena.athena_pb2 import ( +from resolver_athena_client.client.deployment_selector import DeploymentSelector +from resolver_athena_client.generated.athena.athena_pb2 import ( Deployment, ListDeploymentsResponse, ) -from athena_client.grpc_wrappers.classifier_service import ( +from resolver_athena_client.grpc_wrappers.classifier_service import ( ClassifierServiceClient, ) @@ -39,7 +39,7 @@ async def test_list_deployments_success(mock_channel: mock.Mock) -> None: # Setup mock with mock.patch( - "athena_client.client.deployment_selector.ClassifierServiceClient" + "resolver_athena_client.client.deployment_selector.ClassifierServiceClient" ) as mock_client_cls: mock_client = mock_client_cls.return_value mock_client.list_deployments = mock.AsyncMock( @@ -72,7 +72,7 @@ async def test_list_deployments_empty(mock_channel: mock.Mock) -> None: # Setup mock with mock.patch( - "athena_client.client.deployment_selector.ClassifierServiceClient" + "resolver_athena_client.client.deployment_selector.ClassifierServiceClient" ) as mock_client_cls: mock_client = mock_client_cls.return_value mock_client.list_deployments = mock.AsyncMock( @@ -97,7 +97,7 @@ async def test_list_deployments_client_error(mock_channel: mock.Mock) -> None: """Test deployment listing when client raises an error.""" # Setup mock to raise error with mock.patch( - "athena_client.client.deployment_selector.ClassifierServiceClient" + "resolver_athena_client.client.deployment_selector.ClassifierServiceClient" ) as mock_client_cls: mock_client = mock_client_cls.return_value mock_client.list_deployments = mock.AsyncMock( diff --git a/tests/client/test_output_error_handling.py b/tests/client/test_output_error_handling.py index dea1090..0078277 100644 --- a/tests/client/test_output_error_handling.py +++ b/tests/client/test_output_error_handling.py @@ -2,15 +2,15 @@ import pytest -from athena_client.client.exceptions import ClassificationOutputError -from athena_client.client.utils import ( +from resolver_athena_client.client.exceptions import ClassificationOutputError +from resolver_athena_client.client.utils import ( get_output_error_summary, get_successful_outputs, has_output_errors, log_output_errors, process_classification_outputs, ) -from athena_client.generated.athena.athena_pb2 import ( +from resolver_athena_client.generated.athena.athena_pb2 import ( Classification, ClassificationError, ClassificationOutput, diff --git a/tests/client/test_timeout_behavior.py b/tests/client/test_timeout_behavior.py index 02ad136..d09907b 100644 --- a/tests/client/test_timeout_behavior.py +++ b/tests/client/test_timeout_behavior.py @@ -10,10 +10,10 @@ from grpc import StatusCode from grpc.aio._call import AioRpcError -from athena_client.client.athena_client import AthenaClient -from athena_client.client.athena_options import AthenaOptions -from athena_client.client.models import ImageData -from athena_client.generated.athena.athena_pb2 import ( +from resolver_athena_client.client.athena_client import AthenaClient +from resolver_athena_client.client.athena_options import AthenaOptions +from resolver_athena_client.client.models import ImageData +from resolver_athena_client.generated.athena.athena_pb2 import ( ClassificationOutput, ClassifyResponse, ) @@ -78,7 +78,7 @@ async def test_timeout_behavior() -> None: ) with mock.patch( - "athena_client.client.athena_client.ClassifierServiceClient" + "resolver_athena_client.client.athena_client.ClassifierServiceClient" ) as mock_client_cls: mock_client = mock_client_cls.return_value # Create an iterator that will wait longer than the timeout @@ -120,7 +120,7 @@ async def test_infinite_timeout() -> None: ) with mock.patch( - "athena_client.client.athena_client.ClassifierServiceClient" + "resolver_athena_client.client.athena_client.ClassifierServiceClient" ) as mock_client_cls: mock_client = mock_client_cls.return_value # Create an iterator with significant delays @@ -167,7 +167,7 @@ async def test_custom_timeout() -> None: ) with mock.patch( - "athena_client.client.athena_client.ClassifierServiceClient" + "resolver_athena_client.client.athena_client.ClassifierServiceClient" ) as mock_client_cls: mock_client = mock_client_cls.return_value # Create an iterator with delays between responses @@ -202,7 +202,7 @@ async def test_timeout_with_errors() -> None: ) with mock.patch( - "athena_client.client.athena_client.ClassifierServiceClient" + "resolver_athena_client.client.athena_client.ClassifierServiceClient" ) as mock_client_cls: mock_client = mock_client_cls.return_value # Use our custom MockGrpcError @@ -240,7 +240,7 @@ async def test_timeout_with_cancellation() -> None: ) with mock.patch( - "athena_client.client.athena_client.ClassifierServiceClient" + "resolver_athena_client.client.athena_client.ClassifierServiceClient" ) as mock_client_cls: mock_client = mock_client_cls.return_value mock_classify = SlowMockAsyncIterator(test_responses, delay=0.01) diff --git a/tests/client/transformers/test_async_transformer.py b/tests/client/transformers/test_async_transformer.py index bfa2282..85fcc8c 100644 --- a/tests/client/transformers/test_async_transformer.py +++ b/tests/client/transformers/test_async_transformer.py @@ -5,7 +5,9 @@ import pytest -from athena_client.client.transformers.async_transformer import AsyncTransformer +from resolver_athena_client.client.transformers.async_transformer import ( + AsyncTransformer, +) from tests.utils.mock_async_iterator import MockAsyncIterator T = TypeVar("T") diff --git a/tests/client/transformers/test_brotli_compressor.py b/tests/client/transformers/test_brotli_compressor.py index 1a6bea3..6860645 100644 --- a/tests/client/transformers/test_brotli_compressor.py +++ b/tests/client/transformers/test_brotli_compressor.py @@ -3,8 +3,10 @@ import brotli import pytest -from athena_client.client.models import ImageData -from athena_client.client.transformers.brotli_compressor import BrotliCompressor +from resolver_athena_client.client.models import ImageData +from resolver_athena_client.client.transformers.brotli_compressor import ( + BrotliCompressor, +) from tests.utils.mock_async_iterator import MockAsyncIterator diff --git a/tests/client/transformers/test_classification_input.py b/tests/client/transformers/test_classification_input.py index 7e53d4e..4092a92 100644 --- a/tests/client/transformers/test_classification_input.py +++ b/tests/client/transformers/test_classification_input.py @@ -2,12 +2,12 @@ import pytest -from athena_client.client.athena_options import AthenaOptions -from athena_client.client.models import ImageData -from athena_client.client.transformers.classification_input import ( +from resolver_athena_client.client.athena_options import AthenaOptions +from resolver_athena_client.client.models import ImageData +from resolver_athena_client.client.transformers.classification_input import ( ClassificationInputTransformer, ) -from athena_client.generated.athena.athena_pb2 import ( +from resolver_athena_client.generated.athena.athena_pb2 import ( ImageFormat, RequestEncoding, ) diff --git a/tests/client/transformers/test_hash_pipeline.py b/tests/client/transformers/test_hash_pipeline.py index 895b589..c5b1b30 100644 --- a/tests/client/transformers/test_hash_pipeline.py +++ b/tests/client/transformers/test_hash_pipeline.py @@ -6,10 +6,14 @@ import pytest from PIL import Image -from athena_client.client.consts import EXPECTED_HEIGHT, EXPECTED_WIDTH -from athena_client.client.models import ImageData -from athena_client.client.transformers.brotli_compressor import BrotliCompressor -from athena_client.client.transformers.image_resizer import ImageResizer +from resolver_athena_client.client.consts import EXPECTED_HEIGHT, EXPECTED_WIDTH +from resolver_athena_client.client.models import ImageData +from resolver_athena_client.client.transformers.brotli_compressor import ( + BrotliCompressor, +) +from resolver_athena_client.client.transformers.image_resizer import ( + ImageResizer, +) from tests.utils.mock_async_iterator import MockAsyncIterator # Constants for hash count assertions diff --git a/tests/client/transformers/test_image_resizer.py b/tests/client/transformers/test_image_resizer.py index 1db0465..8bfe11a 100644 --- a/tests/client/transformers/test_image_resizer.py +++ b/tests/client/transformers/test_image_resizer.py @@ -7,9 +7,11 @@ import pytest from PIL import Image -from athena_client.client.consts import EXPECTED_HEIGHT, EXPECTED_WIDTH -from athena_client.client.models import ImageData -from athena_client.client.transformers.image_resizer import ImageResizer +from resolver_athena_client.client.consts import EXPECTED_HEIGHT, EXPECTED_WIDTH +from resolver_athena_client.client.models import ImageData +from resolver_athena_client.client.transformers.image_resizer import ( + ImageResizer, +) from tests.utils.mock_async_iterator import MockAsyncIterator # Test constants diff --git a/tests/client/transformers/test_request_batcher.py b/tests/client/transformers/test_request_batcher.py index ea0ff92..5ddb9d3 100644 --- a/tests/client/transformers/test_request_batcher.py +++ b/tests/client/transformers/test_request_batcher.py @@ -6,8 +6,10 @@ import pytest -from athena_client.client.transformers.request_batcher import RequestBatcher -from athena_client.generated.athena.athena_pb2 import ( +from resolver_athena_client.client.transformers.request_batcher import ( + RequestBatcher, +) +from resolver_athena_client.generated.athena.athena_pb2 import ( ClassificationInput, ClassifyRequest, RequestEncoding, diff --git a/tests/grpc_wrappers/classifier_service_test.py b/tests/grpc_wrappers/classifier_service_test.py index 9b16c72..d0e21cf 100644 --- a/tests/grpc_wrappers/classifier_service_test.py +++ b/tests/grpc_wrappers/classifier_service_test.py @@ -3,11 +3,11 @@ import pytest from google.protobuf.empty_pb2 import Empty -from athena_client.generated.athena.athena_pb2 import ( +from resolver_athena_client.generated.athena.athena_pb2 import ( ClassifyResponse, ListDeploymentsResponse, ) -from athena_client.grpc_wrappers.classifier_service import ( +from resolver_athena_client.grpc_wrappers.classifier_service import ( ClassifierServiceClient, ) from tests.utils.mock_stream_call import MockStreamCall diff --git a/tests/test_correlation.py b/tests/test_correlation.py index cec8c3d..81d29c5 100644 --- a/tests/test_correlation.py +++ b/tests/test_correlation.py @@ -2,7 +2,7 @@ import pytest -from athena_client.client.correlation import HashCorrelationProvider +from resolver_athena_client.client.correlation import HashCorrelationProvider def test_hash_correlation_provider_with_bytes() -> None: diff --git a/tests/test_version.py b/tests/test_version.py index 9f77595..bb48844 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -3,8 +3,8 @@ import importlib from unittest import mock -import athena_client.version -from athena_client.version import __version__ +import resolver_athena_client.version +from resolver_athena_client.version import __version__ def test_version_from_metadata() -> None: @@ -13,8 +13,8 @@ def test_version_from_metadata() -> None: mock_version.return_value = "1.2.3" # Force reload of version module to get mocked value - importlib.reload(athena_client.version) - assert athena_client.version.__version__ == "1.2.3" + importlib.reload(resolver_athena_client.version) + assert resolver_athena_client.version.__version__ == "1.2.3" def test_version_exists() -> None: diff --git a/uv.lock b/uv.lock index 7a3f19e..8e819b3 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.11'", @@ -42,72 +42,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" }, ] -[[package]] -name = "athena-client" -version = "0.0.0" -source = { editable = "." } -dependencies = [ - { name = "anyio" }, - { name = "brotli" }, - { name = "grpcio-tools" }, - { name = "httpx" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pillow" }, -] - -[package.dev-dependencies] -dev = [ - { name = "load-dotenv" }, - { name = "mypy-protobuf" }, - { name = "pre-commit" }, - { name = "pyright" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "ruff" }, - { name = "types-protobuf" }, -] -docs = [ - { name = "furo" }, - { name = "myst-parser" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx-autodoc-typehints", version = "3.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sphinx-copybutton" }, -] - -[package.metadata] -requires-dist = [ - { name = "anyio", specifier = ">=4.10.0" }, - { name = "brotli", specifier = ">=1.1.0" }, - { name = "grpcio-tools", specifier = ">=1.74.0" }, - { name = "httpx", specifier = ">=0.25.0" }, - { name = "numpy", specifier = ">=2.2.6" }, - { name = "pillow", specifier = ">=11.3.0" }, -] - -[package.metadata.requires-dev] -dev = [ - { name = "load-dotenv", specifier = ">=0.1.0" }, - { name = "mypy-protobuf", specifier = ">=3.6.0" }, - { name = "pre-commit", specifier = ">=4.2.0" }, - { name = "pyright", specifier = ">=1.1.403" }, - { name = "pytest", specifier = ">=8.4.1" }, - { name = "pytest-asyncio", specifier = ">=1.1.0" }, - { name = "pytest-cov", specifier = ">=6.2.1" }, - { name = "ruff", specifier = ">=0.12.7" }, - { name = "types-protobuf", specifier = ">=6.30.2.20250703" }, -] -docs = [ - { name = "furo", specifier = ">=2024.1.29" }, - { name = "myst-parser", specifier = ">=2.0.0" }, - { name = "sphinx", specifier = ">=7.1.0" }, - { name = "sphinx-autodoc-typehints", specifier = ">=1.25.0" }, - { name = "sphinx-copybutton", specifier = ">=0.5.0" }, -] - [[package]] name = "babel" version = "2.17.0" @@ -1218,6 +1152,72 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, ] +[[package]] +name = "resolver-athena-client" +version = "0.0.0" +source = { editable = "." } +dependencies = [ + { name = "anyio" }, + { name = "brotli" }, + { name = "grpcio-tools" }, + { name = "httpx" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pillow" }, +] + +[package.dev-dependencies] +dev = [ + { name = "load-dotenv" }, + { name = "mypy-protobuf" }, + { name = "pre-commit" }, + { name = "pyright" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-cov" }, + { name = "ruff" }, + { name = "types-protobuf" }, +] +docs = [ + { name = "furo" }, + { name = "myst-parser" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx-autodoc-typehints", version = "3.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx-copybutton" }, +] + +[package.metadata] +requires-dist = [ + { name = "anyio", specifier = ">=4.10.0" }, + { name = "brotli", specifier = ">=1.1.0" }, + { name = "grpcio-tools", specifier = ">=1.74.0" }, + { name = "httpx", specifier = ">=0.25.0" }, + { name = "numpy", specifier = ">=2.2.6" }, + { name = "pillow", specifier = ">=11.3.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "load-dotenv", specifier = ">=0.1.0" }, + { name = "mypy-protobuf", specifier = ">=3.6.0" }, + { name = "pre-commit", specifier = ">=4.2.0" }, + { name = "pyright", specifier = ">=1.1.403" }, + { name = "pytest", specifier = ">=8.4.1" }, + { name = "pytest-asyncio", specifier = ">=1.1.0" }, + { name = "pytest-cov", specifier = ">=6.2.1" }, + { name = "ruff", specifier = ">=0.12.7" }, + { name = "types-protobuf", specifier = ">=6.30.2.20250703" }, +] +docs = [ + { name = "furo", specifier = ">=2024.1.29" }, + { name = "myst-parser", specifier = ">=2.0.0" }, + { name = "sphinx", specifier = ">=7.1.0" }, + { name = "sphinx-autodoc-typehints", specifier = ">=1.25.0" }, + { name = "sphinx-copybutton", specifier = ">=0.5.0" }, +] + [[package]] name = "roman-numerals-py" version = "3.1.0"