diff --git a/.gitignore b/.gitignore index deb14a6..e2447dc 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,8 @@ instance/ # Sphinx documentation docs/_build/ +docs/source/_build/ + # PyBuilder target/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8ceeec5 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,21 @@ +# Contributing to horde_sdk + +Here are a list of code quality tools this project uses: + +* [tox](https://tox.wiki/) + - Creates virtual environments for CI or local pytest runs. + - Note that the CI does not current execute calls to the production API by default. + - Run `tox list` or see `tox.ini` for more info +* [pre-commit](https://pre-commit.com/) + - Creates virtual environments for formatting and linting tools + - Run `pre-commit run --all-files` or see `.pre-commit-config.yaml` for more info. +* [black](https://github.com/psf/black) + - Whitespace formatter +* [ruff](https://github.com/astral-sh/ruff) + - Linting rules from a wide variety of selectable rule sets + - See `pyproject.toml` for the rules used. + - See *all* rules availible in rust [here](https://beta.ruff.rs/docs/rules/). +* [mypy](https://mypy-lang.org/) + - Static type safety + - I recommending using the [mypy daemon](https://mypy.readthedocs.io/en/stable/mypy_daemon.html). + - If you are using VSCode, I recommend the `matangover.mypy` extension, which implements this nicely. diff --git a/README.md b/README.md index 5c99ff2..9cec5cb 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,5 @@ With the power of pydantic, you can simplify interfacing with the [AI-Horde's suite of APIs](https://github.com/db0/AI-Horde). Whether you want to request your own images, or roll your own worker software, this package may suit your needs for anything horde related. -## General notes -- Certain API models have attributes which may collide with a python builtin, such as `id` or `type`. In these cases, the attribute has a trailing underscore, as in `id_`. Ingested json still will work with the field 'id' (its a alias). - -## AI-Horde -`TODO` - -## Ratings API -There is currently some support for the [Image Ratings](https://dbzer0.com/blog/the-image-ratings-are-flooding-in/) API that are rating images from the [DiffusionDB](https://poloclub.github.io/diffusiondb/) dataset. See `example.py` for an idea of how to start. +## Documentation +For detailed documentation, see our [read the docs page](https://horde-sdk.readthedocs.io/en/latest/index.html). diff --git a/examples/ai_horde_api_client.example.py b/examples/ai_horde_api_client.example.py index 3e39bd6..88ed9b5 100644 --- a/examples/ai_horde_api_client.example.py +++ b/examples/ai_horde_api_client.example.py @@ -1,6 +1,6 @@ from horde_sdk.ai_horde_api import AIHordeAPIClient from horde_sdk.ai_horde_api.apimodels import ImageGenerateAsyncRequest -from horde_sdk.generic_api import RequestErrorResponse +from horde_sdk.generic_api.apimodels import RequestErrorResponse def do_generate_check(ai_horde_api_client: AIHordeAPIClient) -> None: diff --git a/examples/async_ai_horde_api_client_example.py b/examples/async_ai_horde_api_client_example.py index 6f4e4da..e9de72f 100644 --- a/examples/async_ai_horde_api_client_example.py +++ b/examples/async_ai_horde_api_client_example.py @@ -4,7 +4,7 @@ from horde_sdk.ai_horde_api import AIHordeAPIClient from horde_sdk.ai_horde_api.apimodels import ImageGenerateAsyncRequest -from horde_sdk.generic_api import RequestErrorResponse +from horde_sdk.generic_api.apimodels import RequestErrorResponse async def main() -> None: diff --git a/examples/ratings_api_client_example.py b/examples/ratings_api_client_example.py index 817a6cd..c50a89e 100644 --- a/examples/ratings_api_client_example.py +++ b/examples/ratings_api_client_example.py @@ -4,14 +4,15 @@ import os import pydantic -from horde_sdk.ratings_api import ( + +from horde_sdk.ratings_api.apimodels import ( ImageRatingsComparisonTypes, - RatingsAPIClient, SelectableReturnFormats, UserValidateRequest, UserValidateResponse, UserValidateResponseRecord, ) +from horde_sdk.ratings_api.ratings_client import RatingsAPIClient # See also in horde_sdk.ratings_api: # UserCheckRequest, diff --git a/src/horde_sdk/__init__.py b/horde_sdk/__init__.py similarity index 100% rename from src/horde_sdk/__init__.py rename to horde_sdk/__init__.py diff --git a/src/horde_sdk/ai_horde_api/__init__.py b/horde_sdk/ai_horde_api/__init__.py similarity index 100% rename from src/horde_sdk/ai_horde_api/__init__.py rename to horde_sdk/ai_horde_api/__init__.py diff --git a/src/horde_sdk/ai_horde_api/ai_horde_client.py b/horde_sdk/ai_horde_api/ai_horde_client.py similarity index 98% rename from src/horde_sdk/ai_horde_api/ai_horde_client.py rename to horde_sdk/ai_horde_api/ai_horde_client.py index 895a619..b1f3be2 100644 --- a/src/horde_sdk/ai_horde_api/ai_horde_client.py +++ b/horde_sdk/ai_horde_api/ai_horde_client.py @@ -13,7 +13,8 @@ from horde_sdk.ai_horde_api.endpoints import AI_HORDE_BASE_URL from horde_sdk.ai_horde_api.fields import GenerationID from horde_sdk.ai_horde_api.metadata import AIHordePathData -from horde_sdk.generic_api import GenericHordeAPIClient, RequestErrorResponse +from horde_sdk.generic_api.apimodels import RequestErrorResponse +from horde_sdk.generic_api.generic_client import GenericHordeAPIClient class AIHordeAPIClient(GenericHordeAPIClient): diff --git a/src/horde_sdk/ai_horde_api/apimodels/__init__.py b/horde_sdk/ai_horde_api/apimodels/__init__.py similarity index 95% rename from src/horde_sdk/ai_horde_api/apimodels/__init__.py rename to horde_sdk/ai_horde_api/apimodels/__init__.py index 784e05f..9776839 100644 --- a/src/horde_sdk/ai_horde_api/apimodels/__init__.py +++ b/horde_sdk/ai_horde_api/apimodels/__init__.py @@ -1,4 +1,4 @@ -from horde_sdk.ai_horde_api.apimodels._stats import StatsImageModels, StatsModelsResponse +from horde_sdk.ai_horde_api.apimodels._stats import StatsImageModelsRequest, StatsModelsResponse from horde_sdk.ai_horde_api.apimodels.generate._async import ImageGenerateAsyncRequest, ImageGenerateAsyncResponse from horde_sdk.ai_horde_api.apimodels.generate._check import ImageGenerateCheckRequest, ImageGenerateCheckResponse from horde_sdk.ai_horde_api.apimodels.generate._pop import ImageGenerateJobPopRequest, ImageGenerateJobResponse @@ -23,7 +23,7 @@ "ImageGenerateStatusRequest", "ImageGenerateStatusResponse", "DeleteImageGenerateRequest", - "StatsImageModels", + "StatsImageModelsRequest", "StatsModelsResponse", "ImageGenerationJobSubmitRequest", "ImageGenerationJobSubmitResponse", diff --git a/src/horde_sdk/ai_horde_api/apimodels/_stats.py b/horde_sdk/ai_horde_api/apimodels/_stats.py similarity index 93% rename from src/horde_sdk/ai_horde_api/apimodels/_stats.py rename to horde_sdk/ai_horde_api/apimodels/_stats.py index bcccf30..694efd5 100644 --- a/src/horde_sdk/ai_horde_api/apimodels/_stats.py +++ b/horde_sdk/ai_horde_api/apimodels/_stats.py @@ -12,8 +12,6 @@ class StatsModelsResponse(BaseResponse): v2 API Model: `ImgModelStats` """ - model_config = {"frozen": True} - day: dict[str, int] month: dict[str, int] total: dict[str, int] @@ -24,7 +22,7 @@ def get_api_model_name(cls) -> str | None: return "ImgModelStats" -class StatsImageModels(BaseAIHordeRequest): +class StatsImageModelsRequest(BaseAIHordeRequest): """Represents the data needed to make a request to the `/v2/stats/img/models` endpoint.""" @override diff --git a/src/horde_sdk/ai_horde_api/apimodels/base.py b/horde_sdk/ai_horde_api/apimodels/base.py similarity index 98% rename from src/horde_sdk/ai_horde_api/apimodels/base.py rename to horde_sdk/ai_horde_api/apimodels/base.py index 22b3e1c..d382540 100644 --- a/src/horde_sdk/ai_horde_api/apimodels/base.py +++ b/horde_sdk/ai_horde_api/apimodels/base.py @@ -9,6 +9,8 @@ class BaseAIHordeRequest(BaseRequest): + """Base class for all AI Horde API requests.""" + @override @classmethod def get_api_url(cls) -> str: diff --git a/src/horde_sdk/ai_horde_api/apimodels/generate/__init__.py b/horde_sdk/ai_horde_api/apimodels/generate/__init__.py similarity index 100% rename from src/horde_sdk/ai_horde_api/apimodels/generate/__init__.py rename to horde_sdk/ai_horde_api/apimodels/generate/__init__.py diff --git a/src/horde_sdk/ai_horde_api/apimodels/generate/_async.py b/horde_sdk/ai_horde_api/apimodels/generate/_async.py similarity index 100% rename from src/horde_sdk/ai_horde_api/apimodels/generate/_async.py rename to horde_sdk/ai_horde_api/apimodels/generate/_async.py diff --git a/src/horde_sdk/ai_horde_api/apimodels/generate/_check.py b/horde_sdk/ai_horde_api/apimodels/generate/_check.py similarity index 100% rename from src/horde_sdk/ai_horde_api/apimodels/generate/_check.py rename to horde_sdk/ai_horde_api/apimodels/generate/_check.py diff --git a/src/horde_sdk/ai_horde_api/apimodels/generate/_pop.py b/horde_sdk/ai_horde_api/apimodels/generate/_pop.py similarity index 100% rename from src/horde_sdk/ai_horde_api/apimodels/generate/_pop.py rename to horde_sdk/ai_horde_api/apimodels/generate/_pop.py diff --git a/src/horde_sdk/ai_horde_api/apimodels/generate/_status.py b/horde_sdk/ai_horde_api/apimodels/generate/_status.py similarity index 100% rename from src/horde_sdk/ai_horde_api/apimodels/generate/_status.py rename to horde_sdk/ai_horde_api/apimodels/generate/_status.py diff --git a/src/horde_sdk/ai_horde_api/apimodels/generate/_submit.py b/horde_sdk/ai_horde_api/apimodels/generate/_submit.py similarity index 100% rename from src/horde_sdk/ai_horde_api/apimodels/generate/_submit.py rename to horde_sdk/ai_horde_api/apimodels/generate/_submit.py diff --git a/horde_sdk/ai_horde_api/apimodels/workers/__init__.py b/horde_sdk/ai_horde_api/apimodels/workers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/horde_sdk/ai_horde_api/apimodels/workers/_workers_all.py b/horde_sdk/ai_horde_api/apimodels/workers/_workers_all.py similarity index 99% rename from src/horde_sdk/ai_horde_api/apimodels/workers/_workers_all.py rename to horde_sdk/ai_horde_api/apimodels/workers/_workers_all.py index 99d977f..9fa440d 100644 --- a/src/horde_sdk/ai_horde_api/apimodels/workers/_workers_all.py +++ b/horde_sdk/ai_horde_api/apimodels/workers/_workers_all.py @@ -1,11 +1,12 @@ +from pydantic import BaseModel, Field +from typing_extensions import override + from horde_sdk.ai_horde_api.apimodels.base import BaseAIHordeRequest from horde_sdk.ai_horde_api.consts import WORKER_TYPE from horde_sdk.ai_horde_api.endpoints import AI_HORDE_API_URL_Literals from horde_sdk.ai_horde_api.fields import TeamID, WorkerID from horde_sdk.consts import HTTPMethod from horde_sdk.generic_api.apimodels import BaseRequestAuthenticated, BaseResponse, HordeAPIModel -from pydantic import BaseModel, Field -from typing_extensions import override class TeamDetailsLite(HordeAPIModel): diff --git a/src/horde_sdk/ai_horde_api/consts.py b/horde_sdk/ai_horde_api/consts.py similarity index 100% rename from src/horde_sdk/ai_horde_api/consts.py rename to horde_sdk/ai_horde_api/consts.py diff --git a/src/horde_sdk/ai_horde_api/endpoints.py b/horde_sdk/ai_horde_api/endpoints.py similarity index 100% rename from src/horde_sdk/ai_horde_api/endpoints.py rename to horde_sdk/ai_horde_api/endpoints.py diff --git a/src/horde_sdk/ai_horde_api/fields.py b/horde_sdk/ai_horde_api/fields.py similarity index 100% rename from src/horde_sdk/ai_horde_api/fields.py rename to horde_sdk/ai_horde_api/fields.py diff --git a/src/horde_sdk/ai_horde_api/metadata.py b/horde_sdk/ai_horde_api/metadata.py similarity index 83% rename from src/horde_sdk/ai_horde_api/metadata.py rename to horde_sdk/ai_horde_api/metadata.py index f05b5ba..0d5cdba 100644 --- a/src/horde_sdk/ai_horde_api/metadata.py +++ b/horde_sdk/ai_horde_api/metadata.py @@ -1,5 +1,5 @@ """Request metadata specific to the AI-Horde API.""" -from horde_sdk.generic_api import GenericPathFields +from horde_sdk.generic_api.metadata import GenericPathFields class AIHordePathData(GenericPathFields): diff --git a/horde_sdk/ai_horde_worker/locale_info/README.md b/horde_sdk/ai_horde_worker/locale_info/README.md new file mode 100644 index 0000000..b7c42a7 --- /dev/null +++ b/horde_sdk/ai_horde_worker/locale_info/README.md @@ -0,0 +1 @@ +If you're making changes to the files in this folder, please only **append** entries, and avoid inserting or otherwise altering the order, as it greatly complicates the localizing process. diff --git a/src/horde_sdk/consts.py b/horde_sdk/consts.py similarity index 100% rename from src/horde_sdk/consts.py rename to horde_sdk/consts.py diff --git a/horde_sdk/generic_api/__init__.py b/horde_sdk/generic_api/__init__.py new file mode 100644 index 0000000..4cdc8a9 --- /dev/null +++ b/horde_sdk/generic_api/__init__.py @@ -0,0 +1 @@ +"""Tools for making or interacting with any horde APIs.""" diff --git a/src/horde_sdk/generic_api/_reflection.py b/horde_sdk/generic_api/_reflection.py similarity index 100% rename from src/horde_sdk/generic_api/_reflection.py rename to horde_sdk/generic_api/_reflection.py diff --git a/src/horde_sdk/generic_api/apimodels.py b/horde_sdk/generic_api/apimodels.py similarity index 96% rename from src/horde_sdk/generic_api/apimodels.py rename to horde_sdk/generic_api/apimodels.py index 389e830..4ecd769 100644 --- a/src/horde_sdk/generic_api/apimodels.py +++ b/horde_sdk/generic_api/apimodels.py @@ -4,17 +4,19 @@ import abc import json -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel, ConfigDict, Field, field_validator from typing_extensions import Self, override from horde_sdk.consts import HTTPMethod, HTTPStatusCode -from horde_sdk.generic_api import GenericAcceptTypes from horde_sdk.generic_api.endpoints import url_with_path +from horde_sdk.generic_api.metadata import GenericAcceptTypes class HordeAPIModel(BaseModel, abc.ABC): """Base class for all Horde API data models, requests, or responses.""" + model_config = ConfigDict(frozen=True) + @classmethod @abc.abstractmethod def get_api_model_name(cls) -> str | None: @@ -39,8 +41,6 @@ class HordeAPIMessage(HordeAPIModel): class BaseResponse(HordeAPIMessage): """Represents any response from any Horde API.""" - model_config = {"frozen": True} - @classmethod def is_array_response(cls) -> bool: """Return whether this response is an array of an internal type.""" @@ -119,8 +119,6 @@ def get_api_model_name(cls) -> str | None: class BaseRequest(HordeAPIMessage): """Represents any request to any Horde API.""" - model_config = {"frozen": True} - @classmethod @abc.abstractmethod def get_http_method(cls) -> HTTPMethod: @@ -161,7 +159,7 @@ def get_success_status_response_pairs(cls) -> dict[HTTPStatusCode, type[BaseResp @classmethod def get_header_fields(cls) -> list[str]: - """Return a dict of field names from this request that should be sent as header fields. + """Return a list of field names from this request object that should be sent as header fields. This is in addition to `GenericHeaderFields`'s values, and possibly the API specific class which inherits from `GenericHeaderFields`, typically found in `horde_sdk._api.metadata`. diff --git a/src/horde_sdk/generic_api/endpoints.py b/horde_sdk/generic_api/endpoints.py similarity index 100% rename from src/horde_sdk/generic_api/endpoints.py rename to horde_sdk/generic_api/endpoints.py diff --git a/src/horde_sdk/generic_api/generic_client.py b/horde_sdk/generic_api/generic_client.py similarity index 99% rename from src/horde_sdk/generic_api/generic_client.py rename to horde_sdk/generic_api/generic_client.py index 39b5c3d..7f21333 100644 --- a/src/horde_sdk/generic_api/generic_client.py +++ b/horde_sdk/generic_api/generic_client.py @@ -7,12 +7,6 @@ from pydantic import BaseModel, ValidationError from strenum import StrEnum -from horde_sdk.generic_api import ( - GenericAcceptTypes, - GenericHeaderFields, - GenericPathFields, - GenericQueryFields, -) from horde_sdk.generic_api.apimodels import ( BaseRequest, BaseRequestAuthenticated, @@ -21,9 +15,17 @@ BaseResponse, RequestErrorResponse, ) +from horde_sdk.generic_api.metadata import ( + GenericAcceptTypes, + GenericHeaderFields, + GenericPathFields, + GenericQueryFields, +) HordeRequest = TypeVar("HordeRequest", bound=BaseRequest) +"""TypeVar for the request type.""" HordeResponse = TypeVar("HordeResponse", bound=BaseResponse) +"""TypeVar for the response type.""" class _ParsedRequest(BaseModel): diff --git a/src/horde_sdk/generic_api/metadata.py b/horde_sdk/generic_api/metadata.py similarity index 100% rename from src/horde_sdk/generic_api/metadata.py rename to horde_sdk/generic_api/metadata.py diff --git a/horde_sdk/generic_api/utils/__init__.py b/horde_sdk/generic_api/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/horde_sdk/generic_api/utils/swagger.py b/horde_sdk/generic_api/utils/swagger.py similarity index 99% rename from src/horde_sdk/generic_api/utils/swagger.py rename to horde_sdk/generic_api/utils/swagger.py index f690c87..a7b1897 100644 --- a/src/horde_sdk/generic_api/utils/swagger.py +++ b/horde_sdk/generic_api/utils/swagger.py @@ -6,11 +6,12 @@ from pathlib import Path import requests -from horde_sdk.consts import PAYLOAD_HTTP_METHODS, HTTPMethod, HTTPStatusCode, is_error_status_code from loguru import logger from pydantic import BaseModel, Field, model_validator from strenum import StrEnum +from horde_sdk.consts import PAYLOAD_HTTP_METHODS, HTTPMethod, HTTPStatusCode, is_error_status_code + class SwaggerModelAdditionalProperty(BaseModel): # TODO: Is this actually a recursive SwaggerModelDefinitionProperty? diff --git a/horde_sdk/localize.py b/horde_sdk/localize.py new file mode 100644 index 0000000..77ff046 --- /dev/null +++ b/horde_sdk/localize.py @@ -0,0 +1,3 @@ +def _L(s: str) -> str: + """Indicates that the string is displayed to the user and should be localized.""" + return s diff --git a/horde_sdk/ratings_api/__init__.py b/horde_sdk/ratings_api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/horde_sdk/ratings_api/apimodels.py b/horde_sdk/ratings_api/apimodels.py similarity index 95% rename from src/horde_sdk/ratings_api/apimodels.py rename to horde_sdk/ratings_api/apimodels.py index ab55e8a..1992614 100644 --- a/src/horde_sdk/ratings_api/apimodels.py +++ b/horde_sdk/ratings_api/apimodels.py @@ -7,8 +7,12 @@ from typing_extensions import override from horde_sdk.consts import _UNDEFINED_MODEL, HTTPMethod -from horde_sdk.generic_api import BaseRequestAuthenticated, BaseRequestUserSpecific -from horde_sdk.generic_api.apimodels import BaseRequest, BaseResponse +from horde_sdk.generic_api.apimodels import ( + BaseRequest, + BaseRequestAuthenticated, + BaseRequestUserSpecific, + BaseResponse, +) from horde_sdk.ratings_api.endpoints import RATING_API_BASE_URL, Rating_API_URL_Literals @@ -23,8 +27,6 @@ def get_api_url(cls) -> str: class BaseImageRatingRecord(BaseModel): """The information about any image rating result.""" - model_config = {"frozen": True} - image: str """The URL to the image.""" rating: int @@ -40,8 +42,6 @@ class BaseImageRatingRecord(BaseModel): class ImageRatingResponseSubRecord(BaseModel): """A single sub-record in a response from the `/v1/image/ratings` endpoint.""" - model_config = {"frozen": True} - username: str rating: int artifacts: int | None @@ -50,8 +50,6 @@ class ImageRatingResponseSubRecord(BaseModel): class ImageRatingsResponse(BaseResponse): """The representation of the full response from `/v1/image/ratings`.""" - model_config = {"frozen": True} - total: int image: str image_id: uuid.UUID @@ -75,8 +73,6 @@ class UserRatingsResponseSubRecord(BaseImageRatingRecord): class UserRatingsResponse(BaseResponse): """The representation of the full response from `/v1/user/ratings`.""" - model_config = {"frozen": True} - total: int """The total number of records in this response.""" ratings: list[UserRatingsResponseSubRecord] @@ -95,8 +91,6 @@ class UserValidateResponseRecord(BaseImageRatingRecord): class UserValidateResponse(BaseResponse): """The representation of the full response from `/v1/validate/{user_id}`.""" - model_config = {"frozen": True} - total: int """The total number of records in this response.""" ratings: list[UserValidateResponseRecord] @@ -111,7 +105,6 @@ def get_api_model_name(cls) -> str | None: class UserCheckResponse(BaseResponse): """A single record from the `/v1/user/check/` endpoint.""" - model_config = {"frozen": True} ratings_in_timeframe: int """The number of ratings this user submitted in the timeframe.""" ratings_per_minute_in_timeframe: float diff --git a/src/horde_sdk/ratings_api/endpoints.py b/horde_sdk/ratings_api/endpoints.py similarity index 100% rename from src/horde_sdk/ratings_api/endpoints.py rename to horde_sdk/ratings_api/endpoints.py diff --git a/src/horde_sdk/ratings_api/metadata.py b/horde_sdk/ratings_api/metadata.py similarity index 90% rename from src/horde_sdk/ratings_api/metadata.py rename to horde_sdk/ratings_api/metadata.py index aaadf52..abeadf5 100644 --- a/src/horde_sdk/ratings_api/metadata.py +++ b/horde_sdk/ratings_api/metadata.py @@ -1,7 +1,7 @@ """Request metadata specific to the Ratings API.""" from enum import auto -from horde_sdk.generic_api import GenericPathFields, GenericQueryFields +from horde_sdk.generic_api.metadata import GenericPathFields, GenericQueryFields class RatingsAPIPathFields(GenericPathFields): diff --git a/src/horde_sdk/ratings_api/ratings_client.py b/horde_sdk/ratings_api/ratings_client.py similarity index 87% rename from src/horde_sdk/ratings_api/ratings_client.py rename to horde_sdk/ratings_api/ratings_client.py index 4f39d05..3b467cb 100644 --- a/src/horde_sdk/ratings_api/ratings_client.py +++ b/horde_sdk/ratings_api/ratings_client.py @@ -1,5 +1,5 @@ """Definitions to help interact with the Ratings API.""" -from horde_sdk.generic_api import GenericHordeAPIClient +from horde_sdk.generic_api.generic_client import GenericHordeAPIClient from horde_sdk.ratings_api.metadata import RatingsAPIPathFields, RatingsAPIQueryFields diff --git a/horde_sdk/scripts/__init__.py b/horde_sdk/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/horde_sdk/scripts/write_all_payload_examples.py b/horde_sdk/scripts/write_all_payload_examples.py similarity index 100% rename from src/horde_sdk/scripts/write_all_payload_examples.py rename to horde_sdk/scripts/write_all_payload_examples.py diff --git a/src/horde_sdk/scripts/write_all_response_examples.py b/horde_sdk/scripts/write_all_response_examples.py similarity index 100% rename from src/horde_sdk/scripts/write_all_response_examples.py rename to horde_sdk/scripts/write_all_response_examples.py diff --git a/src/horde_sdk/utils.py b/horde_sdk/utils.py similarity index 100% rename from src/horde_sdk/utils.py rename to horde_sdk/utils.py diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..5fb18f1 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,21 @@ +site_name: Horde SDK Documentation + +plugins: +- search +- awesome-pages +- autorefs +- mkdocstrings: + handlers: + python: + options: + members_order: source + docstring_section_style: list + show_if_no_docstring: true + separate_signature: true + show_signature_annotations: true + +theme: + name: material + +extra_css: + - stylesheets/extra.css diff --git a/pyproject.toml b/pyproject.toml index e802492..93473cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,9 @@ dependencies = [ "requests", "StrEnum", "loguru", + "babel", + "aiohttp", + "aiodns", ] license = {file = "LICENSE"} classifiers = [ @@ -26,6 +29,10 @@ classifiers = [ "Development Status :: 2 - Pre-Alpha", ] + +[tool.setuptools.packages.find] +include = ["hordesdk*", "horde_sdk.*"] + [project.urls] "Homepage" = "https://github.com/Haidra-Org/horde-sdk" diff --git a/requirements.dev.txt b/requirements.dev.txt index a5601f5..576bb8d 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -9,3 +9,7 @@ types-urllib3 tox==4.6.3 pre-commit==3.3.3 pytest-cov +babel +mkdocstrings +mkdocstrings-python +mkgendocs diff --git a/setup.py b/setup.py index 67a802f..deec7e4 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,12 @@ +# from babel.messages import frontend as babel from setuptools import setup # noqa: D100 +# setup( +# cmdclass={ +# "compile_catalog": babel.compile_catalog, +# "extract_messages": babel.extract_messages, +# "init_catalog": babel.init_catalog, +# "update_catalog": babel.update_catalog, +# }, +# ) setup() diff --git a/src/horde_sdk/generic_api/__init__.py b/src/horde_sdk/generic_api/__init__.py deleted file mode 100644 index 86105d0..0000000 --- a/src/horde_sdk/generic_api/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Tools for making or interacting with any horde APIs.""" -# isort:skip_file -from horde_sdk.generic_api.metadata import ( - GenericAcceptTypes, - GenericHeaderFields, - GenericPathFields, - GenericQueryFields, -) - - -from horde_sdk.generic_api.apimodels import ( - BaseRequest, - BaseRequestAuthenticated, - BaseRequestUserSpecific, - RequestErrorResponse, -) -from horde_sdk.generic_api.generic_client import GenericHordeAPIClient, HordeRequest, HordeResponse - - -__all__ = [ - "RequestErrorResponse", - "BaseRequest", - "BaseRequestAuthenticated", - "BaseRequestUserSpecific", - "GenericHordeAPIClient", - "GenericAcceptTypes", - "GenericHeaderFields", - "GenericPathFields", - "GenericQueryFields", - "HordeRequest", - "HordeResponse", -] diff --git a/src/horde_sdk/ratings_api/__init__.py b/src/horde_sdk/ratings_api/__init__.py deleted file mode 100644 index f8ca45d..0000000 --- a/src/horde_sdk/ratings_api/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Tools for making or interacting with the horde ratings APIs.""" -from horde_sdk.ratings_api.apimodels import ( - ImageRatingsComparisonTypes, - SelectableReturnFormats, - UserCheckRequest, - UserCheckResponse, - UserRatingsRequest, - UserRatingsResponse, - UserValidateRequest, - UserValidateResponse, - UserValidateResponseRecord, -) -from horde_sdk.ratings_api.metadata import RatingsAPIPathFields -from horde_sdk.ratings_api.ratings_client import RatingsAPIClient - -# TODO -# FIXME -__all__ = [ - "ImageRatingsComparisonTypes", - "SelectableReturnFormats", - "UserCheckRequest", - "UserCheckResponse", - "UserRatingsResponse", - "UserRatingsRequest", - "UserValidateRequest", - "UserValidateResponseRecord", - "UserValidateResponse", - "RatingsAPIPathFields", - "RatingsAPIClient", -] diff --git a/tests/ai_horde_api/test_api_calls.py b/tests/ai_horde_api/test_api_calls.py index 47d6c08..1546e1b 100644 --- a/tests/ai_horde_api/test_api_calls.py +++ b/tests/ai_horde_api/test_api_calls.py @@ -1,6 +1,7 @@ from pathlib import Path import pytest + from horde_sdk.ai_horde_api.ai_horde_client import AIHordeAPIClient from horde_sdk.ai_horde_api.apimodels import ( AllWorkersDetailsRequest, @@ -11,7 +12,7 @@ ImageGenerateStatusResponse, ) from horde_sdk.ai_horde_api.consts import WORKER_TYPE -from horde_sdk.generic_api import RequestErrorResponse +from horde_sdk.generic_api.apimodels import RequestErrorResponse from horde_sdk.generic_api.utils.swagger import SwaggerDoc _PRODUCTION_RESPONSES_FOLDER = Path(__file__).parent.parent / "test_data" / "ai_horde_api" / "production_responses" diff --git a/tests/test_dynamically_check_apimodels.py b/tests/test_dynamically_check_apimodels.py index 18c366f..596d3d7 100644 --- a/tests/test_dynamically_check_apimodels.py +++ b/tests/test_dynamically_check_apimodels.py @@ -5,12 +5,11 @@ import horde_sdk.ai_horde_api as ai_horde_api import horde_sdk.ai_horde_api.apimodels -import horde_sdk.generic_api as generic_api import horde_sdk.ratings_api as ratings_api import horde_sdk.ratings_api.apimodels from horde_sdk.consts import HTTPMethod from horde_sdk.generic_api._reflection import get_all_request_types -from horde_sdk.generic_api.apimodels import BaseResponse +from horde_sdk.generic_api.apimodels import BaseRequest, BaseResponse from horde_sdk.generic_api.utils.swagger import SwaggerDoc EXAMPLE_PAYLOADS: dict[ModuleType, Path] = { @@ -42,7 +41,7 @@ def test_get_all_request_types(self) -> None: # noqa: D102 for request_type in all_request_types: assert issubclass( request_type, - generic_api.BaseRequest, + BaseRequest, ), f"Request type is not a subclass if `BaseRequest`: {request_type}" assert issubclass( @@ -63,13 +62,13 @@ def dynamic_json_load(module: ModuleType) -> None: example_payload_folder = EXAMPLE_PAYLOADS[module] example_response_folder = EXAMPLE_RESPONSES[module] - all_request_types: list[type[generic_api.BaseRequest]] = get_all_request_types(module_name) + all_request_types: list[type[BaseRequest]] = get_all_request_types(module_name) for request_type in all_request_types: # print(f"Testing {request_type.__name__}") assert issubclass( request_type, - generic_api.BaseRequest, + BaseRequest, ), f"Request type is not a subclass if `BaseRequest`: {request_type}" response_type: type[BaseResponse] = request_type.get_success_response_type() diff --git a/tests/test_generic.py b/tests/test_generic.py index 27743ac..0130535 100644 --- a/tests/test_generic.py +++ b/tests/test_generic.py @@ -1,6 +1,6 @@ import json -from horde_sdk.generic_api import RequestErrorResponse +from horde_sdk.generic_api.apimodels import RequestErrorResponse def test_error_response() -> None: diff --git a/tests/test_ratings_api_models.py b/tests/test_ratings_api_models.py index 45a67ef..0cc7344 100644 --- a/tests/test_ratings_api_models.py +++ b/tests/test_ratings_api_models.py @@ -1,11 +1,12 @@ """Unit tests for Ratings API models.""" -import horde_sdk.generic_api as generic_api -import horde_sdk.ratings_api as ratings_api import pydantic import pytest +from horde_sdk.generic_api.metadata import GenericAcceptTypes +from horde_sdk.ratings_api.apimodels import UserCheckRequest + class Test_validators: """If you are unfamiliar with pydantic, this may demonstrates some of the validation functionality.""" @@ -13,30 +14,30 @@ class Test_validators: def test_user_check_request(self) -> None: """Shows some of the types of data expected.""" with pytest.raises(pydantic.ValidationError, match=r".*user_id.*"): - ratings_api.UserCheckRequest( + UserCheckRequest( apikey="key", - accept=generic_api.GenericAcceptTypes.json, + accept=GenericAcceptTypes.json, user_id="non_numeric_userid", divergence=3, minutes=180, ) with pytest.raises(pydantic.ValidationError, match=r".*divergence.*"): - ratings_api.UserCheckRequest( + UserCheckRequest( apikey="key", - accept=generic_api.GenericAcceptTypes.json, + accept=GenericAcceptTypes.json, user_id="123", divergence=-1, minutes=180, ) with pytest.raises(pydantic.ValidationError, match=r".*user_id.*"): - ratings_api.UserCheckRequest( + UserCheckRequest( apikey="key", - accept=generic_api.GenericAcceptTypes.json, + accept=GenericAcceptTypes.json, user_id="non_numeric_userid", # user_id has to be a str with only numbers divergence=3, minutes=180, ) - ratings_api.UserCheckRequest( + UserCheckRequest( apikey="key", accept="non-enum_accept_value", # type: ignore user_id="123",