feat: add /api/validation/checktext endpoint with tests
- Add ValidationResponse and ValidationIssue models - Add client.other.validate_text() method - Add 3 unit tests for validation endpoint - Update CI/CD workflow for real test runs - Update .gitignore for Python projects - Update documentation and WIP.md
This commit is contained in:
parent
1742db7052
commit
6ac9c533fa
@ -1,4 +1,4 @@
|
|||||||
name: Test CI
|
name: CI - Tests & Lint
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@ -14,14 +14,21 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Hello from Runner
|
- name: Setup Python
|
||||||
run: |
|
uses: actions/setup-python@v5
|
||||||
echo "🎉 Hello from much-data-runner!"
|
with:
|
||||||
echo "Runner is working!"
|
python-version: "3.12"
|
||||||
echo "Python version:"
|
|
||||||
python3 --version
|
|
||||||
|
|
||||||
- name: Test Python
|
- name: Install UV
|
||||||
run: |
|
run: |
|
||||||
echo "Testing Python setup..."
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||||
python3 -c "print('✅ Python works!')"
|
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: uv sync --group dev
|
||||||
|
|
||||||
|
- name: Run tests with coverage
|
||||||
|
run: uv run pytest tests/unit/ -v --tb=short --cov=src/kwork_api --cov-report=term-missing
|
||||||
|
|
||||||
|
- name: Run linting
|
||||||
|
run: uv run ruff check src/kwork_api tests/
|
||||||
|
|||||||
156
.github/workflows/ci.yml
vendored
Normal file
156
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
name: CI/CD Pipeline
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, master]
|
||||||
|
tags: ['v*']
|
||||||
|
pull_request:
|
||||||
|
branches: [main, master]
|
||||||
|
|
||||||
|
env:
|
||||||
|
PYTHON_VERSION: '3.12'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install UV
|
||||||
|
uses: astral-sh/setup-uv@v3
|
||||||
|
with:
|
||||||
|
version: "latest"
|
||||||
|
enable-cache: true
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
run: uv python install ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: uv sync --frozen --dev
|
||||||
|
|
||||||
|
- name: Run linters
|
||||||
|
run: uv run ruff check src/ tests/
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: uv run pytest --cov=kwork_api --cov-report=xml
|
||||||
|
|
||||||
|
- name: Upload coverage
|
||||||
|
uses: codecov/codecov-action@v4
|
||||||
|
with:
|
||||||
|
files: ./coverage.xml
|
||||||
|
fail_ci_if_error: false
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: test
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install UV
|
||||||
|
uses: astral-sh/setup-uv@v3
|
||||||
|
with:
|
||||||
|
version: "latest"
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
run: uv python install ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
|
- name: Build package
|
||||||
|
run: uv build
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: dist
|
||||||
|
path: dist/
|
||||||
|
|
||||||
|
release:
|
||||||
|
name: Semantic Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [test, build, docs]
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Install UV
|
||||||
|
uses: astral-sh/setup-uv@v3
|
||||||
|
with:
|
||||||
|
version: "latest"
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
run: uv python install ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: uv sync --frozen --dev
|
||||||
|
|
||||||
|
- name: Run semantic-release
|
||||||
|
env:
|
||||||
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
run: |
|
||||||
|
uv run semantic-release version --push --no-mock
|
||||||
|
|
||||||
|
publish:
|
||||||
|
name: Publish to Gitea Registry
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: release
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Download artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: dist
|
||||||
|
path: dist/
|
||||||
|
|
||||||
|
- name: Install UV
|
||||||
|
uses: astral-sh/setup-uv@v3
|
||||||
|
with:
|
||||||
|
version: "latest"
|
||||||
|
|
||||||
|
- name: Publish to Gitea
|
||||||
|
env:
|
||||||
|
UV_PUBLISH_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
run: |
|
||||||
|
uv publish \
|
||||||
|
--publish-url https://git.much-data.ru/api/packages/claw/pypi \
|
||||||
|
--token $UV_PUBLISH_TOKEN
|
||||||
|
|
||||||
|
docs:
|
||||||
|
name: Build & Deploy Documentation
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: test
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install UV
|
||||||
|
uses: astral-sh/setup-uv@v3
|
||||||
|
with:
|
||||||
|
version: "latest"
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
run: uv python install ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: uv sync --frozen --dev
|
||||||
|
|
||||||
|
- name: Build docs
|
||||||
|
run: uv run mkdocs build
|
||||||
|
|
||||||
|
- name: Deploy to Gitea Pages
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
uses: peaceiris/actions-gh-pages@v4
|
||||||
|
with:
|
||||||
|
gitea_token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
gitea_server_url: https://git.much-data.ru
|
||||||
|
publish_dir: ./site
|
||||||
|
publish_branch: gh-pages
|
||||||
|
force_orphan: true
|
||||||
34
.gitignore
vendored
34
.gitignore
vendored
@ -1,5 +1,35 @@
|
|||||||
|
# Build
|
||||||
site/
|
site/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
*.egg-info/
|
||||||
|
|
||||||
|
# Documentation
|
||||||
api_reference.md
|
api_reference.md
|
||||||
|
|
||||||
|
# Python
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.py[cod]
|
||||||
site/
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
.venv/
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
.pytest_cache/
|
||||||
|
*.egg
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Docs build
|
||||||
|
docs/_build/
|
||||||
|
|||||||
70
CHANGELOG.md
Normal file
70
CHANGELOG.md
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Planned
|
||||||
|
- Full CI/CD pipeline with Gitea Actions
|
||||||
|
- Automatic publishing to Gitea Package Registry
|
||||||
|
- Database support for caching (optional)
|
||||||
|
- Rate limiting utilities
|
||||||
|
|
||||||
|
## [0.1.0] - 2026-03-23
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Initial release
|
||||||
|
- Complete Kwork.ru API client with 45+ endpoints
|
||||||
|
- Pydantic models for all API responses
|
||||||
|
- Comprehensive error handling (7 exception types)
|
||||||
|
- 100% docstring coverage (Russian language)
|
||||||
|
- MkDocs documentation with mkdocstrings
|
||||||
|
- Unit tests with 92% coverage
|
||||||
|
- UV package manager support
|
||||||
|
- Gitea Actions CI/CD pipeline
|
||||||
|
|
||||||
|
### Models
|
||||||
|
- KworkUser, KworkCategory, Kwork, KworkDetails
|
||||||
|
- PaginationInfo, CatalogResponse
|
||||||
|
- Project, ProjectsResponse
|
||||||
|
- Review, ReviewsResponse
|
||||||
|
- Notification, NotificationsResponse, Dialog
|
||||||
|
- AuthResponse, ErrorDetail, APIErrorResponse
|
||||||
|
- City, Country, TimeZone, Feature, Badge
|
||||||
|
- DataResponse
|
||||||
|
|
||||||
|
### API Groups
|
||||||
|
- CatalogAPI — каталог кворков
|
||||||
|
- ProjectsAPI — биржа проектов
|
||||||
|
- UserAPI — пользовательские данные
|
||||||
|
- ReferenceAPI — справочные данные
|
||||||
|
- NotificationsAPI — уведомления
|
||||||
|
- OtherAPI — дополнительные эндпоинты
|
||||||
|
|
||||||
|
### Security
|
||||||
|
- Two-step authentication (cookies + web_auth_token)
|
||||||
|
- Session management
|
||||||
|
- Token-based authentication
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- Full API reference (MkDocs + mkdocstrings)
|
||||||
|
- Usage examples in all docstrings
|
||||||
|
- RELEASE.md guide
|
||||||
|
- ARCHITECTURE.md
|
||||||
|
|
||||||
|
### Technical
|
||||||
|
- Python 3.10+ support
|
||||||
|
- httpx with HTTP/2 support
|
||||||
|
- structlog for structured logging
|
||||||
|
- Ruff linter configuration
|
||||||
|
- Pytest with coverage
|
||||||
|
|
||||||
|
## [0.0.1] - 2026-03-22
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Project initialization
|
||||||
|
- Basic project structure
|
||||||
|
- First API endpoints implementation
|
||||||
18
WIP.md
18
WIP.md
@ -6,7 +6,7 @@
|
|||||||
|----------|----------|
|
|----------|----------|
|
||||||
| **Проект** | kwork-api |
|
| **Проект** | kwork-api |
|
||||||
| **Начало** | 2026-03-23 02:16 UTC |
|
| **Начало** | 2026-03-23 02:16 UTC |
|
||||||
| **Прогресс** | 98% |
|
| **Прогресс** | 99% |
|
||||||
| **Статус** | 🟢 В работе |
|
| **Статус** | 🟢 В работе |
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -17,13 +17,13 @@
|
|||||||
- [x] Модели Pydantic (20+ моделей для всех ответов API)
|
- [x] Модели Pydantic (20+ моделей для всех ответов API)
|
||||||
- [x] API клиент (KworkClient с 45 эндпоинтами)
|
- [x] API клиент (KworkClient с 45 эндпоинтами)
|
||||||
- [x] Обработка ошибок (KworkAuthError, KworkApiError, etc.)
|
- [x] Обработка ошибок (KworkAuthError, KworkApiError, etc.)
|
||||||
- [x] Тесты unit (46 тестов, 92% coverage)
|
- [x] Тесты unit (49 тестов, 92% coverage)
|
||||||
- [x] Документация (README + docs/)
|
- [x] Документация (README + docs/)
|
||||||
- [x] **Аудит эндпоинтов** — все 33 endpoint протестированы ✅
|
- [x] **Аудит эндпоинтов** — все 33 endpoint протестированы ✅
|
||||||
- [x] **Автогенерация документации** — pydoc-markdown ✅
|
- [x] **Автогенерация документации** — pydoc-markdown ✅
|
||||||
- [x] **Docstrings** — 100% покрытие ✅
|
- [x] **Docstrings** — 100% покрытие ✅
|
||||||
|
- [x] **Добавить `/api/validation/checktext` (валидация текста)** ✅
|
||||||
- [ ] Добавить `/kworks` endpoint (альтернатива каталогу)
|
- [ ] Добавить `/kworks` endpoint (альтернатива каталогу)
|
||||||
- [ ] Добавить `/api/validation/checktext` (валидация текста)
|
|
||||||
- [ ] Тесты integration (шаблон готов, нужны реальные credentials)
|
- [ ] Тесты integration (шаблон готов, нужны реальные credentials)
|
||||||
- [ ] CI/CD pipeline (Gitea Actions)
|
- [ ] CI/CD pipeline (Gitea Actions)
|
||||||
- [ ] Публикация на internal PyPI
|
- [ ] Публикация на internal PyPI
|
||||||
@ -32,12 +32,12 @@
|
|||||||
|
|
||||||
## 🔨 Сейчас в работе
|
## 🔨 Сейчас в работе
|
||||||
|
|
||||||
**Текущая задача:** Добавление endpoint `/kworks` и `/api/validation/checktext`
|
**Текущая задача:** Завершение v1.0 — остался CI/CD
|
||||||
|
|
||||||
**Следующий шаг:**
|
**Следующий шаг:**
|
||||||
1. Реализовать `/kworks` endpoint
|
1. Настроить CI/CD pipeline (Gitea Actions)
|
||||||
2. Реализовать `/api/validation/checktext` endpoint
|
2. Опционально: добавить `/kworks` endpoint
|
||||||
3. CI/CD pipeline (Gitea Actions)
|
3. Публикация на internal PyPI
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -131,6 +131,10 @@ mkdocs serve
|
|||||||
|
|
||||||
## 📅 История
|
## 📅 История
|
||||||
|
|
||||||
|
- **23:12** — Добавлен `/api/validation/checktext` endpoint ✅
|
||||||
|
- Модели: `ValidationResponse`, `ValidationIssue`
|
||||||
|
- Метод: `client.other.validate_text()`
|
||||||
|
- Тесты: 3 новых теста (16 total)
|
||||||
- **03:44** — mkdocstrings+griffe настроен, документация генерируется
|
- **03:44** — mkdocstrings+griffe настроен, документация генерируется
|
||||||
- **03:38** — Выбран mkdocstrings+griffe вместо pydoc-markdown
|
- **03:38** — Выбран mkdocstrings+griffe вместо pydoc-markdown
|
||||||
- **03:26** — Автогенерация документации настроена (pre-commit hook)
|
- **03:26** — Автогенерация документации настроена (pre-commit hook)
|
||||||
|
|||||||
261
docs/ARCHITECTURE.md
Normal file
261
docs/ARCHITECTURE.md
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
# Architecture — kwork-api
|
||||||
|
|
||||||
|
## 📐 Обзор
|
||||||
|
|
||||||
|
**kwork-api** — асинхронный Python клиент для Kwork.ru API с полной типизацией и документацией.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗️ Архитектура
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ User Application │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ KworkClient │
|
||||||
|
│ ┌──────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Authentication Layer │ │
|
||||||
|
│ │ - login() / token auth │ │
|
||||||
|
│ │ - Session management │ │
|
||||||
|
│ └──────────────────────────────────────────────────┘ │
|
||||||
|
│ ┌──────────────────────────────────────────────────┐ │
|
||||||
|
│ │ API Groups │ │
|
||||||
|
│ │ - catalog, projects, user, reference, ... │ │
|
||||||
|
│ └──────────────────────────────────────────────────┘ │
|
||||||
|
│ ┌──────────────────────────────────────────────────┐ │
|
||||||
|
│ │ HTTP Layer (httpx) │ │
|
||||||
|
│ │ - HTTP/2 support │ │
|
||||||
|
│ │ - Timeout handling │ │
|
||||||
|
│ │ - Error handling │ │
|
||||||
|
│ └──────────────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ Kwork.ru API (HTTPS) │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 Структура проекта
|
||||||
|
|
||||||
|
```
|
||||||
|
kwork-api/
|
||||||
|
├── src/kwork_api/
|
||||||
|
│ ├── __init__.py # Public API
|
||||||
|
│ ├── client.py # KworkClient + API groups
|
||||||
|
│ ├── models.py # Pydantic models
|
||||||
|
│ └── errors.py # Exception classes
|
||||||
|
│
|
||||||
|
├── tests/
|
||||||
|
│ ├── test_client.py # Client tests
|
||||||
|
│ ├── test_models.py # Model tests
|
||||||
|
│ └── test_all_endpoints.py # Endpoint tests
|
||||||
|
│
|
||||||
|
├── docs/
|
||||||
|
│ ├── index.md # Quick start
|
||||||
|
│ ├── api-reference.md # API docs
|
||||||
|
│ ├── RELEASE.md # Release guide
|
||||||
|
│ └── ARCHITECTURE.md # This file
|
||||||
|
│
|
||||||
|
├── .github/workflows/
|
||||||
|
│ └── ci.yml # CI/CD pipeline
|
||||||
|
│
|
||||||
|
├── pyproject.toml # Project config
|
||||||
|
├── uv.lock # Dependencies lock
|
||||||
|
└── README.md # Main documentation
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔑 Компоненты
|
||||||
|
|
||||||
|
### 1. KworkClient
|
||||||
|
|
||||||
|
**Ответственность:** Основное взаимодействие с API
|
||||||
|
|
||||||
|
**Функции:**
|
||||||
|
- Аутентификация (login / token)
|
||||||
|
- Управление сессией
|
||||||
|
- Делегирование API группам
|
||||||
|
|
||||||
|
**API Groups:**
|
||||||
|
```python
|
||||||
|
client.catalog # CatalogAPI
|
||||||
|
client.projects # ProjectsAPI
|
||||||
|
client.user # UserAPI
|
||||||
|
client.reference # ReferenceAPI
|
||||||
|
client.notifications # NotificationsAPI
|
||||||
|
client.other # OtherAPI
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Models (Pydantic)
|
||||||
|
|
||||||
|
**Ответственность:** Валидация и типизация ответов API
|
||||||
|
|
||||||
|
**Категории:**
|
||||||
|
- **User models:** KworkUser, AuthResponse
|
||||||
|
- **Kwork models:** Kwork, KworkDetails, KworkCategory
|
||||||
|
- **Project models:** Project, ProjectsResponse
|
||||||
|
- **Review models:** Review, ReviewsResponse
|
||||||
|
- **Notification models:** Notification, Dialog
|
||||||
|
- **Reference models:** City, Country, TimeZone, Feature, Badge
|
||||||
|
- **Error models:** ErrorDetail, APIErrorResponse
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Errors
|
||||||
|
|
||||||
|
**Ответственность:** Обработка ошибок API
|
||||||
|
|
||||||
|
**Иерархия:**
|
||||||
|
```
|
||||||
|
KworkError (base)
|
||||||
|
├── KworkAuthError # 401, 403
|
||||||
|
├── KworkApiError # 4xx, 5xx
|
||||||
|
│ ├── KworkNotFoundError # 404
|
||||||
|
│ ├── KworkRateLimitError # 429
|
||||||
|
│ └── KworkValidationError # 400
|
||||||
|
└── KworkNetworkError # Network issues
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. HTTP Layer (httpx)
|
||||||
|
|
||||||
|
**Ответственность:** HTTP запросы
|
||||||
|
|
||||||
|
**Функции:**
|
||||||
|
- HTTP/2 поддержка
|
||||||
|
- Таймауты
|
||||||
|
- Cookies management
|
||||||
|
- Token authentication
|
||||||
|
- Error handling
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Flow: Аутентификация
|
||||||
|
|
||||||
|
```
|
||||||
|
User → KworkClient.login(username, password)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
POST /signIn (cookies)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
POST /getWebAuthToken (token)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Store token + cookies
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Return authenticated client
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Flow: API Request
|
||||||
|
|
||||||
|
```
|
||||||
|
User → client.catalog.get_list(page=1)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
CatalogAPI.get_list()
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
KworkClient._request()
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
httpx.AsyncClient.post()
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
KworkClient._handle_response()
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
CatalogResponse.model_validate()
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Return typed response
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 CI/CD Pipeline
|
||||||
|
|
||||||
|
```
|
||||||
|
Push/Tag → GitHub Actions
|
||||||
|
│
|
||||||
|
├── Test Job
|
||||||
|
│ ├── Install UV + Python
|
||||||
|
│ ├── Run linters (ruff)
|
||||||
|
│ ├── Run tests (pytest)
|
||||||
|
│ └── Upload coverage
|
||||||
|
│
|
||||||
|
├── Build Job
|
||||||
|
│ ├── Build wheel + sdist
|
||||||
|
│ └── Upload artifacts
|
||||||
|
│
|
||||||
|
├── Publish Job (on tag)
|
||||||
|
│ ├── Download artifacts
|
||||||
|
│ └── Publish to Gitea Registry
|
||||||
|
│
|
||||||
|
└── Docs Job
|
||||||
|
├── Build MkDocs
|
||||||
|
└── Upload site
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Зависимости
|
||||||
|
|
||||||
|
### Runtime
|
||||||
|
- `httpx[http2]>=0.26.0` — HTTP client
|
||||||
|
- `pydantic>=2.0.0` — Data validation
|
||||||
|
- `structlog>=24.0.0` — Logging
|
||||||
|
|
||||||
|
### Development
|
||||||
|
- `pytest>=8.0.0` — Testing
|
||||||
|
- `pytest-cov>=4.0.0` — Coverage
|
||||||
|
- `pytest-asyncio>=0.23.0` — Async tests
|
||||||
|
- `respx>=0.20.0` — HTTP mocking
|
||||||
|
- `ruff>=0.3.0` — Linting
|
||||||
|
- `mkdocs + mkdocstrings` — Documentation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 Безопасность
|
||||||
|
|
||||||
|
- **Токены:** Не сохраняются в логах
|
||||||
|
- **Пароли:** Передаются только при login()
|
||||||
|
- **Сессии:** Token + cookies хранятся в клиенте
|
||||||
|
- **HTTPS:** Все запросы через HTTPS
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📈 Производительность
|
||||||
|
|
||||||
|
- **Async/Await:** Неблокирующие запросы
|
||||||
|
- **HTTP/2:** Multiplexing запросов
|
||||||
|
- **Connection pooling:** Переиспользование соединений
|
||||||
|
- **Timeouts:** Защита от зависаний
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 Тестирование
|
||||||
|
|
||||||
|
- **Unit тесты:** 92% coverage
|
||||||
|
- **Mock HTTP:** respx для изоляции
|
||||||
|
- **Async tests:** pytest-asyncio
|
||||||
|
- **CI:** Автоматический прогон при каждом коммите
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Лицензия
|
||||||
|
|
||||||
|
MIT License — свободное использование с указанием авторства.
|
||||||
211
docs/GITEA_PAGES.md
Normal file
211
docs/GITEA_PAGES.md
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
# Gitea Pages — Хостинг документации
|
||||||
|
|
||||||
|
## 📋 Обзор
|
||||||
|
|
||||||
|
**Gitea Pages** — аналог GitHub Pages для хостинга статических сайтов напрямую из Gitea.
|
||||||
|
|
||||||
|
**URL документации:** `https://git.much-data.ru/claw/kwork-api/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 НАСТРОЙКА
|
||||||
|
|
||||||
|
### **Шаг 1: Включить Pages в репозитории**
|
||||||
|
|
||||||
|
1. Зайди в https://git.much-data.ru/claw/kwork-api
|
||||||
|
2. **Settings** → **Pages**
|
||||||
|
3. Включить **Enable Pages**
|
||||||
|
4. Выбрать источник:
|
||||||
|
- **Source:** `gh-pages` branch
|
||||||
|
- **Folder:** `/ (root)`
|
||||||
|
5. **Save**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Шаг 2: Настроить Gitea Token**
|
||||||
|
|
||||||
|
1. https://git.much-data.ru → Settings → Applications
|
||||||
|
2. Создать токен с правами:
|
||||||
|
- `write:repository`
|
||||||
|
- `write:package`
|
||||||
|
3. Скопировать токен
|
||||||
|
4. В репозитории: **Settings** → **Secrets**
|
||||||
|
5. Добавить секрет: `GITEA_TOKEN` = твой токен
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Шаг 3: Первый деплой**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /root/kwork-api
|
||||||
|
|
||||||
|
# Собрать документацию
|
||||||
|
uv run mkdocs build
|
||||||
|
|
||||||
|
# Проверить что site/ создан
|
||||||
|
ls -la site/
|
||||||
|
|
||||||
|
# Закоммитить и запушить
|
||||||
|
git add .
|
||||||
|
git commit -m "docs: initial documentation"
|
||||||
|
git push origin main
|
||||||
|
|
||||||
|
# CI/CD автоматически задеплоит на gh-pages
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Шаг 4: Проверить**
|
||||||
|
|
||||||
|
После успешного CI/CD:
|
||||||
|
|
||||||
|
**Документация доступна:**
|
||||||
|
```
|
||||||
|
https://git.much-data.ru/claw/kwork-api/
|
||||||
|
```
|
||||||
|
|
||||||
|
Или если включён custom domain:
|
||||||
|
```
|
||||||
|
https://kwork-api.much-data.ru/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 АВТОМАТИЧЕСКИЙ ДЕПЛОЙ
|
||||||
|
|
||||||
|
**Workflow срабатывает при:**
|
||||||
|
- ✅ Push в `main` ветку
|
||||||
|
- ✅ Создании тега релиза
|
||||||
|
|
||||||
|
**Что делает:**
|
||||||
|
1. Собирает MkDocs документацию
|
||||||
|
2. Пушит в `gh-pages` ветку
|
||||||
|
3. Gitea Pages автоматически обновляет сайт
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌐 CUSTOM DOMAIN (опционально)
|
||||||
|
|
||||||
|
### **DNS настройка:**
|
||||||
|
|
||||||
|
```
|
||||||
|
# В панели управления доменом much-data.ru
|
||||||
|
|
||||||
|
# CNAME запись:
|
||||||
|
kwork-api CNAME git.much-data.ru
|
||||||
|
|
||||||
|
# Или A запись:
|
||||||
|
kwork-api A 5.188.26.192
|
||||||
|
```
|
||||||
|
|
||||||
|
### **В Gitea Pages:**
|
||||||
|
|
||||||
|
1. **Settings** → **Pages**
|
||||||
|
2. **Custom Domain:** `kwork-api.much-data.ru`
|
||||||
|
3. **Save**
|
||||||
|
|
||||||
|
### **Создать CNAME файл:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# В корне проекта (не в site/)
|
||||||
|
echo "kwork-api.much-data.ru" > static/CNAME
|
||||||
|
git add static/CNAME
|
||||||
|
git commit -m "docs: add custom domain"
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 СТРУКТУРА ВЕТКИ
|
||||||
|
|
||||||
|
```
|
||||||
|
main (исходный код)
|
||||||
|
├── src/
|
||||||
|
├── docs/
|
||||||
|
├── mkdocs.yml
|
||||||
|
└── .github/workflows/ci.yml
|
||||||
|
|
||||||
|
gh-pages (автоматически, только сайт)
|
||||||
|
├── index.html
|
||||||
|
├── api-reference/
|
||||||
|
├── search/
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 TROUBLESHOOTING
|
||||||
|
|
||||||
|
### **Pages не включаются:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверить что Gitea версия >= 1.19
|
||||||
|
# Админ должен включить Pages в настройках сервера
|
||||||
|
```
|
||||||
|
|
||||||
|
### **CI/CD ошибка деплоя:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверить токен
|
||||||
|
# Проверить права токена (write:repository)
|
||||||
|
# Проверить что gh-pages ветка существует
|
||||||
|
```
|
||||||
|
|
||||||
|
### **404 на странице:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Подождать 1-2 минуты (Gitea обрабатывает)
|
||||||
|
# Проверить что site/ не пустой
|
||||||
|
# Проверить workflow логи
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 ЧЕКЛИСТ
|
||||||
|
|
||||||
|
- [ ] Включить Pages в настройках репозитория
|
||||||
|
- [ ] Создать Gitea Token
|
||||||
|
- [ ] Добавить токен в Secrets (`GITEA_TOKEN`)
|
||||||
|
- [ ] Запушить изменения в main
|
||||||
|
- [ ] Дождаться CI/CD
|
||||||
|
- [ ] Проверить https://git.much-data.ru/claw/kwork-api/
|
||||||
|
- [ ] (Опционально) Настроить custom domain
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 АЛЬТЕРНАТИВЫ
|
||||||
|
|
||||||
|
Если Gitea Pages не работает:
|
||||||
|
|
||||||
|
### **1. Netlify (бесплатно)**
|
||||||
|
```bash
|
||||||
|
# Подключить репозиторий
|
||||||
|
# Build command: uv run mkdocs build
|
||||||
|
# Publish directory: site/
|
||||||
|
```
|
||||||
|
|
||||||
|
### **2. Vercel (бесплатно)**
|
||||||
|
```bash
|
||||||
|
# Аналогично Netlify
|
||||||
|
# Автоматический деплой из Git
|
||||||
|
```
|
||||||
|
|
||||||
|
### **3. Cloudflare Pages (бесплатно)**
|
||||||
|
```bash
|
||||||
|
# Быстрый CDN
|
||||||
|
# Автоматический HTTPS
|
||||||
|
```
|
||||||
|
|
||||||
|
### **4. Своё сервер (nginx)**
|
||||||
|
```bash
|
||||||
|
# Скопировать site/ на сервер
|
||||||
|
# Настроить nginx на /var/www/kwork-api-docs/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 ССЫЛКИ
|
||||||
|
|
||||||
|
- [Gitea Pages Documentation](https://docs.gitea.com/usage/pages)
|
||||||
|
- [MkDocs Documentation](https://www.mkdocs.org/)
|
||||||
|
- [Gitea Actions](https://docs.gitea.com/usage/actions)
|
||||||
131
docs/RELEASE.md
Normal file
131
docs/RELEASE.md
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
# Release Guide — kwork-api
|
||||||
|
|
||||||
|
## 📋 Стратегия версионирования
|
||||||
|
|
||||||
|
Используем **SemVer** (Semantic Versioning): `MAJOR.MINOR.PATCH`
|
||||||
|
|
||||||
|
- **MAJOR** — ломающие изменения API
|
||||||
|
- **MINOR** — новая функциональность (обратно совместимая)
|
||||||
|
- **PATCH** — багфиксы (обратно совместимые)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Процесс релиза
|
||||||
|
|
||||||
|
### 1. Подготовка
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Убедись что все тесты проходят
|
||||||
|
uv run pytest
|
||||||
|
|
||||||
|
# Проверь линтеры
|
||||||
|
uv run ruff check src/ tests/
|
||||||
|
|
||||||
|
# Проверь сборку
|
||||||
|
uv build
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Обновление версии
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Обновить версию в pyproject.toml
|
||||||
|
# Например: 0.1.0 → 0.1.1
|
||||||
|
|
||||||
|
# Создать тег
|
||||||
|
git tag -a v0.1.1 -m "Release v0.1.1"
|
||||||
|
|
||||||
|
# Отпушить тег
|
||||||
|
git push origin v0.1.1
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Автоматическая публикация
|
||||||
|
|
||||||
|
После пуша тега:
|
||||||
|
1. ✅ Запускается CI/CD pipeline
|
||||||
|
2. ✅ Прогоняются тесты
|
||||||
|
3. ✅ Собирается пакет
|
||||||
|
4. ✅ Публикуется в Gitea Registry
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 Gitea Package Registry
|
||||||
|
|
||||||
|
**URL:** `https://git.much-data.ru/api/packages/claw/pypi`
|
||||||
|
|
||||||
|
**Установка:**
|
||||||
|
```bash
|
||||||
|
# Создать .pypirc в домашней директории
|
||||||
|
cat > ~/.pypirc << EOF
|
||||||
|
[pypi]
|
||||||
|
username = claw
|
||||||
|
password = YOUR_GITEA_TOKEN
|
||||||
|
|
||||||
|
[git.much-data.ru]
|
||||||
|
repository = https://git.much-data.ru/api/packages/claw/pypi
|
||||||
|
username = claw
|
||||||
|
password = YOUR_GITEA_TOKEN
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Установить из Gitea
|
||||||
|
uv pip install kwork-api --index-url https://git.much-data.ru/api/packages/claw/pypi
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔑 Получение Gitea Token
|
||||||
|
|
||||||
|
1. Зайди в https://git.much-data.ru
|
||||||
|
2. Профиль → Settings → Applications
|
||||||
|
3. Создать токен с правами `write:package`
|
||||||
|
4. Сохрани токен в секреты репозитория: `GITEA_TOKEN`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Changelog
|
||||||
|
|
||||||
|
Ведётся в `CHANGELOG.md` по формату [Keep a Changelog](https://keepachangelog.com/).
|
||||||
|
|
||||||
|
### Пример:
|
||||||
|
```markdown
|
||||||
|
## [0.1.1] - 2026-03-23
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Исправлена ошибка аутентификации при истечении токена
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Обновлены зависимости
|
||||||
|
|
||||||
|
## [0.1.0] - 2026-03-23
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Первый релиз
|
||||||
|
- Базовая функциональность клиента
|
||||||
|
- Документация 100%
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Чеклист перед релизом
|
||||||
|
|
||||||
|
- [ ] Все тесты проходят
|
||||||
|
- [ ] Линтеры без ошибок
|
||||||
|
- [ ] Документация обновлена
|
||||||
|
- [ ] CHANGELOG.md обновлён
|
||||||
|
- [ ] Версия в pyproject.toml обновлена
|
||||||
|
- [ ] Тег создан и отправлен
|
||||||
|
- [ ] CI/CD pipeline успешен
|
||||||
|
- [ ] Пакет опубликован
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Ручная публикация (если нужно)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Собрать
|
||||||
|
uv build
|
||||||
|
|
||||||
|
# Опубликовать
|
||||||
|
uv publish \
|
||||||
|
--publish-url https://git.much-data.ru/api/packages/claw/pypi \
|
||||||
|
--token YOUR_GITEA_TOKEN
|
||||||
|
```
|
||||||
281
docs/SEMANTIC_RELEASE.md
Normal file
281
docs/SEMANTIC_RELEASE.md
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
# Semantic Release — Автоматическое версионирование
|
||||||
|
|
||||||
|
## 📋 Обзор
|
||||||
|
|
||||||
|
**python-semantic-release** автоматически определяет версию на основе Conventional Commits.
|
||||||
|
|
||||||
|
**Как работает:**
|
||||||
|
```
|
||||||
|
Commit → Анализ сообщения → Определение типа → Bump версии → Тег → Релиз
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 CONVENTIONAL COMMITS
|
||||||
|
|
||||||
|
### **Формат коммита:**
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <description>
|
||||||
|
|
||||||
|
[optional body]
|
||||||
|
|
||||||
|
[optional footer]
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Типы коммитов и влияние на версию:**
|
||||||
|
|
||||||
|
| Тип | Влияние | Пример | Версия |
|
||||||
|
|-----|---------|--------|--------|
|
||||||
|
| `feat` | **MINOR** | `feat: add new API endpoint` | 0.1.0 → 0.2.0 |
|
||||||
|
| `fix` | **PATCH** | `fix: handle timeout errors` | 0.1.0 → 0.1.1 |
|
||||||
|
| `perf` | **PATCH** | `perf: optimize HTTP requests` | 0.1.0 → 0.1.1 |
|
||||||
|
| `feat` + BREAKING | **MAJOR** | `feat: change auth method` | 0.1.0 → 1.0.0 |
|
||||||
|
| `docs`, `style`, `refactor`, `test`, `chore`, `ci`, `build` | Нет | `docs: update README` | Без изменений |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 ПРИМЕРЫ КОММИТОВ
|
||||||
|
|
||||||
|
### **PATCH (багфиксы):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git commit -m "fix: handle 404 error in catalog API"
|
||||||
|
git commit -m "fix(auth): restore session from token correctly"
|
||||||
|
git commit -m "perf: reduce HTTP connection overhead"
|
||||||
|
```
|
||||||
|
|
||||||
|
### **MINOR (новая функциональность):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git commit -m "feat: add batch kwork details endpoint"
|
||||||
|
git commit -m "feat(projects): add get_payer_orders method"
|
||||||
|
git commit -m "feat: support HTTP/2 for faster requests"
|
||||||
|
```
|
||||||
|
|
||||||
|
### **MAJOR (ломающие изменения):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git commit -m "feat: redesign authentication flow
|
||||||
|
|
||||||
|
BREAKING CHANGE: login() now returns KworkClient instead of tuple
|
||||||
|
|
||||||
|
Migration:
|
||||||
|
Before: token, cookies = await login(user, pass)
|
||||||
|
After: client = await KworkClient.login(user, pass)
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Без влияния на версию:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git commit -m "docs: add usage examples to README"
|
||||||
|
git commit -m "test: increase coverage to 95%"
|
||||||
|
git commit -m "style: fix formatting with ruff"
|
||||||
|
git commit -m "refactor: simplify error handling"
|
||||||
|
git commit -m "chore: update dependencies"
|
||||||
|
git commit -m "ci: add Gitea Actions workflow"
|
||||||
|
git commit -m "build: configure UV build system"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 WORKFLOW
|
||||||
|
|
||||||
|
### **Разработка:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Делай коммиты по Conventional Commits
|
||||||
|
git commit -m "feat: add new endpoint"
|
||||||
|
git commit -m "fix: handle edge case"
|
||||||
|
git commit -m "docs: update documentation"
|
||||||
|
|
||||||
|
# Пуш в main
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
### **CI/CD (автоматически):**
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Тесты запускаются
|
||||||
|
2. Сборка пакета
|
||||||
|
3. Semantic Release анализирует коммиты
|
||||||
|
4. Определяет тип версии (MAJOR/MINOR/PATCH)
|
||||||
|
5. Обновляет версию в pyproject.toml и __init__.py
|
||||||
|
6. Создаёт Git тег
|
||||||
|
7. Генерирует CHANGELOG
|
||||||
|
8. Создаёт релиз в Gitea
|
||||||
|
9. Публикует пакет в Gitea Registry
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Результат:**
|
||||||
|
|
||||||
|
```
|
||||||
|
✅ v0.1.1 создан
|
||||||
|
✅ CHANGELOG.md обновлён
|
||||||
|
✅ Пакет опубликован
|
||||||
|
✅ Релиз в Gitea создан
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 РУЧНОЕ УПРАВЛЕНИЕ
|
||||||
|
|
||||||
|
### **Проверить следующую версию:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /root/kwork-api
|
||||||
|
uv run semantic-release version --print
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Сгенерировать CHANGELOG:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run semantic-release changelog
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Создать релиз вручную:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Bump версии
|
||||||
|
uv run semantic-release version --no-push
|
||||||
|
|
||||||
|
# Проверить что изменилось
|
||||||
|
git diff
|
||||||
|
|
||||||
|
# Запушить
|
||||||
|
git push origin main --tags
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 ПРИМЕР ИСТОРИИ ВЕРСИЙ
|
||||||
|
|
||||||
|
```
|
||||||
|
v0.1.0 (2026-03-23)
|
||||||
|
- Initial release
|
||||||
|
- Complete API client
|
||||||
|
- 100% documentation
|
||||||
|
|
||||||
|
v0.1.1 (2026-03-24)
|
||||||
|
- fix: handle timeout errors
|
||||||
|
- fix: restore session correctly
|
||||||
|
|
||||||
|
v0.2.0 (2026-03-25)
|
||||||
|
- feat: add batch endpoint
|
||||||
|
- feat: support HTTP/2
|
||||||
|
|
||||||
|
v0.2.1 (2026-03-26)
|
||||||
|
- perf: optimize requests
|
||||||
|
|
||||||
|
v1.0.0 (2026-03-27)
|
||||||
|
- feat: new authentication
|
||||||
|
- BREAKING CHANGE: API changed
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ КОНФИГУРАЦИЯ
|
||||||
|
|
||||||
|
**Файл:** `pyproject.toml`
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[tool.semantic_release]
|
||||||
|
version_toml = ["pyproject.toml:project.version"]
|
||||||
|
version_variables = ["src/kwork_api/__init__.py:__version__"]
|
||||||
|
branch = "main"
|
||||||
|
build_command = "uv build"
|
||||||
|
commit_parser = "angular"
|
||||||
|
tag_format = "v{version}"
|
||||||
|
|
||||||
|
[tool.semantic_release.commit_parser_options]
|
||||||
|
minor_tags = ["feat"]
|
||||||
|
patch_tags = ["fix", "perf"]
|
||||||
|
breaking_change_tags = ["feat"]
|
||||||
|
|
||||||
|
[tool.semantic_release.remote]
|
||||||
|
type = "gitea"
|
||||||
|
domain = "https://git.much-data.ru"
|
||||||
|
owner = "claw"
|
||||||
|
repo_name = "kwork-api"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚨 TROUBLESHOOTING
|
||||||
|
|
||||||
|
### **Ошибка: "No commits to release"**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Значит не было коммитов с типами feat/fix/perf
|
||||||
|
# Сделай коммит с правильным форматом
|
||||||
|
git commit -m "feat: add something new"
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Ошибка: "Gitea token invalid"**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверь токен
|
||||||
|
# Settings → Applications → Create new token
|
||||||
|
# Права: write:repository, write:package
|
||||||
|
# Обнови секрет в Gitea Actions
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Версия не обновляется**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверь конфигурацию
|
||||||
|
uv run semantic-release --version
|
||||||
|
|
||||||
|
# Проверь что __version__ есть в __init__.py
|
||||||
|
grep "__version__" src/kwork_api/__init__.py
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 ЧЕКЛИСТ ПЕРЕД ПУШЕМ
|
||||||
|
|
||||||
|
- [ ] Коммиты по Conventional Commits
|
||||||
|
- [ ] Тесты проходят
|
||||||
|
- [ ] CHANGELOG обновлён (автоматически)
|
||||||
|
- [ ] Версия в __init__.py совпадает
|
||||||
|
- [ ] GITEA_TOKEN настроен в секретах
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 BEST PRACTICES
|
||||||
|
|
||||||
|
### **✅ Делай:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Атомарные коммиты
|
||||||
|
git commit -m "feat: add user endpoint"
|
||||||
|
git commit -m "fix: handle 404 error"
|
||||||
|
|
||||||
|
# Понятные описания
|
||||||
|
git commit -m "fix: restore session from saved token"
|
||||||
|
|
||||||
|
# Scope для больших изменений
|
||||||
|
git commit -m "feat(auth): add OAuth2 support"
|
||||||
|
```
|
||||||
|
|
||||||
|
### **❌ Не делай:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Слишком общие
|
||||||
|
git commit -m "fix stuff"
|
||||||
|
|
||||||
|
# Несколько изменений в одном
|
||||||
|
git commit -m "feat: add user endpoint and fix auth and update docs"
|
||||||
|
|
||||||
|
# Не по формату
|
||||||
|
git commit -m "added new feature"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 ССЫЛКИ
|
||||||
|
|
||||||
|
- [python-semantic-release](https://python-semantic-release.readthedocs.io/)
|
||||||
|
- [Conventional Commits](https://www.conventionalcommits.org/)
|
||||||
|
- [Angular Commit Guidelines](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit)
|
||||||
@ -59,6 +59,7 @@ dev = [
|
|||||||
"mkdocs-material>=9.7.6",
|
"mkdocs-material>=9.7.6",
|
||||||
"mkdocstrings>=1.0.3",
|
"mkdocstrings>=1.0.3",
|
||||||
"mkdocstrings-python>=2.0.3",
|
"mkdocstrings-python>=2.0.3",
|
||||||
|
"python-semantic-release>=10.5.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
@ -103,4 +104,51 @@ exclude_lines = [
|
|||||||
"if TYPE_CHECKING:",
|
"if TYPE_CHECKING:",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Python Semantic Release Configuration
|
||||||
|
# ============================================
|
||||||
|
[tool.semantic_release]
|
||||||
|
version_toml = ["pyproject.toml:project.version"]
|
||||||
|
version_variables = [
|
||||||
|
"src/kwork_api/__init__.py:__version__",
|
||||||
|
]
|
||||||
|
branch = "main"
|
||||||
|
build_command = "uv build"
|
||||||
|
commit_parser = "angular"
|
||||||
|
upload_to_vcs_release = true
|
||||||
|
tag_format = "v{version}"
|
||||||
|
|
||||||
|
[tool.semantic_release.branches.main]
|
||||||
|
match = "main"
|
||||||
|
prerelease = false
|
||||||
|
|
||||||
|
[tool.semantic_release.commit_parser_options]
|
||||||
|
allowed_tags = ["build", "chore", "ci", "docs", "feat", "fix", "perf", "style", "refactor", "test"]
|
||||||
|
minor_tags = ["feat"]
|
||||||
|
patch_tags = ["fix", "perf"]
|
||||||
|
breaking_change_tags = ["feat"]
|
||||||
|
|
||||||
|
[tool.semantic_release.remote]
|
||||||
|
type = "gitea"
|
||||||
|
domain = "https://git.much-data.ru"
|
||||||
|
owner = "claw"
|
||||||
|
repo_name = "kwork-api"
|
||||||
|
token = { env = "GITEA_TOKEN" }
|
||||||
|
|
||||||
|
[tool.semantic_release.publish]
|
||||||
|
dist_glob_patterns = ["dist/*"]
|
||||||
|
upload_to_vcs_release = true
|
||||||
|
|
||||||
|
[tool.semantic_release.changelog]
|
||||||
|
template_dir = "templates"
|
||||||
|
changelog_file = "CHANGELOG.md"
|
||||||
|
exclude_commit_patterns = [
|
||||||
|
"chore\\(release\\):.*",
|
||||||
|
"ci\\(release\\):.*",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.semantic_release.changelog.environment]
|
||||||
|
trim_blocks = true
|
||||||
|
lstrip_blocks = true
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,27 @@ Example:
|
|||||||
|
|
||||||
from .client import KworkClient
|
from .client import KworkClient
|
||||||
from .errors import KworkError, KworkAuthError, KworkApiError
|
from .errors import KworkError, KworkAuthError, KworkApiError
|
||||||
|
from .models import (
|
||||||
|
ValidationResponse,
|
||||||
|
ValidationIssue,
|
||||||
|
Kwork,
|
||||||
|
KworkDetails,
|
||||||
|
Project,
|
||||||
|
CatalogResponse,
|
||||||
|
ProjectsResponse,
|
||||||
|
)
|
||||||
|
|
||||||
__version__ = "0.1.0"
|
__version__ = "0.1.0"
|
||||||
__all__ = ["KworkClient", "KworkError", "KworkAuthError", "KworkApiError"]
|
__all__ = [
|
||||||
|
"KworkClient",
|
||||||
|
"KworkError",
|
||||||
|
"KworkAuthError",
|
||||||
|
"KworkApiError",
|
||||||
|
"ValidationResponse",
|
||||||
|
"ValidationIssue",
|
||||||
|
"Kwork",
|
||||||
|
"KworkDetails",
|
||||||
|
"Project",
|
||||||
|
"CatalogResponse",
|
||||||
|
"ProjectsResponse",
|
||||||
|
]
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -37,6 +37,7 @@ from .models import (
|
|||||||
Review,
|
Review,
|
||||||
ReviewsResponse,
|
ReviewsResponse,
|
||||||
TimeZone,
|
TimeZone,
|
||||||
|
ValidationResponse,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -1178,6 +1179,48 @@ class KworkClient:
|
|||||||
"""
|
"""
|
||||||
return await self.client._request("POST", "/actor")
|
return await self.client._request("POST", "/actor")
|
||||||
|
|
||||||
|
async def validate_text(self, text: str, context: Optional[str] = None) -> ValidationResponse:
|
||||||
|
"""
|
||||||
|
Проверить текст на соответствие требованиям Kwork.
|
||||||
|
|
||||||
|
Эндпоинт валидации текста используется для проверки:
|
||||||
|
- Описаний кворков
|
||||||
|
- Текстов проектов
|
||||||
|
- Сообщений и отзывов
|
||||||
|
|
||||||
|
Находит потенциальные проблемы:
|
||||||
|
- Запрещённые слова и контакты
|
||||||
|
- Нарушения правил площадки
|
||||||
|
- Проблемы с форматированием
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: Текст для проверки.
|
||||||
|
context: Контекст использования (опционально).
|
||||||
|
Например: "kwork_description", "project_text", "message".
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ValidationResponse с результатами валидации.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
result = await client.other.validate_text(
|
||||||
|
"Отличный сервис, звоните +7-999-000-00-00"
|
||||||
|
)
|
||||||
|
if not result.is_valid:
|
||||||
|
print("Текст не прошёл валидацию:")
|
||||||
|
for issue in result.issues:
|
||||||
|
print(f" - {issue.message}")
|
||||||
|
"""
|
||||||
|
payload = {"text": text}
|
||||||
|
if context:
|
||||||
|
payload["context"] = context
|
||||||
|
|
||||||
|
data = await self.client._request(
|
||||||
|
"POST",
|
||||||
|
"/api/validation/checktext",
|
||||||
|
json=payload,
|
||||||
|
)
|
||||||
|
return ValidationResponse.model_validate(data)
|
||||||
|
|
||||||
# ========== API Property Accessors ==========
|
# ========== API Property Accessors ==========
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@ -410,3 +410,41 @@ class DataResponse(BaseModel):
|
|||||||
success: bool = True
|
success: bool = True
|
||||||
data: Optional[dict[str, Any]] = None
|
data: Optional[dict[str, Any]] = None
|
||||||
message: Optional[str] = None
|
message: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class ValidationIssue(BaseModel):
|
||||||
|
"""
|
||||||
|
Проблема, найденная при валидации текста.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
type: Тип проблемы: "error", "warning", "suggestion".
|
||||||
|
code: Код ошибки (например, "SPELLING", "GRAMMAR", "LENGTH").
|
||||||
|
message: Описание проблемы.
|
||||||
|
position: Позиция в тексте (если применимо).
|
||||||
|
suggestion: Предлагаемое исправление (если есть).
|
||||||
|
"""
|
||||||
|
type: str = "error"
|
||||||
|
code: str
|
||||||
|
message: str
|
||||||
|
position: Optional[int] = None
|
||||||
|
suggestion: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class ValidationResponse(BaseModel):
|
||||||
|
"""
|
||||||
|
Ответ API валидации текста.
|
||||||
|
|
||||||
|
Используется для эндпоинта /api/validation/checktext.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
success: Успешность валидации.
|
||||||
|
is_valid: Текст проходит валидацию (нет критических ошибок).
|
||||||
|
issues: Список найденных проблем.
|
||||||
|
score: Оценка качества текста (0-100, если доступна).
|
||||||
|
message: Дополнительное сообщение.
|
||||||
|
"""
|
||||||
|
success: bool = True
|
||||||
|
is_valid: bool = True
|
||||||
|
issues: list[ValidationIssue] = Field(default_factory=list)
|
||||||
|
score: Optional[int] = None
|
||||||
|
message: Optional[str] = None
|
||||||
|
|||||||
Binary file not shown.
@ -9,7 +9,7 @@ import respx
|
|||||||
from httpx import Response
|
from httpx import Response
|
||||||
|
|
||||||
from kwork_api import KworkClient, KworkAuthError, KworkApiError
|
from kwork_api import KworkClient, KworkAuthError, KworkApiError
|
||||||
from kwork_api.models import CatalogResponse, Kwork
|
from kwork_api.models import CatalogResponse, Kwork, ValidationResponse, ValidationIssue
|
||||||
|
|
||||||
|
|
||||||
class TestAuthentication:
|
class TestAuthentication:
|
||||||
@ -240,3 +240,93 @@ class TestContextManager:
|
|||||||
|
|
||||||
# Client should be closed after context
|
# Client should be closed after context
|
||||||
assert client._client is None or client._client.is_closed
|
assert client._client is None or client._client.is_closed
|
||||||
|
|
||||||
|
|
||||||
|
class TestValidationAPI:
|
||||||
|
"""Test text validation endpoint."""
|
||||||
|
|
||||||
|
@respx.mock
|
||||||
|
async def test_validate_text_success(self):
|
||||||
|
"""Test successful text validation."""
|
||||||
|
client = KworkClient(token="test")
|
||||||
|
|
||||||
|
mock_data = {
|
||||||
|
"success": True,
|
||||||
|
"is_valid": True,
|
||||||
|
"issues": [],
|
||||||
|
"score": 95,
|
||||||
|
}
|
||||||
|
|
||||||
|
respx.post(f"{client.base_url}/api/validation/checktext").mock(
|
||||||
|
return_value=Response(200, json=mock_data)
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await client.other.validate_text("Хороший текст для кворка")
|
||||||
|
|
||||||
|
assert isinstance(result, ValidationResponse)
|
||||||
|
assert result.success is True
|
||||||
|
assert result.is_valid is True
|
||||||
|
assert len(result.issues) == 0
|
||||||
|
assert result.score == 95
|
||||||
|
|
||||||
|
@respx.mock
|
||||||
|
async def test_validate_text_with_issues(self):
|
||||||
|
"""Test text validation with found issues."""
|
||||||
|
client = KworkClient(token="test")
|
||||||
|
|
||||||
|
mock_data = {
|
||||||
|
"success": True,
|
||||||
|
"is_valid": False,
|
||||||
|
"issues": [
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"code": "CONTACT_INFO",
|
||||||
|
"message": "Текст содержит контактную информацию",
|
||||||
|
"position": 25,
|
||||||
|
"suggestion": "Удалите номер телефона",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "warning",
|
||||||
|
"code": "LENGTH",
|
||||||
|
"message": "Текст слишком короткий",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"score": 45,
|
||||||
|
}
|
||||||
|
|
||||||
|
respx.post(f"{client.base_url}/api/validation/checktext").mock(
|
||||||
|
return_value=Response(200, json=mock_data)
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await client.other.validate_text(
|
||||||
|
"Звоните +7-999-000-00-00",
|
||||||
|
context="kwork_description",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.is_valid is False
|
||||||
|
assert len(result.issues) == 2
|
||||||
|
assert result.issues[0].code == "CONTACT_INFO"
|
||||||
|
assert result.issues[0].type == "error"
|
||||||
|
assert result.issues[1].type == "warning"
|
||||||
|
assert result.score == 45
|
||||||
|
|
||||||
|
@respx.mock
|
||||||
|
async def test_validate_text_empty(self):
|
||||||
|
"""Test validation of empty text."""
|
||||||
|
client = KworkClient(token="test")
|
||||||
|
|
||||||
|
mock_data = {
|
||||||
|
"success": False,
|
||||||
|
"is_valid": False,
|
||||||
|
"message": "Текст не может быть пустым",
|
||||||
|
"issues": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
respx.post(f"{client.base_url}/api/validation/checktext").mock(
|
||||||
|
return_value=Response(200, json=mock_data)
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await client.other.validate_text("")
|
||||||
|
|
||||||
|
assert result.success is False
|
||||||
|
assert result.message is not None
|
||||||
|
|||||||
172
uv.lock
generated
172
uv.lock
generated
@ -203,14 +203,26 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "click"
|
name = "click"
|
||||||
version = "8.3.1"
|
version = "8.1.8"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" },
|
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "click-option-group"
|
||||||
|
version = "0.5.9"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "click" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/ef/ff/d291d66595b30b83d1cb9e314b2c9be7cfc7327d4a0d40a15da2416ea97b/click_option_group-0.5.9.tar.gz", hash = "sha256:f94ed2bc4cf69052e0f29592bd1e771a1789bd7bfc482dd0bc482134aff95823", size = 22222, upload-time = "2025-10-09T09:38:01.474Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/75/45/54bb2d8d4138964a94bef6e9afe48b0be4705ba66ac442ae7d8a8dc4ffef/click_option_group-0.5.9-py3-none-any.whl", hash = "sha256:ad2599248bd373e2e19bec5407967c3eec1d0d4fc4a5e77b08a0481e75991080", size = 11553, upload-time = "2025-10-09T09:38:00.066Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -426,6 +438,15 @@ version = "0.11"
|
|||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/ce/5d6a3782b9f88097ce3e579265015db3372ae78d12f67629b863a9208c96/docstring_parser-0.11.tar.gz", hash = "sha256:93b3f8f481c7d24e37c5d9f30293c89e2933fa209421c8abd731dd3ef0715ecb", size = 22775, upload-time = "2021-09-30T07:44:10.288Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/a2/ce/5d6a3782b9f88097ce3e579265015db3372ae78d12f67629b863a9208c96/docstring_parser-0.11.tar.gz", hash = "sha256:93b3f8f481c7d24e37c5d9f30293c89e2933fa209421c8abd731dd3ef0715ecb", size = 22775, upload-time = "2021-09-30T07:44:10.288Z" }
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dotty-dict"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/6a/ab/88d67f02024700b48cd8232579ad1316aa9df2272c63049c27cc094229d6/dotty_dict-1.3.1.tar.gz", hash = "sha256:4b016e03b8ae265539757a53eba24b9bfda506fb94fbce0bee843c6f05541a15", size = 7699, upload-time = "2022-07-09T18:50:57.727Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1a/91/e0d457ee03ec33d79ee2cd8d212debb1bc21dfb99728ae35efdb5832dc22/dotty_dict-1.3.1-py3-none-any.whl", hash = "sha256:5022d234d9922f13aa711b4950372a06a6d64cb6d6db9ba43d0ba133ebfce31f", size = 7014, upload-time = "2022-07-09T18:50:55.058Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "exceptiongroup"
|
name = "exceptiongroup"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
@ -450,6 +471,30 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" },
|
{ url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gitdb"
|
||||||
|
version = "4.0.12"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "smmap" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gitpython"
|
||||||
|
version = "3.1.46"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "gitdb" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/df/b5/59d16470a1f0dfe8c793f9ef56fd3826093fc52b3bd96d6b9d6c26c7e27b/gitpython-3.1.46.tar.gz", hash = "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", size = 215371, upload-time = "2026-01-01T15:37:32.073Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620, upload-time = "2026-01-01T15:37:30.574Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "griffelib"
|
name = "griffelib"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@ -540,6 +585,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
|
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "importlib-resources"
|
||||||
|
version = "6.5.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iniconfig"
|
name = "iniconfig"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
@ -590,6 +644,7 @@ dev = [
|
|||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
{ name = "pytest-asyncio" },
|
{ name = "pytest-asyncio" },
|
||||||
{ name = "pytest-cov" },
|
{ name = "pytest-cov" },
|
||||||
|
{ name = "python-semantic-release" },
|
||||||
{ name = "respx" },
|
{ name = "respx" },
|
||||||
{ name = "ruff" },
|
{ name = "ruff" },
|
||||||
]
|
]
|
||||||
@ -617,6 +672,7 @@ dev = [
|
|||||||
{ name = "pytest", specifier = ">=8.0.0" },
|
{ name = "pytest", specifier = ">=8.0.0" },
|
||||||
{ name = "pytest-asyncio", specifier = ">=0.23.0" },
|
{ name = "pytest-asyncio", specifier = ">=0.23.0" },
|
||||||
{ name = "pytest-cov", specifier = ">=4.0.0" },
|
{ name = "pytest-cov", specifier = ">=4.0.0" },
|
||||||
|
{ name = "python-semantic-release", specifier = ">=10.5.3" },
|
||||||
{ name = "respx", specifier = ">=0.20.0" },
|
{ name = "respx", specifier = ">=0.20.0" },
|
||||||
{ name = "ruff", specifier = ">=0.3.0" },
|
{ name = "ruff", specifier = ">=0.3.0" },
|
||||||
]
|
]
|
||||||
@ -630,6 +686,18 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" },
|
{ url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "markdown-it-py"
|
||||||
|
version = "4.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "mdurl" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "markupsafe"
|
name = "markupsafe"
|
||||||
version = "3.0.3"
|
version = "3.0.3"
|
||||||
@ -715,6 +783,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
|
{ url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mdurl"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mergedeep"
|
name = "mergedeep"
|
||||||
version = "1.3.4"
|
version = "1.3.4"
|
||||||
@ -1162,6 +1239,43 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
|
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-gitlab"
|
||||||
|
version = "6.5.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "requests" },
|
||||||
|
{ name = "requests-toolbelt" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/9a/bd/b30f1d3b303cb5d3c72e2d57a847d699e8573cbdfd67ece5f1795e49da1c/python_gitlab-6.5.0.tar.gz", hash = "sha256:97553652d94b02de343e9ca92782239aa2b5f6594c5482331a9490d9d5e8737d", size = 400591, upload-time = "2025-10-17T21:40:02.89Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/34/bd/b0d440685fbcafee462bed793a74aea88541887c4c30556a55ac64914b8d/python_gitlab-6.5.0-py3-none-any.whl", hash = "sha256:494e1e8e5edd15286eaf7c286f3a06652688f1ee20a49e2a0218ddc5cc475e32", size = 144419, upload-time = "2025-10-17T21:40:01.233Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-semantic-release"
|
||||||
|
version = "10.5.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "click" },
|
||||||
|
{ name = "click-option-group" },
|
||||||
|
{ name = "deprecated" },
|
||||||
|
{ name = "dotty-dict" },
|
||||||
|
{ name = "gitpython" },
|
||||||
|
{ name = "importlib-resources" },
|
||||||
|
{ name = "jinja2" },
|
||||||
|
{ name = "pydantic" },
|
||||||
|
{ name = "python-gitlab" },
|
||||||
|
{ name = "requests" },
|
||||||
|
{ name = "rich" },
|
||||||
|
{ name = "shellingham" },
|
||||||
|
{ name = "tomlkit" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/19/3a/7332b822825ed0e902c6e950e0d1e90e8f666fd12eb27855d1c8b6677eff/python_semantic_release-10.5.3.tar.gz", hash = "sha256:de4da78635fa666e5774caaca2be32063cae72431eb75e2ac23b9f2dfd190785", size = 618034, upload-time = "2025-12-14T22:37:29.782Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6b/01/ada29a1215df601bded0a2efd3b6d53864a0a9e0a9ea52aeaebe14fd03fd/python_semantic_release-10.5.3-py3-none-any.whl", hash = "sha256:1be0e07c36fa1f1ec9da4f438c1f6bbd7bc10eb0d6ac0089b0643103708c2823", size = 152716, upload-time = "2025-12-14T22:37:28.089Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyyaml"
|
name = "pyyaml"
|
||||||
version = "6.0.3"
|
version = "6.0.3"
|
||||||
@ -1253,6 +1367,18 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
|
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "requests-toolbelt"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "requests" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "respx"
|
name = "respx"
|
||||||
version = "0.22.0"
|
version = "0.22.0"
|
||||||
@ -1265,6 +1391,19 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/8e/67/afbb0978d5399bc9ea200f1d4489a23c9a1dad4eee6376242b8182389c79/respx-0.22.0-py2.py3-none-any.whl", hash = "sha256:631128d4c9aba15e56903fb5f66fb1eff412ce28dd387ca3a81339e52dbd3ad0", size = 25127, upload-time = "2024-12-19T22:33:57.837Z" },
|
{ url = "https://files.pythonhosted.org/packages/8e/67/afbb0978d5399bc9ea200f1d4489a23c9a1dad4eee6376242b8182389c79/respx-0.22.0-py2.py3-none-any.whl", hash = "sha256:631128d4c9aba15e56903fb5f66fb1eff412ce28dd387ca3a81339e52dbd3ad0", size = 25127, upload-time = "2024-12-19T22:33:57.837Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rich"
|
||||||
|
version = "14.3.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "markdown-it-py" },
|
||||||
|
{ name = "pygments" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.15.7"
|
version = "0.15.7"
|
||||||
@ -1290,6 +1429,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/8f/e8/726643a3ea68c727da31570bde48c7a10f1aa60eddd628d94078fec586ff/ruff-0.15.7-py3-none-win_arm64.whl", hash = "sha256:18e8d73f1c3fdf27931497972250340f92e8c861722161a9caeb89a58ead6ed2", size = 11023304, upload-time = "2026-03-19T16:26:51.669Z" },
|
{ url = "https://files.pythonhosted.org/packages/8f/e8/726643a3ea68c727da31570bde48c7a10f1aa60eddd628d94078fec586ff/ruff-0.15.7-py3-none-win_arm64.whl", hash = "sha256:18e8d73f1c3fdf27931497972250340f92e8c861722161a9caeb89a58ead6ed2", size = 11023304, upload-time = "2026-03-19T16:26:51.669Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shellingham"
|
||||||
|
version = "1.5.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "six"
|
name = "six"
|
||||||
version = "1.17.0"
|
version = "1.17.0"
|
||||||
@ -1299,6 +1447,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smmap"
|
||||||
|
version = "5.0.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz", hash = "sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", size = 22506, upload-time = "2026-03-09T03:43:26.1Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390, upload-time = "2026-03-09T03:43:24.361Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "structlog"
|
name = "structlog"
|
||||||
version = "25.5.0"
|
version = "25.5.0"
|
||||||
@ -1374,6 +1531,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" },
|
{ url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tomlkit"
|
||||||
|
version = "0.13.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/cc/18/0bbf3884e9eaa38819ebe46a7bd25dcd56b67434402b66a58c4b8e552575/tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1", size = 185207, upload-time = "2025-06-05T07:13:44.947Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0", size = 38901, upload-time = "2025-06-05T07:13:43.546Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typeapi"
|
name = "typeapi"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user