Self-Task: APIs, JSON, and Asynchronous Python
Create a new public repository or a separate project folder. The assignment does not require branches or code from previous homework.
Imagine you have a list of users in a JSON file and you need to get more information about each user through an API (web service). This is a very common task in real work.
Basic Concepts
1. JSON file
JSON is a text data exchange format. Example:
{
"users": [
{"name": "Иван", "uuid": "123-456"},
{"name": "Мария", "uuid": "789-012"}
]
}
- JSON is widely used to exchange structured data via HTTP API.
uuid— a unique user identifier within the task.
2. API (web service)
- The API describes the contract for interaction between programs. In this task, the client receives data from an HTTP service.
- The client must be able to make GET requests to an address like:
{base_url}/users/{uuid}.base_urlpassed through an argument or configuration, rather than being hard-wired into business logic. - The API returns different responses:
- 200: everything is fine, data received
- 400: request error
- 404: user not found
api.example.com The following is used as an example contract only. A real external service is not needed to complete the task: HTTP responses are reproduced in tests via aioresponses.
3. Asynchronous programming
async def get_user_info(session: aiohttp.ClientSession, base_url: str, uuid: str):
async with session.get(f"{base_url}/users/{uuid}") as response:
response.raise_for_status()
return await response.json()
- Asynchronous code allows you to concurrently wait for multiple network requests without blocking the thread on each wait.
asyncdeclares a coroutine function, andawaitsuspends the current coroutine until the awaitable operation completes.- One
ClientSessionshould be reused for a series of requests: it controls the connection pool.
Decorators
Decorator is a function that changes the behavior of another function. In our case, it will add the current time to the response from the service and do a retry only for selected temporary errors: network failures, 408, 429 taking into account Retry-After and suitable 5xx. Majority 4xx there is no point in repeating.
@async_retry(attempts=3) # Это декоратор
async def my_function():
# Если функция упадёт с ошибкой, декоратор попробует ещё 2 раза
pass
What needs to be implemented?
- Main function:
async def process_users(input_file: str, output_file: str):
# 1. Читаем JSON
# 2. Создаём одну ClientSession
# 3. Запускаем запросы конкурентно через TaskGroup/gather
# и ограничиваем concurrency и timeout
# 4. Сохраняем результаты
pass
- Decorator with parameters:
def async_retry(attempts: int = 3):
# Измеряет время работы
# Пытается повторить при ошибке
# Логирует результаты
pass
Testing
Why are tests needed?
- Check that the code works correctly
- Helps find errors
- Document how the code should work
What will we test?
- Read/Write JSON:
def test_read_json():
data = read_json("test.json")
assert "users" in data
- Working with API (using
aioresponses):
async def test_api_call():
with aioresponses() as mock:
mock.get('http://api.example.com/users/123',
status=200,
payload={"status": "active"})
- Decorator (using
freezegun):freezeguncan be used to capture a calendar date and time as a result. Measure the duration of execution using monotonous clocks (time.monotonic()), and in the test replace this particular time source.
@freeze_time("2024-01-01")
async def test_retry():
# Проверяем, что retry работает
# Проверяем измерение времени
pass
Libraries that we will use
aiohttp- for HTTP requestspytest- to run testsaioresponses- for mocking HTTP requests in testsfreezegun- for working with time in tests
What to pay attention to
- Error handling - what to do if the file does not open?
- Logging - record important events
- Retries - what to do if the request fails?
- Tests should be clear and test important cases
Additional task
- Use the library
clickto run your script with configuration from the command line, like this:
python -m script --input file.json --output result.json
Readiness criteria
- the project is launched according to the instructions from the README;
- input JSON is validated, and read errors and data structures are handled;
- one
ClientSessionreused for a series of queries; - requests are executed concurrently with timeout and concurrency limits;
- retry applies only to selected temporary errors and has a clear number of retries;
- duration is measured by monotonous hours;
- tests are independent of the availability of an external API and check
200,404, temporary error, retry exhaustion and invalid JSON; - the result is saved in the output JSON, and the running and testing commands are described in the README.
Official sources
asyncio: tasks and coroutinesjsonin the standard library- aiohttp client quickstart
- pytest documentation