Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/code-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
python-version: '3.12'
- uses: astral-sh/setup-uv@v5
with:
version: "0.5.31"
version: "0.6.13"
- uses: actions/cache@v4
id: cache
with:
Expand All @@ -29,7 +29,7 @@ jobs:
name: Run tests
strategy:
matrix:
python-version: [ "3.10", "3.11", "3.12" ]
python-version: [ "3.10", "3.13" ]
pydantic-version: [ "pydantic-v1", "pydantic-v2" ]
fail-fast: false
steps:
Expand Down
21 changes: 21 additions & 0 deletions .github/workflows/release_pypi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Release

on:
push:
tags:
# Publish on any tag starting with a `v`, e.g. v1.2.3
- v*

jobs:
pypi:
name: Publish to PyPI
runs-on: ubuntu-latest
environment:
name: release
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- run: uv build
- run: uv publish --trusted-publishing always
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ya_tracker_client/py.typed
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Async Yandex Tracker Client based on aiohttp and pydantic

[![Python](https://img.shields.io/badge/python-^3.10-blue)](https://www.python.org/)
[![Python](https://img.shields.io/badge/python-3.10_|_3.11_|_3.12_|_3.13-blue)](https://www.python.org/)
[![Code linter: ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v1.json)](https://github.com/charliermarsh/ruff)
[![Linters](https://github.com/danfimov/ya_tracker_client/actions/workflows/code-check.yml/badge.svg)](https://github.com/danfimov/ya_tracker_client/actions/workflows/code-check.yml)

Expand Down Expand Up @@ -52,16 +52,16 @@ async def main() -> None:
organisation_id=API_ORGANISATION_ID,
oauth_token=API_TOKEN,
)

# create issue
new_issue = await client.create_issue('New issue', 'TRACKER-QUEUE')

# get issue
issue = await client.get_issue('KEY-1')

# update issue (just pass kwargs)
issue = await client.edit_issue('KEY-1', description='Hello World')

# don't forget to close tracker on app shutdown
await client.stop()

Expand Down
4 changes: 1 addition & 3 deletions examples/get_issue.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import os
from asyncio import run
from pprint import pprint

from dotenv import load_dotenv

Expand All @@ -23,8 +22,7 @@ async def main() -> None:
api_host=API_HOST,
)

issue = await client.get_issue('XYZ-76')
pprint(issue.dict())
await client.get_issue('XYZ-76')

await client.stop()

Expand Down
15 changes: 8 additions & 7 deletions examples/get_test_entities.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
import os
from asyncio import run

Expand All @@ -6,6 +7,9 @@
from ya_tracker_client import YaTrackerClient


logger = logging.getLogger(__name__)


load_dotenv()
# from registered application at Yandex OAuth - https://oauth.yandex.ru/
API_TOKEN = os.getenv('API_TOKEN')
Expand All @@ -15,14 +19,14 @@

async def main() -> None:
if not API_ORGANISATION_ID:
raise RuntimeError('API_ORGANISATION_ID must be set')
msg = 'API_ORGANISATION_ID must be set'
raise RuntimeError(msg)

client = YaTrackerClient(
organisation_id=API_ORGANISATION_ID,
oauth_token=API_TOKEN,
)


try:
# requests for tests
me = await client.get_myself()
Expand Down Expand Up @@ -51,11 +55,8 @@ async def main() -> None:
await client.find_issues()
await client.get_autoactions('TEST')
await client.get_triggers('TEST')
except Exception as e:
print('Test failed')
print(e)
else:
print('Test passed')
except Exception:
logger.exception('Error occurred')

await client.stop()

Expand Down
78 changes: 64 additions & 14 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "ya_tracker_client"
version = "0.0.3"
version = "0.1.0"
description = "Async Yandex Tracker Client"
authors = [
{ name = "Дмитрий Анфимов", email = "work@danfimov.ru" },
Expand All @@ -14,22 +14,24 @@ keywords = [
"async",
]
classifiers = [
"Development Status :: 3 - Alpha",
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Framework :: AsyncIO",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: CPython",
"Topic :: Software Development :: Libraries :: Application Frameworks",
"Typing :: Typed",
]
readme = "README.md"
requires-python=">=3.10,<=3.13"
requires-python=">=3.10,<=3.14"
dependencies = [
"aiohttp>=3.8.5",
"pydantic>= 1.10.12",
"pydantic>=1.10.21",
"certifi>=2023.7.22",
"requests>=2.31.0",
]
Expand All @@ -40,15 +42,15 @@ dependencies = [

[dependency-groups]
dev = [
"pytest>=7.4.2",
"coverage>=7.3.1",
"pytest-asyncio>=0.21.1",
"pytest-cov>=4.1.0",
"ruff>=0.9.7",
"polyfactory>=2.8.2",
"pytest>=8.3.5",
"coverage>=7.8.0",
"pytest-asyncio>=0.26.0",
"pytest-cov>=6.1.1",
"ruff>=0.11.4",
"polyfactory>=2.20.0",
"gevent>=23.9.1",
"pre-commit>=3.4.0",
"python-dotenv>=1.0.0",
"pre-commit>=4.2.0",
"python-dotenv>=v1.1.0",
]

[build-system]
Expand Down Expand Up @@ -95,8 +97,56 @@ line-length = 120
target-version = "py312"

[tool.ruff.lint]
select = ["W", "E", "F", "Q", "B", "I", "N", "ASYNC", "G", "RUF", "COM", "C90"]
ignore = []
select = ["ALL"]
ignore = [
"PLR0913", # Too many arguments in function definition
"A005", # Module `queue` shadows a Python standard-library module

# Missing docstrings
"D100", # in public module
"D104", # in public package
"D103", # in public functions
"D101", # in public class
"D102", # in public method
"D105", # in magic method
"D107", # in __init__

# Conflicted rules
"D203", # with D211
"D212", # with D213

# TODO: Annotations should be fixed in public methods
"ANN001",
"ANN401",
"ANN201",
"ANN202",
"ANN003",

# todo comments
"TD003",
"TD002",
"FIX002",

# strange rule about bool arguments
"FBT001",
"FBT002",
]

[tool.ruff.lint.per-file-ignores]
"tests/*" = [
"INP001", # missing __init__.py
"S101", # assert usage
"S106", # possible hardcoded password
"S311", # standard pseudo-random generators are not suitable for cryptographic purposes
"ARG002", # unused method argument (triggered by fixtures)
]
"examples/*" = [
"BLE001",
"INP001",
]

[tool.ruff.format]
quote-style = "single"

[tool.ruff.lint.flake8-quotes]
docstring-quotes = "double"
Expand Down
6 changes: 6 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from polyfactory import pytest_plugin

from tests.fixtures.issue import IssueFactory


pytest_plugin.register_fixture(factory=IssueFactory, name='issue_factory')
8 changes: 3 additions & 5 deletions tests/fixtures/issue.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
from polyfactory.factories.pydantic_factory import ModelFactory
from polyfactory.pytest_plugin import register_fixture
from polyfactory.factories import pydantic_factory

from ya_tracker_client.domain.entities.issue import Issue


@register_fixture
class IssueFactory(ModelFactory[Issue]):
__model__ = Issue
class IssueFactory(pydantic_factory.ModelFactory[Issue]):
pass
6 changes: 3 additions & 3 deletions tests/test_domain/test_client/test_initialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ def get_client_for_test_initialization(


@pytest.mark.parametrize(
'organisation_id, header_name',
(
('organisation_id', 'header_name'),
[
(randint(1, 1000), 'X-Org-Id'),
(str(randint(1, 1000)), 'X-Org-Id'),
('test_organisation_id', 'X-Cloud-Org-Id'),
),
],
)
def test_init__when_organisation_id_passed__then_use_specific_header_name(organisation_id, header_name) -> None:
client = get_client_for_test_initialization(
Expand Down
22 changes: 8 additions & 14 deletions tests/test_domain/test_client/test_request.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from http import HTTPStatus
from logging import getLogger
from random import randint
from typing import TYPE_CHECKING, Any, Type
from typing import Any

import pytest

Expand All @@ -15,10 +15,6 @@
)


if TYPE_CHECKING:
pass


logger = getLogger(__name__)


Expand Down Expand Up @@ -48,26 +44,24 @@ def create_client_for_test_request_status(status_code: int) -> ClientForTestRequ


class TestCheckStatus:
"""
Test with all statuses from documentation: https://cloud.yandex.com/en/docs/tracker/error-codes
"""
"""Test with all statuses from documentation: https://cloud.yandex.com/en/docs/tracker/error-codes."""

@pytest.mark.parametrize(
'status_code',
(
[
HTTPStatus.OK,
HTTPStatus.CREATED,
HTTPStatus.NO_CONTENT,
),
],
)
async def test_request__when_status_ok__then_not_raise_error(self, status_code: int) -> None:
client = create_client_for_test_request_status(status_code)
response_body = await client.request('GET', '/test_uri')
assert response_body == b'Test response body'

@pytest.mark.parametrize(
'status_code, error_type',
(
('status_code', 'error_type'),
[
(HTTPStatus.BAD_REQUEST, ClientError),
(HTTPStatus.UNAUTHORIZED, ClientAuthError),
(HTTPStatus.FORBIDDEN, ClientSufficientRightsError),
Expand All @@ -76,12 +70,12 @@ async def test_request__when_status_ok__then_not_raise_error(self, status_code:
(HTTPStatus.PRECONDITION_FAILED, ClientObjectConflictError),
(HTTPStatus.UNPROCESSABLE_ENTITY, ClientError),
(HTTPStatus.PRECONDITION_REQUIRED, ClientError),
),
],
)
async def test_request__when_status_not_ok__then_raise_specific_error(
self,
status_code: int,
error_type: Type[ClientError],
error_type: type[ClientError],
) -> None:
client = create_client_for_test_request_status(status_code)
if error_type == ClientError: # always raise ClientException with context
Expand Down
Loading