From 3998ccdc93637db923a8a66b543591c80d5349f0 Mon Sep 17 00:00:00 2001 From: Edward Brown Date: Wed, 18 Sep 2024 09:00:00 +0100 Subject: [PATCH] Add API tools --- fmtr/tools/__init__.py | 5 +++ fmtr/tools/api_tools.py | 74 +++++++++++++++++++++++++++++++++++++++++ fmtr/tools/version | 2 +- requirements.py | 3 +- 4 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 fmtr/tools/api_tools.py diff --git a/fmtr/tools/__init__.py b/fmtr/tools/__init__.py index daac00d..f8373bd 100644 --- a/fmtr/tools/__init__.py +++ b/fmtr/tools/__init__.py @@ -91,6 +91,11 @@ except ImportError as exception: merging = merge = MissingExtraMockModule('merge', exception) +try: + from fmtr.tools import api_tools as api +except ImportError as exception: + api = MissingExtraMockModule('api', exception) + __all__ = [ 'config', diff --git a/fmtr/tools/api_tools.py b/fmtr/tools/api_tools.py new file mode 100644 index 0000000..2f7a7a6 --- /dev/null +++ b/fmtr/tools/api_tools.py @@ -0,0 +1,74 @@ +import uvicorn +from dataclasses import dataclass +from fastapi import FastAPI +from typing import Callable, List, Optional, Union + +from fmtr.tools.iterator_tools import enlist +from fmtr.tools.logging_tools import logger + + +@dataclass +class Endpoint: + """ + + Endpoint-as-method config + + """ + method: Callable + path: str + tags: Optional[Union[str, List[str]]] = None + method_http: Optional[Callable] = None + + def __post_init__(self): + self.tags = enlist(self.tags) + + +class ApiBase: + """ + + Simple API base class, generalising endpoint-as-method config. + + """ + TITLE = 'Base API' + HOST = '0.0.0.0' + PORT = 8080 + + def add_endpoint(self, endpoint: Endpoint): + """ + + Add endpoints from definitions using a single dataclass instance. + + """ + method_http = endpoint.method_http or self.app.post + doc = (endpoint.method.__doc__ or '').strip() or None + + method_http( + endpoint.path, + tags=endpoint.tags, + description=doc, + summary=doc + )(endpoint.method) + + def __init__(self): + self.app = FastAPI(title=self.TITLE) + + for endpoint in self.get_endpoints(): + self.add_endpoint(endpoint) + + def get_endpoints(self) -> List[Endpoint]: + """ + + Define endpoints using a dataclass instance. + + """ + endpoints = [ + + ] + + return endpoints + + @classmethod + def launch(cls): + self = cls() + logger.info(f'Launching API {cls.TITLE}...') + uvicorn.run(self.app, host=self.HOST, port=self.PORT) diff --git a/fmtr/tools/version b/fmtr/tools/version index 9005278..5e65966 100644 --- a/fmtr/tools/version +++ b/fmtr/tools/version @@ -1 +1 @@ -0.8.18 \ No newline at end of file +0.8.19 \ No newline at end of file diff --git a/requirements.py b/requirements.py index b90beab..fc9e3e3 100644 --- a/requirements.py +++ b/requirements.py @@ -19,7 +19,8 @@ 'spaces': ['netrc'], 'netrc': ['tinynetrc'], 'hfh': ['huggingface_hub'], - 'merging': ['deepmerge'] + 'merging': ['deepmerge'], + 'api': ['fastapi', 'uvicorn', 'logging'] } CONSOLE_SCRIPTS = [