|
16 | 16 | from __future__ import annotations |
17 | 17 |
|
18 | 18 | import abc |
| 19 | +import contextlib |
| 20 | +import csv |
19 | 21 | import dataclasses |
| 22 | +import json |
| 23 | +import os |
| 24 | +import pathlib |
20 | 25 | from collections.abc import Sequence |
| 26 | +from typing import Any |
21 | 27 |
|
22 | 28 | import requests |
23 | 29 | from typing_extensions import override |
@@ -74,13 +80,93 @@ def get_response( |
74 | 80 | class FakeApiClient(BaseClient): |
75 | 81 | """Fake class for specifying API client.""" |
76 | 82 |
|
77 | | - def __init__(self, results: Sequence) -> None: |
| 83 | + def __init__(self, results: Sequence[dict[str, Any]], **kwargs: str) -> None: |
78 | 84 | """Initializes FakeApiClient.""" |
79 | 85 | self.results = list(results) |
| 86 | + self.kwargs = kwargs |
80 | 87 |
|
81 | 88 | @override |
82 | 89 | def get_response( |
83 | | - self, request: GarfApiRequest = GarfApiRequest() |
| 90 | + self, request: GarfApiRequest = GarfApiRequest(), **kwargs: str |
84 | 91 | ) -> GarfApiResponse: |
85 | 92 | del request |
86 | 93 | return GarfApiResponse(results=self.results) |
| 94 | + |
| 95 | + @classmethod |
| 96 | + def from_file(cls, file_location: str | os.PathLike[str]) -> FakeApiClient: |
| 97 | + """Initializes FakeApiClient from json or csv files. |
| 98 | +
|
| 99 | + Args: |
| 100 | + file_location: Path of file with data. |
| 101 | +
|
| 102 | + Returns: |
| 103 | + Initialized client. |
| 104 | +
|
| 105 | + Raises: |
| 106 | + GarfApiError: When file with unsupported extension is provided. |
| 107 | + """ |
| 108 | + if str(file_location).endswith('.json'): |
| 109 | + return FakeApiClient.from_json(file_location) |
| 110 | + if str(file_location).endswith('.csv'): |
| 111 | + return FakeApiClient.from_csv(file_location) |
| 112 | + raise GarfApiError( |
| 113 | + 'Unsupported file extension, only csv and json are supported.' |
| 114 | + ) |
| 115 | + |
| 116 | + @classmethod |
| 117 | + def from_json(cls, file_location: str | os.PathLike[str]) -> FakeApiClient: |
| 118 | + """Initializes FakeApiClient from json file. |
| 119 | +
|
| 120 | + Args: |
| 121 | + file_location: Path of file with data. |
| 122 | +
|
| 123 | + Returns: |
| 124 | + Initialized client. |
| 125 | +
|
| 126 | + Raises: |
| 127 | + GarfApiError: When file with data not found. |
| 128 | + """ |
| 129 | + try: |
| 130 | + with pathlib.Path.open(file_location, 'r', encoding='utf-8') as f: |
| 131 | + data = json.load(f) |
| 132 | + return FakeApiClient(data) |
| 133 | + except FileNotFoundError as e: |
| 134 | + raise GarfApiError(f'Failed to open {file_location}') from e |
| 135 | + |
| 136 | + @classmethod |
| 137 | + def from_csv(cls, file_location: str | os.PathLike[str]) -> FakeApiClient: |
| 138 | + """Initializes FakeApiClient from csv file. |
| 139 | +
|
| 140 | + Args: |
| 141 | + file_location: Path of file with data. |
| 142 | +
|
| 143 | + Returns: |
| 144 | + Initialized client. |
| 145 | +
|
| 146 | + Raises: |
| 147 | + GarfApiError: When file with data not found. |
| 148 | + """ |
| 149 | + try: |
| 150 | + with pathlib.Path.open(file_location, 'r', encoding='utf-8') as f: |
| 151 | + reader = csv.DictReader(f) |
| 152 | + data = [] |
| 153 | + for row in reader: |
| 154 | + data.append( |
| 155 | + {key: _field_converter(value) for key, value in row.items()} |
| 156 | + ) |
| 157 | + return FakeApiClient(data) |
| 158 | + except FileNotFoundError as e: |
| 159 | + raise GarfApiError(f'Failed to open {file_location}') from e |
| 160 | + |
| 161 | + |
| 162 | +def _field_converter(field: str): |
| 163 | + if isinstance(field, str) and (lower_field := field.lower()) in ( |
| 164 | + 'true', |
| 165 | + 'false', |
| 166 | + ): |
| 167 | + return lower_field == 'true' |
| 168 | + with contextlib.suppress(ValueError): |
| 169 | + return int(field) |
| 170 | + with contextlib.suppress(ValueError): |
| 171 | + return float(field) |
| 172 | + return field |
0 commit comments