Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions docs/api/hub.polarisfs.md

This file was deleted.

1 change: 0 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ nav:
- Hub:
- Client: api/hub.client.md
- External Auth Client: api/hub.external_client.md
- PolarisFileSystem: api/hub.polarisfs.md
- Additional:
- Dataset Factory: api/factory.md
- Data Converters: api/converters.md
Expand Down
83 changes: 27 additions & 56 deletions polaris/hub/client.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import json
import ssl
from hashlib import md5
from io import BytesIO
from typing import get_args
from urllib.parse import urljoin

import certifi
import httpx
import pandas as pd
import zarr
from authlib.integrations.base_client.errors import InvalidTokenError, MissingTokenError
from authlib.integrations.httpx_client import OAuth2Client, OAuthError
from authlib.oauth2 import OAuth2Error, TokenAuth
from authlib.oauth2.rfc6749 import OAuth2Token
from httpx import HTTPStatusError, Response
from httpx import ConnectError, HTTPStatusError, Response
from loguru import logger
from typing_extensions import Self

Expand All @@ -36,6 +34,7 @@
PolarisCreateArtifactError,
PolarisHubError,
PolarisRetrieveArtifactError,
PolarisSSLError,
PolarisUnauthorizedError,
)
from polaris.utils.types import (
Expand Down Expand Up @@ -189,42 +188,31 @@ def fetch_token(self, **kwargs):

def _base_request_to_hub(self, url: str, method: str, **kwargs) -> Response:
"""Utility function since most API methods follow the same pattern"""
response = self.request(url=url, method=method, **kwargs)
try:
response = self.request(url=url, method=method, **kwargs)
response.raise_for_status()
return response
except HTTPStatusError as error:
response_status_code = response.status_code

# With an internal server error, we are not sure the custom error-handling code on the hub is reached.
if response_status_code == 500:
raise

# If JSON is included in the response body, we retrieve it and format it for output. If not, we fallback to
# retrieving plain text from the body. This is important for handling certain errors thrown from the backend
# which do not contain JSON in the response.
# If JSON is included in the response body, we retrieve it and format it for output. If not, we fall back to
# retrieving plain text from the body. 500 errors will not have a JSON response.
try:
response = response.json()
response = json.dumps(response, indent=2, sort_keys=True)
response_text = error.response.json()
response_text = json.dumps(response_text, indent=2, sort_keys=True)
except (json.JSONDecodeError, TypeError):
response = response.text

# Providing the user a more helpful error message suggesting a re-login when their access
# credentials expire.
if response_status_code == 401:
raise PolarisUnauthorizedError(response=response) from error

# The below two error cases can happen due to the JWT token containing outdated information.
# We therefore throw a custom error with a recommended next step.
if response_status_code == 403:
# This happens when trying to create an artifact for an owner the user has no access to.
raise PolarisCreateArtifactError(response=response) from error

if response_status_code == 404:
# This happens when an artifact doesn't exist _or_ when the user has no access to that artifact.
raise PolarisRetrieveArtifactError(response=response) from error

raise PolarisHubError(response=response) from error
response_text = error.response.text

match error.response.status_code:
case 401:
# Providing the user a more helpful error message suggesting a re-login when their access credentials expire.
raise PolarisUnauthorizedError(response_text=response_text) from error
case 403:
# The error can happen due to the JWT token containing outdated information.
raise PolarisCreateArtifactError(response_text=response_text) from error
case 404:
# This happens when an artifact doesn't exist _or_ when the user has no access to that artifact.
raise PolarisRetrieveArtifactError(response_text=response_text) from error
case _:
raise PolarisHubError(response_text=response_text) from error

def get_metadata_from_response(self, response: Response, key: str) -> str | None:
"""Get custom metadata saved to the R2 object from the headers."""
Expand All @@ -235,31 +223,14 @@ def request(self, method, url, withhold_token=False, auth=httpx.USE_CLIENT_DEFAU
"""Wraps the base request method to handle errors"""
try:
return super().request(method, url, withhold_token, auth, **kwargs)
except httpx.ConnectError as error:
except ConnectError as error:
# NOTE (cwognum): In the stack trace, the more specific SSLCertVerificationError is raised.
# We could use the traceback module to catch this specific error, but that would be overkill here.
if _HTTPX_SSL_ERROR_CODE in str(error):
raise ssl.SSLCertVerificationError(
"We could not verify the SSL certificate. "
f"Please ensure the installed version ({certifi.__version__}) of the `certifi` package is the latest. "
"If you require the usage of a custom CA bundle, you can set the POLARIS_CA_BUNDLE "
"environment variable to the path of your CA bundle. For debugging, you can temporarily disable "
"SSL verification by setting the POLARIS_CA_BUNDLE environment variable to `false`."
) from error
raise error
except (MissingTokenError, InvalidTokenError, httpx.HTTPStatusError, OAuthError) as error:
if isinstance(error, httpx.HTTPStatusError) and error.response.status_code != 401:
raise

# The `MissingTokenError`, `InvalidTokenError` and `OAuthError` errors from the AuthlibBaseError
# class do not hold the `response` attribute. To prevent a misleading `AttributeError` from
# being thrown, we conditionally set the error response below based on the error type.
if isinstance(error, httpx.HTTPStatusError):
error_response = error.response
else:
error_response = None

raise PolarisUnauthorizedError(response=error_response) from error
error_string = str(error)
ExceptionClass = PolarisSSLError if _HTTPX_SSL_ERROR_CODE in error_string else PolarisHubError
raise ExceptionClass(error_string) from error
except (MissingTokenError, InvalidTokenError, OAuthError) as error:
raise PolarisUnauthorizedError() from error

def login(self, overwrite: bool = False, auto_open_browser: bool = True):
"""Login to the Polaris Hub using the OAuth2 protocol.
Expand Down
250 changes: 0 additions & 250 deletions polaris/hub/polarisfs.py

This file was deleted.

Loading
Loading