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
5 changes: 2 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ jobs:
run: uv run --locked pytest
- name: Run pre-commit checks
run: uv run --locked pre-commit run --verbose --all-files --show-diff-on-failure
# FIXME: mypy is failing due to missing types-* packages
# - name: Run mypy
# run: uv run --locked mypy algorithms_keeper/ tests/
- name: Run ty
run: uv run --locked ty check
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

# Tests
.pytest_cache/
.mypy_cache/
__pycache__/

# Local stuff
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![CI](https://github.com/TheAlgorithms/algorithms-keeper/actions/workflows/main.yml/badge.svg)](https://github.com/TheAlgorithms/algorithms-keeper/actions/workflows/main.yml)
[![codecov](https://codecov.io/gh/TheAlgorithms/algorithms-keeper/branch/master/graph/badge.svg?token=QYAZ665UJL)](https://codecov.io/gh/TheAlgorithms/algorithms-keeper)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://docs.astral.sh/ruff/)
[![Checked with mypy](https://img.shields.io/static/v1?label=mypy&message=checked&color=2a6db2&labelColor=505050)](http://mypy-lang.org/)
[![ty](https://img.shields.io/badge/ty-2a6db2)](https://docs.astral.sh/ty/)

</div>

Expand Down Expand Up @@ -52,12 +52,13 @@ Some actions of the bot can be triggered using commands:
## Development

Install [uv](https://docs.astral.sh/uv/getting-started/installation/), then set up
the project and run tests from the repository root:
the project, run tests, and check types from the repository root:

```shell
uv sync
uv run pytest
uv run pre-commit run --all-files
uv run ty check
```

Ruff handles Python linting and formatting through the pre-commit hooks.
Expand Down
6 changes: 5 additions & 1 deletion algorithms_keeper/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ async def main(request: web.Request) -> web.Response:
# Give GitHub some time to reach internal consistency.
await asyncio.sleep(1)
if logger.isEnabledFor(logging.DEBUG):
callbacks = [func.__name__ for func in main_router.fetch(event)]
# Router callbacks can be callable instances without __name__.
callbacks = [
getattr(func, "__name__", type(func).__name__)
for func in main_router.fetch(event)
]
logger.debug("event=%s callbacks=%s", event_info, callbacks)
await main_router.dispatch(event, gh)
if gh.rate_limit is not None: # pragma: no cover
Expand Down
4 changes: 2 additions & 2 deletions algorithms_keeper/parser/rules/naming_convention.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def valid(self, name: str) -> bool:


class NamingConventionRule(lint_rule.ReviewLintRule):
METADATA_DEPENDENCIES = (QualifiedNameProvider,) # type: ignore
METADATA_DEPENDENCIES = (QualifiedNameProvider,)

VALID = [
Valid("type_hint: str"),
Expand Down Expand Up @@ -152,7 +152,7 @@ def visit_AnnAssign(self, node: cst.AnnAssign) -> None:
def visit_AssignTarget(self, node: cst.AssignTarget) -> None:
self._assigntarget_counter += 1

def leave_AssignTarget(self, node: cst.AssignTarget) -> None:
def leave_AssignTarget(self, original_node: cst.AssignTarget) -> None:
self._assigntarget_counter -= 1

def visit_ClassDef(self, node: cst.ClassDef) -> None:
Expand Down
24 changes: 7 additions & 17 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,24 @@ dependencies = [

[dependency-groups]
dev = [
"mypy",
"pre-commit",
"pytest==9.1.1",
"pytest-aiohttp==1.1.1",
"pytest-asyncio==1.4.0",
"pytest-cov==7.1.0",
"ty==0.0.78",
]

[tool.uv]
package = false

[tool.mypy]
ignore_missing_imports = true
warn_unused_configs = true
warn_unused_ignores = true
warn_redundant_casts = true
warn_return_any = true
check_untyped_defs = true
disallow_untyped_defs = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_subclassing_any = true
no_implicit_optional = true
[tool.ty.src]
include = ["algorithms_keeper", "tests"]
# Parser fixtures intentionally omit required annotations.
exclude = ["tests/data"]

[[tool.mypy.overrides]]
module = "tests.data.*"
disallow_untyped_defs = false
check_untyped_defs = false
[tool.ty.terminal]
error-on-warning = true

[tool.ruff]
line-length = 88
Expand Down
20 changes: 14 additions & 6 deletions tests/test_main.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from __future__ import annotations

from typing import TYPE_CHECKING

import pytest
import pytest_asyncio
from aiohttp import web
Expand All @@ -6,9 +10,13 @@

from .utils import number

if TYPE_CHECKING:
from aiohttp.test_utils import TestClient
from pytest_aiohttp.plugin import AiohttpClient


@pytest_asyncio.fixture()
async def client(aiohttp_client): # type: ignore
async def client(aiohttp_client: AiohttpClient) -> TestClient:
app = web.Application()
app.router.add_get("/", main.index)
app.router.add_get("/health", main.health)
Expand All @@ -17,7 +25,7 @@ async def client(aiohttp_client): # type: ignore


@pytest.mark.asyncio
async def test_ping(client): # type: ignore
async def test_ping(client: TestClient) -> None:
headers = {"X-GitHub-Event": "ping", "X-GitHub-Delivery": "1234"}
data = {"zen": "testing is good"}
response = await client.post("/", headers=headers, json=data)
Expand All @@ -26,15 +34,15 @@ async def test_ping(client): # type: ignore


@pytest.mark.asyncio
async def test_failure(client): # type: ignore
async def test_failure(client: TestClient) -> None:
# Even in the face of an exception, the server should not crash.
# Missing key headers.
response = await client.post("/", headers={})
assert response.status == 500


@pytest.mark.asyncio
async def test_success(client): # type: ignore
async def test_success(client: TestClient) -> None:
headers = {"X-GitHub-Event": "project", "X-GitHub-Delivery": "1234"}
# Sending a payload that shouldn't trigger any networking, but no errors
# either.
Expand All @@ -44,15 +52,15 @@ async def test_success(client): # type: ignore


@pytest.mark.asyncio
async def test_index(client): # type: ignore
async def test_index(client: TestClient) -> None:
response = await client.get("/")
assert response.status == 200
assert response.headers["content-type"] == "text/html"
assert "algorithms-keeper" in (await response.text())


@pytest.mark.asyncio
async def test_health(client): # type: ignore
async def test_health(client: TestClient) -> None:
response = await client.get("/health")
assert response.status == 200
assert await response.text() == "OK"
2 changes: 1 addition & 1 deletion tests/test_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def test_rules(
"\n".join(str(e) for e in reports),
)

report = reports[0] # type: ignore
report = reports[0]

if test_case.range is not None:
assert test_case.range == report.range
Expand Down
Loading
Loading