From 69f8c984be446ee94b67158a96bf94b41924e82e Mon Sep 17 00:00:00 2001 From: Ivan Murabito Date: Fri, 20 Dec 2024 12:03:07 +0000 Subject: [PATCH 1/4] feat: add system info retrieval Improve system diagnostics by adding GPU and system information classes --- Makefile | 2 +- focoos/__init__.py | 1 + focoos/local_model.py | 4 +- focoos/ports.py | 52 +++++++++ focoos/runtime.py | 11 +- focoos/utils/system.py | 216 ++++++++++++++++++++++++++++++++++++- notebooks/playground.ipynb | 37 ++++--- pyproject.toml | 3 + test/test_system.py | 101 +++++++++++++++++ tests/test_system.py | 102 ++++++++++++++++++ 10 files changed, 506 insertions(+), 23 deletions(-) create mode 100644 test/test_system.py create mode 100644 tests/test_system.py diff --git a/Makefile b/Makefile index 48c529e..3c11677 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: install install-dev install-pre-commit run-pre-commit +.PHONY: test install install-dev install-pre-commit run-pre-commit install: @pip install . --no-cache-dir diff --git a/focoos/__init__.py b/focoos/__init__.py index b8ab9d6..00f3088 100644 --- a/focoos/__init__.py +++ b/focoos/__init__.py @@ -2,3 +2,4 @@ from .local_model import LocalModel from .ports import * from .remote_model import RemoteModel +from .utils.system import get_system_info diff --git a/focoos/local_model.py b/focoos/local_model.py index 4643db8..71559ab 100644 --- a/focoos/local_model.py +++ b/focoos/local_model.py @@ -110,12 +110,12 @@ def _annotate(self, im: np.ndarray, detections: Detections) -> np.ndarray: if classes is not None: labels = [ f"{classes[int(class_id)]}: {confid*100:.0f}%" - for class_id, confid in zip(detections.class_id, detections.confidence) + for class_id, confid in zip(detections.class_id, detections.confidence) # type: ignore ] else: labels = [ f"{str(class_id)}: {confid*100:.0f}%" - for class_id, confid in zip(detections.class_id, detections.confidence) + for class_id, confid in zip(detections.class_id, detections.confidence) # type: ignore ] if self.metadata.task == FocoosTask.DETECTION: annotated_im = self.box_annotator.annotate( diff --git a/focoos/ports.py b/focoos/ports.py index 0d877e9..452ff97 100644 --- a/focoos/ports.py +++ b/focoos/ports.py @@ -238,3 +238,55 @@ class RuntimeTypes(str, Enum): ONNX_TRT16 = "onnx_trt16" ONNX_CPU = "onnx_cpu" ONNX_COREML = "onnx_coreml" + + +class GPUInfo(FocoosBaseModel): + gpu_id: Optional[int] = None + gpu_name: Optional[str] = None + gpu_memory_total_gb: Optional[float] = None + gpu_memory_used_percentage: Optional[float] = None + gpu_temperature: Optional[float] = None + gpu_load_percentage: Optional[float] = None + + +class SystemInfo(FocoosBaseModel): + focoos_host: Optional[str] = None + system: Optional[str] = None + system_name: Optional[str] = None + cpu_type: Optional[str] = None + cpu_cores: Optional[int] = None + memory_gb: Optional[float] = None + memory_used_percentage: Optional[float] = None + available_providers: Optional[list[str]] = None + disk_space_total_gb: Optional[float] = None + disk_space_used_percentage: Optional[float] = None + gpu_count: Optional[int] = None + gpu_driver: Optional[str] = None + gpu_cuda_version: Optional[str] = None + gpus_info: Optional[list[GPUInfo]] = None + packages_versions: Optional[dict[str, str]] = None + + def pretty_print(self): + print("================ SYSTEM INFO ====================") + for key, value in self.model_dump().items(): + if isinstance(value, list): + print(f"{key}:") + if key == "gpus_info": # Formattazione speciale per gpus_info + for item in value: + print(f"- id: {item['gpu_id']}") + for sub_key, sub_value in item.items(): + if sub_key != "gpu_id" and sub_value is not None: + formatted_key = sub_key.replace("_", "-") + print(f" - {formatted_key}: {sub_value}") + else: + for item in value: + print(f" - {item}") + elif ( + isinstance(value, dict) and key == "packages_versions" + ): # Formattazione speciale per packages_versions + print(f"{key}:") + for pkg_name, pkg_version in value.items(): + print(f" - {pkg_name}: {pkg_version}") + else: + print(f"{key}: {value}") + print("================================================") diff --git a/focoos/runtime.py b/focoos/runtime.py index 76855bb..579cb9c 100644 --- a/focoos/runtime.py +++ b/focoos/runtime.py @@ -36,6 +36,7 @@ RuntimeTypes, ) from focoos.utils.logger import get_logger +from focoos.utils.system import get_cpu_name, get_gpu_name GPU_ID = 0 @@ -266,6 +267,7 @@ def __init__( self.dtype = dtype self.binding = binding self.ort_sess = ort.InferenceSession(model_path, options, providers=providers) + self.active_providers = self.ort_sess.get_providers() self.logger.info( f"[onnxruntime] Active providers:{self.ort_sess.get_providers()}" ) @@ -391,15 +393,20 @@ def benchmark(self, iterations=20, size=640) -> LatencyMetrics: durations.append((end - start) * 1000) durations = np.array(durations) # time.sleep(0.1) + provider = self.active_providers[0] + if provider in ["CUDAExecutionProvider", "TensorrtExecutionProvider"]: + device = get_gpu_name() + else: + device = get_cpu_name() metrics = LatencyMetrics( fps=int(1000 / durations.mean()), - engine="onnx", + engine=f"onnx.{provider}", mean=round(durations.mean(), 3), max=round(durations.max(), 3), min=round(durations.min(), 3), std=round(durations.std(), 3), im_size=size[0], - device="", + device=str(device), ) self.logger.info(f"🔥 FPS: {metrics.fps}") return metrics diff --git a/focoos/utils/system.py b/focoos/utils/system.py index 7f2e35f..7fa41bb 100644 --- a/focoos/utils/system.py +++ b/focoos/utils/system.py @@ -1,16 +1,42 @@ +import importlib.metadata as metadata +import platform +import subprocess from typing import Optional +import GPUtil +import onnxruntime as ort +import psutil import requests from focoos.config import FOCOOS_CONFIG +from focoos.ports import GPUInfo, SystemInfo class HttpClient: + """ + A simple HTTP client for making GET, POST, and DELETE requests. + + This client is initialized with an API key and a host URL, and it + automatically includes the API key in the headers of each request. + + Attributes: + api_key (str): The API key for authorization. + host_url (str): The base URL for the API. + default_headers (dict): Default headers including authorization and user agent. + """ + def __init__( self, api_key: str, host_url: str, ): + """ + Initialize the HttpClient with an API key and host URL. + + Args: + api_key (str): The API key for authorization. + host_url (str): The base URL for the API. + """ self.api_key = api_key self.host_url = host_url @@ -19,7 +45,22 @@ def __init__( "user_agent": "focoos/0.0.1", } - def get_external_url(self, path: str, params: dict = None, stream: bool = False): + def get_external_url( + self, path: str, params: Optional[dict] = None, stream: bool = False + ): + """ + Perform a GET request to an external URL. + + Args: + path (str): The URL path to request. + params (Optional[dict], optional): Query parameters for the request. Defaults to None. + stream (bool, optional): Whether to stream the response. Defaults to False. + + Returns: + Response: The response object from the requests library. + """ + if params is None: + params = {} return requests.get(path, params=params, stream=stream) def get( @@ -29,6 +70,18 @@ def get( extra_headers: Optional[dict] = None, stream: bool = False, ): + """ + Perform a GET request to the specified path on the host URL. + + Args: + path (str): The URL path to request. + params (Optional[dict], optional): Query parameters for the request. Defaults to None. + extra_headers (Optional[dict], optional): Additional headers to include in the request. Defaults to None. + stream (bool, optional): Whether to stream the response. Defaults to False. + + Returns: + Response: The response object from the requests library. + """ url = f"{self.host_url}/{path}" headers = self.default_headers if extra_headers: @@ -42,6 +95,18 @@ def post( extra_headers: Optional[dict] = None, files=None, ): + """ + Perform a POST request to the specified path on the host URL. + + Args: + path (str): The URL path to request. + data (Optional[dict], optional): The JSON data to send in the request body. Defaults to None. + extra_headers (Optional[dict], optional): Additional headers to include in the request. Defaults to None. + files (optional): Files to send in the request. Defaults to None. + + Returns: + Response: The response object from the requests library. + """ url = f"{self.host_url}/{path}" headers = self.default_headers if extra_headers: @@ -49,8 +114,157 @@ def post( return requests.post(url, headers=headers, json=data, files=files) def delete(self, path: str, extra_headers: Optional[dict] = None): + """ + Perform a DELETE request to the specified path on the host URL. + + Args: + path (str): The URL path to request. + extra_headers (Optional[dict], optional): Additional headers to include in the request. Defaults to None. + + Returns: + Response: The response object from the requests library. + """ url = f"{self.host_url}/{path}" headers = self.default_headers if extra_headers: headers.update(extra_headers) return requests.delete(url, headers=headers) + + +def get_cuda_version() -> Optional[str]: + """ + Retrieve the CUDA version installed on the system. + + This function runs the `nvidia-smi` command to fetch the CUDA version. + If the command executes successfully and the CUDA version is found in the output, + it returns the version as a string. If the command fails or the CUDA version is not found, + it returns None. + + Returns: + Optional[str]: The CUDA version if available, otherwise None. + """ + try: + result = subprocess.run( + ["nvidia-smi"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True + ) + + if result.returncode == 0: + output = result.stdout + for line in output.splitlines(): + if "CUDA Version" in line: + cuda_version = line.split(":")[-1].strip() + cuda_version = cuda_version.split()[0] + return cuda_version + return None + else: + return None + except FileNotFoundError: + return None + + +def get_gpu_name() -> Optional[str]: + """ + Retrieve the name of the first available GPU. + + This function uses the GPUtil library to get the name of the first GPU detected. + If no GPUs are available, it returns None. + + Returns: + Optional[str]: The name of the first GPU if available, otherwise None. + """ + try: + return GPUtil.getGPUs()[0].name + except IndexError: + return None + + +def get_cpu_name() -> Optional[str]: + """ + Retrieve the name of the CPU. + + This function uses the psutil library to get the name of the CPU. + If no CPU is available, it returns None. + + Returns: + Optional[str]: The name of the CPU if available, otherwise None. + """ + return platform.processor() + + +def get_system_info() -> SystemInfo: + """ + Gather and return comprehensive system information. + + This function collects various system metrics including CPU, memory, disk, + and GPU details, as well as installed package versions. It returns this + information encapsulated in a SystemInfo object. + + Returns: + SystemInfo: An object containing detailed information about the system's + hardware and software configuration, including: + - System and node name + - CPU type and core count + - Available ONNXRuntime providers + - Memory and disk usage statistics + - GPU count, driver, and CUDA version + - Detailed GPU information if available + - Versions of key installed packages + """ + system_info = platform.uname() + memory_info = psutil.virtual_memory() + disk_info = psutil.disk_usage("/") + gpu_info = GPUtil.getGPUs() + if not gpu_info: + gpu_info = None + gpu_count = 0 + gpu_driver = None + else: + gpu_count = len(gpu_info) + gpu_driver = gpu_info[0].driver + gpus_info = [] + for i, gpu in enumerate(gpu_info): + gpus_info.append( + GPUInfo( + gpu_id=i, + gpu_name=gpu.name, + gpu_memory_total_gb=round(gpu.memoryTotal / 1024, 3), + gpu_memory_used_percentage=round(gpu.memoryUsed / 1024, 3), + gpu_temperature=gpu.temperature, + gpu_load_percentage=gpu.load * 100, + ) + ) + packages = [ + "focoos", + "tensorrt", + "onnxruntime", + "onnxruntime-gpu", + "numpy", + "opencv-python", + "pillow", + "supervision", + "pydantic", + ] + versions = {} + for package in packages: + try: + versions[package] = metadata.version(package) + except metadata.PackageNotFoundError: + versions[package] = "unknown" + + return SystemInfo( + focoos_host=FOCOOS_CONFIG.default_host_url, + system=system_info.system, + system_name=system_info.node, + cpu_type=system_info.machine, + cpu_cores=psutil.cpu_count(logical=True), + available_providers=ort.get_available_providers(), + memory_gb=round(memory_info.total / (1024**3), 3), + memory_used_percentage=round(memory_info.percent, 3), + disk_space_total_gb=round(disk_info.total / (1024**3), 3), + disk_space_used_percentage=round(disk_info.percent, 3), + gpu_count=gpu_count, + gpu_driver=gpu_driver, + gpu_cuda_version=get_cuda_version(), + gpus_info=gpus_info, + packages_versions=versions, + ) diff --git a/notebooks/playground.ipynb b/notebooks/playground.ipynb index 817d75c..5535529 100644 --- a/notebooks/playground.ipynb +++ b/notebooks/playground.ipynb @@ -115,6 +115,7 @@ "import os\n", "from pprint import pprint\n", "from supervision import plot_image\n", + "from focoos import Focoos\n", "\n", "focoos = Focoos(\n", " api_key=os.getenv(\"FOCOOS_API_KEY\"),\n", @@ -149,7 +150,6 @@ "outputs": [], "source": [ "import os\n", - "\n", "from focoos import Focoos\n", "import os\n", "from pprint import pprint\n", @@ -299,22 +299,6 @@ "sv.plot_image(preview)" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Model unload " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "model.unload()" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -454,6 +438,25 @@ "logs = model.train_logs()\n", "pprint(logs)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Get System Info" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from focoos.utils.system import get_system_info\n", + "\n", + "system_info = get_system_info()\n", + "system_info.pretty_print()" + ] } ], "metadata": { diff --git a/pyproject.toml b/pyproject.toml index 16b8ec7..9f18405 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,9 @@ dependencies = [ "tqdm~=4.67.1", "numpy~=1.26.4", "scipy~=1.14.1", + "gputil~=1.4.0", + "psutil~=6.1.1", + "setuptools~=75.6.0", ] authors = [{ name = "focoos.ai", email = "info@focoos.ai" }] diff --git a/test/test_system.py b/test/test_system.py new file mode 100644 index 0000000..ebf9397 --- /dev/null +++ b/test/test_system.py @@ -0,0 +1,101 @@ +from unittest.mock import MagicMock, patch + +import pytest + +from focoos.ports import SystemInfo +from focoos.utils.system import ( + HttpClient, + get_cpu_name, + get_cuda_version, + get_gpu_name, + get_system_info, +) + + +def test_get_cuda_version(): + with patch("subprocess.run") as mock_run: + # Simulate successful nvidia-smi command + mock_run.return_value = MagicMock( + returncode=0, + stdout="NVIDIA-SMI 460.32.03 Driver Version: 460.32.03 CUDA Version: 11.2", + ) + assert get_cuda_version() == "11.2" + + # Simulate nvidia-smi command not found + mock_run.side_effect = FileNotFoundError + assert get_cuda_version() is None + + +def test_get_gpu_name(): + with patch("GPUtil.getGPUs") as mock_get_gpus: + # Simulate GPU available + mock_gpu = MagicMock() + mock_gpu.name = "Tesla T4" + mock_get_gpus.return_value = [mock_gpu] + assert get_gpu_name() == "Tesla T4" + + # Simulate no GPU available + mock_get_gpus.return_value = [] + assert get_gpu_name() is None + + +def test_get_cpu_name(): + with patch( + "platform.processor", return_value="Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz" + ): + assert get_cpu_name() == "Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz" + + +def test_get_system_info(): + system_info = get_system_info() + assert isinstance(system_info, SystemInfo) + assert system_info.system is not None + assert system_info.cpu_cores > 0 + + +def test_http_client_get_external_url(): + client = HttpClient(api_key="test_key", host_url="http://example.com") + with patch("requests.get") as mock_get: + mock_get.return_value.status_code = 200 + response = client.get_external_url("test/path") + assert response.status_code == 200 + mock_get.assert_called_with("test/path", params={}, stream=False) + + +def test_http_client_get(): + client = HttpClient(api_key="test_key", host_url="http://example.com") + with patch("requests.get") as mock_get: + mock_get.return_value.status_code = 200 + response = client.get("test/path") + assert response.status_code == 200 + mock_get.assert_called_with( + "http://example.com/test/path", + headers=client.default_headers, + params=None, + stream=False, + ) + + +def test_http_client_post(): + client = HttpClient(api_key="test_key", host_url="http://example.com") + with patch("requests.post") as mock_post: + mock_post.return_value.status_code = 201 + response = client.post("test/path", data={"key": "value"}) + assert response.status_code == 201 + mock_post.assert_called_with( + "http://example.com/test/path", + headers=client.default_headers, + json={"key": "value"}, + files=None, + ) + + +def test_http_client_delete(): + client = HttpClient(api_key="test_key", host_url="http://example.com") + with patch("requests.delete") as mock_delete: + mock_delete.return_value.status_code = 204 + response = client.delete("test/path") + assert response.status_code == 204 + mock_delete.assert_called_with( + "http://example.com/test/path", headers=client.default_headers + ) diff --git a/tests/test_system.py b/tests/test_system.py new file mode 100644 index 0000000..157eb03 --- /dev/null +++ b/tests/test_system.py @@ -0,0 +1,102 @@ +from unittest.mock import MagicMock, patch + +import pytest + +from focoos.ports import SystemInfo +from focoos.utils.system import ( + HttpClient, + get_cpu_name, + get_cuda_version, + get_gpu_name, + get_system_info, +) + + +def test_get_cuda_version(): + with patch("subprocess.run") as mock_run: + # Simulate successful nvidia-smi command + mock_run.return_value = MagicMock( + returncode=0, + stdout="NVIDIA-SMI 460.32.03 Driver Version: 460.32.03 CUDA Version: 11.2", + ) + assert get_cuda_version() == "11.2" + + # Simulate nvidia-smi command not found + mock_run.side_effect = FileNotFoundError + assert get_cuda_version() is None + + +def test_get_gpu_name(): + with patch("GPUtil.getGPUs") as mock_get_gpus: + # Simulate GPU available + mock_gpu = MagicMock() + mock_gpu.name = "Tesla T4" + mock_get_gpus.return_value = [mock_gpu] + assert get_gpu_name() == "Tesla T4" + + # Simulate no GPU available + mock_get_gpus.return_value = [] + assert get_gpu_name() is None + + +def test_get_cpu_name(): + with patch( + "platform.processor", return_value="Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz" + ): + assert get_cpu_name() == "Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz" + + +def test_get_system_info(): + system_info = get_system_info() + assert isinstance(system_info, SystemInfo) + assert system_info.system is not None + assert system_info.cpu_cores is not None + assert system_info.cpu_cores > 0 + + +def test_http_client_get_external_url(): + client = HttpClient(api_key="test_key", host_url="http://example.com") + with patch("requests.get") as mock_get: + mock_get.return_value.status_code = 200 + response = client.get_external_url("test/path") + assert response.status_code == 200 + mock_get.assert_called_with("test/path", params={}, stream=False) + + +def test_http_client_get(): + client = HttpClient(api_key="test_key", host_url="http://example.com") + with patch("requests.get") as mock_get: + mock_get.return_value.status_code = 200 + response = client.get("test/path") + assert response.status_code == 200 + mock_get.assert_called_with( + "http://example.com/test/path", + headers=client.default_headers, + params=None, + stream=False, + ) + + +def test_http_client_post(): + client = HttpClient(api_key="test_key", host_url="http://example.com") + with patch("requests.post") as mock_post: + mock_post.return_value.status_code = 201 + response = client.post("test/path", data={"key": "value"}) + assert response.status_code == 201 + mock_post.assert_called_with( + "http://example.com/test/path", + headers=client.default_headers, + json={"key": "value"}, + files=None, + ) + + +def test_http_client_delete(): + client = HttpClient(api_key="test_key", host_url="http://example.com") + with patch("requests.delete") as mock_delete: + mock_delete.return_value.status_code = 204 + response = client.delete("test/path") + assert response.status_code == 204 + mock_delete.assert_called_with( + "http://example.com/test/path", headers=client.default_headers + ) From 3ca39f11d701efeb8f1a58b70ba92196febb2e14 Mon Sep 17 00:00:00 2001 From: Ivan Murabito <36967518+CuriousDolphin@users.noreply.github.com> Date: Fri, 20 Dec 2024 13:16:19 +0100 Subject: [PATCH 2/4] fix tests --- focoos/utils/system.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/focoos/utils/system.py b/focoos/utils/system.py index 7fa41bb..8d1d0b3 100644 --- a/focoos/utils/system.py +++ b/focoos/utils/system.py @@ -214,10 +214,10 @@ def get_system_info() -> SystemInfo: memory_info = psutil.virtual_memory() disk_info = psutil.disk_usage("/") gpu_info = GPUtil.getGPUs() - if not gpu_info: - gpu_info = None + if len(gpu_info) == 0: gpu_count = 0 gpu_driver = None + gpus_info = None else: gpu_count = len(gpu_info) gpu_driver = gpu_info[0].driver From 7436d85f0a91bf004ac3e4cd0d46b224faad49bc Mon Sep 17 00:00:00 2001 From: Ivan Murabito <36967518+CuriousDolphin@users.noreply.github.com> Date: Fri, 20 Dec 2024 13:26:01 +0100 Subject: [PATCH 3/4] fix typo --- test/test_system.py | 101 -------------------------------------------- 1 file changed, 101 deletions(-) delete mode 100644 test/test_system.py diff --git a/test/test_system.py b/test/test_system.py deleted file mode 100644 index ebf9397..0000000 --- a/test/test_system.py +++ /dev/null @@ -1,101 +0,0 @@ -from unittest.mock import MagicMock, patch - -import pytest - -from focoos.ports import SystemInfo -from focoos.utils.system import ( - HttpClient, - get_cpu_name, - get_cuda_version, - get_gpu_name, - get_system_info, -) - - -def test_get_cuda_version(): - with patch("subprocess.run") as mock_run: - # Simulate successful nvidia-smi command - mock_run.return_value = MagicMock( - returncode=0, - stdout="NVIDIA-SMI 460.32.03 Driver Version: 460.32.03 CUDA Version: 11.2", - ) - assert get_cuda_version() == "11.2" - - # Simulate nvidia-smi command not found - mock_run.side_effect = FileNotFoundError - assert get_cuda_version() is None - - -def test_get_gpu_name(): - with patch("GPUtil.getGPUs") as mock_get_gpus: - # Simulate GPU available - mock_gpu = MagicMock() - mock_gpu.name = "Tesla T4" - mock_get_gpus.return_value = [mock_gpu] - assert get_gpu_name() == "Tesla T4" - - # Simulate no GPU available - mock_get_gpus.return_value = [] - assert get_gpu_name() is None - - -def test_get_cpu_name(): - with patch( - "platform.processor", return_value="Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz" - ): - assert get_cpu_name() == "Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz" - - -def test_get_system_info(): - system_info = get_system_info() - assert isinstance(system_info, SystemInfo) - assert system_info.system is not None - assert system_info.cpu_cores > 0 - - -def test_http_client_get_external_url(): - client = HttpClient(api_key="test_key", host_url="http://example.com") - with patch("requests.get") as mock_get: - mock_get.return_value.status_code = 200 - response = client.get_external_url("test/path") - assert response.status_code == 200 - mock_get.assert_called_with("test/path", params={}, stream=False) - - -def test_http_client_get(): - client = HttpClient(api_key="test_key", host_url="http://example.com") - with patch("requests.get") as mock_get: - mock_get.return_value.status_code = 200 - response = client.get("test/path") - assert response.status_code == 200 - mock_get.assert_called_with( - "http://example.com/test/path", - headers=client.default_headers, - params=None, - stream=False, - ) - - -def test_http_client_post(): - client = HttpClient(api_key="test_key", host_url="http://example.com") - with patch("requests.post") as mock_post: - mock_post.return_value.status_code = 201 - response = client.post("test/path", data={"key": "value"}) - assert response.status_code == 201 - mock_post.assert_called_with( - "http://example.com/test/path", - headers=client.default_headers, - json={"key": "value"}, - files=None, - ) - - -def test_http_client_delete(): - client = HttpClient(api_key="test_key", host_url="http://example.com") - with patch("requests.delete") as mock_delete: - mock_delete.return_value.status_code = 204 - response = client.delete("test/path") - assert response.status_code == 204 - mock_delete.assert_called_with( - "http://example.com/test/path", headers=client.default_headers - ) From c2c9b5cf3fa8cb784192cf84e399d6453259d85e Mon Sep 17 00:00:00 2001 From: Ivan Murabito Date: Fri, 20 Dec 2024 12:31:51 +0000 Subject: [PATCH 4/4] fix make lint --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 3c11677..bc4f037 100644 --- a/Makefile +++ b/Makefile @@ -8,8 +8,8 @@ install-dev: install-pre-commit: @pre-commit install lint: - @isort . --profile=black - @black . + @isort ./focoos --profile=black + @black ./focoos run-pre-commit: @pre-commit run --all-files test: