Данный проект представляет собой набор инструментов для работы с внешними API(GitHub и ExchangeRate-API). Он включает в себя два сервиса: анализатор репозиториев GitHub и конвертер курсов валют.
Проект выполнен с соблюдением принципов модульности, надежной обработки ошибок и полного покрытия unit-тестами.
1. GitHub Repository Analyzer
- Получение списка публичных репозиториев любого пользователя.
- Анализ статистики: общее количество звезд, самый популярный репозиторий, распределение по языкам программирования.
- Обработка специфических ошибок: "Пользователь не найден", "Превышение лимитов запросов API".
2. Currency Exchange Tool
- Получение актуальных курсов валют относительно выбранной базы.
- Фильтрация вывода по списку интересующих валют.
- Надежная обработка сетевых сбоев, ошибок авторизации(401) и некорректных кодов валют.
- Использование переменных окружения для безопасного хранения API-ключа.
WorkWithAPI/
└── logging_module/
├── my_logger_config.py # Модуль с настройками логирования и инициализацией логгеров
├── my_logging_config.yaml # YAML-файл конфигурации для логирования
└── my_color_formatter.py # Кастомный форматтер для цветного вывода логов в консоль
└── logs/ # Директория для логов
└── task_1 # Директория с файлами для логов первого задания
├── app.log # Информационные логи приложения
└── error.log # Логи ошибок и исключений
└── task_2 # Директория с файлами для логов второго задания
├── app.log # Информационные логи приложения
└── error.log # Логи ошибок и исключений
└── task_1_github_service/ # Директория с файлами программы первого задания
├── analytics.py # Логика расчетов статистики (analyze_repos)
├── github_api.py # Логика взаимодействия с GitHub (get_repos)
├── main.py # Запуск программы
├── test_github.py # unit-тесты для analyze_repos и get_repos
└── task_2_exchangerate_service/ # Директория с файлами программы второго задания
├── exchangerate_api.py # Логика взаимодействия с ExchangeRate-API (get_exchange_rates)
├── main_and_display_rates.py # Запуск программы и функция вывода данных (display_rates)
├── test_exchangerate.py # unit-тесты для get_exchange_rates и display_rates
├── .env.example # Пример переменной окружения
└── requirements.txt # Список библиотек необходимых для работы программы
Python 3.10+python-dotenv(для работы с переменной окружения)requests(для работы с HTTP-запросами)pytestиpytest-mock(для тестирования и имитации ответов сервера)PyYAML(для загрузки конфигурации логирования)Colorama- (для цветного вывода логов в консоль)
- Клонировать репозиторий:
git clone <URL_РЕПОЗИТОРИЯ>
cd <ИМЯ_ПАПКИ_ПРОЕКТА>
- Установить необходимые библиотеки (инструкцию см. ниже).
- Настроить API-ключ (инструкцию см. ниже).
- Запуск сервисов:
- GitHub
python main.py
- Currency Exchange
python main_and_display_rates.py
- В командной строке или окне терминала ввести команду
- Нажать кнопку Enter
pip install -r requirements.txt- Для установки всех библиотек
Для работы сервиса конвертации валют (Currency Exchange Tool) необходимо получить API-ключ.
- Зарегистрируйтесь на сайте https://www.exchangerate-api.com/ и получите бесплатный ключ.
- Создайте в корневой директории проекта файл .env (или переименуйте пример)
- Добавьте в файл .env Ваш ключ в следующем формате
EXCHANGE_API_KEY=ваш_секретный_ключВажно: Никогда не фиксируйте файл .env в системе контроля версий (Git). Убедитесь, что он добавлен в Ваш .gitignore.
Приложение реализует комплексную систему логирования, которая:
- Записывает все события приложения и ошибки.
- Использует различные уровни логирования (INFO, WARNING, ERROR, DEBUG, CRITICAL).
- Обеспечивает цветной вывод в консоль для лучшей читаемости.
- Хранит логи в отдельных файлах(app.log для общей информации, error.log для ошибок) в общей директории
logs/, но в разных папках для каждого задания.
- app.log: Содержит общую информацию о работе приложения, включая действия пользователя, успешное выполнение запросов к API и информационные сообщения.
- error.log: Записывает все сообщения уровня
ERRORи выше, включая ошибки выполнения запросов к API, исключения и критические сбои.
Настройка системы логирования осуществляется через следующие файлы:
- my_logger_config.py: Настраивает конфигурацию логирования
- my_logging_config.yaml: Конфигурация для различных логгеров
- my_color_formatter.py: Кастомный форматтер для цветного вывода в консоль
В проекте реализовано полное покрытие тестами всех модулей, включая успешные сценарии, сетевые ошибки (Timeout, ConnectionError) и ошибки бизнес-логики API.
Файл с тестированием можно запустить как и обычный код, через кнопку run.
Также юнит-тесты можно запустить из командной строки:
pytest- Краткий вариант результата юнит-тестовpytest -v- Более детальный вывод результата юнит-тестов
import pytest
import requests.exceptions
from pytest_mock import MockerFixture
from task_1_github_service.github_api import get_repos, GithubError, UserNotFoundError, RateLimitError
from task_1_github_service.analytics import analyze_repos
def test_analyze_repos_success():
"""
Проверка успешного анализа списка репозиториев.
Проверяет корректность подсчета звезд, общего количества и лучшего репозитория.
"""
# Готовим тестовые данные (имитируем ответ от GitHub)
test_data = [
{"name": "repo1", "stargazers_count": 10, "language": "Python"},
{"name": "repo2", "stargazers_count": 5, "language": "Python"},
{"name": "repo3", "stargazers_count": 20, "language": "JavaScript"}
]
stats = analyze_repos(test_data)
assert stats['total_repos'] == 3
assert stats['total_stars'] == 35
assert stats['best_repo'] == ("repo3", 20)
assert stats['languages'][0] == ("Python", 2)
def test_get_repos_timeout_error(mocker: MockerFixture):
"""
Проверка обработки превышения времени ожидания.
:param mocker: MockerFixture: Объект для имитации ошибок (mocking).
"""
# Вместо возврата ответа, заставляем requests.get выбросить исключение Timeout
mocker.patch("requests.get", side_effect=requests.exceptions.Timeout)
with pytest.raises(GithubError) as excinfo:
get_repos("testuser")
# Проверяем, что в тексте ошибки есть нужные слова
assert "Сервер GitHub слишком долго не отвечает" in str(excinfo.value)import pytest
import requests
from pytest_mock import MockerFixture
from task_2_exchangerate_service.exchangerate_api import get_exchange_rates, CurrencyAPIError
from task_2_exchangerate_service.main_and_display_rates import display_rates
def test_get_exchange_rates_api_business_error(mocker: MockerFixture):
"""
Проверка ошибки, которую возвращает сам API (unsupported-code).
:param mocker: MockerFixture: Объект для имитации ответа от requests (mocking).
"""
mock_resp = mocker.MagicMock()
# API часто присылает 200, но с текстом ошибки внутри
mock_resp.status_code = 200
mock_resp.json.return_value = {
"result": "error",
"error-type": "unsupported-code"
}
mocker.patch("requests.get", return_value=mock_resp)
with pytest.raises(CurrencyAPIError) as excinfo:
get_exchange_rates("ABC")
assert "не поддерживается сервисом" in str(excinfo.value)
@pytest.mark.parametrize("setup_type", ["valid_code", "invalid_codes_all"])
def test_display_rates_with_invalid_codes(capsys: pytest.CaptureFixture, setup_type: str):
"""
Проверка вывода сообщения о некорректных кодах.
:param capsys: pytest.CaptureFixture: Фикстура pytest для перехвата и анализа вывода данных.
:param setup_type: str: Ключ сценария подготовки данных (из parametrize).
"""
data = {
"base_code": "USD",
"conversion_rates": {"EUR": 0.84}
}
if setup_type == "valid_code":
# EUR - корректно, WWW - нет
target_currencies = ["EUR", "WWW"]
expected_text = "[!] Не удалось найти или некорректные коды валют: WWW"
elif setup_type == "invalid_codes_all":
# Все не корректные
target_currencies = ["AAA", "RUS", "888"]
expected_text = "[!] Ни одна из указанных валют не была найдена."
display_rates(data, target_currencies)
captured = capsys.readouterr()
assert expected_text in captured.out
if setup_type == "valid_code":
assert "1 USD = 0.84 EUR" in captured.out
assert expected_text in captured.out