Skip to content

Commit

Permalink
chore: fix docstring warnings and improvements (#93)
Browse files Browse the repository at this point in the history
Co-authored-by: Glenn Jocher <glenn.jocher@ultralytics.com>
Co-authored-by: UltralyticsAssistant <web@ultralytics.com>
  • Loading branch information
3 people committed Jan 26, 2024
1 parent 6afef3c commit 020ca35
Show file tree
Hide file tree
Showing 17 changed files with 549 additions and 422 deletions.
37 changes: 37 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Ultralytics HUB-SDK 🚀, AGPL-3.0 license
# HUB-SDK Continuous Integration (CI) GitHub Actions tests

name: HUB-SDK CI

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
- cron: "0 0 * * *" # runs at 00:00 UTC every day
workflow_dispatch:

jobs:
Docs:
runs-on: ubuntu-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
cache: "pip" # caching pip dependencies

- name: Install Dependencies
run: |
pip install -e ".[dev]"
shell: bash

- name: Build Docs and Check for Warnings
run: |
mkdocs build --strict
shell: bash
106 changes: 78 additions & 28 deletions hub_sdk/base/api_client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Ultralytics HUB-SDK 🚀, AGPL-3.0 License

from typing import Dict, Optional
import requests

from hub_sdk.config import HUB_EXCEPTIONS
Expand All @@ -8,7 +9,22 @@


class APIClientError(Exception):
def __init__(self, message: str, status_code: int = None):
"""
Custom exception class for API client errors.
Attributes:
message (str): A human-readable error message.
status_code (int): The HTTP status code associated with the error, if available.
"""

def __init__(self, message: str, status_code: Optional[int] = None):
"""
Initialize the APIClientError instance.
Args:
message (str): A human-readable error message.
status_code (int, optional): The HTTP status code associated with the error, if available.
"""
super().__init__(message)
self.status_code = status_code
self.message = message
Expand All @@ -18,39 +34,56 @@ def __str__(self) -> str:


class APIClient:
def __init__(self, base_url: str, headers: dict = None):
"""
Represents an API client for making requests to a specified base URL.
Attributes:
base_url (str): The base URL for the API.
headers (dict, None): Headers to be included in each request.
logger (logging.Logger): An instance of the logger for logging purposes.
"""

def __init__(self, base_url: str, headers: Optional[Dict] = None):
"""
Initialize an instance of the APIClient class.
Args:
base_url (str): The base URL for the API.
headers (dict, optional): Headers to be included in each request. Defaults to None.
headers (dict, optional): Headers to be included in each request.
"""
self.base_url = base_url
self.headers = headers
self.logger = logger

def _make_request(
self, method: str, endpoint: str, data: dict = None, json=None, params=None, files=None, stream: bool = False
):
self,
method: str,
endpoint: str,
data: Optional[Dict] = None,
json: Optional[Dict] = None,
params: Optional[Dict] = None,
files: Optional[Dict] = None,
stream: bool = False,
) -> Optional[requests.Response]:
"""
Make an HTTP request to the API.
Args:
method (str): The HTTP method to use for the request (e.g., "GET", "POST").
endpoint (str): The endpoint to append to the base URL for the request.
data (dict, optional): Data to be sent in the request's body. Defaults to None.
json_data (dict, optional): JSON data to be sent in the request's body. Defaults to None.
params (dict, optional): Query parameters for the request. Defaults to None.
files (dict, optional): Files to be sent as part of the form data. Defaults to None.
stream (bool, optional): Whether to stream the response content. Defaults to False.
data (dict, optional): Data to be sent in the request's body.
json (dict, optional): JSON data to be sent in the request's body.
params (dict, optional): Query parameters for the request.
files (dict, optional): Files to be sent as part of the form data.
stream (bool, optional): Whether to stream the response content.
Returns:
requests.Response: The response object from the HTTP request.
(Optional[requests.Response]): The response object from the HTTP request, None if it fails and
HUB_EXCEPTIONS off.
Raises:
APIClientError: If an error occurs during the request, this exception is raised with an appropriate message
based on the HTTP status code.
(APIClientError): If an error occurs during the request, this exception is raised with an appropriate
message based on the HTTP status code.
"""
# Overwrite the base url if a http url is submitted
url = endpoint if endpoint.startswith("http") else self.base_url + endpoint
Expand Down Expand Up @@ -78,68 +111,85 @@ def _make_request(
self.logger.error(error_msg)

if not HUB_EXCEPTIONS:
raise APIClientError(error_msg, status_code=response.status_code) from e
raise APIClientError(error_msg, status_code=status_code) from e

def get(self, endpoint: str, params=None):
def get(self, endpoint: str, params=None) -> Optional[requests.Response]:
"""
Make a GET request to the API.
Args:
endpoint (str): The endpoint to append to the base URL for the request.
params (dict, optional): Query parameters for the request. Defaults to None.
params (dict, optional): Query parameters for the request.
Returns:
requests.Response: The response object from the HTTP GET request.
(Optional[requests.Response]): The response object from the HTTP GET request, None if it fails.
"""
return self._make_request("GET", endpoint, params=params)

def post(self, endpoint: str, data: dict = None, json=None, files=None, stream=False):
def post(
self,
endpoint: str,
data: Optional[Dict] = None,
json: Optional[Dict] = None,
files: Optional[Dict] = None,
stream=False,
) -> Optional[requests.Response]:
"""
Make a POST request to the API.
Args:
endpoint (str): The endpoint to append to the base URL for the request.
data (dict, optional): Data to be sent in the request's body. Defaults to None.
data (dict, optional): Data to be sent in the request's body.
json (dict, optional): JSON data to be sent in the request's body.
files (dict, optional): Files to be included in the request, if any.
stream (bool, optional): If True, the response content will be streamed.
Returns:
requests.Response: The response object from the HTTP POST request.
(Optional[requests.Response]): The response object from the HTTP POST request.
"""
return self._make_request("POST", endpoint, data=data, json=json, files=files, stream=stream)

def put(self, endpoint: str, data=None, json=None):
def put(
self, endpoint: str, data: Optional[Dict] = None, json: Optional[Dict] = None
) -> Optional[requests.Response]:
"""
Make a PUT request to the API.
Args:
endpoint (str): The endpoint to append to the base URL for the request.
data (dict, optional): Data to be sent in the request's body. Defaults to None.
data (Optional[Dict], optional): Data to be sent in the request's body.
json (Optional[Dict], optional): JSON data to be sent in the request's body
Returns:
requests.Response: The response object from the HTTP PUT request.
(Optional[requests.Response]): The response object from the HTTP PUT request.
"""
return self._make_request("PUT", endpoint, data=data, json=json)

def delete(self, endpoint: str, params=None):
def delete(self, endpoint: str, params: Optional[Dict] = None) -> Optional[requests.Response]:
"""
Make a DELETE request to the API.
Args:
endpoint (str): The endpoint to append to the base URL for the request.
params (dict, optional): Parameters to include in the request.
Returns:
requests.Response: The response object from the HTTP DELETE request.
(Optional[requests.Response]): The response object from the HTTP DELETE request, or None if it fails.
"""
return self._make_request("DELETE", endpoint, params=params)

def patch(self, endpoint: str, data=None, json=None):
def patch(
self, endpoint: str, data: Optional[Dict] = None, json: Optional[Dict] = None
) -> Optional[requests.Response]:
"""
Make a PATCH request to the API.
Args:
endpoint (str): The endpoint to append to the base URL for the request.
data (dict, optional): Data to be sent in the request's body. Defaults to None.
data (dict, optional): Data to be sent in the request's body.
json (dict, optional): JSON data to be sent in the request's body.
Returns:
requests.Response: The response object from the HTTP PATCH request.
(Optional[requests.Response]): The response object from the HTTP PATCH request, or None if it fails.
"""
return self._make_request("PATCH", endpoint, data=data, json=json)
25 changes: 19 additions & 6 deletions hub_sdk/base/auth.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Ultralytics HUB-SDK 🚀, AGPL-3.0 License

from typing import Optional
from distutils.sysconfig import PREFIX

import requests
Expand All @@ -10,15 +11,27 @@


class Auth:
"""
Represents an authentication manager.
Attributes:
api_key (str, None): The API key used for authentication.
id_token (str, None): The authentication token.
"""

def __init__(self):
self.get_auth_header = None
self.api_key = None
self.id_token = None

def authenticate(self) -> bool:
"""
Attempt to authenticate with the server using either id_token or API key.
Returns:
bool: True if authentication is successful, False otherwise.
(bool): True if authentication is successful, False otherwise.
Raises:
(ConnectionError): If request response is hasn't success in json, raised connection error exception.
"""
try:
header = self.get_auth_header()
Expand All @@ -41,12 +54,12 @@ def authenticate(self) -> bool:
self.id_token = self.api_key = False # reset invalid
return False

def get_auth_header(self):
def get_auth_header(self) -> Optional[dict]:
"""
Get the authentication header for making API requests.
Returns:
(dict): The authentication header if id_token or API key is set, None otherwise.
(Optional[dict]): The authentication header if id_token or API key is set, None otherwise.
"""
if self.id_token:
return {"authorization": f"Bearer {self.id_token}"}
Expand All @@ -60,7 +73,7 @@ def get_state(self) -> bool:
Get the authentication state.
Returns:
bool: True if either id_token or API key is set, False otherwise.
(bool): True if either id_token or API key is set, False otherwise.
"""
return self.id_token or self.api_key

Expand All @@ -82,7 +95,7 @@ def authorize(self, email: str, password: str) -> bool:
password (str): User's password.
Returns:
bool: True if authorization is successful, False otherwise.
(bool): True if authorization is successful, False otherwise.
"""
try:
headers = {"origin": HUB_WEB_ROOT}
Expand Down
Loading

0 comments on commit 020ca35

Please sign in to comment.